home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / upcgi.zip / upfile.cmd < prev    next >
OS/2 REXX Batch file  |  2000-01-14  |  12KB  |  419 lines

  1. /* 14 Jan 2000. Daniel Hellerstein (Danielh@econ.ag.gov)
  2.    This is hereby released to the public domain.
  3.   
  4.   This is a simpele OS/2 REXX Cgi-bin script to process
  5.   HTML uploads (a "multi-part/form" submission"). 
  6.   It will save the file to an "upload" directory,
  7.   and send a simple confirmation back to the client.
  8.  
  9.   The heart of this is the READ_MULTIPART_DATA procedure.
  10.   You can use it in your own cgi-bin scripts.
  11.  
  12.   For an example of an HTML document that will invoke this
  13.   form, see UPLOAD.HTM
  14.  
  15.   Note: You MUST set the UPLOAD_DIR parameter !!
  16.  
  17. */
  18.  
  19. /***** Begin user configurable parameters ***/
  20.  
  21. /* Root of the upload directory -- use a fully qualified directory name */
  22. upload_dir=''
  23.  
  24. /* Overwrite file it exists (1=yes,0=no) */
  25. overwrite_allowed=1
  26.  
  27.  
  28. /* a response file, used if succesful upload occured.
  29.    Must be a fully qualified file name
  30.    Leave this empty, or set it to 0, and a generic response will be used
  31.    
  32.   Notes:
  33.      * the response file SHOULD be an html document.
  34.      * all occurences of the string 
  35.              <!-- UPLOAD_SIZE --> 
  36.       will be replaced by the size of the file uploaded,
  37.      * all occurences of the string
  38.             <!-- UPLOAD_NAME --> 
  39.        will be replaced by the name give to the uploaded file
  40. */
  41. response_file=''
  42.  
  43. /***** End user configurable parameters ***/
  44.  
  45. foo=rxfuncquery('sysloadfuncs')
  46. if foo=1 then do
  47.   foo=RxFuncAdd( 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs')
  48.   if foo=0 then do
  49.      call SysLoadFuncs
  50.   end
  51.   else do
  52.     say "Content-type: text/plain"
  53.     say 
  54.     say "Server error: rexxutil.dll was not found "
  55.     exit
  56.   end 
  57. end
  58. /* get the request method */
  59. method = value("REQUEST_METHOD",,'os2environment')
  60.  
  61. if method='GET' then do
  62.     list=value("QUERY_STRING",,'os2environment')
  63. end
  64. else do
  65.    tlen = value("CONTENT_LENGTH",,'os2environment')
  66.    list=charin(,,tlen) 
  67. end 
  68.  
  69. /* Note: Multi-part form submissions MUST be POST */
  70. notmulti=0
  71. if method="GET" then do         /* so this is just the normal request line */
  72.     notmulti=1
  73. end 
  74. else do
  75.     conttype=value("CONTENT_TYPE",,'os2environment')     /* Is this a multipart/form-data */
  76.     if abbrev(translate(conttype),'MULTIPART/FORM-DATA')=0 then notmulti=1
  77. end
  78.  
  79.  
  80. /* if not a multi-part form, return an error message */
  81. if notmulti=1 then do
  82.   say "Content-type: text/html "
  83.   say 
  84.   say "<html><head><title>Unable to upload file </title></head><body>"
  85.   say "<h2>Unable to upload file</h2>"
  86.   say "Sorry, your browser does not seem to support FORM based file upload "
  87.   say "</body></html>"
  88.   exit
  89. end 
  90.  
  91.  
  92. /* if here, a multi part form */ 
  93. ndo=read_multipart_data(list,conttype)    /* this does the work . */
  94. origname=''
  95. callit=''
  96. useentry=0
  97.  
  98.  
  99. /* find the CALLIT and FILENAME variables -- 
  100.    CALLIT contains the desired name
  101.    FILENAME contains the original name */
  102. do mm=1 to ndo
  103.    elist=translate(strip(form_data.!list.mm))
  104.    do until elist=''
  105.        parse var elist aword elist
  106.        select
  107.           when aword='FILENAME' then do
  108.             aword2='!'||aword
  109.             avalue=strip(form_data.aword2.mm)
  110.             origname=filespec('n',avalue)          
  111.             useentry=mm
  112.           end /* do */
  113.           when aword="NAME" then do
  114.             aword2='!'||aword
  115.             avalue=strip(form_data.aword2.mm)
  116.             if translate(value)='UPLOAD_FILE' then useentry=mm
  117.             if translate(avalue)<>'CALLIT' then iterate
  118.             callit=strip(form_data.mm)   /* get it's value from the "part" */
  119.             if pos('\',callit)>0 then 
  120.                callit=filespec('p',callit)||filespec('n',callit)
  121.             else
  122.                callit=filespec('n',callit)
  123.           end
  124.           otherwise nop
  125.         end
  126.    end                   /*elist */
  127. end 
  128.  
  129.  
  130. if callit<>'' then
  131.   usename=callit
  132. else
  133.   usename=origname
  134. usename=translate(usename,'\','/')
  135. usename=strip(usename,,'\')
  136.  
  137. /* watch out for sneakys */
  138. if pos('..',usename)<>0 then do
  139.   say "<html><head><title>Bad filename </title></head><body>"
  140.   say "The filename is unacceptable: "usename
  141.   exit
  142. end
  143.  
  144.  
  145. if useentry=0 then do
  146.   say "<html><head><title>No contents </title></head><body>"
  147.   say "There are no contents in your upload request (nothing to save)"
  148.   exit
  149. end
  150.  
  151. say "Content-type: text/html "
  152. say 
  153.  
  154. upload_dir=strip(upload_dir)
  155. upload_dir=strip(upload_dir,,'\')
  156.  
  157. usename2=upload_dir'\'usename
  158.  
  159. usedir=filespec('d',usename2)||filespec('p',usename2)
  160. if ais_dir(usedir)=0 then do
  161.   say "<html><head><title>No such directory </title></head><body>"
  162.   say "No such directory: "usedir
  163.   exit
  164. end
  165.  
  166. foo=stream(usename2,'c','query exists')
  167. if foo<>'' then do
  168.    if overwrite_allowed=0 then do
  169.       say "<html><head><title>File exists</title></head><body>"
  170.       say "A file with this name already exists: "usename2
  171.       exit
  172.    end
  173.    else do
  174.       foo=sysfiledelete(usename2)
  175.   end 
  176. end 
  177.  
  178.  
  179. foo=stream(usename2,'c','open write')
  180. if abbrev(translate(strip(foo)),'READY')<>1 then do
  181.   say "<html><head><title>Unable to open file </title></head><body>"
  182.   say "Unable to open file: "usename2
  183.   exit
  184. end
  185. foo=charout(usename2,form_data.useentry,1)
  186. if foo<>0 then do
  187.   say "<html><head><title>Unable to write file </title></head><body>"
  188.   say "Unable to write file: "usename2
  189.   exit
  190. end
  191.  
  192. foo=stream(usename2,'c','close')
  193. ssize=stream(usename2,'c','query size')
  194.  
  195. /* use a response file? */
  196. if response_file='' | response_file=0 then do      /*use generic response */
  197.   say "<html><head><title>The parts </title></head><body>"
  198.   say " Saving uploaded file to: "usename2
  199.   say "<bR> # of bytes saved: "ssize
  200.   say "</body></html>"
  201.   exit
  202. end
  203.  
  204. oldr=response_file
  205. /* else, use a response file */
  206. response_file=stream(response_file,'c','query exists')
  207. if response_file=''  then do      /*use generic response */
  208.   say "<html><head><title>The parts </title></head><body>"
  209.   say " Saving uploaded file to: "usename2
  210.   say "<bR> # of bytes saved: "ssize
  211.   say "<hr><em>Note: response file was not found = "oldr
  212.   say "</body></html>"
  213.   exit
  214. end
  215.  
  216. foo=stream(response_file,'c','open read')
  217. if abbrev(translate(strip(foo)),'READY')<>1 then do    /* problem with response file */
  218.   say "<html><head><title>The parts </title></head><body>"
  219.   say " Saving uploaded file to: "usename2
  220.   say "<bR> # of bytes saved: "ssize
  221.   say "<hr><em>Note: response file could not be opened = "response_file
  222.   say "</body></html>"
  223.   exit
  224. end
  225.  
  226. rsize=stream(response_file,'c','query size')
  227. if rsize=0 | rsize='' then do
  228.   say "<html><head><title>The parts </title></head><body>"
  229.   say " Saving uploaded file to: "usename2
  230.   say "<bR> # of bytes saved: "ssize
  231.   say "<hr><em>Note: problem reading response file = "response_file
  232.   say "</body></html>"
  233.   exit
  234. end
  235.  
  236. aresp=charin(response_file,1,rsize)
  237. foo=stream(response_File,'c','close')
  238. anew=''
  239. do forever
  240.    if aresp='' then leave
  241.    parse var aresp a1 '<!-- ' ain '-->' aresp
  242.    goo=strip(translate(ain))
  243.    anew=anew||a1
  244.    select
  245.       when goo='UPLOAD_SIZE' then
  246.           anew=anew||ssize
  247.       when goo='UPLOAD_NAME' then
  248.           anew=anew||usename2
  249.       otherwise
  250.           anew=anew||'<!--'||ain||'-->'
  251.     end
  252. end
  253.  
  254. call charout,anew
  255. exit
  256.  
  257.  
  258. exit
  259.  
  260.  
  261. /* ---------------------------------- */
  262. /* return 1 if adir is an existing (possibly empty) directory , 0 if not */
  263. ais_dir:procedure 
  264. parse arg adir
  265. adir=strip(adir)
  266. adir=strip(adir,'t','\')
  267. nowdir=directory()
  268. nowdrive=filespec('d',nowdir'\')
  269. nowpath=filespec('p',nowdir'\')
  270. adr=filespec('d',adir)
  271. if adr='' then do
  272.    if abbrev(adir,'\')=0 then 
  273.        adir=nowdrive||nowpath||adir
  274.    else
  275.        adir=nowdrive||adir
  276. end /* do */
  277. foo=sysfiletree(adir,goo,'D')
  278. if  goo.0>0  then return 1
  279. return 0
  280.  
  281.  
  282.  
  283.  
  284. /*************************************/
  285. /* read data sent back by an html FORM declared with:
  286.    enctype="multipart/form-data" method="POST"
  287.  
  288. Calling syntax:
  289.    form_data.=''
  290.    nentries=read_multipart(stuff,content_type)
  291. where:
  292.           stuff == the body of a POST request 
  293.        nentries == the number of entries found. If error, nentries=0
  294. and 
  295.      form_data. == an "exposed" stem variable which will contain
  296.                    variables extracted from the several parts of this
  297.                    POSTed body.
  298.  
  299. The structure of FORM_DATA is:
  300.  i) FORM_DATA.0 = # of parts (in this multipart submission)
  301.  
  302. ii) FORM_DATA.!list.j = space delimited list of "variable names" in part
  303.                        j (j=1.. FORM_DATA.0)
  304.  
  305. iii) FORM_DATA.!avar.j = value of the "avar" variable from the jth part;
  306.                          where avar is one of the "variable names" contained
  307.                          in FORM_DATA.!list.j
  308.  
  309. iv)  FORM_DATA.j       = the value of the part
  310.  
  311. That is:
  312.    *  For each word in FORM_DATA.!list.j, there is a FORM_DATA. tail.
  313.      Thus, 
  314.         if FORM_DATA.!list.2='FILENAME  NAME'
  315.      then
  316.          FORM_DATA.!FILENAME.2 = the value of the  "FILENAME" variable from the
  317.                                "2nd" part of this requeset
  318.          FORM_DATA.!NAME.2 = the value of the  "NAME" variable from the
  319.                                "2nd" part of this request
  320.       and
  321.          FORM_DATA.2   = the value of this "part"
  322.  
  323.  
  324. I almost all cases, the only "word" will "NAME", which will be
  325. the "xxx" from a name="xxx" attribute of an <INPUT > element in
  326. an HTML form.
  327.  
  328. You may also see a FILENAME word, which is the filename (on the
  329. client's machine) of a file uploaded using a
  330.    <INPUT TYPE="file"  name="a_filename">
  331. element.
  332.  
  333.  
  334. Notes:
  335.     * if an error occurs, a 0 is returmed, and FORM_DATA.!ERROR
  336.       will contain an error message
  337.     * a content-disposition entry, if found, is NOT included in FORM_DATA
  338.  
  339.  
  340. */
  341. read_multipart_data:procedure expose form_data.
  342. parse arg abody,atype
  343.  
  344. drop form_data.
  345.  
  346. crlf='0d0a'x
  347.  
  348. /* is there a content-type request header ? */
  349. if atype="" then do
  350.    form_data.!error=" No  content-type  request header"
  351.    return 0
  352. end
  353.  
  354. parse var atype thetype ";" boog 'boundary=' abound    /* get the type */
  355.  
  356. if translate(thetype)<>"MULTIPART/FORM-DATA" then do
  357.   form_data.!error="No  multipart/form-data in Content-type "
  358.   return 0
  359. end
  360.  
  361. if translate(thetype)<>"MULTIPART/FORM-DATA" then do
  362.   form_data.!error=" BlendGif upload error: No boundary in multipart/form-data header "
  363.   return 0
  364. end
  365.  
  366. abound="--"||abound   /* since boundaries always start with -- */
  367.  
  368. abd2=abound||crlf
  369. /* loop through message, pulling out blocks and storing in stem var bigstuff. */
  370.  
  371. /* Now parse the various parts.*/
  372.  
  373. parse var abody foo1 (abd2) abody    /* move beyond first boundary and it's crlf */
  374. /* check for netscape 2.0 incorrect format */
  375. if pos(abound,abody)=0 then do   /* no ending boundary, so add one */
  376.    abody=abody||crlf||abound||" -- "
  377. end
  378.  
  379. mm=0
  380. do until abody=""
  381.   parse var abody thestuff (abound) abody        /* get a  boundary defined block */
  382.   if strip(left(thestuff,4))="--" then leave        /* -- signals no more */
  383.   if abody="" then leave
  384.   mm=mm+1
  385.   form_data.!list.mm='' ; form_data.mm=''
  386.   do forever            /* get block headers.  Stop when hit a blank line */
  387.      parse var thestuff anarg (crlf) thestuff
  388.      if anarg="" then do
  389.            leave
  390.      end
  391.      else do                    /* extract the arguments on this line */
  392.          do until anarg=""
  393.               parse var anarg anarg1 ";" anarg
  394.               boob1=pos(':',anarg1) ; boob2=pos('=',anarg1)
  395.               if boob1=0 then nixon=boob2
  396.               if boob2=0 then nixon=boob1
  397.               if boob1>0 & boob2>0 then nixon=min(boob1,boob2)
  398.               t1=translate(strip(strip(substr(anarg1,1,nixon-1)),,'"'))
  399.               t2=strip(strip(substr(anarg1,nixon+1)),,'"')
  400.               if t1="CONTENT-DISPOSITION" then iterate /* don't bother retaining this */
  401.               form_data.!list.mm=form_data.!list.mm' 't1
  402.               nm1='!'||t1
  403.               form_data.nm1.mm=t2
  404.           end     /* exract arguments */
  405.      end        /* extract args on this line */
  406.   end                    /* get a line */
  407.   if thestuff<>"" then do
  408.     form_data.mm=left(thestuff,length(thestuff)-2)  /* strip off ending crlf */
  409.     parse var abody foo (crlf) abody   /* jump past extra crlf */
  410.   end
  411.   else do
  412.      form_data.body.mm=""
  413.   end
  414. end
  415.  
  416. return mm
  417.  
  418.  
  419.