POST file to script

How does a browser upload a file? If this is the wrong area please let me know. But does anyone know the exact why a browser sends an upload POST to perl?
microfleetAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

guadalupeCommented:
Do you want to know how to get a hold of the info from inside a perl script or do you want the technical information of how the browser send it the the server...?
0
binkzzCommented:
If you upload a file in the Internet Explorer 4+ <input type=file> item (or netscape 3+ I believe), you can read the uploaded file through the STDIN.

T
0
guadalupeCommented:
This will work as long as you have access to the dir "tmp" if not just edit to a dir you have access to.  And you will need the module CGI.pm


##HTML for (upload)
{

}
<form ENCTYPE="multipart/form-data" action="/cgi-bin/upload.pl" Method=POST>
Attach:<input name="file" type="file">

<INPUT TYPE=SUBMIT>
</form>


#perl for upload
#!/usr/local/bin/perl

use CGI;

$query=new CGI;

$file_path = $query->param('file');


print $file_path,"\n";

$file_path =~ m!.*?!/?([^/]+);

$upload_file = $1;

print "Content-type: text/html\n\n";

`rm /tmp/$upload_file`;
open(MYFILE,"> /tmp/$upload_file") || die $!;
binmode MYFILE;
while($bytesread=read($file_path,$data,1024)){
  $size+=$bytesread;
  print MYFILE $data;
}
close(MYFILE);
close($file_path);

`chmod 777 /tmp/$upload_file`;
print $size;

print "Done Reading";


0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

microfleetAuthor Commented:
guadalupe , I know how to write the HTML code for a browser to upload but that isnt what I need to know. I want to know what the browser is actually POSTing; that is, what does the POST string look like? For example, the POST string for a data POST is something like this:
?name=anon&username=none&password=pass

So looking at that, what string is the browser sending to the perl script that makes the script undestand it is receiving a file and not data to process?
0
guadalupeCommented:
Read the cgi a little more carefully.  What you wrote (?name=anon&username=none&password=pass) does not apply to a post but to a get.  The segment beyond the ? becomes the environment variable QUERY_STRING in a post method everything comes into perl via the STDIN  you can get hold of all this info via the following routine which is quite standard:

sub parseInput()
{
      #Parse form variables
      if ($ENV{'REQUEST_METHOD'} eq "POST")
      {
            read(STDIN, $temp, $ENV{'CONTENT_LENGTH'});
      }

      elsif ($ENV{'REQUEST_METHOD'} eq "GET" )
      {
            $temp = $ENV{'QUERY_STRING'};
      }

      @pairs=split(/&/,$temp);
      
      foreach $item(@pairs)
      {
            ($key,$content)=split (/=/,$item,2);
            $content=~tr/+/ /;
            $content=~ s/%(..)/pack("c",hex($1))/ge;
            $fields{$key}=$content;
      }

}

You will then have all you form fields (including any that might have been of type file) in a hash called %fields where the key will be the name of the field.  Ex :

<input name="file" type="file">

This fields data (the local path of the uploaded file) will be available in $fields{'file'}.

You can then use this variable instead of that created by the cgi.pm to access the file and read out the data...

Does that answer your question?

Knowing this  
0
microfleetAuthor Commented:
guadalupe, I know you are trying to help but you are getting completely off the subject. I don't want to know how to upload. I'm not asking how to upload to a perl script or how the perl script reads, I am simply trying to figure out what the string looks like when a browser submits a file to be uploaded to a script. For instance, say I was to create a browser and I come to the point of needing to write the program code that allows someone to SUBMIT a file for upload. What then would i SUBMIT? What is the browser submitting? I know how to pull the information once it gets to the perl script but what does the submission line look like from the browser point of view?
0
guadalupeCommented:
Sorry you got me.  I have no idea.
0
smorrowCommented:
If you were to be implementing a browser I would assume you would have to be aware of the headers used when sending information.

I am guessing here but I think that it may be a little diferent from a string of parameters since the browser will recogise the the input type=file is being used and in some way attach the file similar to an attachment on email.

So suppose a html page like this is supplied be a server: -

<FORM ACTION="http://server.com/cgi/script.cgi" ENCTYPE="multipart/form-data"       METHOD=POST>
Name: <INPUT TYPE=TEXT NAME=field1>
File: <INPUT TYPE=FILE NAME=filename1>
</FORM>


the user may type "John Smyth" in the name field, and selects the "file.txt" for the file field.

The information sent back by the client may look like the following:

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="filename1"; filename="file.txt"
Content-Type: text/plain

.... contents of file.txt ...
--AaB03x--

