Commit 0f0689d5ee2c708265bd7e70107318ac787dee4d

Authored by Julien Verneuil
1 parent 8645a410

add CXM_jq_radio, replace url (String) argument of all jq_confirm_button to use …

…a JQuery_Actioner instead and update subsequent code thorough the project, some whitespaces may have been added to some files...
web/CXM_jquery.anubis
... ... @@ -3,7 +3,7 @@
3 3 * User: Jeremy
4 4 * Date: 21/08/2012
5 5 * Time: 16:52
6   - *
  6 + *
7 7 */
8 8  
9 9 read calexium_lib/web/CXM_making_a_web_site.anubis
... ... @@ -26,6 +26,69 @@ public type JQuerySelector:
26 26 element_class(String name),
27 27 childs(HtmlId id).
28 28  
  29 +define String
  30 + jquery_button_get_onclick_action_window_options
  31 + (
  32 + List(Other_Window_Option) options,
  33 + String formated_options
  34 + )
  35 + =
  36 + //if there is already formated_options add a comma + space between the options
  37 + with comma = if formated_options="" then "" else ", ",
  38 +
  39 + if options is
  40 + {
  41 + [ ] then formated_options,
  42 + [ h . t ] then
  43 + if h is
  44 + {
  45 + resizable then jquery_button_get_onclick_action_window_options(
  46 + t, formated_options+ comma+ "resizable=yes" ),
  47 +
  48 + scrollbars then jquery_button_get_onclick_action_window_options(
  49 + t, formated_options+ comma + "scrollbars=yes"),
  50 +
  51 + width(w) then jquery_button_get_onclick_action_window_options(
  52 + t, formated_options+ comma + "width="+w),
  53 +
  54 + height(h) then jquery_button_get_onclick_action_window_options(
  55 + t, formated_options+ comma + "height="+h)
  56 + }
  57 + }
  58 + .
  59 +
  60 +public define String
  61 + jquery_button_get_onclick_action
  62 + (
  63 + JQuery_Actioner actioner
  64 + )
  65 + =
  66 + if actioner is jQuery_actioner(target, act_type, str) then
  67 + if target is
  68 + {
  69 + same then
  70 + if act_type is
  71 + {
  72 + jqform then "$('#"+str+"').submit();",
  73 + jqlink then "document.location='/?a="+str+"';",
  74 + jqscript then str
  75 + },
  76 + same(label) then
  77 + if act_type is
  78 + {
  79 + jqform then "$('#"+str+"').submit();",
  80 + jqlink then "document.location='/?a="+str+"';",
  81 + jqscript then str
  82 + },
  83 + other(window_name, window_options) then
  84 + if act_type is
  85 + {
  86 + jqform then "$('#"+str+"').submit();", //il s'agit d'une soumision d'un formulaire => cela se fait uniquement dans la même fenêtre !
  87 + jqlink then "window.open('"+str+"', '"+window_name+"', '"+jquery_button_get_onclick_action_window_options(window_options, "")+"');",
  88 + jqscript then str
  89 + }
  90 + }.
  91 +
29 92 public define String
30 93 format_jquery_selector
31 94 (
... ... @@ -35,10 +98,10 @@ public define String
35 98 {
36 99 element(html_id) then
37 100 if html_id is htmlId(id) then "'#" + id + "'",
38   -
  101 +
39 102 element_class(name) then
40 103 "'." + name + "'",
41   -
  104 +
42 105 childs(html_id) then
43 106 if html_id is htmlId(id) then "'#" + id + " > *'"
44 107 }.
... ... @@ -50,14 +113,14 @@ public define Script
50 113 String code //javascript code to execute when the document is ready
51 114 )=
52 115 script("text/javascript","$(document).ready(function(){"+code+"});").
53   -
  116 +
54 117 public define String
55 118 jquery_ready
56 119 (
57 120 String code //javascript code to execute when the document is ready
58 121 )=
59 122 "$(document).ready(function(){"+code+"});".
60   -
  123 +
61 124 /* jQuery default js files */
62 125 define List(HTML_Head_Tag)
63 126 jquery_defaults
... ... @@ -66,7 +129,7 @@ define List(HTML_Head_Tag)
66 129 js(js_file("/js/jquery/jquery-1.11.3.js")),
67 130 js(js_file("/js/jquery/jquery-ui-1.11.4/jquery-ui.min.js")),
68 131 ].
69   -
  132 +
70 133 /* Initialize jQuery in compatiblity mode with jQueryUI */
71 134 public define List(HTML_Head_Tag)
72 135 jquery_init
... ...
web/CXM_json.anubis
... ... @@ -3,7 +3,7 @@
3 3 * User: ricard
4 4 * Date: 18/08/2008
5 5 * Time: 08:12
6   - *
  6 + *
7 7 * To change this template use Tools | Options | Coding | Edit Standard Headers.
8 8 */
9 9  
... ... @@ -38,7 +38,7 @@ public define JsonMember
38 38 List(JsonMember) value,
39 39 ) =
40 40 json_member(name, json_object(value)).
41   -
  41 +
42 42  
43 43 public define JsonMember
44 44 json_member
... ... @@ -72,7 +72,7 @@ public define JsonMember
72 72 String value,
73 73 ) =
74 74 json_member(name, json_string(value)).
75   -
  75 +
76 76 public define JsonMember
77 77 json_member
78 78 (
... ... @@ -123,7 +123,7 @@ public define Printable_tree
123 123 (
124 124 JsonValue json
125 125 ).
126   -
  126 +
127 127 public define Printable_tree
128 128 to_javascript
129 129 (
... ... @@ -137,7 +137,7 @@ public define Printable_tree
137 137 ) =
138 138 if member is json_member(name, value) then
139 139 [ "\"", name, "\":" . format_json(value) ].
140   -
  140 +
141 141 public define Printable_tree
142 142 json_member_to_js
143 143 (
... ... @@ -151,18 +151,18 @@ define Printable_tree
151 151 (
152 152 List(JsonMember) members
153 153 ) =
154   - if members is
  154 + if members is
155 155 {
156 156 [] then [],
157 157 [h . t] then [ format_json_member(h), (if t is [] then "" else ",") . format_json_object(t) ]
158 158 }.
159   -
  159 +
160 160 define Printable_tree
161 161 json_object_to_js
162 162 (
163 163 List(JsonMember) members
164 164 ) =
165   - if members is
  165 + if members is
166 166 {
167 167 [] then [],
168 168 [h . t] then [ json_member_to_js(h), (if t is [] then "" else ",") . json_object_to_js(t) ]
... ... @@ -173,18 +173,18 @@ define Printable_tree
173 173 (
174 174 List(JsonValue) values
175 175 ) =
176   - if values is
  176 + if values is
177 177 {
178 178 [] then [],
179 179 [h . t] then [ format_json(h), (if t is [] then "" else ",") . format_json_array(t) ]
180 180 }.
181   -
  181 +
182 182 define Printable_tree
183 183 json_array_to_js
184 184 (
185 185 List(JsonValue) values
186 186 ) =
187   - if values is
  187 + if values is
188 188 {
189 189 [] then [],
190 190 [h . t] then [ to_javascript(h), (if t is [] then "" else ",") . format_json_array(t) ]
... ... @@ -205,7 +205,7 @@ public define Printable_tree
205 205 json_bool(b) then str_pt(if b then "true" else "false", []),
206 206 json_null then str_pt("null", [])
207 207 }.
208   -
  208 +
209 209 public define Printable_tree
210 210 to_javascript
211 211 (
... ... @@ -221,26 +221,25 @@ public define Printable_tree
221 221 json_bool(b) then str_pt(if b then "true" else "false", []),
222 222 json_null then str_pt("null", [])
223 223 }.
224   -
  224 +
225 225 public define String
226 226 format_json
227 227 (
228 228 JsonValue json
229 229 ) =
230 230 to_String(format_json(json)).
231   -
  231 +
232 232 public define String
233 233 to_javascript
234 234 (
235 235 JsonValue json
236 236 ) =
237 237 to_String(to_javascript(json)).
238   -
239   -public define WriteFileResult
  238 +
  239 +public define WriteFileResult
240 240 write_to_file
241 241 (
242 242 String filename, //filename where the JSON will be dump
243 243 JsonValue json //JSON to dump
244 244 )=
245 245 write_to_file(filename, format_json(json)).
246   -
... ...
web/CXM_multihost_http_server.anubis
Changes suppressed. Click to show
1 1 
2 2 *Project* The Anubis Project
3   -
  3 +
4 4 *Title* A Multi Host HTTP/HTTPS Server
5   -
6   - *Copyright* Copyright (c) Anubis Team 2003-2007.
  5 +
  6 + *Copyright* Copyright (c) Anubis Team 2003-2007.
7 7  
8 8  
9 9 *Authors* Alain Prouté
10 10 David René
11 11 Cédric Ricard
12   -
13 12  
14   - *Revised*
  13 +
  14 + *Revised*
15 15 June 2015 : Optimization of the file upload
16 16 July 2007 : Initial version in calexium_lib
17   -
18   -
19   - *Overviews*
  17 +
  18 +
  19 + *Overviews*
20 20 In this file a HTTP/HTTPS server is defined, which is able to handle multiple hosts
21 21 (virtual hosts). It answers HTTP/HTTPS requests, sends files (images or any other kind
22 22 of file), constructs HTML pages on the fly using informations received from the client
... ... @@ -30,46 +30,46 @@
30 30 ----------------------------------- Table of Contents ---------------------------------
31 31  
32 32 *** (1) Multihosting and redirections.
33   - *** (2) The incompatibility between SSL and virtual hosts.
  33 + *** (2) The incompatibility between SSL and virtual hosts.
34 34 *** (3) HTTP headers and web arguments.
35   - *** (4) Site descriptions.
36   - *** (5) Protection against denial of service attacks.
37   - *** (6) Starting your HTTP and HTTPS servers.
38   - *** (7) Private download.
39   - *** (8) About web argument names.
40   - *** (9) A web dispatcher.
  35 + *** (4) Site descriptions.
  36 + *** (5) Protection against denial of service attacks.
  37 + *** (6) Starting your HTTP and HTTPS servers.
  38 + *** (7) Private download.
  39 + *** (8) About web argument names.
  40 + *** (9) A web dispatcher.
41 41 *** (10) HTTP Errors
42   -
  42 +
43 43 ---------------------------------------------------------------------------------------
44   -
45 44  
46   -
47   -
  45 +
  46 +
  47 +
48 48 *** (1) Multihosting and redirections.
49   -
  49 +
50 50 This HTTP/HTTPS server can handle several host (also called 'virtual hosts'), in other
51 51 words, you may have several sites on the same server, with the same IP address and same
52 52 port numbers, but distinct 'host names'.
53   -
  53 +
54 54 A HTTP request sent by a browser contains the following informations:
55   -
56   - - a 'host name',
  55 +
  56 + - a 'host name',
57 57 - an URI (Uniform Resource Identifier),
58   - - HTTP headers,
59   - - web arguments (in the form 'name=value').
60   -
  58 + - HTTP headers,
  59 + - web arguments (in the form 'name=value').
  60 +
61 61 Actually, the host name is just the value of the HTTP header whose name is 'Host'. The
62 62 host name indicates which site is requested. Hence, it is the primary information for
63 63 branching to the right site. If there is no 'Host' HTTP header in the request, the
64 64 request is denied.
65   -
  65 +
66 66 From now on, we may assume that the host is determined, and consequently that we are
67 67 concerned by only one site. Each site has his own directories on the server's
68   - disk.
69   -
  68 + disk.
  69 +
70 70 Each site also has a list of 'redirections'. A redirection is a triplet, like this one:
71   -
72   - redirect("/", "www.our-business.com", "/homepage.awp")
  71 +
  72 + redirect("/", "www.our-business.com", "/homepage.awp")
73 73  
74 74 meaning that if the host is "www.our-business.com", and if the requested URI is "/",
75 75 then the URI to be served is "/homepage.awp". 'redirect' is a constructor of the type
... ... @@ -81,12 +81,12 @@
81 81 like ".jpg", ".png", ".txt", etc... and represents a file path relative to the
82 82 'public' directory of the site. If these conditions are satisfied, the file is sent to
83 83 the client. Known extensions are recorded in 'web/mime.anubis'.
84   -
85   -
86 84  
87   -
88   - *** (2) The incompatibility between SSL and virtual hosts.
89   -
  85 +
  86 +
  87 +
  88 + *** (2) The incompatibility between SSL and virtual hosts.
  89 +
90 90 Handling virtual hosts makes a problem under SSL (i.e. when using HTTPS), which is due
91 91 to the fact that the guys at Netscape who designed SSL probably did not have the
92 92 question of virtual hosts in mind. Indeed, the SSL handshake is completed before the
... ... @@ -96,7 +96,7 @@
96 96 of the requested host. The user will have to accept the certificate manually, which is
97 97 not good for the security image of the site. This problem has at least two solutions
98 98 (as far as Anubis is concerned).
99   -
  99 +
100 100 Solution 1. Arrange so that the network interface on which the server is listening
101 101 has at least as many different IP addresses as you have virtual hosts. Such
102 102 supplementary IP addresses are called 'IP Aliases'. In this case, start one HTTPS
... ... @@ -105,7 +105,7 @@
105 105 'anbexec' as you have virtual hosts, because each instance of 'anbexec' can handle only
106 106 one server certificate. Of course, getting IP aliases is another problem to be solved
107 107 with your Internet provider.
108   -
  108 +
109 109 Solution 2. We propose a simple solution, using only one server certificate (hence
110 110 only one instance of 'anbexec'). Since, we have only one server certificate, we must
111 111 introduce a notion of 'main host', i.e. a host containing all other 'virtual
... ... @@ -114,48 +114,48 @@
114 114 that the main host redirects him to the right virtual host. Actually, the process will
115 115 be transparent to the client, except that the client will see the name of the main host
116 116 instead of the name of the virtual host in the 'location' field of the browser.
117   -
  117 +
118 118 So, assume that the name of main host is 'www.securedhost.com', and that the names of
119 119 the virtual hosts are:
120   -
  120 +
121 121 actual name simplified name
122 122 -----------------------------------------------------
123 123 www.virtual1.com virtual1
124 124 www.virtual2.com virtual2
125 125 www.virtual3.com virtual3
126   -
  126 +
127 127 Then the (confidential) document '/doc/my_document.pdf' on 'www.virtual2.com' will have
128 128 the URL:
129   -
  129 +
130 130 https://www.securedhost.com/virtual2/doc/my_document.pdf
131   -
  131 +
132 132 In order to work transparently, this solution must combine HTTP and HTTPS. Indeed, the
133 133 vitual host must have a first page reachable under HTTP, through the URL:
134   -
  134 +
135 135 http://www.virtual2.com/
136   -
  136 +
137 137 The HTTP server will redirect this URL to the awp handler of virtual host 'virtual2'.
138 138 The handler of this virtual host is able to generate a first page containing the
139 139 following HTML meta:
140   -
  140 +
141 141 <meta http-equiv="Refresh" content="0;URL=https://www.securedhost.com/virtual2/">,
142   -
  142 +
143 143 so that the client is immediately redirected to the main host under HTTPS (hence
144 144 accepting tranparently the server certificate). The awp handler of 'virtual2' then
145 145 redirects this URL to the home page (maybe a login page) of 'virtual2'.
146   -
147   - See 'web/making_a_web_site.anubis' for the sequel of this story.
148   -
149   -
150   -
151   -
152   -
  146 +
  147 + See 'web/making_a_web_site.anubis' for the sequel of this story.
  148 +
  149 +
  150 +
  151 +
  152 +
153 153 *** (3) HTTP headers and web arguments.
154   -
  154 +
155 155 Each HTTP request which arrives on the server contains a request line followed by a
156 156 series of HTTP headers. Each HTTP header is a pair '(name,value)' assigning a value to
157   - a name. The type 'HTTP_header' is defined in 'web/common.anubis'.
158   -
  157 + a name. The type 'HTTP_header' is defined in 'web/common.anubis'.
  158 +
159 159 The request may also have a 'body'. The body contains either 'web arguments' or
160 160 uploaded files (or both). The request line itself may also contain web arguments (in a
161 161 so-called 'query string'). Like HTTP headers, 'web arguments' are pairs
... ... @@ -163,26 +163,26 @@
163 163 which the client clicks, while HTTP headers are generated by the browser itself. The
164 164 type 'Web_arg' is defined in 'web/common.anubis'. It has two alternatives, one for
165 165 ordinary web arguments (pairs) and one for uploaded files.
166   -
167   -read CXM_common.anubis
  166 +
  167 +read CXM_common.anubis
168 168 read tools/basis.anubis
169 169 read tools/printable_tree.anubis
170 170 read system/string.anubis
171 171 read system/files.anubis
172 172 read system/lists.anubis
173 173 read system/data_io.anubis
174   -read web/mime.anubis
175   -
176   -
177   -
178   - *** (4) Site descriptions.
  174 +read web/mime.anubis
  175 +
  176 +
  177 +
  178 + *** (4) Site descriptions.
179 179  
180 180 The type HTTP_Info gathers informations comming along with the client's request. These
181 181 informations are rarely used for composing HTML pages. Nevertheless, they are at your
182 182 disposal.
183   -
  183 +
184 184 public type HTTP_Info:
185   - http_info
  185 + http_info
186 186 (
187 187 Word32 ip_address, // IP address of the client
188 188 String hostname, // hostname requested by the client
... ... @@ -191,25 +191,25 @@ public type HTTP_Info:
191 191 Bool is_https
192 192 //One -> String generate_trust_ticket // may be used against denial of
193 193 // service attacks
194   - ).
  194 + ).
  195 +
  196 +
195 197  
196   -
197   -
198 198 Each site is described by a 'web site description', which is a datum of type
199 199 'Web_Site_Description'.
200   -
  200 +
201 201 public type Web_Site_Description:
202 202 web_site_description(
203   - List(String) common_names,
204   - String site_directory,
205   - Redirections redirections,
206   - String charset,
207   - List(String) journal_extensions,
208   - List(String) journal_headers,
209   - String authorization_secret,
210   - List(MIME) known_mime_types,
211   - (String host_name,
212   - HTTP_Info http_info,
  203 + List(String) common_names,
  204 + String site_directory,
  205 + Redirections redirections,
  206 + String charset,
  207 + List(String) journal_extensions,
  208 + List(String) journal_headers,
  209 + String authorization_secret,
  210 + List(MIME) known_mime_types,
  211 + (String host_name,
  212 + HTTP_Info http_info,
213 213 List(Web_arg) lwa,
214 214 Bool is_https) -> (//List(HTTP_header),
215 215 Printable_tree) awp_handler,
... ... @@ -222,36 +222,36 @@ public type Web_Site_Description:
222 222 "www.our-business.com". The reason why we have a list of common names instead of a
223 223 single common name, is that it may be useful to have a common name like "192.168.0.1"
224 224 for testing.
225   -
  225 +
226 226 'charset' is a string which will determine the character encoding to be used by the
227 227 browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
228 228 etc...
229   -
  229 +
230 230 'journal_extensions' is the list of URI extensions for which you want a log in the
231 231 journal (and on the console). When a request arrives, and if the extension is a member
232 232 of this list, a message is printed into the journal of the site including the date, the
233 233 IP address of the client, the complete HTTP request line. The HTTP headers whose name
234 234 is a member of 'journal_headers' are also printed in the journal. A reasonable minimum
235 235 for these two components is:
236   -
  236 +
237 237 [".awp"] for journal_extensions
238 238 ["user-agent"] for journal_headers
239   -
  239 +
240 240 'authorization_secret' is a string which should just be unguessable. You may choose
241 241 something like (but don't choose this one !):
242   -
  242 +
243 243 "Hg8kJe42gCML9jNH-74"
244   -
  244 +
245 245 i.e. a sequence of characters typed at random, long enough to be unguessable. This is
246   - used by the 'private download' mecanism, which is discussed later in this file.
247   -
  246 + used by the 'private download' mecanism, which is discussed later in this file.
  247 +
248 248 The component 'awp_handler' is a function of type:
249   -
250   - (String host_name,
251   - HTTP_Info http_info,
  249 +
  250 + (String host_name,
  251 + HTTP_Info http_info,
252 252 List(Web_arg) web_args,
253 253 Bool is_https) -> Printable_tree
254   -
  254 +
255 255 ('Printable_tree' is a substitute for 'String' and is defined in
256 256 'tools/basis.anubis'). This function is the 'awp handler' for the site. When the URI
257 257 ends by ".awp", this function is called, and the result (an HTML page) is sent to the
... ... @@ -259,173 +259,173 @@ public type Web_Site_Description:
259 259 'true' when the requests arrives through the HTTPS channel, and 'false' when it arrives
260 260 through the HTTP channel.
261 261  
262   -
263   -
264   -
265   -
266   -
267   -
268   - *** (5) Protection against denial of service attacks.
269   -
  262 +
  263 +
  264 +
  265 +
  266 +
  267 +
  268 + *** (5) Protection against denial of service attacks.
  269 +
270 270 We need to protect our servers against 'denial of service' attacks. The attack may be
271 271 send automatically from machines which are infested by viruses. In that case, our
272 272 server is saturated of connections (all virtual machines at work), but nothing is
273 273 comming on the connections. In order to avoid this problem, we propose the following:
274   -
  274 +
275 275 (1) Limit the number of simultaneous connections (say to 100).
276 276 (2) Close a connection if the request is not complete after say 10 seconds.
277 277 (3) Close the connection if the request is bigger than a given size (normal requests
278   - are small except when there are uploaded files.
  278 + are small except when there are uploaded files.
279 279 (4) Close the connection during the sending of the answer if the client is waiting
280 280 too much.
281 281 (5) Record all IP addresses with which we have encountered one of the problems above.
282 282 (6) Immediately close the connections if the IP address is in our list.
283 283 (7) Remove an address from the list only after 5 minutes of inactivity of this
284 284 address.
285   - (8) Maintain a list of reliable IP addresses.
  285 + (8) Maintain a list of reliable IP addresses.
286 286  
287 287 Of course, all the above are approximative solutions which may in some circumstances
288 288 become either cumbersome or also partially block the system. So, it is needed to have a
289 289 set of dynamically modifiable parameters in order to master the behavior of this
290 290 mecanism.
291   -
292   -
  291 +
  292 +
293 293 Each dubious IP address is recorded together with its last activity time.
294   -
  294 +
295 295 public type DubiousIP:
296 296 dubious_ip (Word32 address,
297   - Int last_activity).
298   -
299   -
  297 + Int last_activity).
  298 +
  299 +
300 300 public type DenialOfService:
301   - denial_of_service(Var(Int) max_connections,
  301 + denial_of_service(Var(Int) max_connections,
302 302 Var(Int) request_line_delay, // seconds
303   - Var(Int) headers_delay,
304   - Var(Int) answer_delay,
  303 + Var(Int) headers_delay,
  304 + Var(Int) answer_delay,
305 305 Var(List(DubiousIP)) list_of_dubious,
306   - Var(List(Word32)) reliable_addresses).
307   -
  306 + Var(List(Word32)) reliable_addresses).
  307 +
308 308 The informations in this set of variables are stored serialized into the file
309 309 'my_anubis/web_sites/dos_info'. If this file does not exist a set if variables with
310 310 default values is created. The values are saved on the disk each time they are
311 311 modified.
312   -
313   -public define DenialOfService load_denial_of_service_info.
314   -
315   -
316   -
317   - *** (6) Starting your HTTP and HTTPS servers.
318   -
  312 +
  313 +public define DenialOfService load_denial_of_service_info.
  314 +
  315 +
  316 +
  317 + *** (6) Starting your HTTP and HTTPS servers.
  318 +
319 319 When your web site descriptions are ready, you can start a pair of servers (a HTTP
320 320 server and a HTTPS server) for serving your web sites. Notice that there are always
321 321 two servers, regardless of the number of web sites, and that each web sites normally
322 322 uses the two servers.
323   -
324   -
  323 +
  324 +
325 325 public define StartServerResult
326 326 start_http_server
327 327 (
328   - Word32 ip_address,
329   - Word32 http_port,
  328 + Word32 ip_address,
  329 + Word32 http_port,
330 330 List(Web_Site_Description) web_sites,
331 331 DenialOfService dos
332   - ).
333   -
  332 + ).
  333 +
334 334 public define StartServerResult
335 335 start_https_server
336 336 (
337   - Word32 ip_address,
  337 + Word32 ip_address,
338 338 Word32 https_port,
339   - String certificate_common_name,
  339 + String certificate_common_name,
340 340 List(Web_Site_Description) web_sites,
341 341 DenialOfService dos
342   - ).
343   -
  342 + ).
  343 +
344 344 The first argument 'ip_address' is the IP address on which the servers listen. If you
345 345 put 0, the servers listen on all adresses of the machine (which is useful if the
346 346 machine has several network interfaces). Otherwise, use the function 'ip_address'
347   - defined in 'tools/basis.anubis' for composing a particular IP address.
348   -
  347 + defined in 'tools/basis.anubis' for composing a particular IP address.
  348 +
349 349 The next arguments are the port numbers for HTTP and HTTPS. The usual values are 80 and
350   - 443, but you may have reasons to choose other values.
351   -
  350 + 443, but you may have reasons to choose other values.
  351 +
352 352 The next argument is the list of your web site descriptions. All the sites described in
353 353 this list will be accessible on the server.
354   -
  354 +
355 355 The argument 'dos' is a set of dynamic variables containing the informations for
356 356 protecting the servers against denial of service attacks.
357   -
358   -
359   -
360   -
361   -
362   -
363   - *** (7) Private download.
364   -
  357 +
  358 +
  359 +
  360 +
  361 +
  362 +
  363 + *** (7) Private download.
  364 +
365 365 It may happen that you want to propose private files for download. This means that such
366 366 a file could be downloaded only by the authorized person, and should not be seen by any
367 367 other one. This feature can be used only under HTTPS, not under HTTP.
368 368  
369 369 The file may be located anywhere on the server. Hence, the file has a complete absolute
370 370 path, like for example:
371   -
  371 +
372 372 /home/georges/my_documents/my_text.pdf
373   -
  373 +
374 374 which has nothing to do with the directories of the web server. Now, you may also want
375 375 to show another path or simply just a name to the client, not the actual absolute path
376 376 above, which may need to remain secret. So for example, the same file may appear to the
377 377 client as:
378   -
  378 +
379 379 informations.pdf
380   -
  380 +
381 381 The page must provide a link with an authorization. The authorization is just a web
382 382 argument, whose name is "zauth". The value of this web argument is computed by hashing
383 383 some secret string (known only from the programmer of the web site) with the absolute
384 384 path of the file. The HTTPS request will have the form:
385   -
  385 +
386 386 GET /informations.pdf?zauth=d38161f5b4e87e2d46e06ff8b3e233be563794d1
387   -
388   - The server will search for a file named
389   -
  387 +
  388 + The server will search for a file named
  389 +
390 390 zd38161f5b4e87e2d46e06ff8b3e233be563794d1
391   -
  391 +
392 392 (i.e. "z" concatenated with the value of the authorization) in the subdirectory
393 393 'private_download' of the site directory. This file contains the absolute path of the
394 394 file, i.e:
395   -
  395 +
396 396 /home/georges/my_documents/my_text.pdf
397   -
  397 +
398 398 At that point, the server may hash the secret string and the absolute path together, to
399 399 check if the client is authorized to download the file. If it is the case, it sends the
400 400 file (the MIME type is declared as 'application/octet-stream' if it is not recognized).
401 401 The file is sent under the visible name.
402   -
  402 +
403 403 The server creates automatically the subdirectory 'private_download/' within the 'site
404 404 directory' (for each web site) if it does not already exist. Files in this directory
405 405 are deleted when they become too old (for example, after 3 days of life).
406   -
  406 +
407 407 Here is the function for computing the value of the authorization, and for making the
408 408 authorization file in 'private_download'.
409   -
410   -public define String
  409 +
  410 +public define String
411 411 make_authorization
412 412 (
413   - String site_directory,
  413 + String site_directory,
414 414 String authorization_secret, // known only by the programmer of the web site
415 415 String absolute_path // on server
416   - ).
417   -
418   - See 'web/making_a_web_site.anubis' for the construction of the link for downloading.
419   -
420   -
421   -
422   -
423   -
424   -
425   -
426   -
427   - *** (8) About web argument names.
428   -
  416 + ).
  417 +
  418 + See 'web/making_a_web_site.anubis' for the construction of the link for downloading.
  419 +
  420 +
  421 +
  422 +
  423 +
  424 +
  425 +
  426 +
  427 + *** (8) About web argument names.
  428 +
429 429 The server reserves the name "zauth" for the authorization in the private download
430 430 mecanism. Also, if the name of a web arguments begins by "p" (like 'password'), it does
431 431 not print the value of the web argument neither on the console or in the journal. A
... ... @@ -433,18 +433,18 @@ public define String
433 433 method is used in 'web/making_a_web_site.anubis'. This will avoid clashes of names.
434 434  
435 435  
436   -
437   -
438   -
439   -
440   - *** (9) A web dispatcher.
441   -
  436 +
  437 +
  438 +
  439 +
  440 + *** (9) A web dispatcher.
  441 +
442 442 For hosting several sites you may prefer another method which we now describe. We start
443 443 a HTTP server on port 80 (or on another port). This server is called the
444 444 ``dispatcher''. When a requests arrives, the dispatcher examines the ``host'' HTTP
445 445 header, so that it gets the name of the requested host. Then it sends to the client a
446 446 page like this one:
447   -
  447 +
448 448 <html>
449 449 <head>
450 450 <meta http-equiv="Refresh" content="0;URL=...">
... ... @@ -452,53 +452,53 @@ public define String
452 452 <body>
453 453 </body>
454 454 </html>
455   -
  455 +
456 456 where the URL represented by '...' is the URL of the requested site. This URL may have
457 457 the same IP address as the dispatcher, except that the port number is different. It may
458 458 also have a different IP address.
459   -
  459 +
460 460 The dispatcher uses the file 'my_anubis/web_sites/dispatcher.info'. This file contains
461   - a serialized datum of type 'List(DispatcherInfo)'.
462   -
  461 + a serialized datum of type 'List(DispatcherInfo)'.
  462 +
463 463 public type DispatcherInfo:
464   - site(String common_name,
465   - Word32 http_port).
466   -
  464 + site(String common_name,
  465 + Word32 http_port).
  466 +
467 467 The dispatcher does not write into this file. It reads it when it starts, and rereads
468 468 it each time the date of last modification of the file changes, so that the dispatcher
469 469 always has up to date data. The file may be managed (written and updated) by another
470 470 program.
471   -
  471 +
472 472 So, for each site, the dispatcher knows the common name (needed to recognize the 'host'
473 473 HTTP header), and the pair (ip_address,port) used by the actual site for HTTP. The
474   - dispatcher does not worry about HTTPS. HTTPS must be managed by the actual site.
475   -
476   - The dispatcher is started by:
477   -
  474 + dispatcher does not worry about HTTPS. HTTPS must be managed by the actual site.
  475 +
  476 + The dispatcher is started by:
  477 +
478 478 public define One
479 479 start_web_dispatcher
480 480 (
481 481 Word32 ip_address, // address for listening (typically 0)
482 482 Word32 port, // typically 80
483 483 DenialOfService dos
484   - ).
485   -
  484 + ).
  485 +
486 486 A command line tool for managing the file 'my_anubis/web_sites/dispatcher.info' is also
487 487 provided:
488   -
  488 +
489 489 global define One
490 490 manage_web_dispatcher
491 491 (
492 492 List(String) args
493   - ).
494   -
495   -
  493 + ).
  494 +
  495 +
496 496 *** (10) HTTP Errors
497   -
  497 +
498 498 public type HTTP_Status:
499 499 http_continue, // 100
500 500 http_switching_protocol, // 101
501   -
  501 +
502 502 http_ok, // 200
503 503 http_created, // 201
504 504 http_accepted, // 202
... ... @@ -506,7 +506,7 @@ public type HTTP_Status:
506 506 http_no_content, // 204
507 507 http_reset_content, // 205
508 508 http_partial_content, // 206
509   -
  509 +
510 510 http_multiple_choices,
511 511 http_moved_permanently(String location), // 301
512 512 http_moved_temporarily(String location), // 302
... ... @@ -514,7 +514,7 @@ public type HTTP_Status:
514 514 http_not_modified, // 304
515 515 http_use_proxy(String location), // 305
516 516 http_temporary_redirect(String location), // 307
517   -
  517 +
518 518 http_bad_request, // 400
519 519 http_unauthorized, // 401
520 520 http_payment_required, // 402
... ... @@ -533,14 +533,14 @@ public type HTTP_Status:
533 533 http_unsupported_media_type, // 415
534 534 http_request_range_unsatisfiable, // 416
535 535 http_expectation_failed, // 417
536   -
  536 +
537 537 http_internal_server_error, // 500
538 538 http_not_implemented, // 501
539 539 http_bad_gateway, // 502
540 540 http_service_unavailable, // 503
541 541 http_gateway_timeout, // 504
542 542 http_version_not_supported, // 505
543   -
  543 +
544 544 http_error(Int /*code*/, String /*message*/).
545 545  
546 546 public define (String, List(HTTP_header))
... ... @@ -548,23 +548,23 @@ public define (String, List(HTTP_header))
548 548 (
549 549 HTTP_Status status
550 550 ).
551   -
552   -
553   -
554   -
555   -
  551 +
  552 +
  553 +
  554 +
  555 +
556 556 --- That's all for the public part ! --------------------------------------------------
557   -
  557 +
558 558 define Maybe(String) get_host_header_value(List(HTTP_header) headers).
559   -
  559 +
560 560 define String
561 561 __utime_to_string
562 562 (
563 563 UTime t
564 564 ) =
565 565 to_decimal(t.seconds) + "." + zero_pad_n(6, t.microseconds ) + "s".
566   -
567   -
  566 +
  567 +
568 568 variable UTime t0 = utime(0,0).
569 569 variable UTime t1 = utime(0,0).
570 570  
... ... @@ -572,18 +572,18 @@ define String
572 572 accumulate_t1
573 573 (
574 574 UTime start
575   - ) =
  575 + ) =
