Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Creating dynamically named CGIs

by ryan (Pilgrim)
on Feb 13, 2002 at 05:42 UTC ( [id://145083]=perlquestion: print w/replies, xml ) Need Help??

ryan has asked for the wisdom of the Perl Monks concerning the following question:

This may not be strictly Perl, but I am stuck, and sorry if the title is a little ambiguous. I'm a little unsure how to word this whole cry for help.

I have a site that allows people to download data files stored on the server after authenticating, however the authenticating for reason I will not go into can't be done by Apache, so it is up to the CGIs.

Originally I achieved what I wanted to do by naming one of my CGI files data.zip, such that a call to data.zip?file=somearchive.zip would authenticate the user then output the contents of the requested file to the browser with the appropriate "Content-type: application/zip\n\n" header, but the limitation was the file was always called data.zip at the client end.

I am wanting to make the name of this dynamically generated zip file to be the name of the actual file stored on the server, because the user will download a lot of files in a short space of time on the server, so having unique names is very desirable. I don't want to use a hard link to it for the forementioned security reasons.

Does anyone know if this can be done by either creating some form of dynamically named CGI arrangement or some kind of widely compatible HTML refresh header magic?

Thanks for your time.

Ryan

Replies are listed 'Best First'.
Re: Creating dynamically named CGIs
by screamingeagle (Curate) on Feb 13, 2002 at 06:06 UTC
    something on these lines :
    chdir($filepath); my $filesize = -s $filename; print "Content-disposition: attachment;filename=$filename\n"; print "Content-Length: $filesize\n"; print "Content-Type: application/octet-stream\n\n"; open(READ,$filename) || die; while (<READ>) { print; } close(READ);
    where filepath is the folder in which u store all the files which the users want to download (e.g. "/tmp") and the filename is the value passed along in the URL (or by the POST method)
    hth
      Thanks for the replies everyone, so far I have got it working to my satisfaction, but the browser has other ideas, I'm using CGI::Application so the sub in question (with hard coded parameters) is below:
      sub do_download { my $self = shift; my $post = $self->query(); my $fname = "/storage/surtron/cda/clients/ryan/new/surtron-020 +130-01.zip"; my $fsize = -s $fname; $self->header_props( -type => "application/octet-stream", -attachment => "blahblah2123.zip", -Content_length => $fsize); open(READ,$fname) || die; my @fcontent = <READ>; close(READ); return "@fcontent"; }
      The above code correctly produces the following output
      Content-Disposition: attachment; filename="blahblah2123.zip" content-length: 139124 Content-Type: application/octet-stream PK {rest of zip file}
      Evidently the browser is not honouring the Disposition header, I'm testing it will IE 6.0.2600.0000. Any further ideas?

      Update:
      There is a MS support article on this for IE 5.5: FIX: "Content-Disposition: Attachment" Fails for Known Content Types (Q267991) HERE

      I just went for a drive to test IE 5.5 with the fix, it recognises it needs to download, then pops up 2 prompts, one for the name of the CGI and one for the name passed in the Disposition header, then it saves the file in some unknown format which winzip doesn't like. Hmmm is there any hope?

      Could the code I have provided possibly mangle the data in any way so as to cause it to be an invalid zip file? When I try to recover the zip file I can recover the first 2 files out of the 5 in the archive.

      More Updating:
      I just changed the code to a crude:
      my $filecat = `cat /blah/blah/whatever`; return $filecat;
      and now it all works :-)
Re: Creating dynamically named CGIs
by dreadpiratepeter (Priest) on Feb 13, 2002 at 05:58 UTC
    Try something like this when generating the header.
    $r->header_out( 'Content-Disposition' => "attachment; filename=$save_a +s" );
    Haven't tested it, your results may vary.

    -pete
    Entropy is not what is used to be.
Re: Creating dynamically named CGIs
by tstock (Curate) on Feb 13, 2002 at 06:23 UTC
    I've used a technique that works with apache on Linux and BSD systems and older browsers that don't understant content-disposition headers. I name my CGI without an extention and call it in this fashion:

    http://www.domain.com/cgibindir/mycgi/arg1/arg2/filename.ext

    Apache will execute the right script even though /arg1/arg2/filename.ext doesn't exist on the file system. You then parse $ENV{PATH_INFO} for the arguments you need. This fools pretty much every web browser to save the right filename without the need for 'attachment' headers that might only work on some (but most) browsers.

    Tiago
Re: Creating dynamically named CGIs
by theguvnor (Chaplain) on Feb 13, 2002 at 11:46 UTC
    Full marks to the other posts, but the advice about using "Content-Disposition" headers neglects Netscape 4.x browsers (at least in my experience). To get around this difference in browsers, I have used the following snippet of code, with credit going to Merlyn's web techniques column for the path-info-in-query-string trick:

    # determine the http header to send the browser client: my $user_agent = $q->user_agent; # CGI query object if ($user_agent =~ m/MSIE/i) { # for IE users, the following wo +rks: print "Content-Disposition: attachment; filename = $name\n\n"; } else { # for Netscape clients, need to +do a 2-pass trick here... if ($q->path_info() ) { # 2nd pass: collect the extra path info which Netscape client # will use to name the downloaded file: print $q->header('application/x-octet-stream'); } else { # 1st pass: add the name info to the http header path info var # and redirect back upon itself for the 2nd pass: $q->path_info("/$name"); print $q->redirect($q->self_url()); exit; } }

    Hope this helps

    ..Guv

Re: Creating dynamically named CGIs
by ryan (Pilgrim) on Feb 13, 2002 at 05:55 UTC
    Follow on:
    I just had the thought that using $ENV{PATH_INFO} might enable me to fool the browser by passing the name of the ZIP file as the last paramater to the CGI such as http://somewhere.com/cgi-bin/download/somearchive.zip but I can't see this working if I wanted to POST the name of the file to the CGI, am I thinking straight?
Re: Creating dynamically named CGIs
by dash2 (Hermit) on Feb 13, 2002 at 14:32 UTC
    Danger Will Robinson!

    Make jolly sure that you are checking the contents of param('file'). In particular, make sure that data.cgi?file=../../../../../../etc/passwd does not output bad things to the user.

    You may have done this already, or you may trust your authenticated users, but it never hurts to be careful... I have just been bitten in the ass by this, so I speak from bitter (in)experience!

    dave hj~

      Yes, most definately, thanks for the reinforcement.

      Thanks to all who replied, most helpful, keeping to my usual style I normally post what I ended up doing and why:

      I've gone with the $ENV{PATH_INFO} idea, because the header stuff caused double prompts and general mess in IE, worked perfectly in Opera though.

      However, I'm not actually using the $ENV{PATH_INFO} contents, I'm just calling the CGI in that style and passing the required info via post such as below, 'run' is the CGI and 'cda' is an ExecCGI-d directory:
      <form method="POST" action="/cda/run/surtron-020130-03.zip"> <INPUT TYPE="image" src="/images/icon-zip.gif" ALT="Download surtron-0 +20130-03.zip" BORDER="0"> <input type="hidden" name="mode" value="download"> <input type="hidden" name="file" value="surtron-020130-03.zip"> </form>
      This is almost a shortcut I guess, looks neat from the client side and works well in all cases. As long as I check for tainted posts it seems to do what I want.

      Update:
      I have now changed the above so that the filename hidden field passes a database id instead of the actual filename to make it less h4x0rable.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://145083]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (2)
As of 2024-04-26 02:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found