4

I have this website, where if the user submits a form, a python script is executed through a php page, and the python script creates a zip file and should offer it to the user for download through a link. The file could be huge (a few GB).

As I'm working on a university server, I'm strictly bound to their server rules and capabilities. Here's the problem:

The website is stored in /data/mywebsite, which has limited diskspace. Of course this is owned by www-data as it's mainly accessible by my Apache server.

I'm offered 1 TB storage in /experimentdata/, which is ONLY ACCESSIBLE by a single, specific user, say theuser. This is because this folder is a samba mount that can be accessed by a single and specific user-id.

To create the file in /experimentdata, I use a sudo -u theuser command that will create the file /experimentdata/downloadme.zip as user theuser. Now my problem is: How can I offer this file through a link for download through Apache?

I thought of using a symbolic link that I put in, e.g., /data/mywebsite/download/downloadme.zip. The problem with that is that the user www-data has absolutely no permission to read the file!

How can I let the user download the file /experimentdata/downloadme.zip with the user www-data through the user theuser?

I would like to explicitly say that involving sudo -u theuser is absolutely fine. But I don't know how to make a link out of that to somewhere outside my website folder.

PS: If you require any additional information, please ask.

2
  • If you can not change (or get somebody else to change the apache configuration) you will have to write a script which uses sudo and reads the necessary file and return its. Nov 7, 2015 at 20:15
  • @UlrichDangel Thank you for your response. What apache configuration should I change? And what kind of script should I write? Please provide more details so that I can choose which of these options can solve the problem. Nov 7, 2015 at 20:17

1 Answer 1

2

I think the thing to do is have your php/python return the data directly instead of apache. Your code can do the same thing that apache does. In my experience this is much better than opening up another directory and/or using sudo, or changing file permissions for apache, etc.

If the program produces the large file faster than the internet connection, then you can stream the data directly from your program, which eliminates the extra data file and the code to manage it and the mechanisms to remember it.

This answer on Stack Overflow shows how the code works in php. https://stackoverflow.com/a/4357904/5484716.

For programs that will be called this way, eliminate all stderr stream output and make sure the return code from your python process accurately reflects the success or failure of the process.

The examples below show the popen() calls you would use in the above example scenario from stackoverflow. I've prepended exec 2>/dev/null; to the shell command. This ensures that no output goes to stdandard error, even from the shell itself, because having data coming on both stderr and stdout can be a source of deadlocks with popen().

If you want to download the disk file to your user:

$fp = popen('exec 2>/dev/null; sudo -u theuser cat yourfile.zip', 'r');

If you want to download the data from the active process:

$fp = popen('exec 2>/dev/null; sudo yourpythonscript arg argN', 'r');

These command lines are shell commands and need to be quoted appropriately for shell meta characters.

In the second method, the server would begin sending the data immediately. When the user successfully submits the form, they immediately see a "save as" dialog from their browser. As soon as the user selects the output file, your php script transmits the data directly across the wire and into the remote file.

The python script should print only the zip data on standard output, and return an exit code that accurately represents the success or failure of the zip process. In python the script should write the output on sys.stdout, for example zf = ZipFile(sys.stdout, ....

It is critical to call pclose() and check the return value, because that will be the only way you know if the zip succeeded or not. If pclose() returns anything other than 0, something is wrong.

How the file is handled by the client depends on the settings of these response headers and others: content-type:, content-encoding:, and content-disposition: See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html, look at the response-header and the entity-header information.

8
  • Thank you for the insight. I would appreciate it if you could provide more details on how to do this. My python script uses zipfile to produce a zip file. I use shell_exec() to execute the script. How can I pass such a thing from python to php? Nov 7, 2015 at 22:56
  • 1
    In the first case, when the file is already on disk, I'd look at this answer: For sending a static file from disk: stackoverflow.com/a/1465591/5484716. If you want to stream the data out directly from the zip process, look at answers like these stackoverflow.com/a/4357904/5484716. (This is getting a little outside the scope of U&L.)
    – RobertL
    Nov 8, 2015 at 0:26
  • The readfile() solution would work, except that www-data has no permission to read the file. Is it possible to readfile() as another user? Nov 8, 2015 at 0:29
  • You could write a wrapper script (e.g. that performs the zip to stdout or just cats an existing zip file in the share) and then configure sudo to allow www-data to run that script as the user that owns the samba share.
    – cas
    Nov 8, 2015 at 0:33
  • Yes, sorry Quantum, I missed a little bit of the question. Please see my updated answer. I think either way, whether reading file off disk or live streaming, you will use the second method, probably with popen. The popen examples are very similar to the recommendations in @cas's comment.
    – RobertL
    Nov 8, 2015 at 0:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.