576 576 with delta = (UTime)unow - start,
577 577 t1 <- delta + *t1;
578 578 unique.
579   -
  579 +
580 580 variable UTime t2 = utime(0,0).
581 581  
582 582 define One
583 583 accumulate_t2
584 584 (
585 585 UTime start
586   - ) =
  586 + ) =
587 587 with delta = (UTime)unow - start,
588 588 t2 <- delta + *t2;
589 589 unique.
... ... @@ -593,208 +593,208 @@ define String
593 593 print_delta
594 594 (
595 595 String txt
596   - ) =
  596 + ) =
597 597 println(__utime_to_string((UTime)unow - *t0) + " : " + txt).
598   -
599   -
  598 +
  599 +
600 600 ----------------------------------- Table of Contents ---------------------------------
601   -
602   - *** [1] Types which are private to this file.
603   -
604   - *** [2] Tools.
605   - *** [2.1] Formating an error message.
606   - *** [2.2] Converting IP addresses.
607   - *** [2.3] Reading and unputting characters.
608   - *** [2.4] Reading and discarding characters.
609   - *** [2.5] Reading a character string.
610   - *** [2.6] Padding integers with zeros.
611   - *** [2.7] Converting web arguments to ASCII.
612   - *** [2.8] Server description.
613   -
614   - *** [3] Managing the journal.
615   - *** [3.1] Naming journal files.
616   - *** [3.2] Formating HTTP headers.
617   - *** [3.3] Formating web arguments.
618   - *** [3.4] Formating the whole request.
619   - *** [3.5] Putting it in the journal file (and on the console).
620   -
621   - *** [4] Reading the HTTP request.
622   - *** [4.1] Skipping leading blanks.
623   - *** [4.2] Reading a new line.
624   - *** [4.3] Reading a 'word'.
625   - *** [4.4] Separating the URI from the query string.
626   - *** [4.5] Reading the web arguments.
627   - *** [4.7] Reading the request line.
628   - *** [4.8] Reading the HTTP headers.
629   - *** [4.9] Getting the size of the request's body.
  601 +
  602 + *** [1] Types which are private to this file.
  603 +
  604 + *** [2] Tools.
  605 + *** [2.1] Formating an error message.
  606 + *** [2.2] Converting IP addresses.
  607 + *** [2.3] Reading and unputting characters.
  608 + *** [2.4] Reading and discarding characters.
  609 + *** [2.5] Reading a character string.
  610 + *** [2.6] Padding integers with zeros.
  611 + *** [2.7] Converting web arguments to ASCII.
  612 + *** [2.8] Server description.
  613 +
  614 + *** [3] Managing the journal.
  615 + *** [3.1] Naming journal files.
  616 + *** [3.2] Formating HTTP headers.
  617 + *** [3.3] Formating web arguments.
  618 + *** [3.4] Formating the whole request.
  619 + *** [3.5] Putting it in the journal file (and on the console).
  620 +
  621 + *** [4] Reading the HTTP request.
  622 + *** [4.1] Skipping leading blanks.
  623 + *** [4.2] Reading a new line.
  624 + *** [4.3] Reading a 'word'.
  625 + *** [4.4] Separating the URI from the query string.
  626 + *** [4.5] Reading the web arguments.
  627 + *** [4.7] Reading the request line.
  628 + *** [4.8] Reading the HTTP headers.
  629 + *** [4.9] Getting the size of the request's body.
630 630 *** [4.10] Reading the body of the request.
631   -
632   - *** [5] Making the HTTP answer.
633   - *** [5.1] Avoiding illegal URIs.
634   - *** [5.2] Managing authorizations for downloading private files.
635   - *** [5.3] Recognizing MIME types.
636   - *** [5.4] Formating HTTP headers.
637   - *** [5.5] Sending a file.
638   - *** [5.6] Answering a www-url encoded request.
639   - *** [5.7] Answering a multipart/form-data encoded request.
  631 +
  632 + *** [5] Making the HTTP answer.
  633 + *** [5.1] Avoiding illegal URIs.
  634 + *** [5.2] Managing authorizations for downloading private files.
  635 + *** [5.3] Recognizing MIME types.
  636 + *** [5.4] Formating HTTP headers.
  637 + *** [5.5] Sending a file.
  638 + *** [5.6] Answering a www-url encoded request.
  639 + *** [5.7] Answering a multipart/form-data encoded request.
640 640 *** [5.7.1] Finding the boundary.
641   - *** [5.7.2] Reading attributes from a multipart entity.
642   - *** [5.7.3] Creating a temporary filename for an uploaded file.
643   - *** [5.7.4] Saving an uploaded file under a temporary filename.
644   - *** [5.7.5] Removing the path from a file name.
645   - *** [5.7.6] Reading a multipart entity.
646   - *** [5.8] Handling redirections.
647   - *** [5.9] Answering both sorts of requests.
648   -
649   - *** [6] The HTTP/HTTPS servers.
650   - *** [6.1] The HTTP request handler.
651   - *** [6.2] Server's tasks.
652   - *** [6.3] Starting the HTTP/HTTPS servers.
653   -
654   - *** [7] The web dispatcher.
655   - *** [7.1] The dispatcher server.
656   - *** [7.2] The dispatcher web site.
657   - *** [7.3] Managing the info file.
658   -
  641 + *** [5.7.2] Reading attributes from a multipart entity.
  642 + *** [5.7.3] Creating a temporary filename for an uploaded file.
  643 + *** [5.7.4] Saving an uploaded file under a temporary filename.
  644 + *** [5.7.5] Removing the path from a file name.
  645 + *** [5.7.6] Reading a multipart entity.
  646 + *** [5.8] Handling redirections.
  647 + *** [5.9] Answering both sorts of requests.
  648 +
  649 + *** [6] The HTTP/HTTPS servers.
  650 + *** [6.1] The HTTP request handler.
  651 + *** [6.2] Server's tasks.
  652 + *** [6.3] Starting the HTTP/HTTPS servers.
  653 +
  654 + *** [7] The web dispatcher.
  655 + *** [7.1] The dispatcher server.
  656 + *** [7.2] The dispatcher web site.
  657 + *** [7.3] Managing the info file.
  658 +
659 659 ---------------------------------------------------------------------------------------
660   -
661   -
662   -
663   -
664   -read tools/basis.anubis
665   -read tools/findstring.anubis
666   -read tools/connections.anubis
667   -
668   -
669   -
670   -
671   -
672   - *** [1] Types which are private to this file.
673   -
674   - We use the following self-explanatory types.
675   -
  660 +
  661 +
  662 +
  663 +
  664 +read tools/basis.anubis
  665 +read tools/findstring.anubis
  666 +read tools/connections.anubis
  667 +
  668 +
  669 +
  670 +
  671 +
  672 + *** [1] Types which are private to this file.
  673 +
  674 + We use the following self-explanatory types.
  675 +
676 676 type Error:
677 677 cannot_read_from_connection,
678 678 not_get_or_post_request(String),
679 679 end_of_line_expected,
680 680 incorrect_content_length_value,
681 681 colon_expected,
682   - timeout(Int).
683   -
  682 + timeout(Int).
  683 +
684 684 type HTTP_RequestType:
685 685 get,
686   - post.
687   -
  686 + post.
  687 +
688 688 type HTTP_RequestLine:
689   - request_line (HTTP_RequestType type,
  689 + request_line (HTTP_RequestType type,
690 690 String uri,
691   - List(Web_arg) query_string).
692   -
  691 + List(Web_arg) query_string).
  692 +
693 693 type EncodingType:
694 694 www_url,
695   - multipart_form_data.
696   -
  695 + multipart_form_data.
  696 +
697 697 type BufferedConnection:
698 698 buffered_connection(Connection conn,
699 699 Var(ByteArray) buffer,
700 700 Var(Int) read_pos).
701   -
702   -
703   -
704   - *** [2] Tools.
705   -
706   - *** [2.1] Formating an error message.
707   -
708   - The next function formats an error message.
709   -
  701 +
  702 +
  703 +
  704 + *** [2] Tools.
  705 +
  706 + *** [2.1] Formating an error message.
  707 +
  708 + The next function formats an error message.
  709 +
710 710 define String
711 711 format
712 712 (
713 713 Error msg
714 714 ) =
715   - if msg is
  715 + if msg is
716 716 {
717   - cannot_read_from_connection then
  717 + cannot_read_from_connection then
718 718 "Cannot read from connection.\n",
719   - not_get_or_post_request(s) then
  719 + not_get_or_post_request(s) then
720 720 "The request did not begin by 'GET' or 'POST': "+s+".\n",
721   - end_of_line_expected then
  721 + end_of_line_expected then
722 722 "End of line expected.\n",
723   - incorrect_content_length_value then
  723 + incorrect_content_length_value then
724 724 "Incorrect value for HTTP header 'Content-Length'.\n",
725 725 colon_expected then
726   - "':' was expected.\n",
727   - timeout(n) then
  726 + "':' was expected.\n",
  727 + timeout(n) then
728 728 //"time out: "+n+"\n"
729 729 //"time out.\n"
730 730 ""
731 731 }.
732   -
733   -
734   -
735   -
736   -
737   -
738   - *** [2.2] Converting IP addresses.
739   -
  732 +
  733 +
  734 +
  735 +
  736 +
  737 +
  738 + *** [2.2] Converting IP addresses.
  739 +
740 740 We need two conversion functions for IP addresses:
741   -
  741 +
742 742 (Word8,Word8,Word8,Word8) --> Word32 ip_address
743 743 Word32 --> String ip_addr_to_string
744   -
745   - These conversions are defined in 'tools/basis.anubis'.
746   -
747   -
748   -
749   - *** [2.3] A set of state variables for the server.
750   -
  744 +
  745 + These conversions are defined in 'tools/basis.anubis'.
  746 +
  747 +
  748 +
  749 + *** [2.3] A set of state variables for the server.
  750 +
751 751 public type SState:
752 752 sstate
753 753 (
754 754 Var(List(Word8)) unput_chars, // for reading requests
755 755 Var(Int) sttm, // 'start time'
756   - Var(Int) uploaded_file_count
757   - ).
758   -
759   -
760   - *** [2.4] Reading and unputting characters.
761   -
  756 + Var(Int) uploaded_file_count
  757 + ).
  758 +
  759 +
  760 + *** [2.4] Reading and unputting characters.
  761 +
762 762 We need a mecanism for unputting several characters (actually at least 3). This is
763 763 because when reading the client connection, we must sometimes go ahead several
764 764 characters, and virtually put them back into the connection, so that they can be
765 765 reread. Of course, we do not send them back to the client. We store them in a list
766 766 (hold by the variable 'unput_chars'), and we manage this list, so that characters may
767 767 be virtually put back in the connection (this is called 'unputting').
768   -
769   - variable List(Word8) unput_chars = [].
770   -
  768 +
  769 + variable List(Word8) unput_chars = [].
  770 +
771 771 The most recently read one is the head of list. Fortunately, this variable is private
772 772 to this virtual machine (hence to this client).
773   -
774   -
  773 +
  774 +
775 775 define One
776   - unput // unputting a character (add it in front of the list)
  776 + unput // unputting a character (add it in front of the list)
777 777 (
778 778 Word8 character,
779 779 SState s
780 780 ) =
781   - s.unput_chars <- (List(Word8))[character . *(s.unput_chars)].
782   -
783   -
784   -
785   -define One record_dubious_IP(Word32 addr,DenialOfService dos).
786   -
787   - variable Int sttm = 0. // contains the start time for this connection.
788   -
  781 + s.unput_chars <- (List(Word8))[character . *(s.unput_chars)].
  782 +
  783 +
  784 +
  785 +define One record_dubious_IP(Word32 addr,DenialOfService dos).
  786 +
  787 + variable Int sttm = 0. // contains the start time for this connection.
  788 +
789 789 define Result(Error,Word8)
790 790 record_dubious_connection
791 791 (
792   - Connection conn,
  792 + Connection conn,
793 793 Int dead_line,
794 794 DenialOfService dos,
795 795 SState s
796 796 ) =
797   - if remote_IP_address_and_port(conn) is (addr,port) then
  797 + if remote_IP_address_and_port(conn) is (addr,port) then
798 798 record_dubious_IP(addr,dos);
799 799 print("Recording IP address "+ip_addr_to_string(addr)+
800 800 " as dubious after "+(dead_line-*(s.sttm))+" seconds. Total: "+
... ... @@ -818,9 +818,9 @@ define One
818 818 if nth(i,source) is
819 819 {
820 820 failure then unique,
821   - success(b) then if put(dest,position,b) is
  821 + success(b) then if put(dest,position,b) is
822 822 {
823   - failure then unique,
  823 + failure then unique,
824 824 success(_) then put(source,dest,position+1,i+1)
825 825 }
826 826 }.
... ... @@ -832,9 +832,9 @@ define ReadResult
832 832 Int size,
833 833 Int time_out,
834 834 ByteArray result_buffer,
835   - Int position
  835 + Int position
836 836 ) =
837   - //println(pid + "read_from_connexion(" + size + ")");
  837 + //println(pid + "read_from_connexion(" + size + ")");
838 838  
839 839 if *connection.read_pos < length(*connection.buffer) then
840 840 //println(pid + " reading from buffer (size = " + length(*connection.buffer) + ", pos = " + *connection.read_pos);
... ... @@ -852,7 +852,7 @@ define ReadResult
852 852 // timeout then ok(result),
853 853 // ok(ba) then ok(result + ba)
854 854 // }
855   - else
  855 + else
856 856 ok(result_buffer)
857 857 else
858 858 with t0 = (UTime) unow,
... ... @@ -861,24 +861,24 @@ define ReadResult
861 861 {
862 862 error then println(pid + "read failed ["+to_string(result_buffer)+"]"); error,
863 863 timeout then timeout,
864   - ok(ba) then
  864 + ok(ba) then
865 865 with size_read = length(ba),
866   - //the needed read is higher than the
  866 + //the needed read is higher than the
867 867 if size > size_read then
868 868 put(ba, result_buffer, position, 0);
869   - read_from_connexion(connection, size - size_read, time_out, result_buffer, position+size_read)
  869 + read_from_connexion(connection, size - size_read, time_out, result_buffer, position+size_read)
870 870 else
871   - //println(pid + "ba = " + length(ba) + " duration: " + __utime_to_string((UTime) unow - t0));
  871 + //println(pid + "ba = " + length(ba) + " duration: " + __utime_to_string((UTime) unow - t0));
872 872 connection.buffer <- ba;
873 873 connection.read_pos <- 0;
874   - //println(pid + "rb = " + length(*read_buffer));
875   -
  874 + //println(pid + "rb = " + length(*read_buffer));
  875 +
876 876 terminal read_from_connexion(connection, size, time_out, result_buffer, position)
877 877 }.
878 878  
879 879 define Result(Error,Word8)
880 880 next_char // reading a character (check the list first, and read on the connection
881   - // only when the list is empty).
  881 + // only when the list is empty).
882 882 (
883 883 BufferedConnection connection,
884 884 Int dead_line,
... ... @@ -886,11 +886,11 @@ define Result(Error,Word8)
886 886 SState s
887 887 ) =
888 888 //with t2_tmp = (UTime) now,
889   - if *(s.unput_chars) is
  889 + if *(s.unput_chars) is
890 890 {
891   - [ ] then
  891 + [ ] then
892 892 // ///////////////////
893   - // Buffered reading
  893 + // Buffered reading
894 894 //if unow > dead_line then record_dubious_connection(connection,dead_line,dos) else
895 895 if nth(*connection.read_pos, *connection.buffer) is
896 896 {
... ... @@ -899,48 +899,48 @@ define Result(Error,Word8)
899 899 {
900 900 error then /*accumulate_t2(t2_tmp);*/ error(cannot_read_from_connection),
901 901 timeout then /*accumulate_t2(t2_tmp);*/ error(timeout(600)),
902   - //record_dubious_connection(connection,dead_line,dos),
903   - ok(ba) then if nth(0,ba) is
  902 + //record_dubious_connection(connection,dead_line,dos),
  903 + ok(ba) then if nth(0,ba) is
904 904 {
905 905 failure then /*accumulate_t2(t2_tmp);*/ error(cannot_read_from_connection),
906   - success(c) then
  906 + success(c) then
907 907 //println("-" + pid + "read [" + implode([c]) + "]\t");
908   - //accumulate_t2(t2_tmp);
  908 + //accumulate_t2(t2_tmp);
909 909 ok(c)
910 910 }
911 911 },
912   - success(c) then
  912 + success(c) then
913 913 connection.read_pos <- *connection.read_pos + 1;
914   - //accumulate_t2(t2_tmp);
  914 + //accumulate_t2(t2_tmp);
915 915 ok(c)
916 916 },
917   -
  917 +
918 918 // ///////////////////
919 919 // standard reading
920 920 // if read(connection.conn, 1, 600) is // the connection is closed after 10 minutes of inactivity
921 921 // {
922 922 // error then accumulate_t2(t2_tmp); println(pid + "read failed)"); error(cannot_read_from_connection),
923 923 // timeout then accumulate_t2(t2_tmp); error(timeout(600)),
924   -// ok(ba) then if nth(0,ba) is
  924 +// ok(ba) then if nth(0,ba) is
925 925 // {
926 926 // failure then accumulate_t2(t2_tmp); error(cannot_read_from_connection),
927   -// success(c) then accumulate_t2(t2_tmp);
  927 +// success(c) then accumulate_t2(t2_tmp);
928 928 // ok(c)
929 929 // }
930 930 // },
931   -
932   - [h . t] then
933   - s.unput_chars <- t; //accumulate_t2(t2_tmp);
  931 +
  932 + [h . t] then
  933 + s.unput_chars <- t; //accumulate_t2(t2_tmp);
934 934 ok(h)
935   - }.
936   -
  935 + }.
  936 +
937 937 define ByteArray
938 938 get_and_erase_buffer
939 939 (
940 940 BufferedConnection connection,
941 941 SState s
942 942 )=
943   - with head = to_byte_array(implode(*s.unput_chars)),
  943 + with head = to_byte_array(implode(*s.unput_chars)),
944 944 tail = extract(*connection.buffer, *connection.read_pos, length(*connection.buffer)),
945 945 // println("--- get_and_erase_buffer ----");
946 946 // println("unput char length : "+length(to_string(head)));
... ... @@ -949,105 +949,105 @@ define ByteArray
949 949 // println(" tail length : "+length(tail));
950 950 // println(" -- unputchar content ");
951 951 // println("["+to_string(head)+"]");
952   -// println(" -- buffer content ");
  952 +// println(" -- buffer content ");
953 953 // println("["+to_string(*connection.buffer)+"]");
954   -// println(" -- tail content ");
  954 +// println(" -- tail content ");
955 955 // println("["+to_string(tail)+"]");
956 956 s.unput_chars <- [];
957 957 connection.buffer <- constant_byte_array(0,0);
958 958 connection.read_pos <- 0;
959 959 head + tail
960 960 .
961   -
962   -
963   -
964   -
965   -
966   - *** [2.4] Reading and discarding characters.
967   -
  961 +
  962 +
  963 +
  964 +
  965 +
  966 + *** [2.4] Reading and discarding characters.
  967 +
968 968 The next function reads the specified number of bytes (this is the same as
969 969 'characters') from the connection and discards them. This is used for discarding CR LF
970 970 just before the body of a request.
971   -
  971 +
972 972 define Result(Error,One)
973 973 read_and_ignore
974 974 (
975 975 BufferedConnection connection, // to client
976   - Int dead_line,
  976 + Int dead_line,
977 977 Int number_of_characters, // number of characters to read and ignore
978 978 DenialOfService dos,
979   - SState s
  979 + SState s
980 980 ) =
981 981 if number_of_characters =< 0 then ok(unique) else
982   - if next_char(connection, dead_line, dos, s) is
  982 + if next_char(connection, dead_line, dos, s) is
983 983 {
984 984 error(msg) then error(msg),
985 985 ok(c) then read_and_ignore(connection,dead_line,number_of_characters-1,dos, s)
986 986 }.
987   -
988   -
989   -
990   -
991   -
992   -
993   -
994   - *** [2.5] Reading a character string.
995   -
  987 +
  988 +
  989 +
  990 +
  991 +
  992 +
  993 +
  994 + *** [2.5] Reading a character string.
  995 +
996 996 Sometimes values of HTTP attributes or web args are presented in the form of double
997 997 quoted strings. The next function handles the reading of such things. The leading
998 998 double quote is already read in. We must read subsequent characters until the next non
999 999 backslashed double quote.
1000   -
1001   -define Result(Error,String)
  1000 +
  1001 +define Result(Error,String)
1002 1002 read_string
1003 1003 (
1004 1004 BufferedConnection connection, // connection with the client
1005   - Int dead_line,
  1005 + Int dead_line,
1006 1006 List(Word8) so_far, // characters read so far (in reverse order)
1007 1007 DenialOfService dos,
1008   - SState s
  1008 + SState s
1009 1009 ) =
1010   - if next_char(connection, dead_line,dos, s) is
  1010 + if next_char(connection, dead_line,dos, s) is
1011 1011 {
1012 1012 error(msg) then error(msg),
1013   - ok(c) then
  1013 + ok(c) then
1014 1014 if c = '\\'
1015   - then if next_char(connection,dead_line,dos, s) is
  1015 + then if next_char(connection,dead_line,dos, s) is
1016 1016 {
1017   - error(msg) then error(msg),
1018   - ok(d) then
  1017 + error(msg) then error(msg),
  1018 + ok(d) then
1019 1019 if d = '\"'
1020   - then read_string(connection,dead_line,['\"' . so_far],dos, s)
  1020 + then read_string(connection,dead_line,['\"' . so_far],dos, s)
1021 1021 else read_string(connection,dead_line,[d, c . so_far],dos, s)
1022 1022 }
1023 1023 else if c = '\"'
1024 1024 then ok(implode(reverse(so_far)))
1025 1025 else read_string(connection,dead_line,[c . so_far],dos, s)
1026 1026 }.
1027   -
1028   -
1029   -
1030   -
1031   -
1032   -
1033   -
1034   -
1035   -
1036   -
1037   -
1038   -
1039   - *** [2.7] Converting web arguments to ASCII.
  1027 +
  1028 +
  1029 +
  1030 +
  1031 +
  1032 +
  1033 +
  1034 +
  1035 +
  1036 +
  1037 +
  1038 +
  1039 + *** [2.7] Converting web arguments to ASCII.
1040 1040  
1041 1041 The function 'web_to_ascii' gets a character string and replaces web encoding by normal
1042 1042 ASCII encoding. This amounts to replacing:
1043   -
  1043 +
1044 1044 + by blank
1045 1045 %xx by the character whose ASCII code is xx in hexadecimal
1046   -
1047   - Note: We assume that '9' < 'A' (which is the case for ASCII code).
1048 1046  
1049   -
1050   -
  1047 + Note: We assume that '9' < 'A' (which is the case for ASCII code).
  1048 +
  1049 +
  1050 +
1051 1051 define Word8
1052 1052 web_decode
1053 1053 (
... ... @@ -1056,10 +1056,10 @@ define Word8
1056 1056 ) =
1057 1057 with n1 = if x1 +=< '9' then (x1 - '0') else if x1 +=< 'F' then (x1 - 'A' + 10) else (x1 - 'a' + 10),
1058 1058 n2 = if x2 +=< '9' then (x2 - '0') else if x2 +=< 'F' then (x2 - 'A' + 10) else (x2 - 'a' + 10),
1059   - (n1 << 4) + n2.
1060   -
  1059 + (n1 << 4) + n2.
  1060 +
  1061 +