0
microfleetAuthor Commented:
smorrow, change your comment to an answer. This was actually very helpful though still the technical aspects elude me. But you did answer the question so the points are yours. However, I would like to ask if you know of a good HTTP control that is priced less than $100. I have been fighting with the Mabry control because it doesn't seem to want to upload files. I know it has to be able to post a file some way. If you have any comments on that I would certainly appreciate it.
0
abelCommented:
Hi Microfleet,

Although you are already saying that this discussion should be closed upon the answer given by Smorrow I do want to give this a try, because the question you are asking is about the technical specifics of such a POST command and that's not (yet) answered here.
Well, as a rough guide, I can tell you this:

When the user hits submit, the browser looks up the file and checks the data kind. It doesn't really have to do that and I'm not sure if they, but it should be done for determining the encoding. Next, depending on the type of file, the contents is encoded using Base64 encoding for binary/non-ascii data and with ascii data should be encoded using the Q (Quoted-Printable) method (ie, each 8-bit character is represented as "=" + hexadecimal value, see refs below).
Then, after the encoding, it's send to the server with a header saying HTTP/1.1, method=POST, Content-type=multipart/form-data and a messagebody in the style of MIME/multipart. This last thing is of course what you'll be looking for, aren't you? Well, that looks exactly like the cut 'n' paste part of Smorrows message, which is from one of the refs below :)

Of course, if you want every gory detail, you should look up the references below:

HTTP:
http://www.w3.org/Protocols/rfc2616/rfc2616.html

A note on the multipart types in HTTP:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.2

The RFC that gives al the details about file-upload from a client:
http://www.ietf.org/rfc/rfc1867.txt

The RFC that gives all the details of encoding of non-ASCII
http://www.ietf.org/rfc/rfc1522.txt

The RFC that gives all the details of MIME and Base64:
http://www.ietf.org/rfc/rfc1521.txt

I hope this is of any help to you.

Greetz,
Abel
0
microfleetAuthor Commented:
abel, wow! I've printed out the entire set of text documents you posted. I am certainly glad you intervened. I would like to ask one other thing. I have a total of 386 points. You can have them all if you can get me through a problem I have had for two weeks. With the knowledge you have of HTTP, using an HTTP control such as the Mabry control (I don't suspect it matters much on the control), how would you POST a file to be uploaded to any given perl script? All I am trying to do is to get this lousy control to POST to a perl upload script. I didn't think it would be this difficult. Since it must follow a standard, I know it can be done. I don't have the Microsoft Internet Transfer Control because I only have the Learning Edition of Visual Basic. I mention that because that seems to become the standard solution.

So, my question again is if you were using an HTTP control and needed to upload a file to your server, how would you do it? I'm not looking for you to write out some large program, just show me the proper call to make that would cause a control and a server (normally through a perl script) to agree on the whole matter and just upload a file.

Thanks for your help. (I wonder if it would be too much to ask you to download the Mabry control and take a look at it for me.)
0
abelCommented:
Ok, that will cost me some time, but I like the idea, so I'll dive into that. Funny that we get a VB discussion on HTTP and a control of Mabry in a Perl thread, but who cares :)

I've downloaded the (?) Mabry control. I kinda guessed that you're referring to the HTTP ActiveX/COM object since that's the one that can do the trick. Now that we're on it, you'll probably need the code on http://www.mabry.com/httpx/headers100.htm because it gives a way to encode to base64. The site gives enough info to get things the way we want it, I think. (Un)fortunately I live in Holland, which means that it's about datingtime for me now: my social life's calling me :) That means, I'll get back on this tomorrow evening (that is, in about 24 hrs). Sorry for the delay.

Greetz,
Abel

PS: Before I forget to ask you: what version of VB have you got and what is the platform you're working on? (ie: vb5/nt4 or vb6/w95 or so)
0
microfleetAuthor Commented:
Yes, the HTTP ActiveX is the one. I'm using VB5 on Windows 95/98 (I have both). However, the server I use is NT4

Fortunately, I already know how to encode and decode in base64, both in perl and in Visual Basic. I have looked at every iota on the Mabry site and have yet to find the help I need. The technical support aint too great either - obviously. I do appreciate your help on this; hopefully, you won't find this as difficult as I have.

Thanks.
0
smorrowCommented:
Your right abel I did cut and paste but it was just a comment not an answer. Anyway it pointed the discussion in the right direction so theres no harm in that.
0
abelCommented:
Please allow me to apologize, I didn't mean to offend you in any way. What I actually meant with the remark was showing you that you had already been looking in the right direction. More precisely, I was just wondering why you didn't include the links to your sources because that was just what Microfleet was looking for and it would have given you all the credits you deserve. That's all :)

