1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2014, University of Amsterdam 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(http_multipart_plugin, 37 [ 38 ]). 39:- use_module(http_stream). 40:- use_module(http_header). 41:- use_module(library(debug)). 42:- use_module(library(option)).
55:- multifile
56 http_client:http_convert_data/4,
57 http_parameters:form_data_content_type/1.
multipart/form-data
messages for http_read_data/3.
This plugin adds the folling options to http_read_data/3:
multipart/form-data
, return the
form-data either in one of the following formats:
mime(Properties, Value, [])
. This is a backward
compatibility mode, emulating library(http/http_mime_plugin)
.
Note that if the disposition contains a filename
property, the data is read as binary unless there is a
charset parameter in the Content-Type stating otherwise,
while the old library would use UTF-8 for text files.filename
disposition and no Content-Type with a charset indication.
This is typically the case for input widgets and browsers
encode this using the encoding of the page. As the SWI-Prolog
http library emits pages in UTF-8, the default is utf8
.filename
disposition is found and this
option is given, call CallBack as below. Stream is the
multipart input stream, which has octet (raw) encoding.
Value is returned as result. Note that the callback
may wish to save the result into a file and return e.g.,
file(Path)
to indicate where the file was saved.
call(:CallBack, +Stream, -Value, +Options).
The Options list contains information from the part header.
It always contains name(Name)
and filename(FileName)
. It
may contain a term media(Type/SubType, Params)
if the part
contains a Content-Type header.
100http_clienthttp_convert_data(In, Fields, Data, Options) :-
101 memberchk(content_type(Type), Fields),
102 multipart_type(Type, Boundary),
103 !,
104 setup_call_cleanup(
105 multipart_open(In, Stream, [boundary(Boundary)]),
106 process_parts(Stream, Data, Options),
107 close(Stream)).
multipart/form-data;
boundary="..."
and Boundary is a string describing the
boundary.115multipart_type(Type, Boundary) :- 116 http_parse_header_value(content_type, Type, 117 media(multipart/'form-data', Params)), 118 memberchk(boundary=Boundary, Params). 119 120 121process_parts(Stream, [Part|More], Options) :- 122 http_read_header(Stream, HTTPHeader), 123 part_header(HTTPHeader, Params, Name, Encoding), 124 part_value(Stream, Name, Params, Encoding, Part, Options), 125 debug(multipart(content), 'Got ~q~n', [Part]), 126 ( multipart_open_next(Stream) 127 -> process_parts(Stream, More, Options) 128 ; More = [] 129 ). 130 131set_encoding(text, Stream, _) :- 132 !, 133 ( set_stream(Stream, encoding(bom)) 134 -> ( debugging(multipart(bom)) 135 -> stream_property(Stream, encoding(Enc)), 136 debug(multipart(bom), "BOM: ~q", [Enc]) 137 ; true 138 ) 139 ; set_stream(Stream, encoding(iso_latin_1)) % RFC2616, sec. 3.7.1 140 ). 141set_encoding(input, Stream, Options) :- 142 !, 143 option(input_encoding(Enc), Options, utf8), 144 set_stream(Stream, encoding(Enc)). 145set_encoding(Enc, Stream, _) :- 146 set_stream(Stream, encoding(Enc)).
filename(Name)
media(Type/SubType, MediaParams)
157part_header(PartHeader, Extra, Name, Encoding) :- 158 memberchk(content_disposition(disposition('form-data', DProps)), 159 PartHeader), 160 memberchk(name=Name, DProps), 161 ( filename(DProps, Extra, Extra1) 162 -> part_encoding(PartHeader, Extra1, Encoding) 163 ; Encoding = input, 164 Extra = [] 165 ). 166 167filename(DProps, Extra, Tail) :- 168 memberchk(filename=FileName, DProps), 169 !, 170 Extra = [filename(FileName)|Tail]. 171 172part_encoding(PartHeader, Extra, Encoding) :- 173 memberchk(content_type(TypeA), PartHeader), 174 http_parse_header_value(content_type, TypeA, MediaType), 175 !, 176 Extra = [MediaType], 177 media_type_encoding(MediaType, Encoding). 178 179media_type_encoding(media(_Type, Params), Encoding) :- 180 memberchk(charset=CharSet, Params), 181 charset_encoding(CharSet, Encoding). 182media_type_encoding(media(Type/SubType, _Params), Encoding) :- 183 media_encoding(Type, SubType, Encoding). 184 185charset_encoding(CharSet, utf8) :- 186 sub_atom_icasechk(CharSet, _, 'utf-8'), 187 !. 188charset_encoding(_, octet). 189 190media_encoding(text, _, text) :- !. 191media_encoding(_, _, octet).
196part_value(Stream, Name, Params, Encoding, Part, Options) :- 197 option(form_data(mime), Options), 198 !, 199 set_encoding(Encoding, Stream, Options), 200 Part = mime([disposition('form-data'),name(Name)|Properties], Atom, []), 201 mime_properties(Params, Properties), 202 read_string(Stream, _, String), 203 atom_string(Atom, String). 204part_value(Stream, Name, Params, _, Name=Value, Options) :- 205 memberchk(filename(_), Params), 206 option(on_filename(Goal), Options), 207 !, 208 call(Goal, Stream, Value, [name(Name)|Params]). 209part_value(Stream, Name, _, Encoding, Name=Value, Options) :- 210 set_encoding(Encoding, Stream, Options), 211 read_string(Stream, _, String), 212 atom_string(Value, String). 213 214mime_properties([], []). 215mime_properties([media(Type/SubType, Params)|T0], 216 [type(ContentType)|T]) :- 217 !, 218 atomic_list_concat([Type, SubType], /, ContentType), 219 ( memberchk(charset(CharSet), Params) 220 -> T = [character_set(CharSet)|T1] 221 ; T = T1 222 ), 223 mime_properties(T0, T1). 224mime_properties([H|T0], [H|T]) :- 225 mime_properties(T0, T). 226 227 228http_parametersform_data_content_type(ContentType) :- 229 sub_atom(ContentType, 0, _, _, 'multipart/form-data')
Multipart form-data plugin
This plugin for
library(http_client)
automatically translates messages with content-typemultipart/form-data
into a list of Name = Value pairs, greatly simplifying the processing of forms with this type.After loading this plugin, multipart form-data can be accessed through http_parameters/3 from
library(http/http_parameters)
or http_read_data/3 fromlibrary(http/http_client)
. */