1061 1062  
1062   -
1063 1063 define String
1064 1064 web_to_ascii
1065 1065 (
... ... @@ -1067,93 +1067,93 @@ define String
1067 1067 Int n, // current position in web_string
1068 1068 List(Word8) so_far
1069 1069 ) =
1070   - if nth(n,web_string) is
  1070 + if nth(n,web_string) is
1071 1071 {
1072   - failure then implode(reverse(so_far)),
1073   - success(c) then
  1072 + failure then implode(reverse(so_far)),
  1073 + success(c) then
1074 1074 if c = '+'
1075 1075 then web_to_ascii(web_string,n+1,[' ' . so_far])
1076 1076 else if c = '%'
1077 1077 then if nth(n+1,web_string) is
1078 1078 {
1079 1079 failure then implode(reverse(so_far)),
1080   - success(x1) then if nth(n+2,web_string) is
  1080 + success(x1) then if nth(n+2,web_string) is
1081 1081 {
1082   - failure then implode(reverse(so_far)),
  1082 + failure then implode(reverse(so_far)),
1083 1083 success(x2) then web_to_ascii(web_string,n+3,[web_decode(x1,x2) . so_far])
1084 1084 }
1085 1085 }
1086 1086 else web_to_ascii(web_string,n+1,[c . so_far])
1087 1087 }.
1088   -
1089   -
1090   -
1091   -
1092   -
1093   -
1094   -
1095   -
1096   - *** [3] Managing the journal.
1097   -
  1088 +
  1089 +
  1090 +
  1091 +
  1092 +
  1093 +
  1094 +
  1095 +
  1096 + *** [3] Managing the journal.
  1097 +
1098 1098 Concurrently working machines should not try to access the same file at the same
1099   - time. This problem may be solved by using the 'protect' mecanism.
1100   -
1101   -
1102   -
1103   - *** [3.1] Naming journal files.
1104   -
  1099 + time. This problem may be solved by using the 'protect' mecanism.
  1100 +
  1101 +
  1102 +
  1103 + *** [3.1] Naming journal files.
  1104 +
1105 1105 Since journal messages are rather prolific, we should have at least one file per
1106 1106 hour. Hence, the name of a journal file must be constructed from the current year,
1107 1107 month, day and hour. For example, it may be:
1108   -
  1108 +
1109 1109 2003_03_12_19
1110   -
1111   - (this is for the journal of 7 PM to 8 PM, 2003/mar/12).
1112   -
1113   -define String
  1110 +
  1111 + (this is for the journal of 7 PM to 8 PM, 2003/mar/12).
  1112 +
  1113 +define String
1114 1114 make_current_journal_file_name
1115 1115 =
1116   - if convert_time(now) is date_and_time(y,m,d,h,_,_,_,_,_) then
  1116 + if convert_time(now) is date_and_time(y,m,d,h,_,_,_,_,_) then
1117 1117 to_decimal(y)+"_"+
1118 1118 zero_pad_n(2,m)+"_"+
1119 1119 zero_pad_n(2,d)+"_"+
1120   - zero_pad_n(2,h).
1121   -
1122   -
1123   -
1124   -
1125   -
1126   -
1127   -
1128   - *** [3.2] Formating HTTP headers.
1129   -
  1120 + zero_pad_n(2,h).
  1121 +
  1122 +
  1123 +
  1124 +
  1125 +
  1126 +
  1127 +
  1128 + *** [3.2] Formating HTTP headers.
  1129 +
1130 1130 HTTP headers may be shown on the console or written in the journal. The function below
1131   - formats a list of HTTP headers.
1132   -
1133   -define String
  1131 + formats a list of HTTP headers.
  1132 +
  1133 +define String
1134 1134 show_format
1135 1135 (
1136   - Web_Site_Description desc,
  1136 + Web_Site_Description desc,
1137 1137 List(HTTP_header) headers,
1138 1138 ) =
1139   - if headers is
  1139 + if headers is
1140 1140 {
1141 1141 [ ] then "",
1142   - [h . t] then if h is http_header(name,value) then
  1142 + [h . t] then if h is http_header(name,value) then
1143 1143 if member(journal_headers(desc),name)
1144 1144 then " | "+name+": "+value+"\n"+show_format(desc,t)
1145 1145 else show_format(desc,t)
1146 1146 }.
1147   -
1148   -
1149   -
1150   -
1151   -
1152   -
1153   - *** [3.3] Formating web arguments.
1154   -
1155   - The same thing for web arguments.
1156   -
  1147 +
  1148 +
  1149 +
  1150 +
  1151 +
  1152 +
  1153 + *** [3.3] Formating web arguments.
  1154 +
  1155 + The same thing for web arguments.
  1156 +
1157 1157 define String
1158 1158 show_format
1159 1159 (
... ... @@ -1162,25 +1162,25 @@ define String
1162 1162 if lwa is
1163 1163 {
1164 1164 [ ] then "",
1165   - [h . t] then if h is
  1165 + [h . t] then if h is
1166 1166 {
1167   - web_arg(n,v) then
  1167 + web_arg(n,v) then
1168 1168 " | "+n+"="+(if nth(0,n) = success('p') then "<not shown>" else v)+"\n"+show_format(t),
1169 1169 upload(n,fn,tfn) then
1170 1170 " | "+n+"="+fn+" (uploaded as '"+tfn+"')\n"+show_format(t)
1171 1171 }
1172 1172 }.
1173   -
1174   -
1175   -
1176   -
1177   -
1178   -
1179   - *** [3.4] Formating the whole request.
  1173 +
  1174 +
  1175 +
  1176 +
  1177 +
  1178 +
  1179 + *** [3.4] Formating the whole request.
1180 1180  
1181 1181 It is cheap to transform month numbers into abbreviated month names. This enhances the
1182 1182 readability of the journal.
1183   -
  1183 +
1184 1184 define String
1185 1185 format_month
1186 1186 (
... ... @@ -1198,86 +1198,86 @@ define String
1198 1198 if m = 10 then "oct" else
1199 1199 if m = 11 then "nov" else
1200 1200 if m = 12 then "dec" else
1201   - "???".
1202   -
1203   -
  1201 + "???".
  1202 +
  1203 +
1204 1204 Below we format a whole HTTP request. This may give this (actually, it depends on how
1205 1205 you defined the values of 'journal_headers' and 'journal_extensions'):
1206   -
  1206 +
1207 1207 [3] 2003/mar/10 10:06:57 from 123.456.123.456: /homepage.awp
1208 1208 | host: www.the-best-one.com
1209 1209 | user-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.1) Gecko/20020823 Netscape/7.0
1210 1210  
1211 1211 The leading number between brackets is the number of the virtual machine which served
1212 1212 the URI.
1213   -
  1213 +
1214 1214 define String
1215 1215 format_request
1216 1216 (
1217   - Web_Site_Description desc,
1218   - Connection client_connection,
1219   - HTTP_RequestLine request_line,
1220   - List(HTTP_header) headers,
  1217 + Web_Site_Description desc,
  1218 + Connection client_connection,
  1219 + HTTP_RequestLine request_line,
  1220 + List(HTTP_header) headers,
1221 1221 List(Web_arg) web_args
1222 1222 ) =
1223 1223 with dt = convert_time(now),
1224   - if remote_IP_address_and_port(client_connection) is (addr,port) then
  1224 + if remote_IP_address_and_port(client_connection) is (addr,port) then
1225 1225 to_decimal(year(dt))+"/"+format_month(month(dt))+"/"+zero_pad_n(2,day(dt))+" "+
1226 1226 zero_pad_n(2,hour(dt))+":"+zero_pad_n(2,minute(dt))+":"+zero_pad_n(2,second(dt))+
1227 1227 " from "+ip_addr_to_string(addr)+
1228 1228 ": "+uri(request_line)+"\n"+
1229 1229 show_format(desc,headers)+
1230   - show_format(web_args).
  1230 + show_format(web_args).
  1231 +
  1232 +
  1233 +
  1234 +
1231 1235  
1232 1236  
1233   -
1234   -
1235   -
1236   -
1237   -
1238   - *** [3.5] Putting it in the journal file (and on the console).
  1237 +
  1238 + *** [3.5] Putting it in the journal file (and on the console).
1239 1239  
1240 1240 We must not forget to 'protect' this operation, so that the messages of two machines
1241 1241 (working for the same site) will not be mixed together.
1242   -
1243   -define One
  1242 +
  1243 +define One
1244 1244 log_journal_msg
1245 1245 (
1246   - Web_Site_Description desc,
  1246 + Web_Site_Description desc,
1247 1247 String msg,
1248 1248 ) =
1249   - with ba_msg = to_byte_array("["+virtual_machine_id+"] "+msg+"\n"),
  1249 + with ba_msg = to_byte_array("["+virtual_machine_id+"] "+msg+"\n"),
1250 1250 protect
1251 1251 (
1252 1252 if file(site_directory(desc)+"/journal/"+make_current_journal_file_name,append) is
1253 1253 {
1254 1254 failure then unique,
1255   - success(journal_file) then
  1255 + success(journal_file) then
1256 1256 forget(reliable_write(file(journal_file),ba_msg))
1257 1257 };
1258 1258 forget(reliable_write(file(stdout),ba_msg))
1259 1259 ).
1260   -
1261   -
1262   -
1263   -
1264   -
1265   -
1266   -
1267   - *** [4] Reading the HTTP request.
1268   -
1269   -
1270   - *** [4.1] Skipping leading blanks.
1271   -
  1260 +
  1261 +
  1262 +
  1263 +
  1264 +
  1265 +
  1266 +
  1267 + *** [4] Reading the HTTP request.
  1268 +
  1269 +
  1270 + *** [4.1] Skipping leading blanks.
  1271 +
1272 1272 One of the peculiarities of HTTP is that the characters 13 (carriage return) and 10
1273 1273 (line feed) followed by either a space (32) or a tab (9), is considered as a blank not
1274 1274 containing any new line. 'skip_http_blanks' must skip all blanks characters until the
1275 1275 first non blank character, which should not be read in. Obviously, because of the above
1276 1276 peculiarity, we need at least 3 characters of lookahead to do this. In other words, we
1277 1277 must be able to unput at least 3 characters (hopefully we are).
1278   -
1279   - Strictly blanks characters are 'space' and 'tab'.
1280   -
  1278 +
  1279 + Strictly blanks characters are 'space' and 'tab'.
  1280 +
1281 1281 define Bool
1282 1282 is_strict_blank
1283 1283 (
... ... @@ -1285,46 +1285,46 @@ define Bool
1285 1285 ) =
1286 1286 if c = ' ' then true else c = '\t'.
1287 1287  
1288   -
  1288 +
1289 1289 On the contrary, blanks include 13 and 10.
1290   -
  1290 +
1291 1291 define Bool
1292 1292 is_blank
1293 1293 (
1294 1294 Word8 c
1295 1295 ) =
1296   - if c = ' ' then true else
  1296 + if c = ' ' then true else
1297 1297 if c = '\t' then true else
1298 1298 if c = 13 then true else
1299   - c = 10.
  1299 + c = 10.
  1300 +
  1301 +
  1302 + Skipping HTTP blanks.
1300 1303  
1301   -
1302   - Skipping HTTP blanks.
1303   -
1304 1304 define Result(Error,One)
1305 1305 skip_http_blanks
1306 1306 (
1307 1307 BufferedConnection connection,
1308 1308 Int dead_line,
1309 1309 DenialOfService dos,
1310   - SState s
1311   - ) =
1312   - if next_char(connection, dead_line, dos, s) is
  1310 + SState s
  1311 + ) =
  1312 + if next_char(connection, dead_line, dos, s) is
1313 1313 {
1314   - error(msg) then error(msg),
1315   - ok(c) then
  1314 + error(msg) then error(msg),
  1315 + ok(c) then
1316 1316 if is_strict_blank(c)
1317 1317 then skip_http_blanks(connection, dead_line, dos, s)
1318 1318 else if c = 13
1319 1319 then if next_char(connection, dead_line, dos, s) is
1320 1320 {
1321 1321 error(msg) then error(msg), // (unput(c); ok(unique)),
1322   - ok(d) then
  1322 + ok(d) then
1323 1323 if d = 10
1324   - then if next_char(connection, dead_line, dos, s) is
  1324 + then if next_char(connection, dead_line, dos, s) is
1325 1325 {
1326 1326 error(msg) then error(msg), // (unput(d); unput(c); ok(unique)),
1327   - ok(e) then
  1327 + ok(e) then
1328 1328 if is_strict_blank(e)
1329 1329 then skip_http_blanks(connection, dead_line, dos, s)
1330 1330 else (unput(e, s); unput(d, s); unput(c, s); ok(unique))
... ... @@ -1333,16 +1333,16 @@ define Result(Error,One)
1333 1333 }
1334 1334 else (unput(c, s); ok(unique))
1335 1335 }.
1336   -
1337   -
1338   -
1339   -
1340   -
1341   -
1342   -
1343   -
1344   - *** [4.2] Reading a new line.
1345   -
  1336 +
  1337 +
  1338 +
  1339 +
  1340 +
  1341 +
  1342 +
  1343 +
  1344 + *** [4.2] Reading a new line.
  1345 +
1346 1346 Normally in HTTP a new line is the sequence 13 10 (carriage return line feed), not
1347 1347 followed by a space or tabulator. If it is followed by a space or tabulator, the three
1348 1348 characters are considered blanks, and no new line has been read. Before trying to read
... ... @@ -1350,54 +1350,54 @@ define Result(Error,One)
1350 1350 we read another character. if this character is space or tab, we consider we have read
1351 1351 only blanks and we continue reading in order to find our new line. Otherwise, we unput
1352 1352 this character (which may be for example the first character of the name of the next
1353   - header), and answer that we have seen a new line.
1354   -
  1353 + header), and answer that we have seen a new line.
  1354 +
1355 1355 Warning: we must not use this function for reading the last pair (13,10) before the
1356 1356 beginning of the body, because if the body is empty, there is no character to read
1357 1357 after this pair, so that the server could wait for a character which will never
1358 1358 come. This is the reason for 'read_and_ignore' above, which is used precisely for
1359 1359 reading that last (13,10) pair.
1360   -
  1360 +
1361 1361 define Result(Error,One)
1362 1362 read_new_line
1363 1363 (
1364 1364 BufferedConnection connection,
1365 1365 Int dead_line,
1366 1366 DenialOfService dos,
1367   - SState s
  1367 + SState s
1368 1368 ) =
1369   - if skip_http_blanks(connection, dead_line, dos, s) is
  1369 + if skip_http_blanks(connection, dead_line, dos, s) is
1370 1370 {
1371 1371 error(msg) then error(msg),
1372   - ok(_) then
1373   - if next_char(connection, dead_line, dos, s) is
  1372 + ok(_) then
  1373 + if next_char(connection, dead_line, dos, s) is
1374 1374 {
1375   - error(msg) then error(msg),
  1375 + error(msg) then error(msg),
1376 1376 ok(c) then
1377 1377 if c = 13
1378   - then if next_char(connection, dead_line, dos, s) is
  1378 + then if next_char(connection, dead_line, dos, s) is
1379 1379 {
1380 1380 error(msg) then error(msg),
1381   - ok(d) then
  1381 + ok(d) then
1382 1382 if d = 10
1383 1383 then ok(unique)
1384   - else (unput(d, s);
1385   - unput(c, s);
  1384 + else (unput(d, s);
  1385 + unput(c, s);
1386 1386 error(end_of_line_expected))
1387 1387 }
1388   - else (unput(c, s);
  1388 + else (unput(c, s);
1389 1389 error(end_of_line_expected))
1390 1390 }}.
1391   -
1392   -
1393   -
1394   -
1395   -
1396   -
1397   -
1398   -
1399   - *** [4.3] Reading a 'word'.
1400   -
  1391 +
  1392 +
  1393 +
  1394 +
  1395 +
  1396 +
  1397 +
  1398 +
  1399 + *** [4.3] Reading a 'word'.
  1400 +
1401 1401 A 'word' is a sequence of characters which begins either by a double quote or not by a
1402 1402 double quote. (However, any leading blanks are read in and ignored. This is
1403 1403 accomplished by 'skip_http_blanks'.) If it begins by a double quote, it is read like a
... ... @@ -1407,26 +1407,26 @@ define Result(Error,One)
1407 1407 double quoted, the right delimiting blank character is not read in (it is 'unput' back
1408 1408 into the connection), and may be read in again. This is needed because carriage return
1409 1409 or line feed which are 'blank', also have a meaning in HTTP.
1410   -
  1410 +
1411 1411 define Result(Error,String)
1412 1412 read_word_aux
1413 1413 (
1414   - BufferedConnection connection,
1415   - Int dead_line,
  1414 + BufferedConnection connection,
  1415 + Int dead_line,
1416 1416 List(Word8) so_far,
1417 1417 DenialOfService dos,
1418   - SState s
  1418 + SState s
1419 1419 ) =
1420   - if next_char(connection,dead_line,dos, s) is
  1420 + if next_char(connection,dead_line,dos, s) is
1421 1421 {
1422 1422 error(msg) then error(msg),
1423   - ok(c) then
  1423 + ok(c) then
1424 1424 if is_blank(c)
1425   - then (unput(c, s);
  1425 + then (unput(c, s);
1426 1426 ok(implode(reverse(so_far))))
1427 1427 else read_word_aux(connection,dead_line,[c . so_far],dos, s)
1428 1428 }.
1429   -
  1429 +
1430 1430 define Result(Error,String)
1431 1431 read_word
1432 1432 (
... ... @@ -1435,117 +1435,117 @@ define Result(Error,String)
1435 1435 DenialOfService dos,
1436 1436 SState s
1437 1437 ) =
1438   - if skip_http_blanks(connection,dead_line,dos, s) is
  1438 + if skip_http_blanks(connection,dead_line,dos, s) is
1439 1439 {
1440   - error(msg) then error(msg),
1441   - ok(_) then
1442   - if next_char(connection, dead_line, dos, s) is
  1440 + error(msg) then error(msg),
  1441 + ok(_) then
  1442 + if next_char(connection, dead_line, dos, s) is
1443 1443 {
1444   - error(msg) then error(msg),
1445   - ok(c) then
  1444 + error(msg) then error(msg),
  1445 + ok(c) then
1446 1446 if c = '\"'
1447 1447 then read_string(connection,dead_line,[],dos, s)
1448 1448 else read_word_aux(connection,dead_line,[c],dos, s)
1449 1449 }
1450   - }.
1451   -
1452   -
1453   -
1454   -
1455   -
1456   -
1457   -
1458   -
1459   - *** [4.4] Separating the URI from the query string.
1460   -
  1450 + }.
  1451 +
  1452 +
  1453 +
  1454 +
  1455 +
  1456 +
  1457 +
  1458 +
  1459 + *** [4.4] Separating the URI from the query string.
  1460 +
1461 1461 A 'query string' may be postfixed to the URI, just after a question mark. For example,
1462 1462 the client may send the following request:
1463   -
  1463 +
1464 1464 GET /catalog.awp?item=3&color=blue
1465   -
  1465 +
1466 1466 We separate this into an URI: "/catalog.awp" and the string: "item=3&color=blue" which
1467 1467 will be later transformed into the list:
1468   -
  1468 +
1469 1469 [web_arg("item","3"),web_arg("color","blue")]
1470   -
1471   -
  1470 +
  1471 +
1472 1472 define (String,String)
1473 1473 separate_uri_from_query_string
1474 1474 (
1475 1475 String uri_and_query_string,
1476 1476 Int n
1477 1477 ) =
1478   - if nth(n,uri_and_query_string) is
  1478 + if nth(n,uri_and_query_string) is
1479 1479 {
1480 1480 failure then (uri_and_query_string,""),
1481   - success(c) then
  1481 + success(c) then
1482 1482 if c = '?'
1483 1483 then (substr(uri_and_query_string,0,n),
1484 1484 substr(uri_and_query_string,n+1,length(uri_and_query_string)-(n+1)))
1485 1485 else separate_uri_from_query_string(uri_and_query_string,n+1)
1486 1486 }.
1487   -
1488   -
1489   -
1490   -
1491   -
1492   -
1493   -
1494   -
1495   -
1496   - *** [4.5] Reading the web arguments.
1497   -
1498   - HTTP/HTTPS requests are sent in one of two formats:
1499   -
  1487 +
  1488 +
  1489 +
  1490 +
  1491 +
  1492 +
  1493 +
  1494 +
  1495 +
  1496 + *** [4.5] Reading the web arguments.
  1497 +
  1498 + HTTP/HTTPS requests are sent in one of two formats:
  1499 +
1500 1500 (1) www-url encoded
1501 1501 (2) multipart/form-data encoded
1502   -
  1502 +
1503 1503 The first one is the normal (historical) way of encoding. The second one is required
1504 1504 for uploading files. A server which is supposed to accept upload of files must handle
1505 1505 both formats. The first thing to do is to decide the format of the request. This is
1506 1506 easily done by examining the HTTP headers. If we find the header:
1507   -
  1507 +
1508 1508 Content-Type: multipart/form-data
1509   -
  1509 +
1510 1510 the request is multipart/form-data encoded. Otherwise, it is 'www-url' encoded. We
1511 1511 first consider 'www-url' encoded requests.
1512   -
  1512 +
1513 1513 For a 'www-url' encoded request, the web argument are either in the query string or in
1514 1514 the body of the request, or both. The format is the same for both:
1515   -
  1515 +
1516 1516 name=value&name=value&...
1517   -
  1517 +
1518 1518 However, we may also have
1519   -
  1519 +
1520 1520 name
1521 1521 name=
1522 1522 name=&...
1523 1523 name&...
1524   -
1525   - i.e. some parts may be missing. Hence, we must be careful.
1526   -
  1524 +
  1525 + i.e. some parts may be missing. Hence, we must be careful.
  1526 +
1527 1527 Furthermore, web arguments must be translated from web to ASCII when www-url encoded.
1528   -
  1528 +
1529 1529 define Bool
1530 1530 is_ampersand_or_equal
1531 1531 (
1532 1532 Word8 c
1533 1533 ) =
1534   - if c = '&' then true else c = '='.
1535   -
1536   -
1537   -
  1534 + if c = '&' then true else c = '='.
  1535 +
  1536 +
  1537 +
1538 1538 The function 'read_name_or_value' reads the string 's' starting at position 'n' until
1539   - either the end of the string or the first '&' or '='.
1540   -
1541   -define String
  1539 + either the end of the string or the first '&' or '='.
  1540 +
  1541 +define String
1542 1542 read_name_or_value
1543 1543 (
1544 1544 String s,
1545   - Int start,
  1545 + Int start,
1546 1546 Int i
1547 1547 ) =
1548   - if nth(i,s) is
  1548 + if nth(i,s) is
1549 1549 {
1550 1550 failure then substr(s,start,i - start),
1551 1551 success(c) then
... ... @@ -1553,101 +1553,101 @@ define String
1553 1553 then substr(s,start,i-start) // the separator is not included
1554 1554 else read_name_or_value(s,start,i+1)
1555 1555 }.
1556   -
1557   -
  1556 +
  1557 +
1558 1558 define List(Web_arg)
1559 1559 read_www_url_encoded_web_args
1560 1560 (
1561 1561 String s,
1562   - Int start,
  1562 + Int start,
1563 1563 ) =
1564   - with first = read_name_or_value(s,start,start),
  1564 + with first = read_name_or_value(s,start,start),
1565 1565 if first = ""
1566   - then []
1567   - else with i = start+length(first),
1568   - if nth(i,s) is
  1566 + then []
  1567 + else with i = start+length(first),
  1568 + if nth(i,s) is
1569 1569 {
1570 1570 failure then [web_arg(first,"")],
1571 1571 success(c) then
1572 1572 if c = '&'
1573 1573 then [web_arg(first,"") . read_www_url_encoded_web_args(s,i+1)]
1574   - else if c = '='
  1574 + else if c = '='
1575 1575 then with second1 = read_name_or_value(s,i+1,i+1),
1576   - // print("\""+second1+"\"\n");
  1576 + // print("\""+second1+"\"\n");
1577 1577 with second = web_to_ascii(second1,0,[]),
1578 1578 [web_arg(first,second) . read_www_url_encoded_web_args(s,i+length(second1)+2)]
1579 1579 else print("**** ALERT **** badly formatted argument [" + s + "]!!!\n");
1580 1580 []
1581 1581 }.
1582   -
1583   -
1584   -
1585   -
1586   -
1587   - *** [4.7] Reading the request line.
1588   -
  1582 +
  1583 +
  1584 +
  1585 +
  1586 +
  1587 + *** [4.7] Reading the request line.
  1588 +
1589 1589 'read_request_line' reads three words and a new line from the connection. It tries to
1590 1590 recognize "get" or "post" in the first word, separates the URI from the query string in
1591 1591 the second word, transforms the query string into a list of 'Web_arg', and finally
1592 1592 returns a datum of type 'HTTP_RequestLine' if no error arose.
1593   -
1594   -
  1593 +
  1594 +
1595 1595 define Result(Error,HTTP_RequestType)
1596 1596 identify_get_or_post
1597 1597 (
1598 1598 String s
1599   - ) =
  1599 + ) =
1600 1600 with ls = to_lower(s),
1601 1601 if ls = "get" then ok(get) else
1602 1602 if ls = "post" then ok(post) else
1603   - error(not_get_or_post_request(ls)).
1604   -
1605   -define Result(Error,HTTP_RequestLine)
  1603 + error(not_get_or_post_request(ls)).
  1604 +
  1605 +define Result(Error,HTTP_RequestLine)
1606 1606 read_request_line
1607 1607 (
1608 1608 BufferedConnection connection,
1609 1609 Int dead_line,
1610 1610 DenialOfService dos,
1611   - SState s
  1611 + SState s
1612 1612 ) =
1613   - if read_word(connection, dead_line, dos, s) is
  1613 + if read_word(connection, dead_line, dos, s) is
1614 1614 {
1615   - error(msg) then error(msg),
1616   - ok(get_or_post) then if read_word(connection, dead_line, dos, s) is
  1615 + error(msg) then error(msg),
  1616 + ok(get_or_post) then if read_word(connection, dead_line, dos, s) is
1617 1617 {
1618   - error(msg) then error(msg),
1619   - ok(uri_and_query_string) then if read_word(connection, dead_line, dos, s) is
  1618 + error(msg) then error(msg),
  1619 + ok(uri_and_query_string) then if read_word(connection, dead_line, dos, s) is
1620 1620 {
1621   - error(msg) then error(msg),
1622   - ok(http_version) then if read_new_line(connection, dead_line, dos, s) is
  1621 + error(msg) then error(msg),
  1622 + ok(http_version) then if read_new_line(connection, dead_line, dos, s) is
1623 1623 {
1624 1624 error(msg) then error(msg),
1625   - ok(_) then if separate_uri_from_query_string(uri_and_query_string,0) is
1626   - (uri,query_string) then if identify_get_or_post(get_or_post) is
  1625 + ok(_) then if separate_uri_from_query_string(uri_and_query_string,0) is
  1626 + (uri,query_string) then if identify_get_or_post(get_or_post) is
1627 1627 {
1628 1628 error(msg) then error(msg),
1629   - ok(request_type) then
  1629 + ok(request_type) then
1630 1630 ok(request_line(request_type, web_to_ascii(uri, 0, []), read_www_url_encoded_web_args(query_string,0)))
1631 1631 }
1632   - }
  1632 + }
1633 1633 }
1634 1634 }
1635 1635 }.
1636   -
1637   -
1638   -
1639   -
1640   -
1641   -
1642   -
1643   - *** [4.8] Reading the HTTP headers.
1644   -
  1636 +
  1637 +
  1638 +
  1639 +
  1640 +
  1641 +
  1642 +
  1643 + *** [4.8] Reading the HTTP headers.
  1644 +
1645 1645 Each header is made of a name (containing only letters, the underscore, digits and the
1646 1646 minus sign), a colon, a value, and a new line. The first empty line ends the headers.
1647   -
1648 1647  
1649   - The next function tests characters acceptable in a header name.
1650   -
  1648 +
  1649 + The next function tests characters acceptable in a header name.
  1650 +
1651 1651 define Bool
1652 1652 is_header_name_char
1653 1653 (
... ... @@ -1657,26 +1657,26 @@ define Bool
1657 1657 if ('A' +=< c & c +=< 'Z') then true else
1658 1658 if ('0' +=< c & c +=< '9') then true else
1659 1659 if c = '-' then true else
1660   - c = '_'.
1661   -
  1660 + c = '_'.
  1661 +
1662 1662 define Result(Error,String)
1663 1663 read_header_name
1664 1664 (
1665 1665 BufferedConnection connection,
1666   - Int dead_line,
  1666 + Int dead_line,
1667 1667 List(Word8) so_far,
1668 1668 DenialOfService dos,
1669   - SState s
  1669 + SState s
1670 1670 ) =
1671   - if next_char(connection, dead_line, dos, s) is
  1671 + if next_char(connection, dead_line, dos, s) is
1672 1672 {
1673 1673 error(msg) then error(msg),
1674   - ok(c) then
  1674 + ok(c) then
1675 1675 if is_header_name_char(c)
1676 1676 then read_header_name(connection, dead_line, [to_lower(c) . so_far], dos, s)
1677 1677 else unput(c, s); ok(implode(reverse(so_far)))
1678   - }.
1679   -
  1678 + }.
  1679 +
