package eliom

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file eliom_service.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# 1 "src/lib/eliom_service.server.ml"
(* Ocsigen
 * http://www.ocsigen.org
 * Copyright (C) 2007 Vincent Balat
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, with linking exception;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *)

include Eliom_service_base

let plain_service (type m gp gn pp pn gp') ?(https = false) ~path
    ?keep_nl_params ?priority ~(meth : (m, gp, gn, pp, pn, _, gp') meth) ()
  =
  let get_params, post_params = params_of_meth meth
  and meth = which_meth_internal meth in
  let redirect_suffix = Eliom_parameter.contains_suffix get_params in
  let path =
    (match redirect_suffix with
      | None -> path
      | Some _ -> path @ [Eliom_common.eliom_suffix_internal_name])
    |> Url.remove_slash_at_beginning |> Url.change_empty_list
    |> Url.remove_internal_slash
  in
  (if Eliom_common.get_sp_option () = None
   then
     match Eliom_common.global_register_allowed () with
     | Some current_site_data ->
         Eliom_common.add_unregistered (current_site_data ()) path
     | None ->
         raise (Eliom_common.Eliom_site_information_not_available "service"));
  let reload_fun = Rf_client_fun in
  main_service ~https ~prefix:"" ~path ~kind:`Service ~meth ?redirect_suffix
    ?keep_nl_params ?priority ~get_params ~post_params ~reload_fun ()

let create_attached ?name ?(csrf_safe = false) ?csrf_scope ?csrf_secure ?max_use
    ?timeout ?(https = false) ?keep_nl_params ~fallback ~get_params ~post_params
    ~meth ()
  =
  let is_post = is_post' meth in
  let csrf_scope = default_csrf_scope csrf_scope
  and get_params_type, post_params_type =
    if is_post
    then
      ( get_params
      , Eliom_parameter.add_pref_params Eliom_common.co_param_prefix post_params
      )
    else
      ( Eliom_parameter.add_pref_params Eliom_common.co_param_prefix get_params
      , post_params )
  and k = attached_info fallback in
  { pre_applied_parameters = fallback.pre_applied_parameters
  ; get_params_type
  ; post_params_type
  ; send_appl_content = fallback.send_appl_content
  ; service_mark = service_mark ()
  ; max_use
  ; timeout
  ; meth
  ; kind = `AttachedCoservice
  ; info =
      (let att_name =
         if csrf_safe
         then
           Eliom_common.SAtt_csrf_safe
             (uniqueid (), (csrf_scope :> Eliom_common.user_scope), csrf_secure)
         else
           match name with
           | None -> Eliom_common.SAtt_anon (new_state ())
           | Some name -> Eliom_common.SAtt_named name
       in
       Attached
         { k with
           get_name = (if is_post then k.get_name else att_name)
         ; post_name = (if not is_post then k.post_name else att_name) })
  ; https = https || fallback.https
  ; keep_nl_params =
      (match keep_nl_params with
      | None -> fallback.keep_nl_params
      | Some k -> k)
  ; client_fun = no_client_fun ()
  ; reload_fun = Rf_client_fun }

let create_attached_get =
  create_attached ~meth:Get' ~post_params:Eliom_parameter.unit

let create_attached_post ?name ?csrf_safe ?csrf_scope ?csrf_secure ?max_use
    ?timeout ?https ?keep_nl_params ~fallback ~post_params ()
  =
  let get_params = get_params_type fallback in
  create_attached ~meth:Post' ?name ?csrf_safe ?csrf_scope ?csrf_secure ?max_use
    ?timeout ?https ?keep_nl_params ~fallback ~post_params ~get_params ()

let create_attached_get_unsafe = create_attached_get
let create_attached_post_unsafe = create_attached_post

let coservice' (type m gp gn pp pn) ?name ?(csrf_safe = false) ?csrf_scope
    ?csrf_secure ?max_use ?timeout ?(https = false)
    ?(keep_nl_params = `Persistent) ~(meth : (m, gp, gn, pp, pn, _, unit) meth)
    ()
  =
  let get_params, post_params = params_of_meth meth in
  let meth = which_meth_internal meth and is_post = is_post meth in
  let csrf_scope = default_csrf_scope csrf_scope in
  { max_use
  ; timeout
  ; pre_applied_parameters = Eliom_lib.String.Table.empty, []
  ; get_params_type =
      Eliom_parameter.add_pref_params Eliom_common.na_co_param_prefix get_params
  ; post_params_type = post_params
  ; meth
  ; kind = `NonattachedCoservice
  ; info =
      Nonattached
        { na_name =
            (if csrf_safe
             then
               if is_post
               then
                 Eliom_common.SNa_post_csrf_safe
                   ( uniqueid ()
                   , (csrf_scope :> Eliom_common.user_scope)
                   , csrf_secure )
               else
                 Eliom_common.SNa_get_csrf_safe
                   ( uniqueid ()
                   , (csrf_scope :> Eliom_common.user_scope)
                   , csrf_secure )
             else
               match name, is_post with
               | None, true -> Eliom_common.SNa_post' (new_state ())
               | None, false -> Eliom_common.SNa_get' (new_state ())
               | Some name, true -> Eliom_common.SNa_post_ name
               | Some name, false -> Eliom_common.SNa_get_ name)
        ; keep_get_na_params = true }
  ; https
  ; keep_nl_params
  ; send_appl_content = XNever
  ; service_mark = service_mark ()
  ; client_fun = no_client_fun ()
  ; reload_fun = Rf_client_fun }

let create ?name ?(csrf_safe = false) ?csrf_scope ?csrf_secure ?max_use ?timeout
    ?(https = false) ?(keep_nl_params = `Persistent) ?priority
    (type m gp gn pp pn gp' att_ co_ ext_ reg_ rr)
    ~(meth : (m, gp, gn, pp, pn, _, gp') meth)
    ~(path : (att_, co_, gp') path_option) () :
    (gp, pp, m, att_, co_, ext_, reg_, _, gn, pn, rr) t
  =
  match path with
  | Path path -> plain_service ~https ~keep_nl_params ?priority ~path ~meth ()
  | No_path ->
      coservice' ?name ~csrf_safe ?csrf_scope ?csrf_secure ?max_use ?timeout
        ~https ~keep_nl_params ~meth ()

let create_unsafe = create
let create_ocaml = create

let attach :
     fallback:
       ( unit
         , unit
         , get
         , att
         , _
         , non_ext
         , 'rg1
         , [< suff]
         , unit
         , unit
         , 'return1 )
         t
    -> service:
         ( 'get
           , 'post
           , 'gp
           , non_att
           , co
           , non_ext
           , 'rg2
           , ([< `WithoutSuffix] as 'sf)
           , 'gn
           , 'pn
           , 'return )
           t
    -> unit
    -> ('get, 'post, 'gp, att, co, non_ext, non_reg, 'sf, 'gn, 'pn, 'return) t
  =
 fun ~fallback ~service () ->
  let {na_name; _} = non_attached_info service in
  let fallbackkind = attached_info fallback in
  let open Eliom_common in
  let error_msg =
    "attach' is not implemented for this kind ofservice. Please report a bug if you need this."
  in
  let get_name =
    match na_name with
    | SNa_get_ s -> SAtt_na_named s
    | SNa_get' s -> SAtt_na_anon s
    | SNa_get_csrf_safe a -> SAtt_na_csrf_safe a
    | SNa_post_ _ -> fallbackkind.get_name (*VVV check *)
    | SNa_post' _ -> fallbackkind.get_name (*VVV check *)
    | SNa_post_csrf_safe _ -> fallbackkind.get_name (*VVV check *)
    | _ -> failwith error_msg
  (*VVV Do we want to make possible to attach POST na coservices
          on GET attached coservices? *)
  and post_name =
    match na_name with
    | SNa_get_ _ -> SAtt_no
    | SNa_get' _ -> SAtt_no
    | SNa_get_csrf_safe _ -> SAtt_no
    | SNa_post_ s -> SAtt_na_named s
    | SNa_post' s -> SAtt_na_anon s
    | SNa_post_csrf_safe a -> SAtt_na_csrf_safe a
    | _ -> failwith error_msg
  in
  { service with
    service_mark = service_mark ()
  ; kind = `AttachedCoservice
  ; pre_applied_parameters = fallback.pre_applied_parameters
  ; info = Attached {fallbackkind with get_name; post_name} }

exception Wrong_session_table_for_CSRF_safe_coservice

let eliom_appl_answer_content_type = "application/x-eliom"

(* If there is a client side process, we do an XHR with tab cookies *)
let xhr_with_cookies s =
  if is_external s
  then None
  else
    match s.send_appl_content with
    | XAlways -> Some None
    | XNever ->
        None
        (* actually this will be tested again later in
              get_onload_form_creators *)
    | XSame_appl (_, tmpl) -> Some tmpl

(* Some an = current_page_appl_name *)
(* for now we do not know the current_page_appl_name.  We will know it
   only after calling send.  In case it is not the same name, we will
   not send the onload_form_creator_info. *)

let register_eliom_module name f =
  Ocsigen_loader.set_module_init_function name f

exception Unregistered_CSRF_safe_coservice

let register_delayed_get_or_na_coservice ~sp (k, scope, secure) =
  let f =
    try
      let table =
        !(Eliom_state.get_session_service_table_if_exists ~sp
            ~scope:(scope :> Eliom_common.user_scope)
            ?secure ())
      in
      Eliom_lib.Int.Table.find k
        table.Eliom_common.csrf_get_or_na_registration_functions
    with Not_found -> (
      let table = Eliom_state.get_global_table () in
      try
        Eliom_lib.Int.Table.find k
          table.Eliom_common.csrf_get_or_na_registration_functions
      with Not_found -> raise Unregistered_CSRF_safe_coservice)
  in
  f ~sp

let register_delayed_post_coservice ~sp (k, scope, secure) getname =
  let f =
    try
      let table =
        !(Eliom_state.get_session_service_table_if_exists ~sp
            ~scope:(scope :> Eliom_common.user_scope)
            ?secure ())
      in
      Eliom_lib.Int.Table.find k
        table.Eliom_common.csrf_post_registration_functions
    with Not_found -> (
      let table = Eliom_state.get_global_table () in
      try
        Eliom_lib.Int.Table.find k
          table.Eliom_common.csrf_post_registration_functions
      with Not_found -> raise Unregistered_CSRF_safe_coservice)
  in
  f ~sp getname

let set_delayed_get_or_na_registration_function tables k f =
  tables.Eliom_common.csrf_get_or_na_registration_functions <-
    Eliom_lib.Int.Table.add k f
      tables.Eliom_common.csrf_get_or_na_registration_functions

let set_delayed_post_registration_function tables k f =
  tables.Eliom_common.csrf_post_registration_functions <-
    Eliom_lib.Int.Table.add k f
      tables.Eliom_common.csrf_post_registration_functions

let remove_service table (type m a)
    (service : (_, _, m, a, _, _, _, _, _, _, _) t)
  =
  match info service with
  | Attached attser ->
      let key_kind = which_meth_untyped service in
      let attserget = get_name attser in
      let attserpost = post_name attser in
      let sgpt = get_params_type service in
      let sppt = post_params_type service in
      Eliom_route.remove_service table (sub_path attser)
        { Eliom_common.key_state = attserget, attserpost
        ; Eliom_common.key_meth = key_kind }
        (if attserget = Eliom_common.SAtt_no
            || attserpost = Eliom_common.SAtt_no
         then
           Eliom_parameter.(
             anonymise_params_type sgpt, anonymise_params_type sppt)
         else 0, 0)
  | Nonattached naser ->
      let na_name = na_name naser in
      Eliom_route.remove_naservice table na_name

let unregister ?scope ?secure (type m)
    (service : (_, _, m, _, _, _, _, _, _, _, _) t)
  =
  let sp = Eliom_common.get_sp_option () in
  match scope with
  | None | Some `Site ->
      let table =
        match sp with
        | None -> (
          match Eliom_common.global_register_allowed () with
          | Some get_current_sitedata ->
              let sitedata = get_current_sitedata () in
              sitedata.Eliom_common.global_services
          | _ ->
              raise
                (Eliom_common.Eliom_site_information_not_available "unregister")
          )
        | Some _ -> Eliom_state.get_global_table ()
      in
      remove_service table service
  | Some (#Eliom_common.user_scope as scope) -> (
    match sp with
    | None ->
        raise
          (failwith
             "Unregistering service for non global scope must be done during a request")
    | Some sp ->
        let table =
          !(Eliom_state.get_session_service_table ~sp ?secure ~scope ())
        in
        remove_service table service)

let client_fun _ = None
let has_client_fun _ = false
OCaml

Innovation. Community. Security.