1 ! Copyright (C) 2008 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors combinators io kernel locals math multiline
4 sequences splitting prettyprint namespaces http.parsers
5 ascii assocs unicode.case io.files.unique io.files io.encodings.binary
6 byte-arrays io.encodings make fry ;
9 TUPLE: multipart-stream stream n leftover separator ;
11 : <multipart-stream> ( stream separator -- multipart-stream )
19 : ?append ( seq1 seq2 -- newseq/seq2 )
20 over [ append ] [ nip ] if ;
22 : ?cut* ( seq n -- before after )
23 over length over <= [ drop f swap ] [ cut* ] if ;
25 : read-n ( stream -- bytes end-stream? )
27 [ n>> ] [ stream>> ] bi stream-read [ ?append ] keep not ;
29 : multipart-split ( bytes separator -- before after seq=? )
30 2dup sequence= [ 2drop f f t ] [ split1 f ] if ;
32 :: multipart-step-found ( bytes stream quot: ( bytes -- ) -- ? )
33 bytes [ quot unless-empty ]
34 [ stream (>>leftover) quot unless-empty ] if-empty f ; inline
36 :: multipart-step-not-found ( bytes stream end-stream? separator quot: ( bytes -- ) -- ? )
40 separator length 1- ?cut* stream (>>leftover)
44 :: multipart-step ( stream bytes end-stream? separator quot: ( bytes -- ) -- ? end-stream? )
45 #! return t to loop again
46 bytes separator multipart-split
49 [ stream quot multipart-step-found ]
50 [ stream end-stream? separator quot multipart-step-not-found ] if*
51 ] if stream leftover>> end-stream? not or >boolean ;
54 :: multipart-step-loop ( stream quot1: ( bytes -- ) -- ? )
55 stream dup [ read-n ] [ separator>> ] bi quot1 multipart-step
56 swap [ drop stream quot1 multipart-step-loop ] when ; inline recursive
60 SYMBOL: magic-separator
62 : trim-blanks ( str -- str' ) [ blank? ] trim ;
64 : trim-quotes ( str -- str' )
65 [ [ CHAR: " = ] [ CHAR: ' = ] bi or ] trim ;
67 : parse-content-disposition ( str -- content-disposition hash )
68 ";" split [ first ] [ rest-slice ] bi [ "=" split ] map
69 [ [ trim-blanks ] [ trim-quotes ] bi* ] H{ } assoc-map-as ;
71 : parse-multipart-header ( string -- headers )
73 [ parse-header-line first2 ] H{ } map>assoc ;
75 ERROR: expected-file ;
77 TUPLE: uploaded-file path filename name ;
79 : (parse-multipart) ( stream -- ? )
80 "\r\n\r\n" >>separator
82 dup [ header [ prepend ] change ] multipart-step-loop drop
83 header get dup magic-separator get [ length ] bi@ < [
86 parse-multipart-header
88 "\r\n" magic-separator get append >>separator
89 "factor-upload" "httpd" make-unique-file tuck
90 binary [ [ write ] multipart-step-loop ] with-file-writer swap
91 "content-disposition" parsed-header get at parse-content-disposition
92 nip [ "filename" swap at ] [ "name" swap at ] bi
98 : parse-multipart ( stream -- array )
100 "\r\n" <multipart-stream>
102 dup [ magic-separator [ prepend ] change ]
103 multipart-step-loop drop
104 '[ [ _ (parse-multipart) ] loop ] { } make