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-2022, University of Amsterdam 7 VU University Amsterdam 8 SWI-Prolog Solutions b.v. 9 All rights reserved. 10 11 Redistribution and use in source and binary forms, with or without 12 modification, are permitted provided that the following conditions 13 are met: 14 15 1. Redistributions of source code must retain the above copyright 16 notice, this list of conditions and the following disclaimer. 17 18 2. Redistributions in binary form must reproduce the above copyright 19 notice, this list of conditions and the following disclaimer in 20 the documentation and/or other materials provided with the 21 distribution. 22 23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 POSSIBILITY OF SUCH DAMAGE. 35*/ 36 37:- module(http_multipart_plugin, 38 [ 39 ]). 40:- use_module(http_stream). 41:- use_module(http_header). 42:- use_module(library(debug)). 43:- use_module(library(option)).
56:- multifile
57 http_client:http_convert_data/4,
58 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.
101http_clienthttp_convert_data(In, Fields, Data, Options) :-
102 memberchk(content_type(Type), Fields),
103 multipart_type(Type, Boundary),
104 !,
105 setup_call_cleanup(
106 multipart_open(In, Stream, [boundary(Boundary)]),
107 process_parts(Stream, Data, Options),
108 close(Stream)).
multipart/form-data;
boundary="..."
and Boundary is a string describing the
boundary.116multipart_type(Type, Boundary) :- 117 http_parse_header_value(content_type, Type, 118 media(multipart/'form-data', Params)), 119 memberchk(boundary=Boundary, Params). 120 121 122process_parts(Stream, [Part|More], Options) :- 123 http_read_header(Stream, HTTPHeader), 124 part_header(HTTPHeader, Params, Name, Encoding), 125 part_value(Stream, Name, Params, Encoding, Part, Options), 126 debug(multipart(content), 'Got ~q~n', [Part]), 127 ( multipart_open_next(Stream) 128 -> process_parts(Stream, More, Options) 129 ; More = [] 130 ). 131 132set_encoding(text, Stream, _) :- 133 !, 134 ( set_stream(Stream, encoding(bom)) 135 -> ( debugging(multipart(bom)) 136 -> stream_property(Stream, encoding(Enc)), 137 debug(multipart(bom), "BOM: ~q", [Enc]) 138 ; true 139 ) 140 ; set_stream(Stream, encoding(iso_latin_1)) % RFC2616, sec. 3.7.1 141 ). 142set_encoding(input, Stream, Options) :- 143 !, 144 option(input_encoding(Enc), Options, utf8), 145 set_stream(Stream, encoding(Enc)). 146set_encoding(Enc, Stream, _) :- 147 set_stream(Stream, encoding(Enc)).
filename(Name)
media(Type/SubType, MediaParams)
158part_header(PartHeader, Extra, Name, Encoding) :- 159 memberchk(content_disposition(disposition('form-data', DProps)), 160 PartHeader), 161 memberchk(name=Name, DProps), 162 ( filename(DProps, Extra, Extra1) 163 -> part_encoding(PartHeader, Extra1, Encoding) 164 ; Encoding = input, 165 Extra = [] 166 ). 167 168filename(DProps, Extra, Tail) :- 169 memberchk(filename=FileName, DProps), 170 !, 171 Extra = [filename(FileName)|Tail]. 172 173part_encoding(PartHeader, Extra, Encoding) :- 174 memberchk(content_type(TypeA), PartHeader), 175 http_parse_header_value(content_type, TypeA, MediaType), 176 !, 177 Extra = [MediaType], 178 media_type_encoding(MediaType, Encoding). 179 180media_type_encoding(media(_Type, Params), Encoding) :- 181 memberchk(charset=CharSet, Params), 182 charset_encoding(CharSet, Encoding). 183media_type_encoding(media(Type/SubType, _Params), Encoding) :- 184 media_encoding(Type, SubType, Encoding). 185 186charset_encoding(CharSet, utf8) :- 187 sub_atom_icasechk(CharSet, _, 'utf-8'), 188 !. 189charset_encoding(_, octet). 190 191media_encoding(text, _, text) :- !. 192media_encoding(_, _, octet).
197part_value(Stream, Name, Params, Encoding, Part, Options) :- 198 option(form_data(mime), Options), 199 !, 200 set_encoding(Encoding, Stream, Options), 201 Part = mime([disposition('form-data'),name(Name)|Properties], Atom, []), 202 mime_properties(Params, Properties), 203 read_string(Stream, _, String), 204 atom_string(Atom, String). 205part_value(Stream, Name, Params, _Encoding, Name=Value, Options) :- 206 memberchk(filename(_), Params), 207 option(on_filename(Goal), Options), 208 !, 209% Always save files binary 210% set_encoding(Encoding, Stream, Options), 211 call(Goal, Stream, Value, [name(Name)|Params]). 212part_value(Stream, Name, _, Encoding, Name=Value, Options) :- 213 set_encoding(Encoding, Stream, Options), 214 read_string(Stream, _, String), 215 atom_string(Value, String). 216 217mime_properties([], []). 218mime_properties([media(Type/SubType, Params)|T0], 219 [type(ContentType)|T]) :- 220 !, 221 atomic_list_concat([Type, SubType], /, ContentType), 222 ( memberchk(charset(CharSet), Params) 223 -> T = [character_set(CharSet)|T1] 224 ; T = T1 225 ), 226 mime_properties(T0, T1). 227mime_properties([H|T0], [H|T]) :- 228 mime_properties(T0, T). 229 230 231http_parametersform_data_content_type(ContentType) :- 232 sub_atom(ContentType, 0, _, _, 'multipart/form-data')
Multipart form-data plugin
This plugin for library(http_client) automatically translates messages with content-type
multipart/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 from library(http/http_client). */