1680 1680 define Result(Error,One)
1681 1681 skip_colon
1682 1682 (
... ... @@ -1686,43 +1686,43 @@ define Result(Error,One)
1686 1686 SState s
1687 1687 ) =
1688 1688 //Skip the blank char until ':'
1689   - if skip_http_blanks(connection, dead_line, dos, s) is
  1689 + if skip_http_blanks(connection, dead_line, dos, s) is
1690 1690 {
1691   - error(msg) then error(msg),
1692   - ok(_) then
  1691 + error(msg) then error(msg),
  1692 + ok(_) then
1693 1693 if next_char(connection, dead_line, dos, s) is
1694 1694 {
1695   - error(msg) then error(msg),
1696   - ok(c) then
  1695 + error(msg) then error(msg),
  1696 + ok(c) then
1697 1697 if c = ':'
1698 1698 then ok(unique)
1699 1699 else error(colon_expected)
1700 1700 }}.
1701   -
1702   -
  1701 +
  1702 +
1703 1703 define Result(Error,String)
1704 1704 read_header_value
1705 1705 (
1706 1706 BufferedConnection connection,
1707   - Int dead_line,
  1707 + Int dead_line,
1708 1708 List(Word8) so_far,
1709 1709 DenialOfService dos,
1710 1710 SState s
1711 1711 ) =
1712   - if next_char(connection, dead_line, dos, s) is
  1712 + if next_char(connection, dead_line, dos, s) is
1713 1713 {
1714 1714 error(msg) then error(msg),
1715   - ok(c) then
  1715 + ok(c) then
1716 1716 if c = 13
1717   - then if next_char(connection, dead_line, dos, s) is
  1717 + then if next_char(connection, dead_line, dos, s) is
1718 1718 {
1719 1719 error(msg) then error(msg),
1720 1720 ok(d) then
1721 1721 if d = 10
1722   - then if next_char(connection, dead_line, dos, s) is
  1722 + then if next_char(connection, dead_line, dos, s) is
1723 1723 {
1724   - error(msg) then error(msg),
1725   - ok(e) then
  1724 + error(msg) then error(msg),
  1725 + ok(e) then
1726 1726 if is_strict_blank(e)
1727 1727 then read_header_value(connection,dead_line, [e . so_far], dos, s)
1728 1728 else (unput(e, s); ok(implode(reverse(so_far))))
... ... @@ -1731,106 +1731,106 @@ define Result(Error,String)
1731 1731 }
1732 1732 else read_header_value(connection,dead_line,[c . so_far],dos, s)
1733 1733 }.
1734   -
1735   -
  1734 +
  1735 +
1736 1736 Reading a single header.
1737   -
  1737 +
1738 1738 define Result(Error,Maybe(HTTP_header))
1739 1739 read_header
1740 1740 (
1741 1741 BufferedConnection connection,
1742 1742 Int dead_line,
1743 1743 DenialOfService dos,
1744   - SState s
  1744 + SState s
1745 1745 ) =
1746 1746 //Find the name
1747   - if read_header_name(connection, dead_line, [], dos, s) is
  1747 + if read_header_name(connection, dead_line, [], dos, s) is
1748 1748 {
1749   - error(msg) then error(msg),
1750   - ok(name) then
1751   - if name = "" then
  1749 + error(msg) then error(msg),
  1750 + ok(name) then
  1751 + if name = "" then
1752 1752 if read_and_ignore(connection, dead_line, 2, dos, s) /* 13 and 10 */ is
1753 1753 {
1754   - error(msg) then error(msg),
  1754 + error(msg) then error(msg),
1755 1755 ok(_) then // this is the blank line
1756 1756 ok(failure) // end of headers
1757 1757 }
1758 1758 //skip the ':' and blank before and after it
1759   - else if skip_colon(connection, dead_line, dos, s) is
  1759 + else if skip_colon(connection, dead_line, dos, s) is
1760 1760 {
1761   - error(msg) then error(msg),
1762   - ok(_) then
  1761 + error(msg) then error(msg),
  1762 + ok(_) then
1763 1763 //skip the blank char after the ':'
1764 1764 if skip_http_blanks(connection, dead_line, dos, s) is
1765 1765 {
1766 1766 error(msg) then error(msg),
1767   - ok(_) then
  1767 + ok(_) then
1768 1768 //Now read the value
1769 1769 if read_header_value(connection, dead_line, [], dos, s) is
1770 1770 {
1771   - error(msg) then error(msg),
  1771 + error(msg) then error(msg),
1772 1772 ok(value) then ok(success(http_header(name,value)))
1773 1773 }
1774 1774 }
1775 1775 }
1776 1776 }.
1777 1777  
1778   -
1779   -
1780   - Reading all the headers.
1781   -
  1778 +
  1779 +
  1780 + Reading all the headers.
  1781 +
1782 1782 define Result(Error,List(HTTP_header))
1783 1783 read_http_headers
1784 1784 (
1785   - BufferedConnection connection,
  1785 + BufferedConnection connection,
1786 1786 Int dead_line,
1787 1787 DenialOfService dos,
1788   - SState s
  1788 + SState s
1789 1789 ) =
1790   - if read_header(connection, dead_line, dos, s) is
  1790 + if read_header(connection, dead_line, dos, s) is
1791 1791 {
1792   - error(msg) then error(msg),
  1792 + error(msg) then error(msg),
1793 1793 ok(mbh) then if mbh is
1794 1794 {
1795   - failure then ok([ ]),
1796   - success(header) then
1797   - if read_http_headers(connection, dead_line, dos, s) is
  1795 + failure then ok([ ]),
  1796 + success(header) then
  1797 + if read_http_headers(connection, dead_line, dos, s) is
1798 1798 {
1799 1799 error(msg) then error(msg),
1800 1800 ok(others) then ok([header . others])
1801 1801 }
1802 1802 }
1803 1803 }.
1804   -
1805   -
1806   -
1807   -
1808   -
1809   -
1810   -
1811   - *** [4.9] Getting the size of the request's body.
  1804 +
  1805 +
  1806 +
  1807 +
  1808 +
  1809 +
  1810 +
  1811 + *** [4.9] Getting the size of the request's body.
1812 1812  
1813 1813 The size of the body of the request is given under the 'Content-Length' header. If this
1814   - header is not present, the size is assumed to be zero.
1815   -
1816   -define Result(Error,Int)
  1814 + header is not present, the size is assumed to be zero.
  1815 +
  1816 +define Result(Error,Int)
1817 1817 get_body_size
1818 1818 (
1819 1819 List(HTTP_header) headers
1820   - ) =
1821   - if headers is
  1820 + ) =
  1821 + if headers is
1822 1822 {
1823   - [ ] then ok(0),
1824   - [h . t] then if h is http_header(name,value) then
  1823 + [ ] then ok(0),
  1824 + [h . t] then if h is http_header(name,value) then
1825 1825 if name = "content-length" then
1826 1826 if decimal_scan(value) is
1827 1827 {
1828   - failure then error(incorrect_content_length_value),
  1828 + failure then error(incorrect_content_length_value),
1829 1829 success(n) then ok(n)
1830 1830 }
1831 1831 else get_body_size(t)
1832 1832 }.
1833   -
  1833 +
1834 1834  
1835 1835 *** [4.10] Reading the body of the request.
1836 1836  
... ... @@ -1842,11 +1842,11 @@ define Result(Error,Int)
1842 1842 required number of bytes. However, if the number of bytes read is zero, the connection
1843 1843 may be broken. In that case, we must not try to read indefinitely. On the contrary, we
1844 1844 make at most 10 retries, with a small sleeping time between any two of them.
1845   -
  1845 +
1846 1846 define Result(Error, ByteArray)
1847 1847 read_http_body
1848 1848 (
1849   - BufferedConnection connection,
  1849 + BufferedConnection connection,
1850 1850 Int body_size,
1851 1851 ByteArray so_far, // when calling this function, 'so_far' is the empty byte array
1852 1852 Int retries // this function is called with retries = 10
... ... @@ -1854,7 +1854,7 @@ define Result(Error, ByteArray)
1854 1854 if body_size = 0 then ok(constant_byte_array(0,0)) else
1855 1855 if retries =< 0 then error(cannot_read_from_connection) else
1856 1856  
1857   - if read_from_connexion(connection,body_size,60,constant_byte_array(body_size,0),0) is
  1857 + if read_from_connexion(connection,body_size,60,constant_byte_array(body_size,0),0) is
1858 1858 {
1859 1859 error then error(cannot_read_from_connection),
1860 1860 timeout then error(timeout(60)),
... ... @@ -1864,54 +1864,54 @@ define Result(Error, ByteArray)
1864 1864 nn = length(new_bytes), // number of bytes just read
1865 1865 if nr < body_size // must read more bytes
1866 1866 then if nn > 0 // if connection seems to work
1867   -
1868   - then
  1867 +
  1868 + then
1869 1869 println("http body ("+body_size+") read "+nn+" bytes current size "+nr+" Bytes");
1870 1870 read_http_body(connection,body_size,ba,1000) // continue reading
1871 1871 else sleep(100); // otherwise, sleep 1/10 of second
1872 1872 read_http_body(connection,body_size,ba, // and retry reading
1873 1873 retries-1) // but no more than 10 times
1874   - else ok(ba) // required number of bytes has been read
1875   - }.
1876   -
1877   -
  1874 + else ok(ba) // required number of bytes has been read
  1875 + }.
  1876 +
  1877 +
1878 1878 Note: During sleeping, 'anbexec' runs other machines. Actually, calling 'sleep', even
1879 1879 for one millisecond, is some way of giving up explicitly, so that other virtual
1880 1880 machines may work.
1881   -
1882   -
1883   -
1884   -
1885   -
1886   -
1887   -
1888   -
1889   -
1890   -
1891   -
1892   -
1893   - *** [5] Making the HTTP answer.
1894   -
  1881 +
  1882 +
  1883 +
  1884 +
  1885 +
  1886 +
  1887 +
  1888 +
  1889 +
  1890 +
  1891 +
  1892 +
  1893 + *** [5] Making the HTTP answer.
  1894 +
1895 1895 At that point we have read the request line, the headers and the body of the
1896 1896 request, and we must decide what to do.
1897   -
  1897 +
1898 1898 Actually, we can do one of the following:
1899   -
  1899 +
1900 1900 - send a file,
1901   - - execute 'tickets_and_web_page' in case of an ".awp" URI.
1902   -
  1901 + - execute 'tickets_and_web_page' in case of an ".awp" URI.
  1902 +
1903 1903 The uploaded file (which are in the body of the request) are saved into temporary files
1904 1904 below.
1905 1905  
1906   -
1907   -
1908   -
1909   -
1910   - *** [5.1] Avoiding illegal URIs.
1911   -
  1906 +
  1907 +
  1908 +
  1909 +
  1910 + *** [5.1] Avoiding illegal URIs.
  1911 +
1912 1912 For security reasons, we must avoid illegal URIs, for example those which may climb up
1913 1913 in the file hierarchy. First we accept only few characters in URIs.
1914   -
  1914 +
1915 1915 define Bool
1916 1916 is_legal_uri_char
1917 1917 (
... ... @@ -1921,28 +1921,28 @@ define Bool
1921 1921 if ('A' +=< c & c +=< 'Z') then true else // accept 'A' to 'Z'
1922 1922 if ('0' +=< c & c +=< '9') then true else // accept '0' to '9'
1923 1923 if c = '.' then true else // accept '.' '-' '/' and '_'
1924   - if c = '-' then true else
  1924 + if c = '-' then true else
1925 1925 if c = '/' then true else
1926   - c = '_'.
1927   -
  1926 + c = '_'.
  1927 +
1928 1928 We do not accept ~ which is some way of climbing. Of course, we cannot disallow single
1929 1929 dots, which are most often present in legal URIs, but we must avoid double dots ..
1930 1930 which mean 'climb up'.
1931   -
  1931 +
1932 1932 define Bool
1933 1933 is_illegal_uri
1934 1934 (
1935 1935 String uri,
1936 1936 Int n
1937 1937 ) =
1938   - if nth(n,uri) is
  1938 + if nth(n,uri) is
1939 1939 {
1940   - failure then false,
  1940 + failure then false,
1941 1941 success(c) then
1942 1942 if c = '.' // first dot
1943   - then if nth(n+1,uri) is
  1943 + then if nth(n+1,uri) is
1944 1944 {
1945   - failure then false,
  1945 + failure then false,
1946 1946 success(d) then
1947 1947 if d = '.' // second dot
1948 1948 then true
... ... @@ -1950,79 +1950,79 @@ define Bool
1950 1950 }
1951 1951 else is_illegal_uri(uri,n+1)
1952 1952 }.
1953   -
1954   -
1955   -
1956   -
1957   -
1958   -
1959   - *** [5.2] Managing authorizations for downloading private files.
  1953 +
  1954 +
  1955 +
  1956 +
  1957 +
  1958 +
  1959 + *** [5.2] Managing authorizations for downloading private files.
1960 1960  
1961 1961 Computing the authorization and making the authorization file (containing the absolute
1962 1962 path of the file on the server).
1963   -
1964   -
1965   -define String
1966   - compute_authorization
  1963 +
  1964 +
  1965 +define String
  1966 + compute_authorization
1967 1967 (
1968   - String authorization_secret,
  1968 + String authorization_secret,
1969 1969 String absolute_path
1970 1970 ) =
1971 1971 to_ascii(sha1((authorization_secret,
1972   - absolute_path))).
1973   -
1974   -
1975   -public define String
  1972 + absolute_path))).
  1973 +
  1974 +
  1975 +public define String
1976 1976 make_authorization
1977 1977 (
1978 1978 String site_directory,
1979   - String authorization_secret,
  1979 + String authorization_secret,
1980 1980 String absolute_path
1981 1981 ) =
1982   - with private_download_dir = site_directory+"/private_download",
  1982 + with private_download_dir = site_directory+"/private_download",
1983 1983 auth = compute_authorization(authorization_secret,
1984   - absolute_path),
  1984 + absolute_path),
1985 1985 forget(save(absolute_path,
1986   - private_download_dir+"/z"+auth));
1987   - auth.
1988   -
1989   -
1990   - The function 'send_file' defined below handles the recognition of authorizations.
1991   -
1992   -
1993   -
1994   -
1995   -
1996   - *** [5.3] Recognizing MIME types.
1997   -
  1986 + private_download_dir+"/z"+auth));
  1987 + auth.
  1988 +
  1989 +
  1990 + The function 'send_file' defined below handles the recognition of authorizations.
  1991 +
  1992 +
  1993 +
  1994 +
  1995 +
  1996 + *** [5.3] Recognizing MIME types.
  1997 +
1998 1998 The extension of the (redirected) URI must be either ".awp" or recognized as associated
1999 1999 to a MIME type. Otherwise, the server will not send the file. This is for security, but
2000 2000 also because, we must generate a 'Content-Type' header in the answer, with the right
2001 2001 MIME type.
2002   -
  2002 +
2003 2003 define String
2004 2004 get_uri_extension_aux
2005 2005 (
2006 2006 String uri,
2007 2007 Int n // used for searching backwards
2008 2008 ) =
2009   - if nth(n,uri) is
  2009 + if nth(n,uri) is
2010 2010 {
2011   - failure then "",
2012   - success(c) then
  2011 + failure then "",
  2012 + success(c) then
2013 2013 if c = '.' then substr(uri,n,length(uri)-n)
2014 2014 else if c = '/' then ""
2015 2015 else get_uri_extension_aux(uri,n-1)
2016 2016 }.
2017   -
  2017 +
2018 2018 public define String
2019 2019 get_uri_extension
2020 2020 (
2021 2021 String uri
2022 2022 ) =
2023 2023 get_uri_extension_aux(uri,
2024   - length(uri)-1). // search starts at the right end
2025   -
  2024 + length(uri)-1). // search starts at the right end
  2025 +
2026 2026 public define Bool
2027 2027 contains_no_case
2028 2028 (
... ... @@ -2032,42 +2032,42 @@ public define Bool
2032 2032 if l is
2033 2033 {
2034 2034 [] then false,
2035   - [h . t] then
  2035 + [h . t] then
2036 2036 if insensitive_equal(h, val) then true
2037 2037 else contains_no_case(t, val)
2038 2038 }.
2039 2039  
2040   -
2041   -define Maybe(MIME)
  2040 +
  2041 +define Maybe(MIME)
2042 2042 recognize_mime_type_from_ext
2043 2043 (
2044 2044 String ext,
2045 2045 List(MIME) l
2046 2046 ) =
2047   - if l is
  2047 + if l is
2048 2048 {
2049   - [ ] then success(mime("application", "octet-stream", [])), // failure,
  2049 + [ ] then success(mime("application", "octet-stream", [])), // failure,
2050 2050 [h . t] then if h is mime(type, subtype, extensions) then
2051 2051 if contains_no_case(extensions, ext)
2052 2052 then success(h)
2053 2053 else recognize_mime_type_from_ext(ext,t)
2054 2054 }.
2055   -
  2055 +
2056 2056 define Maybe(MIME)
2057 2057 recognize_mime_type_from_uri
2058 2058 (
2059   - Web_Site_Description desc,
  2059 + Web_Site_Description desc,
2060 2060 String uri
2061 2061 ) =
2062 2062 recognize_mime_type_from_ext(get_uri_extension(uri),known_mime_types(desc)).
2063   -
2064 2063  
2065   -
2066   - *** [5.4] Formating HTTP headers.
2067   -
  2064 +
  2065 +
  2066 + *** [5.4] Formating HTTP headers.
  2067 +
2068 2068 This is the formating for sending to the client (hence, it has nothing to do with the
2069 2069 component 'journal_headers' in the web site description).
2070   -
  2070 +
2071 2071 public define Printable_tree
2072 2072 format_headers
2073 2073 (
... ... @@ -2079,8 +2079,8 @@ public define Printable_tree
2079 2079 [h . t] then if h is http_header(name,value) then
2080 2080 [name,": ",value,crlf . format_headers(t)]
2081 2081 }.
2082   -
2083   -
  2082 +
  2083 +
2084 2084  
2085 2085 define String
2086 2086 month_abrv
... ... @@ -2099,7 +2099,7 @@ define String
2099 2099 else if d.month = 10 then "Oct"
2100 2100 else if d.month = 11 then "Nov"
2101 2101 else if d.month = 12 then "Dec"
2102   - else
  2102 + else
2103 2103 println("Bad month value [" + d.month + "] on Date_and_Time");
2104 2104 "XXX".
2105 2105  
... ... @@ -2115,10 +2115,10 @@ define String
2115 2115 else if d.week_day = 4 then "Thu"
2116 2116 else if d.week_day = 5 then "Fri"
2117 2117 else if d.week_day = 6 then "Sat"
2118   - else
  2118 + else
2119 2119 println("Bad weekday value [" + d.week_day + "] on Date_and_Time");
2120 2120 "XXX".
2121   -
  2121 +
2122 2122 /**
2123 2123 * Format a date with the followin format : "Mon, 23 Jul 2007 11:33:43 GMT"
2124 2124 * Currently, this function can't output a GMT time, but only local time.
... ... @@ -2129,7 +2129,7 @@ public define String
2129 2129 (
2130 2130 Date_and_Time d
2131 2131 ) =
2132   - weekday_abrv(d) + ", " + zero_pad_n(2,day(d)) + " " + month_abrv(d) + " " + year(d)
  2132 + weekday_abrv(d) + ", " + zero_pad_n(2,day(d)) + " " + month_abrv(d) + " " + year(d)
2133 2133 + " " + zero_pad_n(2,hour(d)) + ":" + zero_pad_n(2,minute(d)) + ":" + zero_pad_n(2,second(d)) + " GMT".
2134 2134  
2135 2135 /**
... ... @@ -2141,12 +2141,12 @@ public define String
2141 2141 Int date
2142 2142 ) =
2143 2143 format_http_date(convert_time(date)).
2144   -
2145 2144  
2146   - *** [5.5] Sending a file.
2147 2145  
2148   - We send 2 headers 'Content-Type' and 'Content-Length'.
2149   -
  2146 + *** [5.5] Sending a file.
  2147 +
  2148 + We send 2 headers 'Content-Type' and 'Content-Length'.
  2149 +
2150 2150 define List(HTTP_header)
2151 2151 headers_for_send_file
2152 2152 (
... ... @@ -2167,15 +2167,15 @@ define List(HTTP_header)
2167 2167 success(ftimes) then [http_header("Last-Modified", format_http_date(to_Int(ftimes.last_modified))) . headers]
2168 2168 }
2169 2169 .
2170   -
2171   -
2172   -
2173   - Sending the body of the answer (i.e. the file itself).
2174   -
  2170 +
  2171 +
  2172 +
  2173 + Sending the body of the answer (i.e. the file itself).
  2174 +
2175 2175 define One
2176 2176 send_file_body
2177 2177 (
2178   - Web_Site_Description desc,
  2178 + Web_Site_Description desc,
2179 2179 Connection connection, // connection with the client
2180 2180 Connection file, // file to be sent already opened
2181 2181 Int size, // size of file
... ... @@ -2183,21 +2183,21 @@ define One
2183 2183 String filename // name of file
2184 2184 ) =
2185 2185 if sent >= size then unique else
2186   - if read(file,min(16384,size-sent),60) is
  2186 + if read(file,min(16384,size-sent),60) is
2187 2187 {
2188 2188 error then log_journal_msg(desc,"Cannot read from file '"+filename+"'.\n"),
2189 2189 timeout then log_journal_msg(desc,"Cannot read from file timeoput'"+filename+"'.\n"),
2190   - ok(ba) then
  2190 + ok(ba) then
2191 2191 with nr = length(ba), // get the number of bytes read
2192   - if reliable_write(connection, ba) is
  2192 + if reliable_write(connection, ba) is
2193 2193 {
2194   - failure then log_journal_msg(desc,"Cannot write into connection delirering '"+filename+"' (sent="+sent+"; size="+size+"; current="+nr+").\n"),
2195   - success(nw) then
2196   - send_file_body(desc,connection,file,size,sent+nw,filename)
  2194 + failure then log_journal_msg(desc,"Cannot write into connection delirering '"+filename+"' (sent="+sent+"; size="+size+"; current="+nr+").\n"),
  2195 + success(nw) then
  2196 + send_file_body(desc,connection,file,size,sent+nw,filename)
2197 2197 }
2198   - }.
  2198 + }.
  2199 +
2199 2200  
2200   -
2201 2201 define String
2202 2202 compute_etag
2203 2203 (
... ... @@ -2205,7 +2205,7 @@ define String
2205 2205 Maybe(FileTimes) mb_ftimes,
2206 2206 Int size,
2207 2207 ) =
2208   - if mb_ftimes is
  2208 + if mb_ftimes is
2209 2209 {
2210 2210 failure then println("Warning: no file times for '" + filename + "', etag won't be very accurate."); to_ascii(sha1((filename, size))),
2211 2211 success(ftimes) then to_ascii(md5((filename, ftimes, size)))
... ... @@ -2222,13 +2222,13 @@ define Bool
2222 2222 failure then false,
2223 2223 success(etag) then trim_token(etag, '\"') = current_etag //remove triming " because this is quoted string
2224 2224 }.
2225   -
2226   - Sending the answer line, the headers and the body.
2227 2225  
2228   -define One
  2226 + Sending the answer line, the headers and the body.
  2227 +
  2228 +define One
2229 2229 send_file
2230 2230 (
2231   - Web_Site_Description desc,
  2231 + Web_Site_Description desc,
2232 2232 Connection connection,
2233 2233 List(HTTP_header) input_headers,
2234 2234 List(HTTP_header) headers,
... ... @@ -2239,79 +2239,79 @@ define One
2239 2239 MIME mime_type,
2240 2240 One -> One action_before_send_file
2241 2241 ) =
2242   - action_before_send_file(unique);
2243   - with input_etag = http_header_value(input_headers, "If-None-Match"),
  2242 + action_before_send_file(unique);
  2243 + with input_etag = http_header_value(input_headers, "If-None-Match"),
2244 2244 mb_ftimes = get_file_times(full_path),
2245 2245 current_etag = compute_etag(full_path, mb_ftimes, size),
2246 2246 if are_same_etag(input_etag, current_etag) is
2247 2247 {
2248   - false then
2249   - forget(reliable_write(connection,to_byte_array("HTTP/1.1 200 OK"+crlf)));
  2248 + false then
  2249 + forget(reliable_write(connection,to_byte_array("HTTP/1.1 200 OK"+crlf)));
2250 2250 forget(reliable_write(connection,[format_headers(headers + headers_for_send_file(mime_type, size, current_etag, mb_ftimes)) , crlf]));
2251 2251 //forget(copy_file_to_Connection(file, connection, size)),
2252 2252 send_file_body(desc,connection,file,size,0,filename),
2253 2253 true then
2254   - forget(reliable_write(connection,to_byte_array("HTTP/1.1 304 Not Modified"+crlf)));
  2254 + forget(reliable_write(connection,to_byte_array("HTTP/1.1 304 Not Modified"+crlf)));
2255 2255 forget(reliable_write(connection,[format_headers([http_header("Content-length","0") . [http_header("Etag", "\""+current_etag+"\"") . headers]]) , crlf]))
2256 2256 //send_file_body(desc,connection,file,size,0,filename)
2257   - }.
2258   -
  2257 + }.
  2258 +
2259 2259  
2260 2260  
2261   - Checking if a connection is under SSL.
2262   -
  2261 + Checking if a connection is under SSL.
  2262 +
2263 2263 define Bool
2264 2264 is_SSL
2265 2265 (
2266 2266 Connection c
2267 2267 ) =
2268   - if c is
  2268 + if c is
2269 2269 {
2270   - file_r(_) then false,
2271   - file_w(_) then false,
2272   - file_rw(_) then false,
2273   - tcp(_) then false,
  2270 + file_r(_) then false,
  2271 + file_w(_) then false,
  2272 + file_rw(_) then false,
  2273 + tcp(_) then false,
2274 2274 ssl(_) then true
2275   - }.
  2275 + }.
  2276 +
  2277 +
2276 2278  
2277   -
2278   -
2279 2279 Before opening and sending a file, we check the MIME type. It must be recognized,
2280 2280 except if there is a valid authorization for private download.
2281 2281  
2282 2282 define One
2283 2283 send_file
2284 2284 (
2285   - Web_Site_Description desc,
2286   - Connection connection,
2287   - String uri,
  2285 + Web_Site_Description desc,
  2286 + Connection connection,
  2287 + String uri,
2288 2288 List(HTTP_header) input_headers,
2289 2289 List(HTTP_header) output_headers,
2290 2290 Maybe(String) mbauthorization,
2291 2291 One -> One action_before_send_file
2292 2292 ) =
2293   - if mbauthorization is
  2293 + if mbauthorization is
2294 2294 {
2295 2295 //--- file without authorization: take it from public ---
2296   - failure then if recognize_mime_type_from_uri(desc,uri) is
  2296 + failure then if recognize_mime_type_from_uri(desc,uri) is
2297 2297 {
2298 2298 failure then log_journal_msg(desc,"No MIME type found for '"+uri+"'.\n"),
2299   - success(mime_type) then
  2299 + success(mime_type) then
2300 2300 with path = site_directory(desc)+"/public"+uri,
2301   - if (Maybe(RStream))file(path, read) is
  2301 + if (Maybe(RStream))file(path, read) is
2302 2302 {
2303   - failure then
  2303 + failure then
2304 2304 println("HTTP/1.1 404 Not Found"+path);
2305 2305 forget(reliable_write(connection,to_byte_array("HTTP/1.1 404 Not Found"+crlf+
2306 2306 "Content-Length: 0"+crlf+crlf
2307 2307 /*+"Connection: close"+crlf+crlf*/)));
2308 2308 log_journal_msg(desc,"Cannot find file '"+path+"'.\n"),
2309   - success(f) then with size = file_size(f),
  2309 + success(f) then with size = file_size(f),
2310 2310 send_file(desc,
2311 2311 connection,
2312 2312 input_headers,
2313 2313 output_headers,
2314   - size,
  2314 + size,
2315 2315 file(f),
2316 2316 uri,
2317 2317 path,
... ... @@ -2319,60 +2319,60 @@ define One
2319 2319 action_before_send_file)
2320 2320 }
2321 2321 },
2322   -
  2322 +
