35
36:- module(http_parameters,
37 [ http_parameters/2, 38 http_parameters/3, 39
40 http_convert_parameter/4, 41 http_convert_parameters/2, 42 http_convert_parameters/3 43 ]). 44:- use_module(http_client). 45:- use_module(http_multipart_plugin). 46:- use_module(http_hook). 47:- use_module(library(debug)). 48:- use_module(library(option)). 49:- use_module(library(error)). 50:- use_module(library(broadcast)). 51
52:- multifile
53 http:convert_parameter/3. 54
55:- predicate_options(http_parameters/3, 3,
56 [ form_data(-list),
57 attribute_declarations(callable)
58 ]). 59
81
82:- meta_predicate
83 http_parameters(+, ?, :),
84 http_convert_parameters(+, ?, 2). 85
130
131http_parameters(Request, Params) :-
132 http_parameters(Request, Params, []).
133
134http_parameters(Request, Params, Options) :-
135 must_be(list, Params),
136 meta_options(is_meta, Options, QOptions),
137 option(attribute_declarations(DeclGoal), QOptions, no_decl_goal),
138 http_parms(Request, Params, DeclGoal, Form),
139 ( memberchk(form_data(RForm), QOptions)
140 -> RForm = Form
141 ; true
142 ).
143
144is_meta(attribute_declarations).
145
146
147http_parms(Request, Params, DeclGoal, Search) :-
148 memberchk(search(Search), Request),
149 !,
150 fill_parameters(Params, Search, DeclGoal).
151http_parms(Request, Params, DeclGoal, Data) :-
152 memberchk(method(Method), Request),
153 Method == post,
154 memberchk(content_type(Content), Request),
155 form_data_content_type(Content),
156 !,
157 debug(post_request, 'POST Request: ~p', [Request]),
158 posted_form(Request, Data),
159 fill_parameters(Params, Data, DeclGoal).
160http_parms(_Request, Params, DeclGoal, []) :-
161 fill_parameters(Params, [], DeclGoal).
162
163:- multifile
164 form_data_content_type/1. 165
166form_data_content_type('application/x-www-form-urlencoded') :- !.
167form_data_content_type(ContentType) :-
168 sub_atom(ContentType, 0, _, _, 'application/x-www-form-urlencoded;').
169
174
175posted_form(Request, _Data) :-
176 nb_current(http_post_data, read),
177 !,
178 option(request_uri(URI), Request),
179 throw(error(permission_error('re-read', 'POST data', URI),
180 context(_, 'Attempt to re-read POST data'))).
181posted_form(Request, Data) :-
182 http_read_data(Request, Data, []),
183 nb_setval(http_post_data, read),
184 debug(post, 'POST Data: ~p', [Data]).
185
186wipe_posted_data :-
187 debug(post, 'Wiping posted data', []),
188 nb_delete(http_post_data).
189
190:- listen(http(request_finished(_Id, _Code, _Status, _CPU, _Bytes)),
191 wipe_posted_data). 192
193
197
198:- meta_predicate fill_parameters(+, +, 2). 199
200fill_parameters([], _, _).
201fill_parameters([H|T], FormData, DeclGoal) :-
202 fill_parameter(H, FormData, DeclGoal),
203 fill_parameters(T, FormData, DeclGoal).
204
205fill_parameter(H, _, _) :-
206 var(H),
207 !,
208 instantiation_error(H).
209fill_parameter(group(Members, _Options), FormData, DeclGoal) :-
210 is_list(Members),
211 !,
212 fill_parameters(Members, FormData, DeclGoal).
213fill_parameter(H, FormData, _) :-
214 H =.. [Name,Value,Options],
215 !,
216 fill_param(Name, Value, Options, FormData).
217fill_parameter(H, FormData, DeclGoal) :-
218 H =.. [Name,Value],
219 ( DeclGoal \== (-),
220 call(DeclGoal, Name, Options)
221 -> true
222 ; throw(error(existence_error(attribute_declaration, Name), _))
223 ),
224 fill_param(Name, Value, Options, FormData).
225
226fill_param(Name, Values, Options, FormData) :-
227 memberchk(zero_or_more, Options),
228 !,
229 fill_param_list(FormData, Name, Values, Options).
230fill_param(Name, Values, Options, FormData) :-
231 memberchk(list(Type), Options),
232 !,
233 fill_param_list(FormData, Name, Values, [Type|Options]).
234fill_param(Name, Value, Options, FormData) :-
235 ( memberchk(Name=Value0, FormData),
236 Value0 \== '' 237 -> http_convert_parameter(Options, Name, Value0, Value)
238 ; memberchk(default(Value), Options)
239 -> true
240 ; memberchk(optional(true), Options)
241 -> true
242 ; throw(error(existence_error(http_parameter, Name), _))
243 ).
244
245
246fill_param_list([], _, [], _).
247fill_param_list([Name=Value0|Form], Name, [Value|VT], Options) :-
248 !,
249 http_convert_parameter(Options, Name, Value0, Value),
250 fill_param_list(Form, Name, VT, Options).
251fill_param_list([_|Form], Name, VT, Options) :-
252 fill_param_list(Form, Name, VT, Options).
253
254
267
268http_convert_parameters(Data, ParamDecls) :-
269 fill_parameters(ParamDecls, Data, no_decl_goal).
270http_convert_parameters(Data, ParamDecls, DeclGoal) :-
271 fill_parameters(ParamDecls, Data, DeclGoal).
272
273no_decl_goal(_,_) :- fail.
274
285
286http_convert_parameter([], _, Value, Value).
287http_convert_parameter([H|T], Field, Value0, Value) :-
288 ( check_type_no_error(H, Value0, Value1)
289 -> catch(http_convert_parameter(T, Field, Value1, Value),
290 error(Formal, _),
291 throw(error(Formal, context(_, http_parameter(Field)))))
292 ; throw(error(type_error(H, Value0),
293 context(_, http_parameter(Field))))
294 ).
295
296check_type_no_error(Type, In, Out) :-
297 http:convert_parameter(Type, In, Out),
298 !.
299check_type_no_error(Type, In, Out) :-
300 check_type3(Type, In, Out).
301
305
306check_type3((T1;T2), In, Out) :-
307 !,
308 ( check_type_no_error(T1, In, Out)
309 -> true
310 ; check_type_no_error(T2, In, Out)
311 ).
312check_type3(string, Atom, String) :-
313 !,
314 to_string(Atom, String).
315check_type3(number, Atom, Number) :-
316 !,
317 to_number(Atom, Number).
318check_type3(integer, Atom, Integer) :-
319 !,
320 to_number(Atom, Integer),
321 integer(Integer).
322check_type3(nonneg, Atom, Integer) :-
323 !,
324 to_number(Atom, Integer),
325 integer(Integer),
326 Integer >= 0.
327check_type3(float, Atom, Float) :-
328 !,
329 to_number(Atom, Number),
330 Float is float(Number).
331check_type3(between(Low, High), Atom, Value) :-
332 !,
333 to_number(Atom, Number),
334 ( (float(Low) ; float(High))
335 -> Value is float(Number)
336 ; Value = Number
337 ),
338 is_of_type(between(Low, High), Value).
339check_type3(boolean, Atom, Bool) :-
340 !,
341 truth(Atom, Bool).
342check_type3(Type, Atom, Atom) :-
343 check_type2(Type, Atom).
344
345to_number(In, Number) :-
346 number(In), !, Number = In.
347to_number(In, Number) :-
348 atom(In),
349 atom_number(In, Number).
350
351to_string(In, String) :- string(In), !, String = In.
352to_string(In, String) :- atom(In), !, atom_string(In, String).
353to_string(In, String) :- number(In), !, number_string(In, String).
354
358
359check_type2(oneof(Set), Value) :-
360 !,
361 memberchk(Value, Set).
362check_type2(length > N, Value) :-
363 !,
364 atom_length(Value, Len),
365 Len > N.
366check_type2(length >= N, Value) :-
367 !,
368 atom_length(Value, Len),
369 Len >= N.
370check_type2(length < N, Value) :-
371 !,
372 atom_length(Value, Len),
373 Len < N.
374check_type2(length =< N, Value) :-
375 !,
376 atom_length(Value, Len),
377 Len =< N.
378check_type2(_, _).
379
384
385truth(true, true).
386truth('TRUE', true).
387truth(yes, true).
388truth('YES', true).
389truth(on, true).
390truth('ON', true). 391truth('1', true).
392
393truth(false, false).
394truth('FALSE', false).
395truth(no, false).
396truth('NO', false).
397truth(off, false).
398truth('OFF', false).
399truth('0', false).
400
401
402 405
406:- multifile
407 prolog:called_by/2,
408 emacs_prolog_colours:goal_colours/2. 409
410prolog:called_by(http_parameters(_,_,Options), [G+2]) :-
411 option(attribute_declarations(G), Options, _),
412 callable(G),
413 !.
414
415emacs_prolog_colours:goal_colours(http_parameters(_,_,Options),
416 built_in-[classify, classify, Colours]) :-
417 option_list_colours(Options, Colours).
418
419option_list_colours(Var, error) :-
420 var(Var),
421 !.
422option_list_colours([], classify) :- !.
423option_list_colours(Term, list-Elements) :-
424 Term = [_|_],
425 !,
426 option_list_colours_2(Term, Elements).
427option_list_colours(_, error).
428
429option_list_colours_2(Var, classify) :-
430 var(Var).
431option_list_colours_2([], []).
432option_list_colours_2([H0|T0], [H|T]) :-
433 option_colours(H0, H),
434 option_list_colours_2(T0, T).
435
436option_colours(Var, classify) :-
437 var(Var),
438 !.
439option_colours(_=_, built_in-[classify,classify]) :- !.
440option_colours(attribute_declarations(_), 441 option(attribute_declarations)-[dcg]) :- !.
442option_colours(Term, option(Name)-[classify]) :-
443 compound(Term),
444 Term =.. [Name,_Value],
445 !.
446option_colours(_, error).
447
448 451
452:- multifile prolog:error_message//1. 453:- multifile prolog:message//1. 454
455prolog:error_message(existence_error(http_parameter, Name)) -->
456 [ 'Missing value for parameter "~w".'-[Name] ].
457prolog:message(error(type_error(Type, Term), context(_, http_parameter(Param)))) -->
458 { atom(Param) },
459 [ 'Parameter "~w" must be '-[Param] ],
460 param_type(Type),
461 ['. Found "~w".'-[Term] ].
462
463param_type(length>N) -->
464 !,
465 ['longer than ~D characters'-[N]].
466param_type(length>=N) -->
467 !,
468 ['at least ~D characters'-[N]].
469param_type(length<N) -->
470 !,
471 ['shorter than ~D characters'-[N]].
472param_type(length=<N) -->
473 !,
474 ['at most ~D characters'-[N]].
475param_type(between(Low,High)) -->
476 !,
477 ( {float(Low);float(High)}
478 -> ['a number between ~w and ~w'-[Low,High]]
479 ; ['an integer between ~w and ~w'-[Low,High]]
480 ).
481param_type(oneof([Only])) -->
482 !,
483 ['"~w"'-[Only]].
484param_type(oneof(List)) -->
485 !,
486 ['one of '-[]], oneof(List).
487param_type(T) -->
488 ['of type ~p'-[T]].
489
490
491oneof([]) --> [].
492oneof([H|T]) -->
493 ['"~w"'-[H]],
494 ( {T == []}
495 -> []
496 ; {T = [Last]}
497 -> [' or "~w"'-[Last] ]
498 ; [', '-[]],
499 oneof(T)
500 )