View source with raw comments or as raw
    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)).

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). */

   55:- multifile
   56    http_client:http_convert_data/4,
   57    http_parameters:form_data_content_type/1.
 http_client:http_convert_data(+In, +Fields, -Data, +Options) is semidet
Convert multipart/form-data messages for http_read_data/3. This plugin adds the folling options to http_read_data/3:
form_data(+AsForm)
If the content-type is multipart/form-data, return the form-data either in one of the following formats:
AsForm=form
A list of Name=Value, where Value is an atom.
AsForm=mime
A list of 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.
input_encoding(+Encoding)
Encoding to be used for parts that have no 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.
on_filename(:CallBack)
If a part with a 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_client:http_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_type(+Type, -Boundary) is semidet
True if Type is of the form 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)).
 part_header(+PartHeader, -Params, -Name, -Encoding) is det
Extract the form-field Name, the content Encoding and possible other properties of the form-field. Extra properties are:
  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).
 part_value(+Stream, +Name, +Params, +Encoding, -Part, +Options)
  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_parameters:form_data_content_type(ContentType) :-
  229    sub_atom(ContentType, 0, _, _, 'multipart/form-data')