2323 2323 //--- file with authorization: apply 'private download' mecanism ---
2324   - success(authorization) then
2325   - with private_download_dir = site_directory(desc)+"/private_download",
2326   - if (RetrieveResult(String))retrieve(private_download_dir+"/z"+authorization)
2327   - is ok(absolute_path)
  2324 + success(authorization) then
  2325 + with private_download_dir = site_directory(desc)+"/private_download",
  2326 + if (RetrieveResult(String))retrieve(private_download_dir+"/z"+authorization)
  2327 + is ok(absolute_path)
2328 2328 then (
2329 2329 with new_hash = compute_authorization(authorization_secret(desc),
2330   - absolute_path),
2331   - if (Maybe(RStream))file(absolute_path, read) is
  2330 + absolute_path),
  2331 + if (Maybe(RStream))file(absolute_path, read) is
2332 2332 {
2333   - failure then
  2333 + failure then
2334 2334 println("HTTP/1.1 404 Not Found"+absolute_path);
2335 2335 forget(reliable_write(connection,to_byte_array("HTTP/1.1 404 Not Found"+crlf+
2336 2336 "Content-Length: 0"+crlf+crlf
2337 2337 /*+"Connection: close"+crlf+crlf*/)));
2338 2338 log_journal_msg(desc,"Cannot find file '"+absolute_path+"'.\n"),
2339   - success(f) then with size = file_size(f),
2340   - mime_type = if recognize_mime_type_from_uri(desc,uri) is
  2339 + success(f) then with size = file_size(f),
  2340 + mime_type = if recognize_mime_type_from_uri(desc,uri) is
2341 2341 {
2342 2342 failure then mime("application", "octet-stream", []),
2343 2343 success(mime_type) then mime_type
2344 2344 },
2345 2345 send_file(desc,
2346   - connection,
  2346 + connection,
2347 2347 input_headers,
2348 2348 output_headers,
2349   - size,
2350   - file(f),
  2349 + size,
  2350 + file(f),
2351 2351 uri,
2352 2352 absolute_path,
2353 2353 mime_type,
2354 2354 action_before_send_file)
2355 2355 }
2356 2356 )
2357   - else log_journal_msg(desc,"Cannot find or read authorization file.\n")
  2357 + else log_journal_msg(desc,"Cannot find or read authorization file.\n")
2358 2358 }.
2359   -
2360   -
2361   -
2362   -
2363   -
2364   -
2365   -
2366   -
2367   - *** [5.6] Answering a www-url encoded request.
2368   -
2369   - Standard headers are for answering ".awp" requests.
2370   -
  2359 +
  2360 +
  2361 +
  2362 +
  2363 +
  2364 +
  2365 +
  2366 +
  2367 + *** [5.6] Answering a www-url encoded request.
  2368 +
  2369 + Standard headers are for answering ".awp" requests.
  2370 +
2371 2371 public define List(HTTP_header)
2372 2372 standard_headers
2373 2373 =
2374 2374 [
2375   - http_header("Date", format_http_date(now)),
  2375 + http_header("Date", format_http_date(now)),
2376 2376 http_header("Server", "Anubis Embedded Server v" + major_version_number + "." + minor_version_number),
2377 2377 /*http_header("Connection", "close"), */
2378 2378 ].
... ... @@ -2385,10 +2385,10 @@ public define List(HTTP_header)
2385 2385 Maybe(String) mb_charset,
2386 2386 ) =
2387 2387 [
2388   - http_header("Content-Type", mime_type + if mb_charset is success(charset) then "; charset="+charset else ""),
  2388 + http_header("Content-Type", mime_type + if mb_charset is success(charset) then "; charset="+charset else ""),
2389 2389 http_header("Content-length", to_decimal(answer_body_size))
2390 2390 ].
2391   -
  2391 +
2392 2392 public define List(HTTP_header)
2393 2393 file_attached_header
2394 2394 (
... ... @@ -2396,34 +2396,34 @@ public define List(HTTP_header)
2396 2396 ) =
2397 2397 [
2398 2398 http_header("Content-Disposition", "attachment; filename=\"" + filename +"\"")
2399   - ].
2400   -
  2399 + ].
  2400 +
2401 2401  
2402 2402 define One
2403 2403 www_url_answer
2404 2404 (
2405   - String host_name,
2406   - Web_Site_Description desc,
  2405 + String host_name,
  2406 + Web_Site_Description desc,
2407 2407 Connection connection, // with the client
2408 2408 Word32 ip_addr, // of the client
2409 2409 HTTP_RequestLine request_line,
2410   - List(HTTP_header) headers,
  2410 + List(HTTP_header) headers,
2411 2411 ByteArray body,
2412 2412 One -> String generate_tt // trust ticket generation
2413   - ) =
2414   - with all_web_args = query_string(request_line) +
  2413 + ) =
  2414 + with all_web_args = query_string(request_line) +
2415 2415 read_www_url_encoded_web_args(to_string(body),0),
2416 2416 uri = uri(request_line),
2417   - ext = get_uri_extension(uri),
  2417 + ext = get_uri_extension(uri),
2418 2418 http_inf = http_info(ip_addr, host_name, uri, headers, is_SSL(connection)),
2419 2419 (if member(journal_extensions(desc),ext)
2420 2420 then log_journal_msg(desc,
2421 2421 format_request(desc,connection,request_line,headers,all_web_args))
2422   - else unique);
2423   - if is_illegal_uri(uri,0)
2424   - then log_journal_msg(desc,"Received illegal URI: "+uri+"\n")
  2422 + else unique);
  2423 + if is_illegal_uri(uri,0)
  2424 + then log_journal_msg(desc,"Received illegal URI: "+uri+"\n")
2425 2425 else (if (ext = ".awp" | ext = "")
2426   - then (with answer_headers_body = awp_handler(desc)(host_name,
  2426 + then (with answer_headers_body = awp_handler(desc)(host_name,
2427 2427 http_inf,
2428 2428 all_web_args,
2429 2429 is_SSL(connection)),
... ... @@ -2436,7 +2436,7 @@ define One
2436 2436 uri,
2437 2437 headers,
2438 2438 standard_headers,
2439   - if web_arg_value(all_web_args,"zauth") is
  2439 + if web_arg_value(all_web_args,"zauth") is
2440 2440 {
2441 2441 not_found then failure,
2442 2442 found(v) then success(v)
... ... @@ -2444,30 +2444,30 @@ define One
2444 2444 (One u) |-> before_send_file(desc)(http_inf, all_web_args))
2445 2445 //print_delta("After sending file")
2446 2446 )).
2447   -
2448   -
2449   -
2450   -
2451   -
2452   -
2453   -
2454   - *** [5.7] Answering a multipart/form-data encoded request.
2455   -
  2447 +
  2448 +
  2449 +
  2450 +
  2451 +
  2452 +
  2453 +
  2454 + *** [5.7] Answering a multipart/form-data encoded request.
  2455 +
2456 2456 In order to support upload of files, we must be able to read web arguments which are
2457 2457 encoded in a multipart/form-data body. The first thing to do is to find the
2458 2458 boundary. The boundary is a special string which delimits the various parts of the
2459 2459 'multipart' body. It is found within the value of the 'Content-Type' HTTP header, as
2460 2460 the value of the 'boundary' attribute.
2461   -
2462   -
2463   -
2464   -
2465   -
  2461 +
  2462 +
  2463 +
  2464 +
  2465 +
2466 2466 *** [5.7.1] Finding the boundary.
2467   -
  2467 +
2468 2468 Hence, we just have to find the string 'boundary=' within the value of the
2469 2469 'Content-Type' header, and read the value of the boundary from there.
2470   -
  2470 +
2471 2471 define Bool
2472 2472 delimits_boundary
2473 2473 (
... ... @@ -2478,101 +2478,101 @@ define Bool
2478 2478 if c = 10 then true else
2479 2479 if c = 0 then true else
2480 2480 if c = ',' then true else
2481   - c = ';'.
2482   -
2483   -
  2481 + c = ';'.
  2482 +
  2483 +
2484 2484 define Maybe(String)
2485 2485 get_boundary_value_3
2486 2486 (
2487   - String s,
2488   - Int i,
  2487 + String s,
  2488 + Int i,
2489 2489 List(Word8) so_far
2490 2490 ) =
2491   - if nth(i,s) is
  2491 + if nth(i,s) is
2492 2492 {
2493 2493 failure then success(implode(reverse(so_far))),
2494   - success(c) then
  2494 + success(c) then
2495 2495 if delimits_boundary(c)
2496   - then success(implode(reverse(so_far)))
  2496 + then success(implode(reverse(so_far)))
2497 2497 else get_boundary_value_3(s,i+1,[c . so_far])
2498   - }.
2499   -
2500   -
2501   -
  2498 + }.
  2499 +
  2500 +
  2501 +
2502 2502 define Maybe(String)
2503 2503 get_boundary_value_2
2504 2504 (
2505 2505 String s,
2506 2506 Int i,
2507 2507 ) =
2508   - if nth(i,s) is
  2508 + if nth(i,s) is
2509 2509 {
2510   - failure then failure,
  2510 + failure then failure,
2511 2511 success(c) then
2512   - if is_blank(c)
  2512 + if is_blank(c)
2513 2513 then get_boundary_value_2(s,i+1)
2514 2514 else get_boundary_value_3(s,i+1,[c])
2515   - }.
2516   -
  2515 + }.
  2516 +
2517 2517 define Maybe(String)
2518 2518 get_boundary_value_1
2519 2519 (
2520 2520 String s, // string into which we must find '= ...'
2521 2521 Int i // position of start of search
2522   - ) =
2523   - if nth(i,s) is
  2522 + ) =
  2523 + if nth(i,s) is
2524 2524 {
2525   - failure then failure,
  2525 + failure then failure,
2526 2526 success(c) then
2527   - if is_blank(c)
  2527 + if is_blank(c)
2528 2528 then get_boundary_value_1(s,i+1)
2529 2529 else if c = '='
2530 2530 then get_boundary_value_2(s,i+1)
2531 2531 else failure
2532 2532 }.
2533   -
2534   -
  2533 +
  2534 +
2535 2535 define Maybe(String)
2536 2536 get_boundary
2537 2537 (
2538 2538 String content_type_header_value
2539 2539 ) =
2540   - if find("boundary",content_type_header_value,0) is
  2540 + if find("boundary",content_type_header_value,0) is
2541 2541 {
2542   - failure then failure,
2543   - success(n) then // 'boundary' has been found at position n
  2542 + failure then failure,
  2543 + success(n) then // 'boundary' has been found at position n
2544 2544 get_boundary_value_1(content_type_header_value,n+8)
2545   - }.
2546   -
  2545 + }.
  2546 +
2547 2547 define Maybe(String)
2548 2548 get_boundary
2549 2549 (
2550 2550 List(HTTP_header) headers
2551 2551 )=
2552   - if headers is
  2552 + if headers is
2553 2553 {
2554   - [ ] then failure,
2555   - [h . t] then if h is http_header(name,value) then
  2554 + [ ] then failure,
  2555 + [h . t] then if h is http_header(name,value) then
2556 2556 if name = "content-type"
2557 2557 then get_boundary(value)
2558 2558 else get_boundary(t)
2559   - }.
2560   -
2561   -
2562   -
2563   -
2564   -
2565   -
2566   -
2567   -
2568   - *** [5.7.2] Reading attributes from a multipart entity.
2569   -
  2559 + }.
  2560 +
  2561 +
  2562 +
  2563 +
  2564 +
  2565 +
  2566 +
  2567 +
  2568 + *** [5.7.2] Reading attributes from a multipart entity.
  2569 +
2570 2570 Entities in a multipart/form-data body are separated by instances of the string:
2571   -
  2571 +
2572 2572 --bbbbb
2573   -
  2573 +
2574 2574 where bbbbb is the boundary computed above. Actually, the body has the form:
2575   -
  2575 +
2576 2576 --bbbbb
2577 2577 <entity 1>
2578 2578 --bbbbb
... ... @@ -2582,273 +2582,273 @@ define Maybe(String)
2582 2582 --bbbbb
2583 2583 <last entity>
2584 2584 --bbbbb
2585   -
2586   -
  2585 +
  2586 +
2587 2587 We have to extract an entity which is in the body between offsets 'start' and 'end'
2588 2588 (computed when boundaries have been localized). The entity itself is made of two parts:
2589 2589 headers and body. The body is separated from the headers by a blank line. This blank
2590 2590 line (a double crlf) marks the beginning of the body of the entity. Within the headers
2591 2591 of the entity, we look for a 'Content-Disposition' header, which should look like this:
2592   -
  2592 +
2593 2593 Content-Disposition: form-data; name="..."; filename="..." crlf
2594   -
  2594 +
2595 2595 We are just interested in the name and the file name. Hence we first search
2596 2596 'Content-Disposition', then we search 'name' and read the value, and we do the same for
2597 2597 'filename'.
2598   -
  2598 +
2599 2599 If the 'filename' attribute is not present, the web arg is an ordinary one, otherwise,
2600   - it is an uploaded file.
2601   -
  2600 + it is an uploaded file.
  2601 +
2602 2602  
2603 2603 Below is a variant of 'find' (see 'tools/findstring.anubis'), with an extra 'end'
2604 2604 argument.
2605   -
2606   -define Maybe(Int)
  2605 +
  2606 +define Maybe(Int)
2607 2607 find
2608 2608 (
2609   - String what,
2610   - ByteArray where,
  2609 + String what,
  2610 + ByteArray where,
2611 2611 Int start,
2612 2612 Int end
2613 2613 ) =
2614   - if find(to_byte_array(what),where,start) is
  2614 + if find(to_byte_array(what),where,start) is
2615 2615 {
2616   - failure then failure,
2617   - success(n) then
2618   - if n+length(what) >= end
  2616 + failure then failure,
  2617 + success(n) then
  2618 + if n+length(what) >= end
2619 2619 then failure
2620 2620 else success(n)
2621 2621 }.
2622 2622  
2623   -define Maybe(Int)
  2623 +define Maybe(Int)
2624 2624 find
2625 2625 (
2626   - String what,
2627   - String where,
  2626 + String what,
  2627 + String where,
2628 2628 Int start,
2629 2629 Int end
2630 2630 ) =
2631   - if find_string(where,what,start) is
  2631 + if find_string(where,what,start) is
2632 2632 {
2633   - failure then failure,
2634   - success(n) then
2635   - if n+length(what) >= end
  2633 + failure then failure,
  2634 + success(n) then
  2635 + if n+length(what) >= end
2636 2636 then failure
2637 2637 else success(n)
2638   - }.
2639   -
  2638 + }.
  2639 +
2640 2640 define String
2641 2641 read_attribute_value
2642 2642 (
2643   - ByteArray where,
  2643 + ByteArray where,
2644 2644 Int start,
2645 2645 Int end,
2646 2646 List(Word8) so_far
2647 2647 ) =
2648 2648 if start >= end then implode(reverse(so_far)) else
2649   - if nth(start,where) is
  2649 + if nth(start,where) is
2650 2650 {
2651 2651 failure then implode(reverse(so_far)),
2652   - success(c) then
  2652 + success(c) then
2653 2653 if c = '\"'
2654 2654 then implode(reverse(so_far))
2655 2655 else read_attribute_value(where,start+1,end,[c . so_far])
2656 2656 }.
2657   -
2658   -define Maybe(String)
  2657 +
  2658 +define Maybe(String)
2659 2659 find_attribute
2660 2660 (
2661   - String name,
2662   - ByteArray where,
2663   - Int start,
  2661 + String name,
  2662 + ByteArray where,
  2663 + Int start,
2664 2664 Int end
2665 2665 ) =
2666 2666 with prefix = name+"=\"",
2667 2667 if find(to_byte_array(prefix),where,start) is
2668 2668 {
2669 2669 failure then failure,
2670   - success(n) then
  2670 + success(n) then
2671 2671 if n+length(prefix) >= end
2672 2672 then failure
2673 2673 else success(read_attribute_value(where,n+length(prefix),end,[]))
2674 2674 }.
2675   -
2676   -
2677   -
  2675 +
  2676 +
  2677 +
2678 2678 define Maybe((String,Maybe(String)))
2679 2679 find_name_and_filename
2680 2680 (
2681   - ByteArray body,
2682   - Int start,
  2681 + ByteArray body,
  2682 + Int start,
2683 2683 Int end
2684 2684 ) =
2685   - if find(to_byte_array("Content-Disposition"),body,start) is
  2685 + if find(to_byte_array("Content-Disposition"),body,start) is
2686 2686 {
2687   - failure then failure,
2688   - success(n) then
2689   - if find_attribute("name",body,n+19,end) is
  2687 + failure then failure,
  2688 + success(n) then
  2689 + if find_attribute("name",body,n+19,end) is
2690 2690 {
2691 2691 failure then failure,
2692   - success(name_value) then if find_attribute("filename",body,n+19,end) is
  2692 + success(name_value) then if find_attribute("filename",body,n+19,end) is
2693 2693 {
2694 2694 failure then success((name_value,failure)),
2695 2695 success(filename_value) then success((name_value,success(filename_value)))
2696 2696 }
2697 2697 }
2698 2698 }.
2699   -
2700   -
2701   -
2702   -
2703   -
2704   -
2705   -
2706   -
2707   -
2708   -
2709   - *** [5.7.3] Creating a temporary filename for an uploaded file.
2710   -
2711   - variable Int uploaded_file_count = 0.
2712   -
  2699 +
  2700 +
  2701 +
  2702 +
  2703 +
  2704 +
  2705 +
  2706 +
  2707 +
  2708 +
  2709 + *** [5.7.3] Creating a temporary filename for an uploaded file.
  2710 +
  2711 + variable Int uploaded_file_count = 0.
  2712 +
2713 2713 This variable is local to the virtual machine. Hence, its value is 0 each time a new
2714 2714 requests arrives. Temporary uploaded files are stored in the directory represented by
2715 2715 'upload_temporary_directory'. The filenames have the form:
2716   -
  2716 +
2717 2717 _m_n
2718   -
  2718 +
2719 2719 where 'm' is the number of the virtual machine, and 'n' a number obtained by
2720 2720 incrementing 'uploaded_file_count'. Notice that the program must do something with this
2721 2721 file (move it to some directory/name), otherwise, it will probably be overwritten the
2722 2722 next time the same machine works.
2723   -
2724   -
2725   -
2726   -
2727   -
2728   -
2729   - *** [5.7.4] Saving an uploaded file under a temporary filename.
2730   -
  2723 +
  2724 +
  2725 +
  2726 +
  2727 +
  2728 +
  2729 + *** [5.7.4] Saving an uploaded file under a temporary filename.
  2730 +
2731 2731 define Maybe(String) // returns the temporary file name
2732 2732 save_uploaded_file
2733 2733 (
2734 2734 String web_site_dir,
2735   -// Web_Site_Description desc,
2736   -
  2735 +// Web_Site_Description desc,
  2736 +
2737 2737 RStream body_fd,
2738   -// ByteArray body,
  2738 +// ByteArray body,
2739 2739 Int start,
2740 2740 Int end,
2741   - SState s
  2741 + SState s
2742 2742 ) =
2743   - s.uploaded_file_count <- 1 + *(s.uploaded_file_count);
  2743 + s.uploaded_file_count <- 1 + *(s.uploaded_file_count);
2744 2744 with tfn = "_"+to_decimal(virtual_machine_id)+"_"+to_decimal(*(s.uploaded_file_count)),
2745 2745 // println("save_uploaded_file to :"+site_directory(desc)+"/upload_temporary/"+tfn);
2746 2746 // println("start offset = "+start+" end offset = "+end);
2747 2747 //make data_io which is the size of the file to extract
2748   - if copy_Data_IO_to_file(make_data_io(body_fd, start, end - start), web_site_dir+"/upload_temporary/"+tfn) is copy_ok(_)
  2748 + if copy_Data_IO_to_file(make_data_io(body_fd, start, end - start), web_site_dir+"/upload_temporary/"+tfn) is copy_ok(_)
2749 2749 then success(tfn)
2750 2750 else failure
2751 2751 .
2752   -
2753   -
2754   -
2755   -// if (Maybe(RWStream))file(site_directory(desc)+"/upload_temporary/"+tfn, new) is
  2752 +
  2753 +
  2754 +
  2755 +// if (Maybe(RWStream))file(site_directory(desc)+"/upload_temporary/"+tfn, new) is
2756 2756 // {
2757   -// failure then failure,
2758   -// success(f) then
2759   -// if reliable_write(file(f),extract(body,start,end)) is
  2757 +// failure then failure,
  2758 +// success(f) then
  2759 +// if reliable_write(file(f),extract(body,start,end)) is
2760 2760 // {
2761   -// failure then failure,
  2761 +// failure then failure,
2762 2762 // success(nw) then
2763 2763 // if nw = end - start
2764 2764 // then success(tfn)
2765 2765 // else failure
2766 2766 // }
2767   -// }.
2768   -
2769   -
2770   -
2771   -
2772   -
2773   -
2774   -
2775   -
2776   - *** [5.7.5] Removing the path from a file name.
  2767 +// }.
  2768 +
  2769 +
  2770 +
  2771 +
  2772 +
  2773 +
  2774 +
  2775 +
  2776 + *** [5.7.5] Removing the path from a file name.
2777 2777  
2778 2778 When a file is uploaded, the browser sends the complete path of the file on the client
2779 2779 machine as the file name. Actually, this is not quite normal. Nevertheless, we need to
2780   - remove the path, and keep only the file name. This is achieved by 'remove_path' below.
2781   -
  2780 + remove the path, and keep only the file name. This is achieved by 'remove_path' below.
  2781 +
2782 2782 define Int
2783 2783 file_name_begin
2784 2784 (
2785 2785 String full_name,
2786 2786 Int i
2787 2787 ) =
2788   - if nth(i,full_name) is
  2788 + if nth(i,full_name) is
2789 2789 {
2790   - failure then 0,
2791   - success(c) then
  2790 + failure then 0,
  2791 + success(c) then
2792 2792 if c = '/' then i+1 else
2793 2793 if c = '\\' then i+1 else
2794 2794 file_name_begin(full_name,i-1)
2795   - }.
2796   -
  2795 + }.
  2796 +
2797 2797 define String
2798 2798 remove_path
2799 2799 (
2800 2800 String full_name
2801 2801 ) =
2802   - with l = length(full_name),
  2802 + with l = length(full_name),
2803 2803 b = file_name_begin(full_name,l-1),
2804   - substr(full_name,b,l-b).
2805   -
2806   -
2807   -
2808   -
2809   -
2810   - *** [5.7.6] Reading a multipart entity.
2811   -
  2804 + substr(full_name,b,l-b).
  2805 +
  2806 +
  2807 +
  2808 +
  2809 +
  2810 + *** [5.7.6] Reading a multipart entity.
  2811 +
2812 2812 define Maybe(Web_arg)
2813 2813 get_multipart_entity
2814 2814 (
2815   - //Web_Site_Description desc,
  2815 + //Web_Site_Description desc,
2816 2816 String web_site_dir,
2817   - String body_temp_file,
  2817 + String body_temp_file,
2818 2818 Int start_offset, //real offset in source file of the part
2819 2819 Int end_offset, //real offset in source file of the part
2820   - SState s
  2820 + SState s
2821 2821 )=
2822   - //Get header of the part
2823   - if find_the_first(body_temp_file, crlf+crlf, start_offset, end_offset) is
  2822 + //Get header of the part
  2823 + if find_the_first(body_temp_file, crlf+crlf, start_offset, end_offset) is
2824 2824 {
2825   - failure then failure,
2826   - success(k) then
  2825 + failure then failure,
  2826 + success(k) then
2827 2827 if (Maybe(RStream))file(body_temp_file, read) is
2828 2828 {
2829 2829 failure then failure,
2830 2830 success(body_fd) then
2831 2831 if read_bytes(make_data_io(body_fd, start_offset, k), k) is success(header_part) then
2832   -
  2832 +
2833 2833 if find_name_and_filename(header_part, 0, k) is
2834 2834 {
2835   - failure then failure,
2836   - success(n_mbfn) then if n_mbfn is (name, mbfn) then
2837   - if mbfn is
  2835 + failure then failure,
  2836 + success(n_mbfn) then if n_mbfn is (name, mbfn) then
  2837 + if mbfn is
2838 2838 {
2839   - failure then
  2839 + failure then
2840 2840 if read_bytes(make_data_io(body_fd, start_offset+k+4, (end_offset-2)-(start_offset+k+4)), (end_offset-2)-(start_offset+k+4)) is success(attachement) then
2841 2841 success(web_arg(name,to_string(attachement)))
2842 2842 // we must substract 2 to end because of crlf just before the boundary
2843 2843 else
2844 2844 failure,
2845   -
2846   - success(fn) then
2847   - if save_uploaded_file(web_site_dir, body_fd, start_offset+k+4, end_offset-2, s) is
  2845 +
  2846 + success(fn) then
  2847 + if save_uploaded_file(web_site_dir, body_fd, start_offset+k+4, end_offset-2, s) is
2848 2848 {
2849   - failure then failure,
  2849 + failure then failure,
2850 2850 success(tfn) then success(upload(name, remove_path(fn), web_site_dir+"/upload_temporary/"+tfn))
2851   -
  2851 +
2852 2852 }
2853 2853 }
2854 2854 }
... ... @@ -2857,17 +2857,17 @@ define Maybe(Web_arg)
2857 2857 }
2858 2858 }.
2859 2859  
2860   -
2861   -
  2860 +
  2861 +
2862 2862 public define List(Web_arg)
2863 2863 read_multipart_form_data_encoded_web_args
2864 2864 (
2865 2865 String web_site_dir,
2866   - //Web_Site_Description desc,
  2866 + //Web_Site_Description desc,
2867 2867 String body_temp_file,
2868   - String __boundary,
  2868 + String __boundary,
2869 2869 Int file_offset,
2870   - SState s
  2870 + SState s
2871 2871 ) =
2872 2872 with boundary_length = length(__boundary),
2873 2873 println(
... ... @@ -2876,90 +2876,90 @@ public define List(Web_arg)
2876 2876 body_temp_file : "+body_temp_file+"
2877 2877 file_offset : "+file_offset);
2878 2878 //get the first boundary position
2879   - if find_the_first(body_temp_file, __boundary, file_offset) is
  2879 + if find_the_first(body_temp_file, __boundary, file_offset) is
2880 2880 {
2881   - failure then println("first boundary NOT found");[ ],
2882   - success(first) then
2883   - with first = first + file_offset, //adjust the offset to real offset in file
  2881 + failure then println("first boundary NOT found");[ ],
  2882 + success(first) then
  2883 + with first = first + file_offset, //adjust the offset to real offset in file
2884 2884 //get the second boundary position
2885   - if find_the_first(body_temp_file, __boundary, first + boundary_length) is
  2885 + if find_the_first(body_temp_file, __boundary, first + boundary_length) is
2886 2886 {
2887   - failure then println("last boundary NOT found");[ ],
2888   - success(last) then
2889   - with last = last + first + boundary_length, //adjust the offset to real offset in file
  2887 + failure then println("last boundary NOT found");[ ],
  2888 + success(last) then
  2889 + with last = last + first + boundary_length, //adjust the offset to real offset in file
2890 2890 //Extract the file content here
2891   - if get_multipart_entity(web_site_dir, body_temp_file, first+boundary_length, last, s) is
  2891 + if get_multipart_entity(web_site_dir, body_temp_file, first+boundary_length, last, s) is
2892 2892 {
2893 2893 failure then [ ],
2894   - success(wa) then
2895   - [wa . read_multipart_form_data_encoded_web_args(web_site_dir, body_temp_file, __boundary, last, s)]
  2894 + success(wa) then
  2895 + [wa . read_multipart_form_data_encoded_web_args(web_site_dir, body_temp_file, __boundary, last, s)]
2896 2896 }
2897 2897 }
2898 2898 }.
2899   -
2900   -
2901   -
  2899 +
  2900 +
  2901 +
2902 2902 define One
2903 2903 multipart_form_data_answer
2904 2904 (
2905 2905 String host_name,
2906   - Web_Site_Description desc,
2907   - Connection connection,
2908   - Word32 ip_addr,
2909   - HTTP_RequestLine request_line,
2910   - List(HTTP_header) headers,
  2906 + Web_Site_Description desc,
  2907 + Connection connection,
  2908 + Word32 ip_addr,
  2909 + HTTP_RequestLine request_line,
  2910 + List(HTTP_header) headers,
2911 2911 String body_temp_file,
2912 2912 One -> String generate_tt,
2913   - SState s
2914   - ) =
2915   - if get_boundary(headers) is
  2913 + SState s
  2914 + ) =
  2915 + if get_boundary(headers) is