Greetz,
Abel
0
microfleetAuthor Commented:
Abel, any luck with that HTTP control? If it's getting to be too much trouble don't worry about it but it would certainly be nice to get it to work.
0
abelCommented:
Well, I have been trying the control and it does some things that it should, and some other things that it should not. You might already be familiar with it, but at least I found the following:

A "regular" post is ok. The method "post" does that alright. The only funny thing is found in the reply which sometimes is the same when it shouldn't be (different data in the post, same reply).
That caused some trouble testing with the file-upload. It first appeared to act normal, though it didn't really upload a file. I didn't have the time to make my own ASP/Perl/ISAPI app to handle everything and with the test-ASP I was using, I didn't have full control over the data being sent. I mean: it gets triggered but the upload is not done and I can't yet see why. And the strange caching behaviour (maybe it was my server?) mad it even worse for testing.

Is it possible for you to use any other language then VB? Ie can you run Perl to execute such a request or can you use Java for that purpose? It might be easier and then I won't have to deal with a "black box".

In the meantime I will make a more suitable testing environment so that I can more easily see what's going wrong with the Mabry control.

Greetz,
Abel
0
microfleetAuthor Commented:
Abel, the language to use isn't a big deal; I can use C++ just as well. It's just so much quicker for me in Visual Basic. But as far as running perl or Java, I thought those were just server side languages; that is, don't I need a browser interface for Java and a server that executes perl script? Obviously, my server does support perl (or else I wouldn't even mess with it), but I guess I'm really not certain what you are asking. Do you mean I can get my application on the client side to upload a file with just the use of perl?

Ok, here's what I've come up with and attempted (without much success). I read through a lot of those links you sent and concluded that the browser is actually sending the complete body of a file within a variable. So I did the following:

1) I filled a variable with the body of a file I was going to upload
2) I set the variable in the POST body (eg: http.request.body = "&upfile")
3) I then posted the information as if I were sending a username.

The result was that the file did go up and loaded; however, only about 1/4 of the file made the transmission. I don't know what happened to the rest of it. I certainly hope you have more success. Thanks for your help.
0
abelCommented:
Well, it appears that you got further then I did :)
If you get 1/4 of the file, you should also be able to get the whole file uploaded. Since I don't have a good testing environment (yet), I'm not able to see what I actually uploaded or didn't upload. Maybe there's something wrong with your encoding? Post your exact code from step 1 to 3, then we can bundle our experiences on the Mabry control and HTTP.

About other languages. Indeed, C(++) is quite cumbersome, but should get the job done. Perl can be used in several ways. Actually, it's just a console-mode command tool that can be used for actually any task. It's possible to convert .pl into .exe, it's possible to write a full blown win32 application (with userinterface!) and it's possible to use it clientside as well as serverside. I have several scripts running getting data from the web by POSTing or GETting to URLs. It's not so hard, but it means mixing languages. If you're already this far with the Mabry control, I think that a bit more effort will bring us to a solution.

Greetz,
Abel
0
microfleetAuthor Commented:
Here is the code I used to POST a file. Sometimes it does 1/4 of the file, sometimes less.

'******************************
' Visual Basic code begin
'******************************

Dim BodyOfFile$
Dim filename$
Dim FileNum%

'assign file number
FileNum = FreeFile

'point to a file on hard drive
filename = "Some-File-On-Hard-Drive"

'create buffer
BodyOfFile = Space(FileLen(filename))

'fill buffer var with contents of file
Open filename For Binary Access Read As FileNum
    Get #FileNum, , BodyOfFile
Close FileNum

'POST the file
Http.Port = 80
Http.Host = "www.mysite.com"
Http.Request.URI = "/myperl.pl"
Http.Request.Body = "&upname = test&upfile = " & BodyOfFile
Http.Post

'******************************
' Visual Basic code end
'******************************

I'm curious how I can make my perl script an executable. That might really be a clever way to do things. Hope something here gets us in the right direction.

0
abelCommented:
I will look into it. If you're interested in converting perl to executables, take a look at: http://www.dynamicstate.com/perl2exe.htm

Greetz,
Abel
0
abelCommented:
Oh, forgot to tell you. The new perl builds have the possibility to generate C-code. Then you can compile it to any executable you want. I haven't tried it yet and I think it's not (yet) in the distributed binaries, but the sources have it, see http://www.perl.com/pub/language/info/software.html#stable
0
abelCommented:
Back again!