2916 2916 {
2917   - failure then unique,
2918   - success(boundary) then
2919   - with all_web_args = query_string(request_line) +
  2917 + failure then unique,
  2918 + success(boundary) then
  2919 + with all_web_args = query_string(request_line) +
2920 2920 read_multipart_form_data_encoded_web_args(desc.site_directory,
2921 2921 body_temp_file,
2922 2922 "--"+boundary,
2923 2923 0,
2924 2924 s),
2925   - uri = uri(request_line),
2926   - ext = get_uri_extension(uri),
  2925 + uri = uri(request_line),
  2926 + ext = get_uri_extension(uri),
2927 2927 log_journal_msg(desc,
2928   - format_request(desc,connection,request_line,headers,all_web_args));
2929   - if is_illegal_uri(uri,0)
2930   - then log_journal_msg(desc,"Received illegal URI: "+uri+"\n")
  2928 + format_request(desc,connection,request_line,headers,all_web_args));
  2929 + if is_illegal_uri(uri,0)
  2930 + then log_journal_msg(desc,"Received illegal URI: "+uri+"\n")
2931 2931 else
2932   - if (ext = ".awp" | ext = "") then
2933   - (with answer_headers_body = awp_handler(desc)(host_name,
  2932 + if (ext = ".awp" | ext = "") then
  2933 + (with answer_headers_body = awp_handler(desc)(host_name,
2934 2934 http_info(ip_addr, host_name, uri, headers, is_SSL(connection)),
2935 2935 all_web_args,
2936 2936 is_SSL(connection)),
2937 2937 forget(reliable_write(connection, answer_headers_body)))
2938 2938 else unique
2939   - }.
2940   -
2941   -
2942   -
2943   -
2944   -
2945   -
2946   -
2947   -
2948   - *** [5.8] Handling redirections.
2949   -
  2939 + }.
  2940 +
  2941 +
  2942 +
  2943 +
  2944 +
  2945 +
  2946 +
  2947 +
  2948 + *** [5.8] Handling redirections.
  2949 +
2950 2950 'redirections' (of type 'List(Redirection)') contains redirection directives. Each one
2951 2951 has the form:
2952   -
2953   - redirect(required_uri,required_host,corresponding_uri).
2954   -
  2952 +
  2953 + redirect(required_uri,required_host,corresponding_uri).
  2954 +
2955 2955 The host required by the client may be found in the 'Host' HTTP header. The URI
2956 2956 required by the client is given below as 'uri'. We just have to find the required host
2957 2957 in the headers, and to find the corresponding redirection directive.
2958   -
2959   -
  2958 +
  2959 +
2960 2960 In the next fonction, the required host and URI are known. We just have to search in
2961 2961 the 'redirections' list.
2962   -
  2962 +
2963 2963 define String
2964 2964 handle_redirection
2965 2965 (
... ... @@ -2967,50 +2967,50 @@ define String
2967 2967 String required_host,
2968 2968 List(Redirection) redirections
2969 2969 ) =
2970   - if redirections is
  2970 + if redirections is
2971 2971 {
2972 2972 [ ] then required_uri,
2973   - [h . t] then if h is redirect(uri,host,target) then
  2973 + [h . t] then if h is redirect(uri,host,target) then
2974 2974 if host = required_host
2975 2975 then if uri = required_uri
2976 2976 then target
2977 2977 else handle_redirection(required_uri,required_host,t)
2978 2978 else handle_redirection(required_uri,required_host,t)
2979   - }.
2980   -
2981   -
2982   -
  2979 + }.
  2980 +
  2981 +
  2982 +
2983 2983 The host name may be encumbered by a port number, like
2984   -
  2984 +
2985 2985 www.our-business.com:1607
2986   -
2987   - We must remove this port number, otherwise the host name may not be recognized.
2988   -
2989   -define String
  2986 +
  2987 + We must remove this port number, otherwise the host name may not be recognized.
  2988 +
  2989 +define String
2990 2990 strip_port
2991 2991 (
2992 2992 String name,
2993 2993 Int i
2994 2994 ) =
2995   - if nth(i,name) is
  2995 + if nth(i,name) is
2996 2996 {
2997 2997 failure then name,
2998   - success(c) then
  2998 + success(c) then
2999 2999 if c = ':'
3000 3000 then substr(name,0,i)
3001 3001 else strip_port(name,i+1)
3002   - }.
3003   -
3004   -
3005   -
3006   -
3007   -
3008   - Finding the 'Host' header. No redirection is performed if this header is not found.
3009   -
3010   -define String
  3002 + }.
  3003 +
  3004 +
  3005 +
  3006 +
  3007 +
  3008 + Finding the 'Host' header. No redirection is performed if this header is not found.
  3009 +
  3010 +define String
3011 3011 handle_redirection // returns the redirected URI
3012 3012 (
3013   - Redirections redirections,
  3013 + Redirections redirections,
3014 3014 String uri, // original URI
3015 3015 List(HTTP_header) headers
3016 3016 )=
... ... @@ -3022,125 +3022,125 @@ define String
3022 3022 {
3023 3023 redirection_list(l) then handle_redirection(uri, host, l)
3024 3024 redirection_fn(f) then f(uri, host)
3025   - }
  3025 + }
3026 3026 }.
3027   -
3028   -
3029   -
3030   -
3031   -
3032   -
3033   - *** [5.9] Answering both sorts of requests.
3034   -
  3027 +
  3028 +
  3029 +
  3030 +
  3031 +
  3032 +
  3033 + *** [5.9] Answering both sorts of requests.
  3034 +
3035 3035 We must decide if the request is www-url encoded or multipart/form-data encoded. This
3036 3036 is achieved through the header 'Content-Type'.
3037   -
  3037 +
3038 3038 define EncodingType
3039 3039 get_encoding_type
3040 3040 (
3041 3041 List(HTTP_header) headers
3042 3042 ) =
3043   - if headers is
  3043 + if headers is
3044 3044 {
3045   - [ ] then www_url, // this is the default
3046   - [h . t] then if h is http_header(name, value) then
  3045 + [ ] then www_url, // this is the default
  3046 + [h . t] then if h is http_header(name, value) then
3047 3047 if name = "content-type"
3048   - then if find("multipart/form-data",value,0) is
  3048 + then if find("multipart/form-data",value,0) is
3049 3049 {
3050 3050 failure then www_url,
3051 3051 success(_) then multipart_form_data
3052 3052 }
3053   - else
  3053 + else
3054 3054 get_encoding_type(t) //content-type not found, check the next header line
3055 3055 }.
3056   -
3057   -
3058   -
  3056 +
  3057 +
  3058 +
3059 3059 define One
3060 3060 send_answer
3061 3061 (
3062   - String host_name,
3063   - Web_Site_Description desc,
3064   - Connection connection,
3065   - HTTP_RequestLine rqline,
3066   - List(HTTP_header) headers,
  3062 + String host_name,
  3063 + Web_Site_Description desc,
  3064 + Connection connection,
  3065 + HTTP_RequestLine rqline,
  3066 + List(HTTP_header) headers,
3067 3067 ByteArray body,
3068 3068 One -> String generate_tt,
3069   - SState s
3070   - )=
3071   - if rqline is request_line(type, uri, qstring) then
3072   - with rqline2 = request_line(type, handle_redirection(redirections(desc), uri, headers), qstring),
  3069 + SState s
  3070 + )=
  3071 + if rqline is request_line(type, uri, qstring) then
  3072 + with rqline2 = request_line(type, handle_redirection(redirections(desc), uri, headers), qstring),
3073 3073 if remote_IP_address_and_port(connection) is (ip_addr,_) then
3074   - if get_encoding_type(headers) is
  3074 + if get_encoding_type(headers) is
3075 3075 {
3076 3076 www_url then www_url_answer(host_name, desc, connection, ip_addr, rqline2, headers, body, generate_tt),
3077 3077 multipart_form_data then multipart_form_data_answer(host_name, desc, connection, ip_addr, rqline2, headers, body, generate_tt, s)
3078 3078 }.
3079   -
3080   -
3081   -
3082   -
3083   -
3084   -
3085   -
3086   - *** [6] The HTTP/HTTPS server.
  3079 +
  3080 +
  3081 +
  3082 +
  3083 +
  3084 +
  3085 +
  3086 + *** [6] The HTTP/HTTPS server.
3087 3087  
3088 3088 The command 'start_server' (declared in 'predefined.anubis') starts a virtual machine
3089 3089 which opens a server TCP/IP connection, and which continuously listens to this
3090 3090 connection. When a request arrives, this machine delegates the work of deciphering and
3091 3091 answering the request to another virtual machine, and continues to listen. The job of
3092   - the delegated machine is defined by the HTTP request handler below.
3093   -
  3092 + the delegated machine is defined by the HTTP request handler below.
  3093 +
3094 3094  
3095 3095  
3096 3096  
3097   -
3098   - *** [6.1] Determining the requested host.
3099   -
  3097 +
  3098 + *** [6.1] Determining the requested host.
  3099 +
3100 3100 When a request arrives to one of our two servers, we must decide which site (host) is
3101 3101 requested.
3102   -
3103   -define Maybe(String)
  3102 +
  3103 +define Maybe(String)
3104 3104 get_host_header_value
3105 3105 (
3106 3106 List(HTTP_header) headers
3107 3107 ) =
3108   - if headers is
  3108 + if headers is
3109 3109 {
3110   - [ ] then failure,
3111   - [h . t] then if h is http_header(name,value) then
  3110 + [ ] then failure,
  3111 + [h . t] then if h is http_header(name,value) then
3112 3112 if name = "host"
3113 3113 then success(strip_port(value,0))
3114 3114 else get_host_header_value(t)
3115   - }.
3116   -
  3115 + }.
  3116 +
3117 3117 define Maybe((String,Web_Site_Description))
3118 3118 get_site
3119 3119 (
3120   - String requested_host,
  3120 + String requested_host,
3121 3121 List(Web_Site_Description) sites
3122 3122 ) =
3123   - if sites is
  3123 + if sites is
3124 3124 {
3125   - [ ] then print("Requested host '"+requested_host+"' does not exist.\n"); failure,
3126   - [site1 . others] then
3127   - if site1 is web_site_description(common_names,_,_,_,_,_,_,_,_,_) then
  3125 + [ ] then print("Requested host '"+requested_host+"' does not exist.\n"); failure,
  3126 + [site1 . others] then
  3127 + if site1 is web_site_description(common_names,_,_,_,_,_,_,_,_,_) then
3128 3128 if member(common_names,requested_host)
3129 3129 then success((requested_host,site1))
3130 3130 else get_site(requested_host,others)
3131 3131 }.
3132   -
3133   -
  3132 +
  3133 +
3134 3134 define Maybe((String,Web_Site_Description))
3135 3135 get_site
3136 3136 (
3137   - List(HTTP_header) headers,
  3137 + List(HTTP_header) headers,
3138 3138 List(Web_Site_Description) sites
3139 3139 ) =
3140   - if get_host_header_value(headers) is
  3140 + if get_host_header_value(headers) is
3141 3141 {
3142   - failure then print("No 'Host' HTTP header.\n"); failure,
3143   - success(requested_host) then
  3142 + failure then print("No 'Host' HTTP header.\n"); failure,
  3143 + success(requested_host) then
3144 3144 //here we treat the case with only one site. hence we accept any host request
3145 3145 //print("*** there is " +length(sites) + " sites \n");
3146 3146 if length(sites) = 1 then
... ... @@ -3152,90 +3152,90 @@ define Maybe((String,Web_Site_Description))
3152 3152 }
3153 3153 else
3154 3154 get_site(requested_host,sites)
3155   - }.
3156   -
3157   -
3158   -
3159   -
3160   -
3161   - *** [6.2] The HTTP request handler.
3162   -
  3155 + }.
  3156 +
  3157 +
  3158 +
  3159 +
  3160 +
  3161 + *** [6.2] The HTTP request handler.
  3162 +
3163 3163 Here is the HTTP/HTTPS handler. It is called at each new request in a separate virtual
3164 3164 machine. It reads the headers of the HTTP request, determines the host, determines body
3165 3165 size, reads the body of the HTTP request, and answers the request.
3166   -
3167   -
3168   -
3169   -define One -> String make_generate_trust_ticket(DenialOfService dos).
3170   -
  3166 +
  3167 +
  3168 +
  3169 +define One -> String make_generate_trust_ticket(DenialOfService dos).
  3170 +
3171 3171 define One
3172 3172 http_https_handler
3173 3173 (
3174 3174 List(Web_Site_Description) sites,
3175   - BufferedConnection connection,
  3175 + BufferedConnection connection,
3176 3176 Bool is_https,
3177 3177 DenialOfService dos,
3178 3178 SState s
3179 3179 ) =
3180 3180 //t0 <- (UTime)unow;
3181   - with start_time = (Int)now,
3182   - s.sttm <- start_time;
  3181 + with start_time = (Int)now,
  3182 + s.sttm <- start_time;
3183 3183 //println("Request time: " + format_http_date(start_time));
3184   - if dos is denial_of_service(mc_v,rld_v,hd_v,ad_v,ld_v,ra_v) then
3185   - if remote_IP_address_and_port(connection.conn) is (ip_addr,port) then
3186   - if read_request_line(connection, start_time+*rld_v, dos, s) is
  3184 + if dos is denial_of_service(mc_v,rld_v,hd_v,ad_v,ld_v,ra_v) then
  3185 + if remote_IP_address_and_port(connection.conn) is (ip_addr,port) then
  3186 + if read_request_line(connection, start_time+*rld_v, dos, s) is
3187 3187 {
3188 3188 error(msg) then print(format(msg)),
3189 3189 ok(rqline) then //request line
3190 3190 //print_delta("read_request_line");
3191   - if read_http_headers(connection, start_time+*hd_v, dos, s) is
  3191 + if read_http_headers(connection, start_time+*hd_v, dos, s) is
3192 3192 {
3193   - error(msg) then print(format(msg)),
3194   - ok(headers) then //print_delta("read_http_headers");
  3193 + error(msg) then print(format(msg)),
  3194 + ok(headers) then //print_delta("read_http_headers");
3195 3195 if get_site(headers,sites) is
3196 3196 {
3197 3197 failure then unique,
3198   - success(p) then if p is (host_name, desc) then
  3198 + success(p) then if p is (host_name, desc) then
3199 3199 //Body Size
3200   - if get_body_size(headers) is
  3200 + if get_body_size(headers) is
3201 3201 {
3202 3202 error(msg) then log_journal_msg(desc,format(msg)),
3203   - ok(body_size) then
3204   - if rqline is request_line(type, uri, qstring) then
3205   - with rqline2 = request_line(type, handle_redirection(redirections(desc), uri, headers), qstring),
  3203 + ok(body_size) then
  3204 + if rqline is request_line(type, uri, qstring) then
  3205 + with rqline2 = request_line(type, handle_redirection(redirections(desc), uri, headers), qstring),
3206 3206  
3207 3207 //Get the type of encoding which decide if we read the content in ByteArray for www_url or in
3208 3208 //temporary file for multipart.
3209 3209 with generate_tt = make_generate_trust_ticket(dos),
3210   - if get_encoding_type(headers) is
  3210 + if get_encoding_type(headers) is
3211 3211 {
3212 3212 //WWW_URL
3213   - www_url then
3214   - if read_http_body(connection, body_size, constant_byte_array(0,0), 1000) is
  3213 + www_url then
  3214 + if read_http_body(connection, body_size, constant_byte_array(0,0), 1000) is
3215 3215 {
3216   - error(msg) then log_journal_msg(desc,format(msg)),
3217   - ok(body) then
  3216 + error(msg) then log_journal_msg(desc,format(msg)),
  3217 + ok(body) then
3218 3218 www_url_answer(host_name, desc, connection.conn, ip_addr, rqline2, headers, body, generate_tt);
3219 3219 //it's HTTP 1.1 keep-alive is default
3220 3220 http_https_handler(sites, connection, is_https, dos, s)
3221 3221 }
3222   - //MULTIPART_FORM_DATA
3223   - multipart_form_data then
  3222 + //MULTIPART_FORM_DATA
  3223 + multipart_form_data then
3224 3224  
3225   - if body_size > 0 then
  3225 + if body_size > 0 then
3226 3226 with t0 = (UTime)unow,
3227   -
  3227 +
3228 3228 if get_socket_from_connection(conn(connection)) is
3229 3229 {
3230 3230 failure then println("can't get socket"),
3231 3231 success(source) then
3232   -
  3232 +
3233 3233 with tmp_body_file = "/temp/" + virtual_machine_id + "-" + now,
3234 3234 if (Maybe(RWStream))file(desc.site_directory + tmp_body_file, new) is
3235 3235 {
3236 3236 failure then println("can't create target file "+desc.site_directory + tmp_body_file),//nothing to write
3237   - success(target) then
3238   -
  3237 + success(target) then
  3238 +
3239 3239 //get the content of the current buffer and unput char list
3240 3240 with buffer = get_and_erase_buffer(connection, s),
3241 3241 buffer_size = length(buffer),
... ... @@ -3244,24 +3244,24 @@ define One
3244 3244 if flush(buffer, weaken(target)) is
3245 3245 {
3246 3246 failure then println("Can't flush the buffer"),
3247   - success(_) then
3248   - if copy_file(weaken(source), weaken(target), body_size - buffer_size) is copy_ok(read_size) then
3249   -
  3247 + success(_) then
  3248 + if copy_file(weaken(source), weaken(target), body_size - buffer_size) is copy_ok(read_size) then
  3249 +
3250 3250 with duration = (UTime) unow - t0,
3251 3251 println("Read body "+read_size+" duration: " + __utime_to_string(duration));
3252   -
  3252 +
3253 3253 multipart_form_data_answer(host_name, desc, connection.conn, ip_addr, rqline2, headers, desc.site_directory + tmp_body_file, generate_tt, s);
3254   -
  3254 +
3255 3255 //it's HTTP 1.1 keep-alive is default
3256   - http_https_handler(sites, connection, is_https, dos, s)
3257   -
  3256 + http_https_handler(sites, connection, is_https, dos, s)
  3257 +
3258 3258 else
3259 3259 println("Can't copy data from stream to temporary file ")
3260 3260 }}}
3261 3261 else
3262 3262 println("body_size = 0 !")
3263 3263 }
3264   -
  3264 +
3265 3265 //print_delta("before send_answer");
3266 3266 // with body = constant_byte_array(0,0),
3267 3267 // send_answer(host_name, desc,connection.conn, request_line, headers, body,
... ... @@ -3272,29 +3272,29 @@ define One
3272 3272 //println("Request duration: " + __utime_to_string(duration))
3273 3273 //println("BufferRead duration: " + __utime_to_string(*t1));
3274 3274 //println("next_char duration: " + __utime_to_string(*t2))
3275   -
  3275 +
3276 3276 }
3277 3277 }
3278 3278 }
3279 3279 }.
3280   -
3281   -
  3280 +
  3281 +
3282 3282 Below are the two tools for constructing the handlers required by 'start_server' and
3283 3283 'start_ssl_server' (see 'predefined.anubis').
3284   -
3285   -define Bool is_dubious_IP(Word32 ip, DenialOfService dos).
3286   -
  3284 +
  3285 +define Bool is_dubious_IP(Word32 ip, DenialOfService dos).
  3286 +
3287 3287 define Server -> ((RWStream) -> One)
3288 3288 make_http_handler
3289 3289 (
3290   - List(Web_Site_Description) sites,
3291   - DenialOfService dos
  3290 + List(Web_Site_Description) sites,
  3291 + DenialOfService dos
3292 3292 ) =
3293   - (Server server) |-> (RWStream conn) |->
3294   - if remote_IP_address_and_port(conn) is (addr,_) then
  3293 + (Server server) |-> (RWStream conn) |->
  3294 + if remote_IP_address_and_port(conn) is (addr,_) then
3295 3295 if is_dubious_IP(addr,dos)
3296 3296 then print("Rejecting dubious IP address "+ip_addr_to_string(addr)+"\n")
3297   - else
  3297 + else
3298 3298 with connection = buffered_connection(tcp(conn), var(constant_byte_array(0, 0)), var(0)),
3299 3299 http_https_handler(sites, connection, false, dos, sstate(var([]),var(0),var(0))).
3300 3300 public define One
... ... @@ -3304,51 +3304,51 @@ public define One
3304 3304 RWStream conn,
3305 3305 DenialOfService dos
3306 3306 ) =
3307   - if remote_IP_address_and_port(conn) is (addr,_) then
  3307 + if remote_IP_address_and_port(conn) is (addr,_) then
3308 3308 if is_dubious_IP(addr,dos)
3309 3309 then print("Rejecting dubious IP address "+ip_addr_to_string(addr)+"\n")
3310   - else
  3310 + else
3311 3311 with connection = buffered_connection(tcp(conn), var(constant_byte_array(0, 0)), var(0)),
3312 3312 http_https_handler(sites, connection, false, dos, sstate(var([]),var(0),var(0))).
3313 3313  
3314   -
  3314 +
3315 3315 define Server -> (SSL_Connection -> One)
3316 3316 make_https_handler
3317 3317 (
3318   - List(Web_Site_Description) sites,
  3318 + List(Web_Site_Description) sites,
3319 3319 DenialOfService dos
3320 3320 ) =
3321   - (Server server) |-> (SSL_Connection conn) |->
  3321 + (Server server) |-> (SSL_Connection conn) |->
3322 3322 with connection = buffered_connection(ssl(conn), var(constant_byte_array(0, 0)), var(0)),
3323   - http_https_handler(sites, connection, true, dos, sstate(var([]),var(0),var(0))).
3324   -
3325   -
3326   -
3327   -
3328   - *** [6.3] Server's tasks.
3329   -
  3323 + http_https_handler(sites, connection, true, dos, sstate(var([]),var(0),var(0))).
  3324 +
  3325 +
  3326 +
  3327 +
  3328 + *** [6.3] Server's tasks.
  3329 +
3330 3330 Some tasks must be executed periodically, for example for cleaning up directories from
3331 3331 short life time files.
3332 3332  
3333 3333 The next function removes from the given directory (and recursively from its
3334 3334 subdirectories) all the files which are more than 10 minutes old.
3335   -
3336   -define One
  3335 +
  3336 +define One
3337 3337 cleanup_directory_10mn
3338 3338 (
3339 3339 String dir // path of private download directory (or subdirectory) with trailing slash
3340 3340 ) =
3341   - forget(map((FileDescription fd) |-> if fd is
  3341 + forget(map((FileDescription fd) |-> if fd is
3342 3342 {
3343   - no_info(name) then forget(remove(dir+name)),
  3343 + no_info(name) then forget(remove(dir+name)),
3344 3344 file(name,_,_,d) then if to_Int(d)+600 < now then forget(remove(dir+name)) else unique,
3345 3345 link(name,_,_,d) then if to_Int(d)+600 < now then forget(remove(dir+name)) else unique,
3346   - directory(name,_,_) then cleanup_directory_10mn(dir+name+"/"),
  3346 + directory(name,_,_) then cleanup_directory_10mn(dir+name+"/"),
3347 3347 },
3348   - directory_full_list(dir,"*","*","*"))).
3349   -
3350   -
3351   -define One
  3348 + directory_full_list(dir,"*","*","*"))).
  3349 +
  3350 +
  3351 +define One
3352 3352 http_servers_tasks
3353 3353 (
3354 3354 List(Web_Site_Description) sites,
... ... @@ -3359,22 +3359,22 @@ define One
3359 3359 if mapand(is_down,servers)
3360 3360 then unique
3361 3361 else if now > next_time
3362   - then
  3362 + then
3363 3363 (
3364 3364 /*
3365   - forget(map((Web_Site_Description wsd) |->
  3365 + forget(map((Web_Site_Description wsd) |->
3366 3366 cleanup_directory_10mn(site_directory(wsd)+"/private_download/"),
3367   - sites));
3368   - */
  3367 + sites));
  3368 + */
3369 3369 http_servers_tasks(sites,servers,period,next_time+period)
3370 3370 )
3371 3371 else
3372 3372 (
3373   - sleep(1000);
  3373 + sleep(1000);
3374 3374 http_servers_tasks(sites,servers,period,next_time)
3375 3375 ).
3376   -
3377   -
  3376 +
  3377 +
3378 3378 public define One
3379 3379 start_http_servers_tasks
3380 3380 (
... ... @@ -3382,91 +3382,91 @@ public define One
3382 3382 List(Server) servers,
3383 3383 Int period
3384 3384 ) =
3385   - delegate http_servers_tasks(sites,servers,period,now),
3386   - unique.
3387   -
3388   -
3389   -
3390   -
3391   - *** [6.4] Protection against 'denial of service' attacks.
3392   -
3393   -
3394   - *** [6.4.1] Counting connections.
3395   -
  3385 + delegate http_servers_tasks(sites,servers,period,now),
  3386 + unique.
  3387 +
  3388 +
  3389 +
  3390 +
  3391 + *** [6.4] Protection against 'denial of service' attacks.
  3392 +
  3393 +
  3394 + *** [6.4.1] Counting connections.
  3395 +
3396 3396 define Bool // returns false if the counter cannot be incremented (too many connections)
3397 3397 increment_connections_counter
3398 3398 (
3399 3399 Var(Int) counter
3400 3400 ) =
3401   - protect with n = *counter,
  3401 + protect with n = *counter,
3402 3402 if n >= 100
3403 3403 then false
3404   - else (counter <- (*counter)+1); true.
3405   -
  3404 + else (counter <- (*counter)+1); true.
  3405 +
3406 3406 define One
3407 3407 decrement_connections_counter
3408 3408 (
3409 3409 Var(Int) counter
3410 3410 ) =
3411   - protect counter <- (*counter)-1.
3412   -
3413   -
3414   -
3415   -
3416   -
  3411 + protect counter <- (*counter)-1.
  3412 +
  3413 +
  3414 +
  3415 +
  3416 +
3417 3417 *** [6.4.2] Recording dubious IP addresses.
3418   -
3419   -
  3418 +
  3419 +
3420 3420 define List(DubiousIP)
3421 3421 record_dubious_IP
3422 3422 (
3423 3423 Word32 ip,
3424 3424 List(DubiousIP) l
3425 3425 ) =
3426   - if l is
  3426 + if l is
3427 3427 {
3428 3428 [ ] then [dubious_ip(ip,now)],
3429   - [h . t] then if h is dubious_ip(addr,time) then
  3429 + [h . t] then if h is dubious_ip(addr,time) then
3430 3430 if addr = ip
3431 3431 then [dubious_ip(addr,now) . t]
3432 3432 else [h . record_dubious_IP(ip,t)]
3433 3433 }.
3434   -
3435   -
  3434 +
  3435 +
3436 3436 define One
3437 3437 record_dubious_IP
3438 3438 (
3439 3439 Word32 dubious_IP,
3440 3440 Var(List(DubiousIP)) v
3441 3441 ) =
3442   - protect v <- record_dubious_IP(dubious_IP,*v).
3443   -
3444   -
  3442 + protect v <- record_dubious_IP(dubious_IP,*v).
  3443 +
  3444 +
3445 3445 define One
3446 3446 record_dubious_IP
3447 3447 (
3448   - Word32 addr,
  3448 + Word32 addr,
3449 3449 DenialOfService dos
3450 3450 ) =
3451   - record_dubious_IP(addr,list_of_dubious(dos)).
3452   -
3453   -
3454   -public define DenialOfService
  3451 + record_dubious_IP(addr,list_of_dubious(dos)).
  3452 +
  3453 +
  3454 +public define DenialOfService
3455 3455 load_denial_of_service_info
3456 3456 =
3457   - if (RetrieveResult(DenialOfService))retrieve(my_anubis_directory+"/web_sites/dos_info") is
  3457 + if (RetrieveResult(DenialOfService))retrieve(my_anubis_directory+"/web_sites/dos_info") is
3458 3458 ok(dos) then dos else denial_of_service(
3459 3459 var(100),
3460 3460 var(1000),
3461   - var(1500),
3462   - var(2000),
  3461 + var(1500),
  3462 + var(2000),
3463 3463 var([]),
3464   - var([])).
3465   -
3466   -
3467   -
3468   -
3469   - *** [6.4.3] Testing if an address is dubious.
  3464 + var([])).
  3465 +
  3466 +
  3467 +
  3468 +
  3469 + *** [6.4.3] Testing if an address is dubious.
3470 3470  
3471 3471 define Bool
3472 3472 is_dubious_IP
... ... @@ -3474,41 +3474,41 @@ define Bool
3474 3474 Word32 ip,
3475 3475 List(DubiousIP) l
3476 3476 ) =
3477   - if l is
  3477 + if l is
3478 3478 {
3479   - [ ] then false,
3480   - [h . t] then if h is dubious_ip(addr,time) then
  3479 + [ ] then false,
  3480 + [h . t] then if h is dubious_ip(addr,time) then
3481 3481 if ip = addr
3482 3482 then true
3483 3483 else is_dubious_IP(ip,t)
3484 3484 }.
3485   -
3486   -
  3485 +
  3486 +