Because you said that another language is fine too, here it goes in Perl. I have tried some more with the HTTPX control of Mabry and I think I can get it working in the end, but the problem lies in the construction of the request by hand. The way you do it is by application/x-www-form-urlencoded and the way it should be done is by multipart/form-data, which is (slightly) more difficult. This is mainly the reason why your attempts partly fail. It should, for example, work fully with a file with one line, no CRLF in it, spaces converted to plus-signs and not non-printable characters in it. But that's probably rarely the case. Oh, and not to forget, big sizes of files might bring you to even other troubles.

Let's look at Perl then. Maybe I come back with an attempt on HTTPX, but it would've better if it supported multipart/form-data natively. Perl does.

I created a test-environment using the file_upload.cgi example script provided with the Active Perl bin package for doing the hard work. I placed it in an IIS 4 server, but it should also work with any other. For my upload script I took the HTTP::Request::Common package for POSTing the file. This is how the script looks like:

################ START OF SCRIPT ################
use HTTP::Request::Common;
require LWP::UserAgent;

$req = new LWP::UserAgent;

$response = $req->request(POST 'http://harry/scripts/perl/file_upload.pl',
       Content_Type => 'form-data',
       Content      => [
                                      count => "count words",
                                      count => "count characters",
                                      count => "count lines",
                         filename  => ["c:/boot.ini"],
                      ]);
print "HTTP1.1\n200/OK\n\n",$response->content;

################# END OF SCRIPT #################

Notice the following things:

* The script you want to post to is places as string right after POST.
* The headers follow (content-length is provided by the package)
* The values "count" can be omitted. That's only for this specific script. If you also test with this script, you can test with these values to get the countings working.
* The header "filename" is the _name_ of the <INPUT TYPE="FILE" NAME="filename"> tag. What follows is an array that looks like (and I quote from the man-pages): [ $file, $filename, Header => Value... ]
* LWP::UserAgent is needed for making the request.
* The last print-statement is formatted as for an HTTP-client, but you can do (of course) whatever you like with it. The variable $response->content contains the full response of the POST-request.

Look up the documentation in perl's manual about HTTP::Request::Common and you'll find additional information on using these packages.

I haven't tried to convert it to an EXE, but that should work anyway. I hope this script helps you out a bit further.

If you have any more questions or if you encounter any troubles using the script, don't hesitate, I'll be happy to help you further.

Greetz,
Abel
0
abelCommented:
Hmmm, as usual, the make-up is lost. Here's a more readible version of the script (I hope):

use HTTP::Request::Common;
require LWP::UserAgent;

$req = new LWP::UserAgent;

$response = $req->request(POST 'http://someserver/scripts/perl/file_upload.pl',
       Content_Type => 'form-data',
       Content      => [
                                    count => "count words",
                                    count => "count characters",
                                    count => "count lines",
                                    filename  => ["c:/boot.ini"],
                      ]);
print "HTTP1.1\n200/OK\n\n",$response->content;


0
microfleetAuthor Commented:
Adjusted points to 200
0
microfleetAuthor Commented:
abel, thanks for all your help. Unfortunately, as I was testing and putting together the code you set for me, I realized that it would actually need to be made into an execute. So, I've had to make a decision: since I am having to buy the HTTP control already, I don't really want to have to pay another $50 for the Perl-to-EXE program. But what you have posted has really been helpful and if I ever get to a point where I can actually purchase that EXE program, then I will be even more grateful.

I am increasing the points to 200, but I'm keeping hold of the rest simply because I might need to ask a few more things, I'm not sure.

Thanks again!


Oh yea....make your comment and answer
0
microfleetAuthor Commented:
Adjusted points to 300
0
microfleetAuthor Commented:
abel, I've changed my mind once again. The heck with the HTTP, I'm going to go with the perl executable. I'm testing the C compiler at this time, but if that is not satisfactory, I'll go with the Perl-to-EXE that Dynamicstate is offering. The code you posted for the upload works fantastic! I'm in the process of trying to figure out how to configure the upload progress. I've also increased the points to 300 simply because anyone who wants to check out all this will actually get some worthwhile information.

This is my email   admin@youngerbrothers.com

Drop me a line and I'll tell you about the project I'm working on.

Steve
0
abelCommented:
Ok, I'll drop you a line shortly. I've been trying the HTTP-X control for some more time now, but it has some nasty drawbacks. The most important one is that is isn't quite possible to be of total control of the request. If you were, you might've been able to form the right request yourself. After I thought I finally managed making some kind of right request with it (we're still and only talking about the upload-request's here, content-type multipart/form-data, because all other requests work fine) I continuously get back the 400-error, meaning that the request itself is wrong.