3487 3487 define Bool
3488 3488 is_dubious_IP
3489 3489 (
3490 3490 Word32 ip,
3491 3491 DenialOfService dos
3492 3492 ) =
3493   - if dos is
  3493 + if dos is
3494 3494 {
3495   - denial_of_service(mc_v,rld_v,hd_v,ad_v,ld_v,ra_v) then
  3495 + denial_of_service(mc_v,rld_v,hd_v,ad_v,ld_v,ra_v) then
3496 3496 if member(*ra_v,ip) then false else
3497 3497 is_dubious_IP(ip,*ld_v)
3498   - }.
3499   -
3500   -
3501   -
3502   -
3503   - *** [6.4.4] Removing inactive dubious IP addresses.
3504   -
  3498 + }.
  3499 +
  3500 +
  3501 +
  3502 +
  3503 + *** [6.4.4] Removing inactive dubious IP addresses.
  3504 +
3505 3505 define List(DubiousIP)
3506 3506 remove_inactive_dubious_IP
3507 3507 (
3508 3508 List(DubiousIP) l,
3509 3509 Int ref_time,
3510 3510 ) =
3511   - if l is
  3511 + if l is
3512 3512 {
3513 3513 [ ] then [ ],
3514 3514 [h . t] then if h is dubious_ip(addr,time) then
... ... @@ -3516,8 +3516,8 @@ define List(DubiousIP)
3516 3516 then (print(ip_addr_to_string(addr)+" removed from dubious addresses list.\n");
3517 3517 remove_inactive_dubious_IP(t,ref_time))
3518 3518 else [h . remove_inactive_dubious_IP(t,ref_time)]
3519   - }.
3520   -
  3519 + }.
  3520 +
3521 3521 define One
3522 3522 remove_inactive_dubious_IP
3523 3523 (
... ... @@ -3525,62 +3525,62 @@ define One
3525 3525 ) =
3526 3526 protect
3527 3527 with ref_time = (Int)now - 600, // 10 minutes
3528   - v <- remove_inactive_dubious_IP(*v,ref_time).
3529   -
3530   -
3531   - The above function will be executed periodically by the servers's tasks machine.
3532   -
3533   -
3534   -
3535   - *** [6.4.5] Making the function for generating trust tickets.
3536   -
3537   -define One -> String
  3528 + v <- remove_inactive_dubious_IP(*v,ref_time).
  3529 +
  3530 +
  3531 + The above function will be executed periodically by the servers's tasks machine.
  3532 +
  3533 +
  3534 +
  3535 + *** [6.4.5] Making the function for generating trust tickets.
  3536 +
  3537 +define One -> String
3538 3538 make_generate_trust_ticket
3539 3539 (
3540 3540 DenialOfService dos
3541 3541 ) =
3542   - (One _) |-> "".
3543   -
3544   -
3545   -
3546   -
3547   -
3548   -
3549   -
3550   - *** [6.5] Starting the HTTP/HTTPS server.
3551   -
3552   -
  3542 + (One _) |-> "".
  3543 +
  3544 +
  3545 +
  3546 +
  3547 +
  3548 +
  3549 +
  3550 + *** [6.5] Starting the HTTP/HTTPS server.
  3551 +
  3552 +
3553 3553 The next function creates the directories for all sites (if they don't already exist).
3554   -
  3554 +
3555 3555 define One
3556 3556 create_directories
3557 3557 (
3558 3558 List(Web_Site_Description) sites
3559 3559 ) =
3560   - if sites is
  3560 + if sites is
3561 3561 {
3562   - [ ] then unique,
3563   - [s1 . others] then
3564   - with site_dir = site_directory(s1),
  3562 + [ ] then unique,
  3563 + [s1 . others] then
  3564 + with site_dir = site_directory(s1),
3565 3565 forget(make_directory(site_dir+"/public",default_directory_mode));
3566 3566 forget(make_directory(site_dir+"/upload_temporary",default_directory_mode));
3567 3567 forget(make_directory(site_dir+"/private_download",default_directory_mode));
3568 3568 forget(make_directory(site_dir+"/journal",default_directory_mode));
3569 3569 create_directories(others)
3570   - }.
3571   -
3572   -
3573   -
3574   -
3575   -
3576   - Below are the commands for starting an HTTP server and an HTTPS server.
3577   -
3578   -
  3570 + }.
  3571 +
  3572 +
  3573 +
  3574 +
  3575 +
  3576 + Below are the commands for starting an HTTP server and an HTTPS server.
  3577 +
  3578 +
3579 3579 define StartServerResult
3580 3580 start_http_server
3581 3581 (
3582 3582 Word32 ip_address,
3583   - Word32 port,
  3583 + Word32 port,
3584 3584 Server -> ((RWStream) -> One) handler,
3585 3585 Int retries,
3586 3586 DenialOfService dos
... ... @@ -3588,160 +3588,160 @@ define StartServerResult
3588 3588 if start_server(ip_address,
3589 3589 port,
3590 3590 handler,
3591   - identity) is ok(server)
3592   - then print(" \r");
  3591 + identity) is ok(server)
  3592 + then print(" \r");
3593 3593 ok(server)
3594   - else print("Port "+port+": retry number "+retries+"\r");
3595   - sleep(1000);
3596   - start_http_server(ip_address,port,handler,retries+1,dos).
3597   -
  3594 + else print("Port "+port+": retry number "+retries+"\r");
  3595 + sleep(1000);
  3596 + start_http_server(ip_address,port,handler,retries+1,dos).
  3597 +
3598 3598 public define StartServerResult
3599 3599 start_http_server
3600 3600 (
3601 3601 Word32 ip_address,
3602   - Word32 port,
  3602 + Word32 port,
3603 3603 List(Web_Site_Description) sites,
3604   - DenialOfService dos
  3604 + DenialOfService dos
3605 3605 ) =
3606 3606 create_directories(sites);
3607 3607 start_http_server(ip_address,port,
3608 3608 make_http_handler(sites, dos),
3609 3609 0,
3610   - dos).
3611   -
3612   -
  3610 + dos).
  3611 +
  3612 +
3613 3613 For the HTTPS server, we have a problem which is due to the fact that 'anbexec' is not
3614 3614 yet able to manipulate several SSL server certificates. 'anbexec' and
3615 3615 'predefined.anubis' must be changed. Sorry ! This will be done as soon as possible. The
3616 3616 'solution' for the time being is to provide the common name of the unique SSL server
3617 3617 certificate.
3618   -
3619   -
  3618 +
  3619 +
3620 3620 define StartServerResult
3621 3621 start_https_server
3622 3622 (
3623 3623 Word32 ip_address,
3624   - Word32 port,
  3624 + Word32 port,
3625 3625 String certificate_common_name,
3626 3626 Server -> (SSL_Connection -> One) handler,
3627 3627 Int retries,
3628 3628 DenialOfService dos
3629   - ) =
  3629 + ) =
3630 3630 if start_ssl_server(ip_address,
3631 3631 port,
3632   - certificate_common_name,
  3632 + certificate_common_name,
3633 3633 handler,
3634   - identity) is ok(server)
3635   - then print(" \r");
  3634 + identity) is ok(server)
  3635 + then print(" \r");
3636 3636 ok(server)
3637   - else print("Port "+port+": retry number "+retries+"\r");
3638   - sleep(1000);
  3637 + else print("Port "+port+": retry number "+retries+"\r");
  3638 + sleep(1000);
3639 3639 start_https_server(ip_address,port,
3640 3640 certificate_common_name,
3641 3641 handler,retries+1,
3642   - dos).
  3642 + dos).
  3643 +
3643 3644  
3644   -
3645 3645 public define StartServerResult
3646 3646 start_https_server
3647 3647 (
3648 3648 Word32 ip_address,
3649   - Word32 port,
  3649 + Word32 port,
3650 3650 String certificate_common_name, // of SSL server certificate
3651 3651 List(Web_Site_Description) sites,
3652   - DenialOfService dos
  3652 + DenialOfService dos
3653 3653 ) =
3654 3654 create_directories(sites);
3655 3655 start_https_server(ip_address,port,certificate_common_name,
3656 3656 make_https_handler(sites, dos),
3657   - 0,dos).
3658   -
3659   -
3660   -
3661   -
3662   -
3663   -
3664   -
3665   -
3666   -
3667   - *** [7] The web dispatcher.
3668   -
3669   -
3670   - *** [7.1] The dispatcher server.
3671   -
  3657 + 0,dos).
  3658 +
  3659 +
  3660 +
  3661 +
  3662 +
  3663 +
  3664 +
  3665 +
  3666 +
  3667 + *** [7] The web dispatcher.
  3668 +
  3669 +
  3670 + *** [7.1] The dispatcher server.
  3671 +
3672 3672 define One
3673 3673 send_dispatching_page
3674 3674 (
3675   - RWStream conn,
3676   - String common_name,
  3675 + RWStream conn,
  3676 + String common_name,
3677 3677 Word32 port
3678 3678 ) =
3679   - print("Dispatching '"+common_name+"' to port "+port+"\n");
  3679 + print("Dispatching '"+common_name+"' to port "+port+"\n");
3680 3680 forget(reliable_write(conn,to_byte_array(
3681 3681 "<html><head><meta http-equiv=\"Refresh\" content=\"0;URL="+
3682 3682 "http://"+common_name+":"+port+"/"+
3683 3683 "\"></head><body></body></html>"
3684 3684 ))).
3685   -
3686   -
3687   -
3688   -define Maybe(DispatcherInfo)
  3685 +
  3686 +
  3687 +
  3688 +define Maybe(DispatcherInfo)
3689 3689 find_host
3690 3690 (
3691 3691 List(DispatcherInfo) l,
3692 3692 String host
3693 3693 ) =
3694   - if l is
  3694 + if l is
3695 3695 {
3696   - [ ] then failure,
3697   - [h . t] then if h is site(name,port) then
  3696 + [ ] then failure,
  3697 + [h . t] then if h is site(name,port) then
3698 3698 if name = host
3699 3699 then success(h)
3700 3700 else find_host(t,host)
3701 3701 }.
3702   -
3703   -
3704   -
  3702 +
  3703 +
  3704 +
3705 3705 define Server -> ((RWStream) -> One)
3706 3706 make_dispatcher_handler
3707 3707 (
3708 3708 Var(List(DispatcherInfo)) info_v,
3709 3709 DenialOfService dos,
3710   - SState ss
  3710 + SState ss
3711 3711 ) =
3712   - (Server server) |-> (RWStream conn) |->
  3712 + (Server server) |-> (RWStream conn) |->
3713 3713 with start_time = (Int)now,
3714 3714 connection = buffered_connection(tcp(conn), var(constant_byte_array(0, 0)), var(0)),
3715   - if read_request_line(connection, start_time+*request_line_delay(dos), dos, ss) is
  3715 + if read_request_line(connection, start_time+*request_line_delay(dos), dos, ss) is
3716 3716 {
3717   - error(msg) then print(format(msg)),
3718   - ok(request_line) then
3719   - if read_http_headers(connection, start_time+*headers_delay(dos), dos, ss) is
  3717 + error(msg) then print(format(msg)),
  3718 + ok(request_line) then
  3719 + if read_http_headers(connection, start_time+*headers_delay(dos), dos, ss) is
3720 3720 {
3721 3721 error(msg) then print(format(msg)),
3722   - ok(headers) then if get_host_header_value(headers) is
  3722 + ok(headers) then if get_host_header_value(headers) is
3723 3723 {
3724 3724 failure then print("No 'HOST' HTTP header.\n"),
3725   - success(host) then
3726   - if find_host(*info_v,host) is
  3725 + success(host) then
  3726 + if find_host(*info_v,host) is
3727 3727 {
3728 3728 failure then print("Host: '"+host+"' not registered.\n"),
3729   - success(s) then if s is site(common_name,ip_port) then
  3729 + success(s) then if s is site(common_name,ip_port) then
3730 3730 send_dispatching_page(conn,common_name,ip_port)
3731 3731 }
3732 3732 }
3733 3733 }
3734 3734 }.
3735   -
3736   -
  3735 +
  3736 +
3737 3737 define One
3738 3738 dispatcher_update_error
3739 3739 (
3740 3740 String file_path
3741 3741 ) =
3742   - print("web_dispatcher: unable to reread file: '"+file_path+"'.\n").
3743   -
3744   -
  3742 + print("web_dispatcher: unable to reread file: '"+file_path+"'.\n").
  3743 +
  3744 +
3745 3745 define Bool
3746 3746 dispatcher_update_data
3747 3747 (
... ... @@ -3751,104 +3751,104 @@ define Bool
3751 3751 ) =
3752 3752 if directory_full_list(my_anubis_directory+"/web_sites","dispatcher.info","","") is
3753 3753 {
3754   - [ ] then false,
3755   - [h . t] then if h is
  3754 + [ ] then false,
  3755 + [h . t] then if h is
3756 3756 {
3757   - no_info(n) then false,
  3757 + no_info(n) then false,
3758 3758 file(n,_,_,d) then if n = "dispatcher.info"
3759   - then (info_date_v <- to_Int(d);
  3759 + then (info_date_v <- to_Int(d);
3760 3760 if (RetrieveResult(List(DispatcherInfo)))retrieve(info_file_path) is
3761 3761 {
3762   - cannot_find_file then false,
3763   - read_error then false,
3764   - type_error then false,
  3762 + cannot_find_file then false,
  3763 + read_error then false,
  3764 + type_error then false,
3765 3765 ok(info) then info_v <- info; true
3766 3766 })
3767   - else false,
3768   - link(_,_,_,_) then false,
  3767 + else false,
  3768 + link(_,_,_,_) then false,
3769 3769 directory(_,_,_) then false
3770   - }
  3770 + }
3771 3771 }.
3772   -
3773   -
3774   -
  3772 +
  3773 +
  3774 +
3775 3775 The loop within which the dispatcher updates its data every 3 seconds:
3776   -
  3776 +
3777 3777 define One
3778 3778 dispatcher_update_task
3779 3779 (
3780 3780 String info_file_path,
3781 3781 Var(List(DispatcherInfo)) info_v,
3782   - Var(Int) info_date_v
  3782 + Var(Int) info_date_v
3783 3783 ) =
3784   - sleep(3000);
  3784 + sleep(3000);
3785 3785 (if dispatcher_update_data(info_file_path,info_v,info_date_v)
3786 3786 then unique
3787 3787 else dispatcher_update_error(info_file_path));
3788   - dispatcher_update_task(info_file_path,info_v,info_date_v).
3789   -
3790   -
  3788 + dispatcher_update_task(info_file_path,info_v,info_date_v).
  3789 +
  3790 +
3791 3791 public define One
3792 3792 start_web_dispatcher
3793 3793 (
3794 3794 Word32 ip_address, // address for listening (typically 0: listen on all interfaces)
3795 3795 Word32 http_port, // typically 80
3796 3796 DenialOfService dos,
3797   - SState s
  3797 + SState s
3798 3798 ) =
3799   - with info_file_path = my_anubis_directory+"/web_sites/dispatcher.info",
3800   - info_v = var((List(DispatcherInfo))[]),
  3799 + with info_file_path = my_anubis_directory+"/web_sites/dispatcher.info",
  3800 + info_v = var((List(DispatcherInfo))[]),
3801 3801 info_date_v = var((Int)0),
3802 3802 if dispatcher_update_data(info_file_path,info_v,info_date_v)
3803 3803 then if start_server(ip_address,
3804 3804 http_port,
3805 3805 make_dispatcher_handler(info_v, dos, s),
3806   - (One u)|->u) is
  3806 + (One u)|->u) is
3807 3807 {
3808   - cannot_create_the_socket then
  3808 + cannot_create_the_socket then
3809 3809 print("Cannot create the socket for HTTP server.\n"),
3810   - cannot_bind_to_port then
  3810 + cannot_bind_to_port then
3811 3811 print("Cannot bind HTTP server to port "+http_port+".\n"),
3812   - cannot_listen_on_port then
  3812 + cannot_listen_on_port then
3813 3813 print("HTTP server cannot listen on port "+http_port+".\n"),
3814   - ok(http_server) then
  3814 + ok(http_server) then
3815 3815 dispatcher_update_task(info_file_path,info_v,info_date_v)
3816 3816 }
3817 3817 else dispatcher_update_error(info_file_path).
3818   -
3819   -
3820   -
3821   - *** [7.2] The dispatcher web site.
3822   -
3823   - global define One
  3818 +
  3819 +
  3820 +
  3821 + *** [7.2] The dispatcher web site.
  3822 +
  3823 + global define One
3824 3824 web_dispatcher
3825 3825 (
3826 3826 List(String) args
3827 3827 ) =
3828   - start_web_dispatcher(0,80,load_denial_of_service_info).
3829   -
3830   -
3831   -
3832   -
3833   -
3834   -
3835   - *** [7.3] Managing the info file.
3836   -
  3828 + start_web_dispatcher(0,80,load_denial_of_service_info).
  3829 +
  3830 +
  3831 +
  3832 +
  3833 +
  3834 +
  3835 + *** [7.3] Managing the info file.
  3836 +
3837 3837 define Word32
3838 3838 register_ip_address
3839 3839 =
3840   - if ip_address(prompt(" numerical IP address (for HTTP): ")) is
  3840 + if ip_address(prompt(" numerical IP address (for HTTP): ")) is
3841 3841 {
3842 3842 failure then print(" *** Error: incorrect IP address.\n");
3843 3843 register_ip_address,
3844 3844 success(n) then n
3845 3845 }.
3846   -
3847   -
  3846 +
  3847 +
3848 3848 define Word32
3849 3849 register_ip_port
3850 3850 =
3851   - if decimal_scan(prompt(" IP port (for HTTP): ")) is
  3851 + if decimal_scan(prompt(" IP port (for HTTP): ")) is
3852 3852 {
3853 3853 failure then print(" *** Error: incorrect IP port.\n");
3854 3854 register_ip_port,
... ... @@ -3857,109 +3857,109 @@ define Word32
3857 3857 else print(" *** Error: IP port out of bounds.\n");
3858 3858 register_ip_port
3859 3859 }.
3860   -
3861   -
  3860 +
  3861 +
3862 3862 define One
3863 3863 register_new_site
3864 3864 (
3865 3865 Var(List(DispatcherInfo)) info_v
3866 3866 ) =
3867   - print("\n");
  3867 + print("\n");
3868 3868 print(" Registering a new site:\n");
3869   - with name = prompt(" Site name: "),
  3869 + with name = prompt(" Site name: "),
3870 3870 with addr = register_ip_address,
3871 3871 with port = register_ip_port,
3872 3872 (protect info_v <- [site(name,port) . *info_v]);
3873 3873 print(" Site "+name+" at "+ip_addr_to_string(addr)+":"+port+" added\n (but not saved to disk).\n").
3874   -
3875   -
3876   -define List(DispatcherInfo)
  3874 +
  3875 +
  3876 +define List(DispatcherInfo)
3877 3877 find_sites
3878 3878 (
3879 3879 List(DispatcherInfo) l,
3880 3880 String name
3881 3881 ) =
3882   - if l is
  3882 + if l is
3883 3883 {
3884   - [ ] then [ ],
3885   - [h . t] then if h is site(n,_) then
3886   - if find(name,n,0) is
  3884 + [ ] then [ ],
  3885 + [h . t] then if h is site(n,_) then
  3886 + if find(name,n,0) is
3887 3887 {
3888 3888 failure then find_sites(t,name),
3889 3889 success(_) then [h . find_sites(t,name)]
3890 3890 }
3891   - }.
3892   -
  3891 + }.
  3892 +
3893 3893  
3894 3894 define String
3895 3895 pad
3896 3896 (
3897   - String s,
  3897 + String s,
3898 3898 Int l
3899 3899 ) =
3900 3900 if length(s) >= l
3901 3901 then s
3902   - else s+constant_string(l-length(s),' ').
3903   -
3904   -
3905   -
  3902 + else s+constant_string(l-length(s),' ').
  3903 +
  3904 +
  3905 +
3906 3906 define One
3907 3907 show_sites_1
3908 3908 (
3909   - List(DispatcherInfo) l,
  3909 + List(DispatcherInfo) l,
3910 3910 Int i
3911 3911 ) =
3912   - if l is
  3912 + if l is
3913 3913 {
3914   - [ ] then unique,
3915   - [h . t] then if h is site(name,port) then
  3914 + [ ] then unique,
  3915 + [h . t] then if h is site(name,port) then
3916 3916 print(" ["+i+"] "+pad(name,40)+" "+" "+port+"\n");
3917 3917 show_sites_1(t,i+1)
3918 3918 }.
3919   -
3920   -
  3919 +
  3920 +
3921 3921 define One
3922 3922 show_sites
3923 3923 (
3924   - List(DispatcherInfo) l,
  3924 + List(DispatcherInfo) l,
3925 3925 Int i
3926 3926 ) =
3927 3927 print(" Name Port\n");
3928 3928 print(" --------------------------------------------------------\n");
3929   - show_sites_1(l,i).
3930   -
  3929 + show_sites_1(l,i).
  3930 +
3931 3931 define List(DispatcherInfo)
3932 3932 replace_info
3933 3933 (
3934   - List(DispatcherInfo) l,
  3934 + List(DispatcherInfo) l,
3935 3935 String site_name,
3936 3936 Word32 new_port
3937   - ) =
3938   - if l is
  3937 + ) =
  3938 + if l is
3939 3939 {
3940   - [ ] then print("ALERT: Empty list into replace_info() [" + __FILE__ + "]\n"); [],
3941   - [h . t] then if h is site(n,_) then
  3940 + [ ] then print("ALERT: Empty list into replace_info() [" + __FILE__ + "]\n"); [],
  3941 + [h . t] then if h is site(n,_) then
3942 3942 if n = site_name
3943   - then [site(n,new_port) . t]
  3943 + then [site(n,new_port) . t]
3944 3944 else [h . replace_info(t,site_name,new_port)]
3945 3945 }.
3946   -
  3946 +
3947 3947 define List(DispatcherInfo)
3948 3948 delete_info
3949 3949 (
3950   - List(DispatcherInfo) l,
  3950 + List(DispatcherInfo) l,
3951 3951 String site_name,
3952   - ) =
3953   - if l is
  3952 + ) =
  3953 + if l is
3954 3954 {
3955   - [ ] then print("ALERT: Empty list into delete_info() [" + __FILE__ + "]\n"); [],
3956   - [h . t] then if h is site(n,_) then
  3955 + [ ] then print("ALERT: Empty list into delete_info() [" + __FILE__ + "]\n"); [],
  3956 + [h . t] then if h is site(n,_) then
3957 3957 if n = site_name
3958   - then t
  3958 + then t
3959 3959 else [h . delete_info(t,site_name)]
3960 3960 }.
3961   -
3962   -
  3961 +
  3962 +
3963 3963 define One
3964 3964 update_site
3965 3965 (
... ... @@ -3969,54 +3969,54 @@ define One
3969 3969 ) =
3970 3970 print("\n");
3971 3971 print(" Updating site '"+site_name+"': (currently: "+old_port+")\n");
3972   - with new_port = register_ip_port,
  3972 + with new_port = register_ip_port,
3973 3973 answer = prompt(" Update '"+site_name+"' as: "+new_port+" [Y/N] ? "),
3974 3974 if (answer = "Y" | answer = "y")
3975 3975 then info_v <- replace_info(*info_v,site_name,new_port)
3976   - else unique.
3977   -
3978   -
3979   -
  3976 + else unique.
  3977 +
  3978 +
  3979 +
3980 3980 define Bool
3981 3981 compare
3982 3982 (
3983   - DispatcherInfo d1,
  3983 + DispatcherInfo d1,
3984 3984 DispatcherInfo d2
3985 3985 ) =
3986   - if d1 is site(n1,_) then
  3986 + if d1 is site(n1,_) then
3987 3987 if d2 is site(n2,_) then
3988   - string_less(n1,n2).
3989   -
3990   -
3991   -
  3988 + string_less(n1,n2).
  3989 +
  3990 +
  3991 +
3992 3992 define One
3993 3993 update_site
3994 3994 (
3995 3995 Var(List(DispatcherInfo)) info_v
3996 3996 ) =
3997   - print("\n");
3998   - with prefix = prompt(" Search for site to update: "),
3999   - if find_sites(*info_v,prefix) is
  3997 + print("\n");
  3998 + with prefix = prompt(" Search for site to update: "),
  3999 + if find_sites(*info_v,prefix) is
4000 4000 {
4001 4001 [ ] then print(" No site found.\n");
4002 4002 update_site(info_v),
4003   - [h . t] then
  4003 + [h . t] then
4004 4004 show_sites(qsort([h . t],compare),1);
4005 4005 with i1 = prompt(" Choose a site to update [1/.../"+(length(t)+1)+"]: "),
4006   - if decimal_scan(i1) is
  4006 + if decimal_scan(i1) is
4007 4007 {
4008 4008 failure then print(" *** Error: site number not recognized.\n");
4009 4009 update_site(info_v),
4010   - success(ii1) then if nth(ii1-1,*info_v) is
  4010 + success(ii1) then if nth(ii1-1,*info_v) is
4011 4011 {
4012 4012 failure then print(" *** Error: site number "+i1+" does not exist.\n");
4013 4013 update_site(info_v),
4014   - success(site_info) then if site_info is site(name,old_port) then
  4014 + success(site_info) then if site_info is site(name,old_port) then
4015 4015 update_site(info_v,name,old_port)
4016 4016 }
4017 4017 }
4018 4018 }.
4019   -
  4019 +
4020 4020  
4021 4021 define One
4022 4022 delete_site
... ... @@ -4030,38 +4030,38 @@ define One
4030 4030 with answer = prompt(" Are you sure you want to delete site: '"+site_name+"' [Y/N] ? "),
4031 4031 if (answer = "Y" | answer = "y")
4032 4032 then info_v <- delete_info(*info_v,site_name)
4033   - else print(" Site '"+site_name+"' not deleted.\n").
4034   -
4035   -
  4033 + else print(" Site '"+site_name+"' not deleted.\n").
  4034 +
  4035 +
4036 4036 define One
4037 4037 delete_site
4038 4038 (
4039 4039 Var(List(DispatcherInfo)) info_v
4040 4040 ) =
4041   - print("\n");
4042   - with prefix = prompt(" Search for site to delete: "),
4043   - if find_sites(*info_v,prefix) is
  4041 + print("\n");
  4042 + with prefix = prompt(" Search for site to delete: "),
  4043 + if find_sites(*info_v,prefix) is
4044 4044 {
4045 4045 [ ] then print(" No site found.\n");
4046 4046 delete_site(info_v),
4047   - [h . t] then
  4047 + [h . t] then
4048 4048 show_sites(qsort([h . t],compare),1);
4049 4049 with i1 = prompt(" Choose a site to delete [1/.../"+(length(t)+1)+"]: "),
4050   - if decimal_scan(i1) is
  4050 + if decimal_scan(i1) is
4051 4051 {
4052 4052 failure then print(" *** Error: site number not recognized.\n");
4053 4053 delete_site(info_v),
4054   - success(ii1) then if nth(ii1-1,*info_v) is
  4054 + success(ii1) then if nth(ii1-1,*info_v) is
4055 4055 {
4056 4056 failure then print(" *** Error: site number "+i1+" does not exist.\n");
4057 4057 delete_site(info_v),
4058   - success(site_info) then if site_info is site(name,old_port) then
  4058 + success(site_info) then if site_info is site(name,old_port) then
4059 4059 delete_site(info_v,name,old_port)
4060 4060 }
4061 4061 }
4062 4062 }.
4063   -
4064   -
  4063 +
  4064 +