Good to hear that you want to go for the "easier" solution. I haven't (yet) been working with the C-code generator. If you work with C-code you might also think of a possible implementation using MFC? But then you'll have to start all over again, so that's probably not such a good idea. And I don't have any experience in making such requests using C++/MFC, though it sounds like an interesting challenge: I can make a better COM-object than Mabry's! And sell it :-)

Thanks in advance for the points. Actually I hoped, for all these points, to find some better solution for you. I'm sorry to have failed, although it seems to be Mabry to get all the blame. By the way, have you noticed that Mabry says, in there helpfiles, that what we're trying is impossible with their control? I quote: "Please note that this method will not 'upload' any document and store it on a remote server. Rather, information will be sent and the remote server will proccess it, and/or store it internally in some database."

By the way, referring to your comment of febr 14, do you really need an executable? Anybody having the perl.exe and the necessary libraries can run the code from the console/command prompt. Or even from you VB-application if you want it to. Then you need not buy the perl2exe package after all and you don't have to fuzz with the C-generator.

Let me know if you get any further. Let's keep the discussion at this place for the sake of anybody paying 30 after this one's a PAQ. But I'll drop you a line anyway :)

Greetz,
Abel
0
microfleetAuthor Commented:
Yea I noticed that Mabry was denying the control would work but yet they claimed it was HTTP compliant, seemed like a twist of truths. I am completely unhappy with their work. But as far as the executable is concerned, I am looking at that option because the computers using my program most likely (say 999 out of 1000) will NOT have perl installed. These are not prgrammers, just everyday surfers. At this time though, I don't need the Perl-to-EXE generator because I'm creating a way to compile everything I need into a compact file to be read as the script needs it. In truth, it will be a program itself, though I will have to ship the interpreter separately. Right now, I have created a perl script to POST,Download, and, yes, finally, thanks to your help, to Upload. They work great! I also learned how to use the WIN32::CONSOLE to give a fairly decent report during the processes. I was just going to kill the console but when I realized I could manipulate it, I just decided to use it for feedback.
I like perl because it is robust and reliable. And you're right, I can't possibly start into the MFC now. Besides, I'm a VB programmer, and only adequate at that. But the program I've been working on has taken nearly 4 months to complete and 10 months in preparation (that is, "trial and error").

Thanks for your help.
0
microfleetAuthor Commented:
Yea I noticed that Mabry was denying the control would work but yet they claimed it was HTTP compliant, seemed like a twist of truths. I am completely unhappy with their work. But as far as the executable is concerned, I am looking at that option because the computers using my program most likely (say 999 out of 1000) will NOT have perl installed. These are not prgrammers, just everyday surfers. At this time though, I don't need the Perl-to-EXE generator because I'm creating a way to compile everything I need into a compact file to be read as the script needs it. In truth, it will be a program itself, though I will have to ship the interpreter separately. Right now, I have created a perl script to POST,Download, and, yes, finally, thanks to your help, to Upload. They work great! I also learned how to use the WIN32::CONSOLE to give a fairly decent report during the processes. I was just going to kill the console but when I realized I could manipulate it, I just decided to use it for feedback.
I like perl because it is robust and reliable. And you're right, I can't possibly start into the MFC now. Besides, I'm a VB programmer, and only adequate at that. But the program I've been working on has taken nearly 4 months to complete and 10 months in preparation (that is, "trial and error").

Thanks for your help.
0
microfleetAuthor Commented:
I wonder why that comment repeated.

Just want to ask if you know why this line "Content-type: html/plain" is being added to the top of every HTML and text file I upload? I'm using that script you posted and it works great except that it adds that header to every file. I don't want it to do that. I want the upload to not add anything extra to the file, just upload it. Any ideas on how to stop it from doing that?

thanks
0
abelCommented:
Well, I wonder how you read it in, because the thing you mention, "Content-type: html/plain" is part of the header and you can't avoid it being sent with the upload. It's needed for the server to determine what type the file is you're sending. For example, it can also be something like "Content-type: mpeg/x-octetstream" when you upload an mpeg-file. In other words, show me how you read the file once it's uploaded, maybe your CGI-script has some error and that's the reason that you see that part of the header. There are more headers that you should be able to read, I wonder why that ones aren't added on top of the textfiles.

By the way, if you upload a textfile, it should read "Content-type: text/plain", I wonder why it says "Content-type: html/plain" in your case.

Greetz,
Abel
0
abelCommented:
PS: the reason that you see that comment twice is probably because you hit reload on your browser and said yes to the question about sending the information again. That's why these pages have a reload link top-left on the page :)
0
microfleetAuthor Commented:
Maybe it did say text/plain, I didn't go back to look because I thought it said html/plain, but no matter, whatever it says I dont want it. Anyway, the script I am using on the server to accept the upload is something I found and then modified a little. I know my modification isn't causing the problem because all it does is find a usuable filename so that I don't overwrite an existing file. I have pasted the whole thing in here but let me know if you would rather me send it by email.

#---------------BEGIN SERVER UPLOAD SCRIPT-----------------------#

#
# Perl-based Upload Script
#
# Copyright 1998 Tod Sambar
# All rights reserved.
#
# Demonstrates how to upload data via multipart/form-data.
#

## I ADDED THIS - my server sometimes has trouble with my path so I ##make it an absolute path at the get-go: ---BEGIN ADDITION
#
unless ($FULLPATH) {&SETFULLENVPATH($ENV{PATH_TRANSLATED});}

sub SETFULLENVPATH {
      my($tmpFULLPATH) = $_[0];
      my($POSITION) = rindex($tmpFULLPATH, "\\");
      my($savedFULLPATH) = substr($tmpFULLPATH, 0, $POSITION);
      $POSITION = rindex($savedFULLPATH, "\\");
      $FULLPATH = substr($savedFULLPATH, 0, $POSITION + 1);
      $_ = $FULLPATH;
      $savedFULLPATH = s/\\/\//g; # Change the backslashes to forwardslashes
      $FULLPATH = "$_";
}
#
## ----END ADDITION



#
# PARSE THE CGI FORM
#
$content_type = $ENV{'CONTENT_TYPE'};
$content_len = $ENV{'CONTENT_LENGTH'};
$host_test = $ENV{'REMOTE_ADDR'};


# Buffer the POST content
binmode STDIN;
read(STDIN, $buffer, $content_len);