4065 4065 define One
4066 4066 manager
4067 4067 (
... ... @@ -4071,64 +4071,64 @@ define One
4071 4071 print("\n");
4072 4072 print(" --- Welcome to the Web Dispatcher Manager ---\n");
4073 4073 with l = length(*info_v),
4074   - print(" "+l+" site"+(if l>1 then "s" else "")+" currently registred.\n");
  4074 + print(" "+l+" site"+(if l>1 then "s" else "")+" currently registred.\n");
4075 4075 print(" [L] List registered sites.\n");
4076 4076 print(" [R] Register a new site.\n");
4077   - print(" [U] Update a registred site.\n");
4078   - print(" [D] Delete a registred site.\n");
4079   - with propose_write_v = var((Bool)true),
4080   - action = prompt(" Choose an action [L/R/U/D]: "),
  4077 + print(" [U] Update a registred site.\n");
  4078 + print(" [D] Delete a registred site.\n");
  4079 + with propose_write_v = var((Bool)true),
  4080 + action = prompt(" Choose an action [L/R/U/D]: "),
4081 4081 (if (action = "L" | action = "l") then (show_sites(*info_v,1); propose_write_v <- false) else
4082 4082 if (action = "R" | action = "r") then register_new_site(info_v) else
4083 4083 if (action = "U" | action = "u") then update_site(info_v) else
4084 4084 if (action = "D" | action = "d") then delete_site(info_v) else
4085 4085 print("Action not recognized.\n"));
4086   - print("\n");
4087   - if *propose_write_v then
  4086 + print("\n");
  4087 + if *propose_write_v then
4088 4088 with result = prompt(" Write modifications to data base [Y/N] ?"),
4089 4089 if (result = "Y" | result = "y")
4090   - then if save(*info_v,file_path) is
  4090 + then if save(*info_v,file_path) is
4091 4091 {
4092 4092 cannot_open_file then print(" File '"+file_path+"' not found.\n"),
4093 4093 write_error then print(" Error while writing file '"+file_path+"'.\n"),
4094 4094 ok then print(" Data base has been modified.\n")
4095 4095 }
4096 4096 else print(" Data base not modified.\n")
4097   - else unique.
4098   -
4099   -
4100   -
  4097 + else unique.
  4098 +
  4099 +
  4100 +
4101 4101 global define One
4102 4102 manage_web_dispatcher
4103 4103 (
4104 4104 List(String) args
4105 4105 ) =
4106   - with info_v = var((List(DispatcherInfo))[]),
4107   - with file_path = my_anubis_directory+"/web_sites/dispatcher.info",
  4106 + with info_v = var((List(DispatcherInfo))[]),
  4107 + with file_path = my_anubis_directory+"/web_sites/dispatcher.info",
4108 4108 if (RetrieveResult(List(DispatcherInfo)))retrieve(file_path) is
4109 4109 {
4110 4110 cannot_find_file then print("File '"+file_path+"' does not exist.\n");
4111 4111 with answer = prompt("Create it [Y/N] ? "),
4112 4112 if (answer = "Y" | answer = "y")
4113   - then if save((List(DispatcherInfo))[],file_path) is
  4113 + then if save((List(DispatcherInfo))[],file_path) is
4114 4114 {
4115   - cannot_open_file then
  4115 + cannot_open_file then
4116 4116 print("Cannot create file '"+file_path+"'.\n"),
4117 4117 write_error then
4118 4118 print("Error while creating file '"+file_path+"'.\n"),
4119 4119 ok then manager(info_v,file_path)
4120 4120 }
4121 4121 else unique,
4122   - read_error then print("Error while reading file '"+file_path+"'.\n"),
4123   - type_error then print("File '"+file_path+"' is corrupted.\n"),
4124   - ok(info) then info_v <- info;
  4122 + read_error then print("Error while reading file '"+file_path+"'.\n"),
  4123 + type_error then print("File '"+file_path+"' is corrupted.\n"),
  4124 + ok(info) then info_v <- info;
4125 4125 manager(info_v,file_path)
4126 4126 }.
4127   -
4128   -
4129   -
4130   -
4131   -
  4127 +
  4128 +
  4129 +
  4130 +
  4131 +
4132 4132 public define (String, List(HTTP_header))
4133 4133 format
4134 4134 (
... ... @@ -4138,7 +4138,7 @@ public define (String, List(HTTP_header))
4138 4138 {
4139 4139 http_continue then ("100 Continue", []),
4140 4140 http_switching_protocol then ("101 Switching Protocols", []),
4141   -
  4141 +
4142 4142 http_ok then ("200 OK", []),
4143 4143 http_created then ("201 Created", []),
4144 4144 http_accepted then ("202 Accepted", []),
... ... @@ -4146,7 +4146,7 @@ public define (String, List(HTTP_header))
4146 4146 http_no_content then ("204 No Content", []),
4147 4147 http_reset_content then ("205 Reset Content", []),
4148 4148 http_partial_content then ("206 Partial Content", []),
4149   -
  4149 +
4150 4150 http_multiple_choices then ("300 Multiple Choices", []),
4151 4151 http_moved_permanently(loc) then ("301 Moved Permanently", [http_header("Location", loc)]),
4152 4152 http_moved_temporarily(loc) then ("302 Moved Temporarily", [http_header("Location", loc)]),
... ... @@ -4154,7 +4154,7 @@ public define (String, List(HTTP_header))
4154 4154 http_not_modified then ("304 Not Modified", []),
4155 4155 http_use_proxy(loc) then ("305 Use Proxy", [http_header("Location", loc)]),
4156 4156 http_temporary_redirect(loc) then ("307 Temporary Redirect", [http_header("Location", loc)]),
4157   -
  4157 +
4158 4158 http_bad_request then ("400 Bad Request", []),
4159 4159 http_unauthorized then ("401 Unauthorized", []),
4160 4160 http_payment_required then ("402 Payment Required", []),
... ... @@ -4173,15 +4173,13 @@ public define (String, List(HTTP_header))
4173 4173 http_unsupported_media_type then ("415 Unsupported Media Type", []),
4174 4174 http_request_range_unsatisfiable then ("416 Requested range unsatisfiable", []),
4175 4175 http_expectation_failed then ("417 Expectation failed", []),
4176   -
  4176 +
4177 4177 http_internal_server_error then ("500 Internal Server Error", []),
4178 4178 http_not_implemented then ("501 Not Implemented", []),
4179 4179 http_bad_gateway then ("502 Bad Gateway", []),
4180 4180 http_service_unavailable then ("503 Service Unavailable", []),
4181 4181 http_gateway_timeout then ("504 Gateway Time-out", []),
4182 4182 http_version_not_supported then ("505 HTTP Version not supported", []),
4183   -
  4183 +
4184 4184 http_error(code, message) then (abs_to_decimal(code) + " " + message, [])
4185 4185 }.
4186   -
4187   -
... ...
web/jQuery/CXM_jq_checkbox.anubis
1 1 /*
2 2 * Created by PyramIDE.
3   - * User: フランスのトトロ
  3 + * User: フランスのトトロ
4 4 * Date: 28/06/2015
5 5 * Time: 02:22
6   - * © Calexium
  6 + * © Calexium
7 7 */
8 8  
9 9 transmit calexium_lib/web/CXM_making_a_web_site.anubis
... ... @@ -15,25 +15,25 @@ transmit tools/basis.anubis
15 15 transmit system/string.anubis
16 16 transmit tools/random.anubis
17 17  
18   -define String
  18 +define String
19 19 checkbox_init_script
20 20 (
21 21 String checkbox_id,
22 22 Bool initial_state
23   - ) =
  23 + ) =
24 24 "var checkbox = $('#" + checkbox_id + "');" +
25   - if initial_state then
  25 + if initial_state then
26 26 "checkbox.addClass('checked');"
27 27 else
28 28 "checkbox.addClass('unchecked');".
29 29  
30   -define String
  30 +define String
31 31 checkbox_onclick_script
32 32 (
33 33 String checkbox_id,
34 34 String on_script,
35 35 String off_script,
36   - ) =
  36 + ) =
37 37 "var checkbox = $('#" + checkbox_id + "');
38 38 checkbox.toggleClass('checked');
39 39 checkbox.toggleClass('unchecked');
... ... @@ -42,14 +42,14 @@ define String
42 42 } else {
43 43 " + off_script + "
44 44 }".
45   -
  45 +
46 46 public define HTML_Partial_Content
47 47 jq_img_checkbox
48 48 (
49 49 String checkbox_id,
50 50 Bool initial_state,
51 51 String image_url,
52   - String checkbox_label,
  52 + String checkbox_label,
53 53 String on_script,
54 54 String off_script,
55 55 Icon_orientation orientation
... ... @@ -60,14 +60,14 @@ public define HTML_Partial_Content
60 60 ],
61 61 jquery_button(jquery_img_button(image_url, checkbox_label, checkbox_id, jQuery_actioner(same, jqscript, checkbox_onclick_script(checkbox_id, on_script, off_script)), orientation))
62 62 ).
63   -
  63 +
64 64 public define HTML_Partial_Content
65 65 jq_img_checkbox
66 66 (
67 67 String checkbox_id,
68 68 Bool initial_state,
69 69 String image_url,
70   - String checkbox_label,
  70 + String checkbox_label,
71 71 String script,
72 72 Icon_orientation orientation
73 73 ) =
... ...
web/jQuery/CXM_jq_radio.anubis 0 → 100644
  1 +/*
  2 + * Created by PyramIDE.
  3 + * User: フランスのトトロ
  4 + * Date: 28/06/2015
  5 + * Time: 02:22
  6 + * © Calexium
  7 + */
  8 +
  9 +transmit calexium_lib/web/CXM_making_a_web_site.anubis
  10 +transmit calexium_lib/web/CXM_jquery.anubis
  11 +transmit calexium_lib/web/jQuery/CXM_jquery_button.anubis
  12 +transmit calexium_lib/web/jQuery/CXM_jquery_icons.anubis
  13 +transmit calexium_lib/web/js_tools.anubis
  14 +transmit tools/basis.anubis
  15 +transmit system/string.anubis
  16 +transmit tools/random.anubis
  17 +
  18 +public type RadioGroup:
  19 + group(String name),
  20 + none.
  21 +
  22 +define String
  23 + radio_init_script
  24 + (
  25 + String radio_id,
  26 + RadioGroup radio_group,
  27 + Bool initial_state
  28 + ) =
  29 + "var radio = $('#" + radio_id + "');" +
  30 +
  31 + if radio_group is
  32 + {
  33 + group(name) then
  34 + "radio.addClass('radio-group-" + name + "');",
  35 +
  36 + none then
  37 + ""
  38 + } +
  39 +
  40 + if initial_state then
  41 + "radio.addClass('checked');"
  42 + else
  43 + "radio.addClass('unchecked');".
  44 +
  45 +define String
  46 + radio_onclick_script
  47 + (
  48 + String radio_id,
  49 + RadioGroup radio_group,
  50 + String on_script
  51 + ) =
  52 + "
  53 + var radio = $('#" + radio_id + "');
  54 + if (radio.hasClass('checked')) return;
  55 + " +
  56 +
  57 + if radio_group is
  58 + {
  59 + group(name) then
  60 + "$('.radio-group-" + name + "').addClass('unchecked');",
  61 +
  62 + none then
  63 + ""
  64 + } +
  65 +
  66 + "radio.removeClass('unchecked');
  67 + radio.addClass('checked');" + on_script.
  68 +
  69 +public define HTML_Partial_Content
  70 + jq_img_radio
  71 + (
  72 + String radio_id,
  73 + RadioGroup radio_group,
  74 + Bool initial_state,
  75 +//Bool multi_select_with_shift,
  76 + String image_url,
  77 + String checkbox_label,
  78 + String on_script,
  79 + Icon_orientation orientation
  80 + ) =
  81 + partial_content(
  82 + [
  83 + js_inline(jquery_ready(radio_init_script(radio_id, radio_group, initial_state)))
  84 + ],
  85 + jquery_button(jquery_img_button(image_url, checkbox_label, radio_id, jQuery_actioner(same, jqscript, radio_onclick_script(radio_id, radio_group, on_script)), orientation))
  86 + ).
  87 +
  88 +public define HTML_Partial_Content
  89 + jq_img_radio
  90 + (
  91 + String radio_id,
  92 + Bool initial_state,
  93 + String image_url,
  94 + String checkbox_label,
  95 + String script,
  96 + Icon_orientation orientation
  97 + ) =
  98 + jq_img_radio(radio_id, none, initial_state, image_url, checkbox_label, script, orientation).
... ...
web/jQuery/CXM_jquery_button.anubis
... ... @@ -3,7 +3,7 @@
3 3 * User: Jeremy
4 4 * Date: 07/09/2012
5 5 * Time: 15:10
6   - *
  6 + *
7 7 */
8 8  
9 9 transmit calexium_lib/web/CXM_making_a_web_site.anubis
... ... @@ -18,14 +18,14 @@ transmit tools/random.anubis
18 18 public type JQuery_button_type:
19 19 button,
20 20 reset,
21   - submit. //button with confirmation dialog
22   -
  21 + submit. //button with confirmation dialog
  22 +
23 23 public define String
24 24 to_String
25 25 (
26 26 JQuery_button_type bt_type
27 27 )=
28   - if bt_type is
  28 + if bt_type is
29 29 {
30 30 button then "button",
31 31 reset then "reset",
... ... @@ -33,7 +33,7 @@ public define String
33 33 }.
34 34  
35 35 public type JQuery_button:
36   - jQuery_button( JQuery_button_type bt_type,
  36 + jQuery_button( JQuery_button_type bt_type,
37 37 String button_id, String button_name, String button_label, JQuery_Actioner actioner, JQuery_icon primary, JQuery_icon secondary // primary icon is for left icon, secondary icon is for right icon and button_id must set with an unique ID in order to have icons working
38 38 )
39 39 .
... ... @@ -42,43 +42,12 @@ public define JQuery_button
42 42 jQuery_button
43 43 (
44 44 JQuery_button_type bt_type,
45   - String button_id,
46   - String button_name,
47   - String button_label,
  45 + String button_id,
  46 + String button_name,
  47 + String button_label,
48 48 JQuery_Actioner actioner
49 49 ) =
50 50 jQuery_button(bt_type, button_id, button_name, button_label, actioner, jq_icon_none, jq_icon_none).
51   -
52   -define String
53   - jquery_button_get_onclick_action_window_options
54   - (
55   - List(Other_Window_Option) options,
56   - String formated_options
57   - )
58   - =
59   - //if there is already formated_options add a comma + space between the options
60   - with comma = if formated_options="" then "" else ", ",
61   -
62   - if options is
63   - {
64   - [ ] then formated_options,
65   - [ h . t ] then
66   - if h is
67   - {
68   - resizable then jquery_button_get_onclick_action_window_options(
69   - t, formated_options+ comma+ "resizable=yes" ),
70   -
71   - scrollbars then jquery_button_get_onclick_action_window_options(
72   - t, formated_options+ comma + "scrollbars=yes"),
73   -
74   - width(w) then jquery_button_get_onclick_action_window_options(
75   - t, formated_options+ comma + "width="+w),
76   -
77   - height(h) then jquery_button_get_onclick_action_window_options(
78   - t, formated_options+ comma + "height="+h)
79   - }
80   - }
81   - .
82 51  
83 52 define HTML_Head_Tag
84 53 jquery_button_get_onclick_action
... ... @@ -87,31 +56,8 @@ define HTML_Head_Tag
87 56 JQuery_Actioner actioner
88 57 )
89 58 =
90   - if actioner is jQuery_actioner(target, act_type, str) then
91   - if target is
92   - {
93   - same then
94   - if act_type is
95   - {
96   - jqform then js_inline(jquery_ready("$('#"+button_id+"').click(function(){$('#"+str+"').submit();});")),
97   - jqlink then js_inline(jquery_ready("$('#"+button_id+"').click(function(){document.location='/?a="+str+"';});")),
98   - jqscript then js_inline(jquery_ready("$('#"+button_id+"').click(function(){"+str+"});"))
99   - },
100   - same(label) then
101   - if act_type is
102   - {
103   - jqform then js_inline(jquery_ready("$('#"+button_id+"').click(function(){$('#"+str+"').submit();});")),
104   - jqlink then js_inline(jquery_ready("$('#"+button_id+"').click(function(){document.location='/?a="+str+"';});")),
105   - jqscript then js_inline(jquery_ready("$('#"+button_id+"').click(function(){"+str+"});"))
106   - },
107   - other(window_name, window_options) then
108   - if act_type is
109   - {
110   - jqform then js_inline(jquery_ready("$('#"+button_id+"').click(function(){$('#"+str+"').submit();});")), //il s'agit d'une soumision d'un formulaire => cela se fait uniquement dans la même fenêtre !
111   - jqlink then js_inline(jquery_ready("$('#"+button_id+"').click(function(){window.open('"+str+"', '"+window_name+"', '"+jquery_button_get_onclick_action_window_options(window_options, "")+"');});")),
112   - jqscript then js_inline(jquery_ready("$('#"+button_id+"').click(function(){"+str+"});"))
113   - }
114   - }
  59 + with js_action = jquery_button_get_onclick_action(actioner),
  60 + js_inline(jquery_ready("$('#"+button_id+"').click(function(e){" + js_action + "});"))
115 61 .
116 62  
117 63 define HTML_Partial_Content
... ... @@ -138,7 +84,7 @@ define HTML_Partial_Content
138 84 [ onclick_action . html_tags ],
139 85 [
140 86 literal("<button type=\""+to_String(bt_type)+"\" name=\""+name+"\">"+label+"</button>")
141   -
  87 +
142 88 .
143 89 html_buttons
144 90 ]
... ... @@ -265,7 +211,7 @@ public define HTML_Partial_Content
265 211 literal("<button type=\""+to_String(bt_type)+"\" id=\""+id+"\">"+label+"</button>")
266 212 )
267 213 .
268   -
  214 +
269 215 public type Icon_orientation:
270 216 top,
271 217 bottom,
... ... @@ -274,7 +220,7 @@ public type Icon_orientation:
274 220  
275 221 public type JQuery_img_button:
276 222 jquery_img_button(String img_url, String label_text, String id, JQuery_Actioner jq_action, Icon_orientation orientation).
277   -
  223 +
278 224 public define HTML_Off_Form
279 225 jquery_button_html
280 226 (
... ... @@ -292,7 +238,7 @@ public define HTML_Off_Form
292 238 }).
293 239  
294 240  
295   -
  241 +
296 242 public define HTML_Partial_Content
297 243 jquery_button
298 244 (
... ... @@ -309,9 +255,9 @@ public define HTML_Partial_Content
309 255 css(css_file("css/cxm/cxm_button.css")),
310 256 js_inline(jquery_ready("$('.jq_img_btn').button();"))
311 257 ],
312   - jquery_button_html(button, button_id_str))
  258 + jquery_button_html(button, button_id_str))
313 259 .
314   -
  260 +
315 261 public define HTML_Partial_Content
316 262 jq_button_confirm
317 263 (
... ... @@ -320,10 +266,10 @@ public define HTML_Partial_Content
320 266 String dlg_text,
321 267 String dlg_ok,
322 268 String dlg_cancel,
323   - String url,
324   - List((String, String)) extra_ops
  269 + //String url,
  270 + JQuery_Actioner actioner
325 271 )=
326   - with uid = generate_random_string(20),
  272 + with uid = generate_random_string(10),
327 273  
328 274 js_confirm_dialog_data_obj = "CalexiumToolBox.data.confirm_dialog[" + to_js_string(uid) + "]",
329 275 js_data = js_confirm_dialog_data_obj + " = {
... ... @@ -333,26 +279,12 @@ public define HTML_Partial_Content
333 279 cancel: " + to_js_string(dlg_cancel) + "};",
334 280  
335 281 button_name = uid,
336   - actioner = jQuery_actioner(same, jqscript, "CalexiumToolBox.make_confirm_dialog(this, " + to_js_string("/?a=" + url + format_extra_operands(extra_ops))+");"),
  282 + actioner = jQuery_actioner(same, jqscript, "CalexiumToolBox.make_confirm_dialog(this, " /*+ to_js_string("/?a=" + url + format_extra_operands(extra_ops))+*/ + to_js_string(jquery_button_get_onclick_action(actioner)) + ");"),
337 283  
338 284 partial_content([js(js_file("js/cxm/cxm.js")), js_inline(jquery_ready(js_data))],sequence([
339   - partial(jquery_button(jQuery_button(button, uid, button_name, button_label, actioner, jq_icon_none, jq_icon_none)))
340   - ])).
  285 + partial(jquery_button(jQuery_button(button, uid, button_name, button_label, actioner, jq_icon_none, jq_icon_none)))
  286 + ])).
341 287  
342   -
343   -
344   -public define HTML_Partial_Content
345   - jq_button_confirm
346   - (
347   - String button_label,
348   - String dlg_title,
349   - String dlg_text,
350   - String dlg_ok,
351   - String dlg_cancel,
352   - String url
353   - )=
354   - jq_button_confirm(button_label, dlg_title, dlg_text, dlg_ok, dlg_cancel, url, []).
355   -
356 288 public define HTML_Partial_Content
357 289 img_submit_button
358 290 (
... ...
web/jQuery/ui-addon/datetimepicker.anubis
1 1 /*
2 2 * Created by PyramIDE.
3   - * User: フランスのトトロ
  3 + * User: フランスのトトロ
4 4 * Date: 16/11/2014
5 5 * Time: 12:14
6   - * © Calexium
  6 + * © Calexium
7 7 */
8 8  
9 9 read calexium_lib/web/CXM_making_a_web_site.anubis
... ... @@ -13,7 +13,7 @@ read calexium_lib/web/CXM_jquery.anubis
13 13 public define List(HTML_Head_Tag)
14 14 jq_ui_datetimepicker_init
15 15 =
16   - [
  16 + [
17 17 css(css_file("/css/jquery/ui/datetimepicker.css")),
18 18 js(js_file("js/jquery/ui/jquery-ui-timepicker-addon.js")),
19 19 js_inline(jquery_ready("// Datepicker\n
... ... @@ -23,7 +23,7 @@ read calexium_lib/web/CXM_jquery.anubis
23 23 ]
24 24 .
25 25  
26   -
  26 +
27 27 /* Initialize jQuery datetimepicker */
28 28 public define List(HTML_Head_Tag)
29 29 jq_datetimepicker_init
... ... @@ -31,7 +31,7 @@ public define List(HTML_Head_Tag)
31 31 String lang
32 32 )
33 33 =
34   - [
  34 + [
35 35 css(css_file("js/jquery/plug-in/datetimepicker/datetimepicker.css")),
36 36 js(js_file("js/jquery/plug-in/datetimepicker/datetimepicker.js")),
37 37 js_inline(jquery_ready("// Datetimepicker\n
... ...
web/widgets/button.anubis
... ... @@ -3,7 +3,7 @@
3 3 * User: フランスのトトロ & Juju
4 4 * Date: 18/04/2015
5 5 * Time: 20:46
6   - * © Calexium
  6 + * © Calexium
7 7 */
8 8  
9 9 read calexium_lib/web/CXM_making_a_web_site.anubis
... ... @@ -33,7 +33,7 @@ public define HTML_Partial_Content
33 33 String url
34 34 )=
35 35 img_button_flink(img_path, same, url).
36   -
  36 +
37 37 public define HTML_Partial_Content
38 38 img_button
39 39 (
... ... @@ -43,7 +43,7 @@ public define HTML_Partial_Content
43 43 List((String, String)) extra_ops
44 44 )=
45 45 partial_content(actioner(same, same, img_link(img_path, ""), url, extra_ops)).
46   -
  46 +
47 47 public define HTML_Partial_Content
48 48 img_button
49 49 (
... ... @@ -60,7 +60,7 @@ public define HTML_Partial_Content
60 60 List((String, String)) extra_ops
61 61 )=
62 62 img_button(img_path, same, url, extra_ops).
63   -
  63 +
64 64 define inline String
65 65 img_button_confirm_js
66 66 (
... ... @@ -70,10 +70,10 @@ define inline String
70 70 String dlg_ok,
71 71 String dlg_cancel
72 72 ) =
73   - "CalexiumToolBox.data.confirm_dialog[" + to_js_string(uid) + "] = " +
74   - "{ title: " + to_js_string(dlg_title) +
  73 + "CalexiumToolBox.data.confirm_dialog[" + to_js_string(uid) + "] = " +
  74 + "{ title: " + to_js_string(dlg_title) +
75 75 ", text: " + to_js_string(dlg_text) +
76   - ", ok : " + to_js_string(dlg_ok) +
  76 + ", ok : " + to_js_string(dlg_ok) +
77 77 ", cancel: " + to_js_string(dlg_cancel) +
78 78 "};".
79 79  
... ... @@ -82,10 +82,9 @@ define List(CoreAttrs)
82 82 (
83 83 List(CoreAttrs) core_attrs,
84 84 String uid,
85   - String url,
86   - List((String, String)) extra_ops
  85 + JQuery_Actioner actioner
87 86 ) =
88   - core_attrs + [id(uid), style("cursor: pointer"), event(onclick, "CalexiumToolBox.make_confirm_dialog(this, " + to_js_string("/?a=" + url + format_extra_operands(extra_ops))+");")].
  87 + core_attrs + [id(uid), style("cursor: pointer"), event(onclick, "CalexiumToolBox.make_confirm_dialog(this, "/* + to_js_string("/?a=" + url + format_extra_operands(extra_ops))+*/+ to_js_string(jquery_button_get_onclick_action(actioner)) +");")].
89 88  
90 89 public define HTML_Partial_Content
91 90 img_button_confirm
... ... @@ -96,13 +95,12 @@ public define HTML_Partial_Content
96 95 String dlg_text,
97 96 String dlg_ok,
98 97 String dlg_cancel,
99   - String url,
100   - List((String, String)) extra_ops
  98 + JQuery_Actioner actioner
101 99 )=
102 100 with uid = generate_random_string(20),
103 101  
104 102 partial_content([js(js_file("js/cxm/cxm.js")), js_inline(jquery_ready(img_button_confirm_js(uid, dlg_title, dlg_text, dlg_ok, dlg_cancel)))],
105   - image(img_button_confirm_image_attrs(core_attrs, uid, url, extra_ops), img_path, "")).
  103 + image(img_button_confirm_image_attrs(core_attrs, uid, actioner), img_path, "")).
106 104  
107 105 public define HTML_Partial_Content
108 106 img_button_confirm
... ... @@ -112,35 +110,9 @@ public define HTML_Partial_Content
112 110 String dlg_text,
113 111 String dlg_ok,
114 112 String dlg_cancel,
115   - String url
116   - )=
117   - img_button_confirm(img_path, [], dlg_title, dlg_text, dlg_ok, dlg_cancel, url, []).
118   -
119   -public define HTML_Partial_Content
120   - img_button_confirm
121   - (
122   - String img_path,
123   - List(CoreAttrs) core_attrs,
124   - String dlg_title,
125   - String dlg_text,
126   - String dlg_ok,
127   - String dlg_cancel,
128   - String url
129   - )=
130   - img_button_confirm(img_path, core_attrs, dlg_title, dlg_text, dlg_ok, dlg_cancel, url, []).
131   -
132   -public define HTML_Partial_Content
133   - img_button_confirm
134   - (
135   - String img_path,
136   - String dlg_title,
137   - String dlg_text,
138   - String dlg_ok,
139   - String dlg_cancel,
140   - String url,
141   - List((String, String)) extra_ops
  113 + JQuery_Actioner actioner
142 114 )=
143   - img_button_confirm(img_path, [], dlg_title, dlg_text, dlg_ok, dlg_cancel, url, extra_ops).
  115 + img_button_confirm(img_path, [], dlg_title, dlg_text, dlg_ok, dlg_cancel, actioner).
144 116  
145 117 public define String
146 118 img_button_confirm
... ... @@ -151,13 +123,12 @@ public define String
151 123 String dlg_text,
152 124 String dlg_ok,
153 125 String dlg_cancel,
154   - String url,
155   - List((String, String)) extra_ops
  126 + JQuery_Actioner actioner
156 127 ) =
157 128 with uid = generate_random_string(20),
158 129  
159   - "<script type=\"text/javascript\">" + jquery_ready(img_button_confirm_js(uid, dlg_title, dlg_text, dlg_ok, dlg_cancel)) + "</script>" +
160   - "<img " + format_attrs(img_button_confirm_image_attrs(core_attrs, uid, url, extra_ops)) + " src=\"" + img_path + "\"></img>".
  130 + "<script type=\"text/javascript\">" + jquery_ready(img_button_confirm_js(uid, dlg_title, dlg_text, dlg_ok, dlg_cancel)) + "</script>" +
  131 + "<img " + format_attrs(img_button_confirm_image_attrs(core_attrs, uid, actioner)) + " src=\"" + img_path + "\"></img>".
161 132  
162 133 public define String
163 134 img_button_confirm
... ... @@ -167,33 +138,6 @@ public define String
167 138 String dlg_text,
168 139 String dlg_ok,
169 140 String dlg_cancel,
170   - String url
171   - )=
172   - img_button_confirm(img_path, [], dlg_title, dlg_text, dlg_ok, dlg_cancel, url, []).
173   -
174   -public define String
175   - img_button_confirm
176   - (
177   - String img_path,
178   - List(CoreAttrs) core_attrs,
179   - String dlg_title,
180   - String dlg_text,
181   - String dlg_ok,
182   - String dlg_cancel,
183   - String url
184   - )=
185   - img_button_confirm(img_path, core_attrs, dlg_title, dlg_text, dlg_ok, dlg_cancel, url, []).
186   -
187   -public define String
188   - img_button_confirm
189   - (
190   - String img_path,
191   - String dlg_title,
192   - String dlg_text,
193   - String dlg_ok,
194   - String dlg_cancel,
195   - String url,
196   - List((String, String)) extra_ops
  141 + JQuery_Actioner actioner
197 142 )=
198   - img_button_confirm(img_path, [], dlg_title, dlg_text, dlg_ok, dlg_cancel, url, extra_ops).
199   -
  143 + img_button_confirm(img_path, [], dlg_title, dlg_text, dlg_ok, dlg_cancel, actioner).
... ...