if ((!$content_type) ||
     ($content_type eq 'application/x-www-form-urlencoded'))
{
    # Process the name=value argument pairs
    @args = split(/&/, $buffer);

     $data = '';
    foreach $pair (@args)
    {
        ($name, $value) = split(/=/, $pair);
        # Unescape the argument value
        $value =~ tr/+/ /;
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

        # Save the name=value pair for use below.
        $FORM{$name} = $value;
    }
}
elsif ($content_type =~ m#^multipart/form-data#)
{
    # find boundary
    # Eric Poulsen fixed the following to allow for quotes.
    #
    # ($boundary = $content_type) =~ s/^.*boundary=(.*)$/\1/;
    ($boundary = $content_type) =~ s/^.*boundary="?(.*?)"?$/\1/;

    @pairs = split(/--$boundary/, $buffer);
            @pairs = splice(@pairs,1,$#pairs-1);

    for $part (@pairs)
    {
    ($dump,$fline,$value) = split(/\r\n/,$part,3);
    next if $fline =~ /filename=\"\"/;
    $fline =~ s/^Content-Disposition: form-data; //;
    (@columns) = split(/;\s+/, $fline);
    ($name = $columns[0]) =~ s/^name="([^"]+)"$/\1/g;

    if ($#columns > 0)
    {
        if ($value =~ /^Content-Type:/)
        {
          ($dump,$dump,$value) = split(/\r\n/,$value,3);
        }
        else
        {
        ($dump,$value) = split(/\r\n/,$value,2);
        }
    }
    else
    {
        ($dump,$value) = split(/\r\n/,$value,2);
        if (grep(/^$name$/, keys(%CGI)))
        {
          if (@{$FORM{$name}} > 0)
        {                                              push(@{$FORM{$name}}, $value);
        }
        else
        {
          $arrvalue = $FORM{$name};
          undef $FORM{$name};
                                              $FORM{$name}[0] = $arrvalue;
                                              push(@{$FORM{$name}}, $value);
        }
    }
    else
    {
        next if $value =~ /^\s*$/;
        $FORM{$name} = $value;
    }
    next;
    }

    $FORM{$name} = $value;
    }
}
else
{
    print "Invalid content type!\n";
    exit(1);
}



#
# VERIFY THE FORM DATA
#
$upfile = $FORM{'upfile'};
$upname = $FORM{'upname'};
if (!($upfile) || !($upname))
{
    # be sure header is printed
    print "Content-type: text/html\n\n";

    print "<HTML><TITLE>Missing fields</TITLE><BODY>\n";
    print "No upload file specified!\n";
    print "</BODY></HTML>\n";
    exit(1);
}


#
# CLOSE SECURITY PROBLEMS.
#
if ($upname =~ /[;><&\'\|\/\\]/ )
{
    # be sure header is printed
    print "Content-type: text/html\n\n";

    print "<HTML><TITLE>Invalid file name</TITLE><BODY>\n";
    print "The upload file name is invalid.\n";
    print "</BODY></HTML>\n";
    exit(1);
}




#
# Write out the upload file
#

# Change the backslashes to forwardslashes
$upname =~ s/-/\//g;

#set filename
$filename = $upname;

#--------------------
#--I added this to parse the upload name and from it find an available name #on the server to use
#
#remove ending "#" from filename
$DirPositionCheck = rindex($upname, "\/");
$DirCheck = substr($upname, 0, $DirPositionCheck);
$FilePositionCheck = rindex($upname, "#");

$filename = substr($upname,0,$FilePositionCheck);
$upname = $filename; #substr($upname,0,$FilePositionCheck);

# check directory existence
if (!(-e "${FULLPATH}cgi_user_dir/${DirCheck}")) {
    # be sure header is printed
    print "Content-type: text/html\n\n";

    print "Invalid Directory Names -\n";
    print "*${DirCheck}*\n";
    exit;
}

# find free file name
$NewFilename = $filename;
$NewFilename =~ s/.que.csn//i;
$extension = ".que.csn";
$nCount = 0;

while ( -e "${FULLPATH}cgi_user_dir/${filename}") {
    ++$nCount;
    $filename = "${NewFilename}${nCount}${extension}";
};             
$nCount = 0;
$filename = "${FULLPATH}cgi_user_dir/".$filename;
#
#---End of my addition--
#------------

#upload file
open(FILE, ">${filename}") || exit(1);
    binmode FILE;

    print FILE $upfile;
close FILE;

#
# DONE
#

# be sure header is printed
print "Content-type: text/html\n\n";

print "Files Uploaded Successfully -\n";
print "#$upname#\n";

exit(0);


#---------------END SERVER UPLOAD SCRIPT-------------------------#

Obviously any part I've added could be done better I'm sure but it does work for me.
0
abelCommented:
There are some problems with your script at the part that you're reading in the file, more specifically, at this line: $fline =~ s/^Content-Disposition: form-data; //;

What it does, or does not, is deleting some information you don't want in your saved files. But it's not all, as you've found out. There is more at that part, like the Content-type header for each file you upload (after each boundary)

I have a resolution for this and a much easier way too: you don't need to know the full protocol as you need to for this script (or the guy that made it). unfortunately I can't post it anymore before the weekend, but you'll get it a quickly as I can (where I'm now I don't have the right tools and stuff).

Greetz,
Abel
0
microfleetAuthor Commented:
Great! Thanks for the help, I look forward to the new script! I'm setting all the files I need for my setup program and will have that done today. At the moment, I'm using the Internet Explorer WebBrowser control for my uploads, but I will be glad to pull that out and use the perl (I have the Visual Basic part of it complete, all I have to do is uncomment the appropriate lines).
0
abelCommented:
Well, I hope it was worth waiting :)

Here's the script. It's an adoption of a sample script provided by ActiveState's Perl. The only thing it does is taking two parameters from a form (I also provide the code for the HTML page, just for convenience), one being the file, the other being the remote filename. It then saves the file on the remote server, where the perl script resides. It doesn't matter what file is being sent, any file will be saved correctly.

You'll need to adjust the script like you did with the one you posted here. You'll also need to adjust the ACTION-tag of the HMTL-part and the $filepath of the Perl-part. If you have questions about this script, or if things aren't working the way they should, feel free to drop a line.

BTW, I've tested the script on IIS 4 on NT 4 SP6.

Greetz,
Abel



############## Start of the Script ##############

#We make use of the CGI library.
use CGI qw(:standard);
use CGI::Carp qw/fatalsToBrowser/;

#default variables (change these for your local path):
$filepath = "D:\\";

print header();
print start_html("File Upload Example (FUE)");
h2("File Upload Example (FUE), the results: ");

# Process the form if there is a file name entered
if ($file = param('filename'))
{
    $rname  = param('remotename');
   
    #Open the local file, depending on $rname. If it's empty, the name of
    #the uploaded file is used. If the open fails, $openfailed will be flagged for the errormessage.
    if(open(SAVE, ">" . ($saveas = $filepath.($file =~ /.*\\(.+)$/, $rname?$rname:$1))) or $openfailed = "failed")
    {
          #Binmode is only needed on Windoew platforms, as far as I know.
          #It's not needed for text-based files.
        binmode SAVE if(!($mimetype =~ /text|html|plain/));
        print SAVE <$file>;        #Save everything
        close SAVE;
    }
   
    #Print the gathered information.
    print strong("Name and location of uploaded filename:"), br, $file, p,
          strong("Name and location of temporary file on server:"), br, tmpFileName($file), p,
          strong("MIME Type: "), br, (uploadInfo($file)->{'Content-Type'} || ''), p,
          strong("File is saved under the name: "), br, $openfailed?"not saved: couldn't open file: ":'','"', $saveas, '"';
   
    close $file;
}
else
{
    #No filename was provided
    print strong("You haven't profided a filename on the form. Please hit the back-button and try again");
}
print end_html;

#When you use PerlIIS
1;

############### End of the script ###############

############## Start of the HTML ##############

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML><HEAD><TITLE>File Upload Example</TITLE>
</HEAD><BODY>
<H2>File Upload Example (FUE)</H2>
This example shows how to upload arbitrary files to the server using a Perl script,
as basically as possible, this one being a special adoption to the needs of Microfleet :-)

<FORM METHOD="POST"  ENCTYPE="multipart/form-data" ACTION="/scripts/perl/file_upload.pl">
      <FONT SIZE=-1><B>Enter the file to upload:<BR>
      <INPUT TYPE="file" NAME="filename"  SIZE=45>
      <BR>
      Enter the realative remote filename or leave blank for same as above:<BR>
      <INPUT TYPE="text" NAME="remotename" SIZE=45 VALUE="">
      <P>
      <INPUT TYPE="reset">
      <INPUT TYPE="submit" NAME="submit" VALUE="Process File">
      </B></FONT>
</FORM>

</BODY>
</HTML>

############### End of the HTML ###############
0
binkzzCommented:
0
abelCommented:
Binkzz, it's ok to listen in of course, but you can also do that without leaving a blank comment: just make sure the commentfield (wherein I'm typing this text) is blank, not even a return should be in it. That way your name won't be on the thread but you _will_ be notified of changes.
You can, of course, leave a message as to why or for introducing yourself :-)

Greetz,
Abel
0
microfleetAuthor Commented:
abel, was there ever any real doubt that it would work? After a little bit of trial and error to situate the particular code I need for my uploads, everything worked great. I can now finally remove the Internet Explorer Control from my project. It was the last control that I really didn't want to have to use. The ZIP file comes to 4.77MB, which is fine. About 1 meg of that are graphics from artists that lent there work. I actually have 60MB of artwork but I just chose one graphic from each artist for the ZIP file. However, I will probably give them each a web page and provide all their work at the site.
The perl files I need come out to 1.34MB. I'm only using the absolute minimum files I need from all the perl modules and accessories. I'll send you a link to the ZIP after I give it a few tests. It is a beta of course but not because it has a chance to crash a system (or to even crash itself) but because parts of it are still incomplete, such as the templates, and a few cosmetic difficulties, and possibly, too, the HELP system needs a little re-working, but fortunately none of that have anything to do with how the program operates.
Thanks for your help and as soon as you are ready to receive the points just let me know.
0
abelCommented:
Good to hear that! Great that it works great :-)

One tiny detail to think of... the part where the code saves the uploaded file to the local file is a bit slow, especially on large files. It doesn't consume any CPU-power however, it just takes some time before the statement "print SAVE <$file>;" is executed. To resolve that problem (it's something you'll probably notice as soon as your project goes live, not in debug stadia) change the code to a read-line and a print line, like the save-lines in the above code of guadalupe:

while($bytesread=read($file,$data,1024)){
    #You might want to sum $bytesread to know the length of the saved file
    print SAVE $data;
}

Depending on the buffersize (here 1024) it should be lots faster :-) Actually I think that in my code, though it's much more concise, the buffersize is close to a byte or a word or so and I guess that's the main reason that saving the file takes so long.

About my points, you can reward me when you want to, just choose a comment you think fits best the answer to your question and choose "accept comment as answer".

I'm looking forward to downloading the ZIP-file. I'm quite curious by now!

Greetz,
Abel
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
microfleetAuthor Commented:
Abel, out of town a couple days and then decided I didnt like the size of my setup program. So I decided to remove the graphics and just allow them to be downloaded from the web; also, I'm going ahead and getting rid of the internet browser control (it's 500K!). I was going to just upload it all so you could take a look at it, but now I'm going to take the time to desize it a bit. Wont take long, should have it done before the weekend is through. Wish there was a way to change the title of this question.
Thanks for your help, I'll send the link via email soon.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Perl

From novice to tech pro — start learning today.