Commit 9026a8dbc8b960090bac999a3663c706e19c56f2

Authored by David RENE
1 parent 38d4c6fc

add the management of js_file in html header

Showing 1 changed file with 3740 additions and 3722 deletions   Show diff stats
calexium_lib/web/CXM_making_a_web_site.anubis
1 -  
2 -  
3 - *Project* Anubis  
4 -  
5 - *Title* Making interactive Web sites.  
6 -  
7 - *Copyright* Copyright (c) Alain Prouté 2004-2005.  
8 -  
9 -  
10 - *Author* Alain Prouté  
11 -  
12 - *Revised* January 2005.  
13 -  
14 -  
15 - *Overview*  
16 -  
17 - In this file we propose simple tools for making well structured interactive and secured  
18 - web sites.  
19 -  
20 -  
21 - ----------------------------------- Table of Contents ---------------------------------  
22 -  
23 - * (1) Structure of a web site.  
24 - ** (1.1) Three sorts of data.  
25 - ** (1.2) How requests are handled.  
26 - ** (1.3) What web pages are made of.  
27 - ** (1.4) Actions.  
28 - ** (1.5) States.  
29 -  
30 - * (2) Carrying on.  
31 - ** (2.1) Describing your web sites.  
32 - ** (2.2) Directories on the server's disk.  
33 - ** (2.3) Starting your web sites.  
34 -  
35 - * (3) The HTML interface.  
36 - ** (3.1) Types used by the HTML interface.  
37 - ** (3.2) ``in form'' versus ``off form''.  
38 - ** (3.3) Defining your own style.  
39 - ** (3.4) Actioners and forms.  
40 - ** (3.5) Local popup.  
41 -  
42 - ---------------------------------------------------------------------------------------  
43 -  
44 -  
45 -read tools/basis.anubis  
46 -read CXM_common.anubis  
47 -read CXM_multihost_http_server.anubis  
48 -read CXM_mime.anubis  
49 -  
50 -  
51 -  
52 - * (1) Structure of a web site.  
53 -  
54 - First of all we need to explain what a web site should be made of. Ideally, the  
55 - visitor (also called the 'client') should see the web site working as any other  
56 - interactive computer software. So, it should be clear that a 'session' (i.e. a visit  
57 - to the web site, including the consultation of several pages) is some kind of  
58 - conversation between the visitor and the web site, and that the web site should  
59 - maintain a 'current state' of this conversation. At each new request (click) from the  
60 - visitor, this state must be updated. This whole conversation is called a 'session' and  
61 - should not be confused with a single request.  
62 -  
63 -  
64 -  
65 - ** (1.1) Three sorts of data.  
66 -  
67 - All the data needed for putting a web site at work may be dispatched into three  
68 - categories:  
69 -  
70 - 1. Constant data (data that never change). These data may be hard coded into the  
71 - Anubis source files of the web site.  
72 -  
73 - 2. Permanent data (data which always exist independantly of the users connected to  
74 - the web site). These data are normally recorded into data bases.  
75 -  
76 - 3. Session data (data which depend on a particular visitor and which exist only  
77 - during the time he visits the web site). These data are stored into so-called  
78 - 'states'.  
79 -  
80 -  
81 - It is important to determine which data belongs to which category. This is part of your  
82 - design decisions.  
83 -  
84 -  
85 -  
86 - ** (1.2) How requests are handled.  
87 -  
88 - We want to separate the following two functionalities (which are used at each request  
89 - (click) during a single session):  
90 -  
91 - - computing the new state from the previous state and from the client request, and  
92 - updating the data base,  
93 -  
94 - - computing the page to be sent to the client from the new current state and from  
95 - the informations in the data base.  
96 -  
97 -  
98 - The next picture shows the structure we have in mind:  
99 -  
100 -  
101 - request +---------+ HTML page (with a hidden state name)  
102 - .-------------------| client |<--------------.  
103 - | .-----------------| | |  
104 - | | previous state +---------+ |  
105 - | | name (if any) |  
106 - | | | client side  
107 - ............................................................................  
108 - | | | server side  
109 - | | |  
110 - | | .-------------------. |  
111 - | | | previous state | |  
112 - V V V | |  
113 - +---------------+ +---------------+ +--------------+  
114 - | compute state | | server's disk | | compute page |  
115 - +---------------+ +---------------+ +--------------+  
116 - ^ | | ^ ^ ^ ^ ^  
117 - | | | | | | | |  
118 - | | `--------------------+--------------------' | |  
119 - | | new state | | |  
120 - read | `------------------------+--------------------' |  
121 - write | new state name |  
122 - update V |  
123 - +-----------+ |  
124 - | data base |--------------------------------------------'  
125 - +-----------+ read only  
126 -  
127 -  
128 - When the client begins a session, there is no previous state. In this case, a default  
129 - 'initial state' is used instead.  
130 -  
131 - The data base may be updated by 'compute state' box, but should not be update by the  
132 - 'compute page' box. The 'compute page' box should be allowed only to read the data  
133 - base.  
134 -  
135 - In this file, all the above stuff is defined, except the 'compute state' and 'compute  
136 - page' boxes. You just have to provide the function for computing a new state (compute  
137 - state) and the function for computing the page (compute page) from the new state. You  
138 - don't have to worry about state names, saving and retrieving states and the like.  
139 -  
140 -  
141 -  
142 -  
143 -  
144 - ** (1.3) What web pages are made of.  
145 -  
146 - What the client can see in his browser's window may be called a 'page'. Within a page,  
147 - we have several sorts of components:  
148 -  
149 - - 'local' components, i.e. all components which do not open a connection, like  
150 - texts, images, etc... possibly using JavaScript programmation,  
151 -  
152 - - 'actioners', which, when clicked upon, open a connection with our web site; they  
153 - may appear as links or buttons, etc...  
154 -  
155 - - 'foreign links', which when clicked upon, open a connection with another web site  
156 - (or ours eventually).  
157 -  
158 - Of course, what an actioner does is just ask our web site to perform an action. To that  
159 - end, the actioner essentially sends the name of the action to be performed. However, it  
160 - may be necessary to provide additional informations which may be seen as 'operands' of  
161 - the action. In order to attach operands to an action, HTML provides the notion of  
162 - 'form'. Indeed, a form contains essentially a set of input fields into which the client  
163 - may put values for the required operands of the action, and a submit button, which is  
164 - the actioner itself. Notice that a single form may contain several submit buttons,  
165 - which simply means that there are several distincts actions taking the same set of  
166 - operands.  
167 -  
168 - Restrictions must be put on the use of all theses gadgets. Indeed, for example,  
169 - putting a form within another form is officially meaningless in HTML, and the client's  
170 - browser may be seriously disturbed by this. In this file, we propose an interface to  
171 - the HTML language, which forbids such meaningless things, simply by imposing a strict  
172 - typing of HTML concepts.  
173 -  
174 - Each web site may be accessible through two communication channels:  
175 -  
176 - - a non secured channel (HTTP),  
177 - - a secured channel (HTTPS).  
178 -  
179 - Nevertheless, the whole thing should be considered as a single web site. For example,  
180 - you may have a secured page, obtained through HTTPS, containing public images obtained  
181 - through HTTP. An actioner in a non secured page may open a secured connection, and  
182 - conversely.  
183 -  
184 - Summarizing, a web page is made of local elements, foreign links and actioners.  
185 - Actioners receive operands from forms, and they also choose to communicate through the  
186 - non secured or through the secured channel.  
187 -  
188 -  
189 -  
190 - +-------------------+  
191 - | page |  
192 - | | +---------------+  
193 - | +--------------+ | | next page |  
194 - | | form | | | (non secured) |  
195 - | | +----------+ | | HTTP | |  
196 - | | | actioner |---------------------------->| |  
197 - | | +----------+ | | +---------------+  
198 - | | | |  
199 - | | +----------+ | | +---------------+  
200 - | | | actioner |---------------------------->| next page |  
201 - | | +----------+ | | HTTPS | (secured) |  
202 - | | | | | |  
203 - | +--------------+ | | |  
204 - | | +---------------+  
205 - | |  
206 - +-------------------+  
207 -  
208 -  
209 - Notice that actioners need no be necessarily put into forms. In that case, they work as  
210 - ordinary links, but they still may receive operands as we shall see.  
211 -  
212 -  
213 -  
214 -  
215 - ** (1.4) Actions.  
216 -  
217 - The client opens a new connection with our web site whenever he clicks on an  
218 - actioner. The result is that a request is sent, essentially made of a list of 'web  
219 - arguments'. Each web argument is a pair (name,value). One of these web arguments, the  
220 - 'action' web argument (whose name is "a"), determines the action to be performed. The  
221 - other web arguments (not including "s", used to identify the state) are the operands  
222 - for this action.  
223 -  
224 - Hence, the 'compute state' box in the picture above, splits naturally into as many  
225 - sub-boxes as there are actions. For this reason, we define the following type for  
226 - representing actions (where '$State' is the type representing session informations):  
227 -  
228 -public type Web_Action($SessionTicket, $State):  
229 - http_action (String name, // name of action  
230 - (Maybe($State)) -> Bool allow, // true if action allowed  
231 - (HTTP_Info http_info,  
232 - List(Web_arg) web_args, // actually only 'operands' web arguments  
233 - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),  
234 - https_action (String name, // name of action  
235 - (Maybe($State)) -> Bool allow, // true if action allowed  
236 - (HTTP_Info http_info,  
237 - List(Web_arg) web_args, // actually only 'operands' web arguments  
238 - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),  
239 - http_https_action (String name,  
240 - (Maybe($State)) -> Bool allow, // true if action allowed  
241 - (HTTP_Info http_info,  
242 - List(Web_arg) web_args,  
243 - Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).  
244 -  
245 - 'http_action's are executed only under HTTP, and 'https_action's are executed only  
246 - under HTTPS. 'http_https_action's may be executed under both types of connections.  
247 -  
248 - Each action has a name, which is used to identify the action. Each action also has a  
249 - function 'allow' whose job is to verify that the action is allowed in the current  
250 - state, and a function 'do_it' for performing the action. The function 'do_it' receives  
251 - a lot of informations:  
252 -  
253 - - 'HTTP informations':  
254 - - the IP address of the client,  
255 - - the URI requested by the client (after redirection),  
256 - - the list of HTTP headers generated by the client's browser,  
257 - - the list of web arguments sent by the client (except "s" and "a"),  
258 - - the previous state (or the 'initial' or 'ticket expired' state if no previous  
259 - state can be found).  
260 -  
261 - In most cases, HTTP informations are not used. This is the reason why they are gathered  
262 - for simplicity into a unique datum of type 'HTTP_Info'.  
263 -  
264 -// For your convenience, we introduce the following simpler variants:  
265 -//  
266 -//public define Web_Action($State)  
267 -// http_action  
268 -// (  
269 -// String name,  
270 -// $State -> Bool allow,  
271 -// (List(Web_arg),$State) -> $State do_it  
272 -// ) =  
273 -// http_action(name,  
274 -// (Maybe($State) ms) |-> if ms is  
275 -// {  
276 -// failure then true,  
277 -// success(s) then allow(s)  
278 -// },  
279 -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is  
280 -// {  
281 -// failure then (failure, []),  
282 -// success(s2) then (success(do_it(l,s2)), [])  
283 -// }).  
284 -//  
285 -//public define Web_Action($State)  
286 -// https_action  
287 -// (  
288 -// String name,  
289 -// $State -> Bool allow,  
290 -// (List(Web_arg),$State) -> $State do_it  
291 -// ) =  
292 -// https_action(name,  
293 -// (Maybe($State) ms) |-> if ms is  
294 -// {  
295 -// failure then true,  
296 -// success(s) then allow(s)  
297 -// },  
298 -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is  
299 -// {  
300 -// failure then (failure, []),  
301 -// success(s2) then (success(do_it(l,s2)), [])  
302 -// }).  
303 -//  
304 -//public define Web_Action($State)  
305 -// http_https_action  
306 -// (  
307 -// String name,  
308 -// $State -> Bool allow,  
309 -// (List(Web_arg),$State) -> $State do_it  
310 -// ) =  
311 -// http_https_action(name,  
312 -// (Maybe($State) ms) |-> if ms is  
313 -// {  
314 -// failure then true,  
315 -// success(s) then allow(s)  
316 -// },  
317 -// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is  
318 -// {  
319 -// failure then (failure, []),  
320 -// success(s2) then (success(do_it(l,s2)), [])  
321 -// }).  
322 -  
323 -  
324 -  
325 - When you define your web site, you must provide the list of all the actions of the  
326 - site. When a new state has been computed, a graphical representation of this state  
327 - must be sent to the client. To that end, you must provide a function (named below  
328 - 'compute_page') of type:  
329 -  
330 - $State -> HTML_Page  
331 -  
332 - where the type 'HTML_Page' (defined below in this file) abstractly represents HTML  
333 - pages.  
334 -  
335 -public type HTML_Page:...  
336 -  
337 - It should be clear that states and pages are deeply linked together. Indeed, we really  
338 - understand the page shown to the client as a representation of the current state of the  
339 - conversation between the client and the web site, but also containing informations  
340 - taken from the data bases.  
341 -  
342 -  
343 -  
344 -  
345 -  
346 - ** (1.5) States.  
347 -  
348 - Now, we explain how you can define the type (say 'State') to be used as an instance of  
349 - the type parameter '$State'. The following is just a suggestion.  
350 -  
351 - Each state determines a page (since 'compute_page' computes a page from a  
352 - state). However, some components of the state may be independant of the page. It may be  
353 - the case for example for the indication of the natural language used by the  
354 - client. Hence, a state should be made of (at least) two parts:  
355 -  
356 - - informations which are the same for all pages,  
357 - - informations which are particular to each page.  
358 -  
359 - For example, you could define:  
360 -  
361 - type Page: // one alternative per page, with particular informations  
362 - login(...), // in the components  
363 - main_page(...),  
364 - ...etc...  
365 -  
366 - Now, the type 'State' could be defined as follows:  
367 -  
368 - type State:  
369 - state(Language, // informations valid for all pages  
370 - ...,  
371 - Page). // informations particular to a page  
372 -  
373 - However, if you are making a secured web site within which clients should be identified  
374 - (by id and password), it may be a good idea to have two sorts of states, one for non  
375 - identified clients and one for identified clients. In this case, define the type  
376 - 'State' as follows (this is just a suggestion):  
377 -  
378 - type State:  
379 - non_identified(Language),  
380 - identified(String id,  
381 - Language,  
382 - Page).  
383 -  
384 - When a request arrives, check if the previous state is 'identified(...)' or  
385 - 'non_identified(...)', and don't provide access to certain pages to non identified  
386 - clients. This is required for security.  
387 -  
388 - Some more words on security. If your site needs to identify clients, define the  
389 - initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and  
390 - password of the client. If the id and password are correct, then change the state of  
391 - the client to 'identified(...)'. No other action should be able to do that. Now, be  
392 - confident that clients cannot forge states. The only information they have is the name  
393 - of a state, not the state itself which is never sent over the network, but only stored  
394 - on the server's disk. The name of the state is constructed using strong cryptographical  
395 - methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even  
396 - state names cannot be seen by a third party. So, if the system retrieves a previous  
397 - state of the form 'identified(...)', you can be confident that your client is well  
398 - identified, and you can send him confidential informations.  
399 -  
400 - States have a limited life time. It may happen that a client clicks on a button at a  
401 - time its state is out of date. In this case, this system considers that the new state  
402 - is a special state named 'ticket expired'. You must provide a function producing this  
403 - state when you describe your web site. The page corresponding to this state must just  
404 - inform the client that he/she waited a too long time before clicking on a button, and  
405 - has to restart (a new conversation) from the begining.  
406 -  
407 -  
408 -  
409 - * (2) Carrying on.  
410 -  
411 - ** (2.1) Describing your web sites.  
412 -  
413 - Before you may start your web site, you must describe it, i.e. produce a datum of the  
414 - opaque type 'Web_Site'.  
415 -  
416 -public type Web_Site:...  
417 -  
418 - Producing such a datum may be performed by:  
419 -  
420 -public define Web_Site  
421 - make_web_site_description  
422 - (  
423 - List(String) common_names, // for example: ["www.our-business.com",  
424 - // "192.168.0.1"]  
425 - // the second one is just for testing  
426 - String site_directory, // where 'public' and other directories are  
427 - // located (should NOT end with '/')  
428 - One -> One init,  
429 - (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,  
430 - ($State expired,  
431 - HTTP_Info,  
432 - List(Web_arg),  
433 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,  
434 - (HTTP_Info,  
435 - List(Web_arg),  
436 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,  
437 - List(Web_Action($SessionTicket, $State)) actions,  
438 - (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,  
439 - Int32 timeout, // seconds (todo: minutes)  
440 - List(Redirection) redirections,  
441 - String charset,  
442 - List(String) journal_extensions,  
443 - List(String) journal_headers,  
444 - String authorization_secret,  
445 - List(MIME) known_mime_types,  
446 - (String action_name,  
447 - List(Web_arg) args)-> One before_send_file  
448 - ).  
449 -  
450 -  
451 - Explanations:  
452 -  
453 - 'common_names' is the list of names of the site (the name the browser must send as the  
454 - value of the 'Host' HTTP header in order to access the site must be in that list). Such  
455 - a name generally looks like this:  
456 -  
457 - www.somewhere.com  
458 -  
459 - If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of  
460 - the site must be exactly the same as the name on the certificate (which is precisely  
461 - called the 'common name' in the X.509 jargon). If the two names do not match, the site  
462 - will still work, but the transaction will not be transparent to the client. His browser  
463 - will complain that the name of the certificate does not match the name of the site, and  
464 - he will have to accept the certificate manually.  
465 -  
466 - 'site_directory' is the absolute path to the directory where the files needed by the  
467 - site are located. Usually this directory looks like:  
468 -  
469 - my_anubis/web_sites/www.somewhere.com  
470 -  
471 - However, this information is not computed from 'common_name', so that you can change  
472 - the common name (for example temporarily, for networking reasons) without loosing  
473 - access to the files.  
474 -  
475 - 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state  
476 - whose graphical representation is a page explaining to the user that its 'ticket' (or  
477 - 'session information') has expired, and that he/she must close all popup windows and  
478 - start a new session. The arguments of the function contain the previous (expired)  
479 - state and all current informations concerning the user. This arguments may be useful  
480 - for example for producing the expiration message in the language chosen by the user.  
481 - You can also (and this may be much smarter) send a 'ticket prolongation page'  
482 - (including a new login for example), and resume the same conversation, since you have  
483 - all the pertinent informations at hand. In the case the ticket is definitely lost, the  
484 - second fonction 'ticket_lost_state' is used.  
485 -  
486 - Notice that despite the fact that the parameter $State is involved in the arguments of  
487 - the above function, the type 'Web_Site' does not depend on this parameter. This allows  
488 - to produce lists of web site descriptions, where each description may be constructed  
489 - with a different instance of $State. This is required because distinct sites must have  
490 - distinct types of session informations. This is made possible by the fact that the  
491 - type is obscure, and the constructor replaced by a function which assembles  
492 - 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single  
493 - entity not depending on $State. You should have a look to the private part of this file  
494 - if you want more precisions about this programming technique.  
495 -  
496 - 'charset' is a string which will determine the character encoding to be used by the  
497 - browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",  
498 - etc...  
499 -  
500 - 'before_send_file' is a function which is executed just before the HTTP server sends a  
501 - file. It gets an action name and the web arguments received with the request for that  
502 - file. Notice that this action name and these web arguments may be put into a  
503 - 'private_download' element, and will come back to the server at the time of the  
504 - download.  
505 -  
506 -  
507 -  
508 - ** (2.2) Directories on the server's disk.  
509 -  
510 - The description of you site contains the name of the directory within which the  
511 - required files are located. This may be for example:  
512 -  
513 - my_anubis/web_sites/www.our-business.com/  
514 -  
515 - This is called the 'site directory' (for the given site). Within the site directory,  
516 - the following directories are created by this program:  
517 -  
518 - states  
519 - public  
520 - journal  
521 - private_download  
522 - upload_temporary  
523 -  
524 - The directory 'states' is used for storing states (session informations). Out of date  
525 - states are automatically removed after some time.  
526 -  
527 - The tree rooted at 'public' contains files that the server is allowed to send to the  
528 - clients. For security reasons, the server never sends a file which is not within the  
529 - tree whose root is this 'public' directory (except for the 'private download' mecanism;  
530 - see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')  
531 - must have been recognized before the file may be sent.  
532 -  
533 - The directory 'journal' contains the jounal files. The roles of the remaining  
534 - directories 'private_download' and 'upload_temporary' is explained in  
535 - 'multihost_http_server.anubis', where you will also find further informations on  
536 - 'public' and 'journal'.  
537 -  
538 -  
539 -  
540 -  
541 - ** (2.3) Web servers parameters.  
542 -  
543 - The web servers have several parameters useful for administration. They are described  
544 - as follows:  
545 -  
546 - public type WebServersParameters:  
547 - wsparms(Var(Bool) shutdown_required,  
548 -  
549 -  
550 -  
551 -  
552 - ** (2.4) Starting your web sites.  
553 -  
554 - When you have described all your web sites (you may want to have several web sites, and  
555 - they are distinguished by their 'common name'), you may start them all together using  
556 - 'start_web_sites' below. This function returns a result of the following type:  
557 -  
558 -public type Start_Web_Sites_Result:  
559 - cannot_bind_to_port(Int32),  
560 - cannot_bind_to_port(Int32,Int32),  
561 - ok(Server http_server,  
562 - Server https_server).  
563 -  
564 - Indeed, it may happen that the system cannot bind (begin to listen) to one of the two  
565 - ports (or to both). The main reason is that another server is already listening on that  
566 - port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that  
567 - the 's' bit has not been set for 'user' and 'group' (there is not such problem under  
568 - Windows). Also notice that the Linux kernel may need a rather long time (up to several  
569 - minutes) before liberating a listening port. Now, if the system can bind to the two  
570 - ports, the pair of the two servers is returned. Two tools are useful for manipulating  
571 - servers:  
572 -  
573 - shutdown of type Server -> One  
574 - is_down of type Server -> Bool  
575 -  
576 - They are defined in 'predefined.anubis' (together with the type 'Server').  
577 -  
578 -  
579 -public define Start_Web_Sites_Result  
580 - start_web_sites  
581 - (  
582 - Int32 ip_address, // the IP address shared by the web sites  
583 - Int32 http_port, // usually: 80  
584 - Int32 https_port, // usually: 443  
585 - String ssl_certificate_common_name,  
586 - List(Web_Site) web_sites, // web sites to be started  
587 - Var(Bool) shutdown_required  
588 - ).  
589 -  
590 - 'ip_address' is the IP address on which the two servers listen. If you put 0, the  
591 - servers listen on all the IP addresses of the machine. This may be useful if the  
592 - machine has several network interfaces.  
593 -  
594 - 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'  
595 - loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server  
596 - certificate. This is due to a problem of conception of SSL itself. See the book 'SSL  
597 - and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.  
598 -  
599 - Notice that the number of servers is always 2, regardless of the number of web sites  
600 - you are starting.  
601 -  
602 - The dynamic variable 'shutdown_required' may be used to control the shutdown of the two  
603 - servers from within the web site (typically the administration part). The servers will  
604 - shutdown as soon as this variable contains 'true'. So you must provide a variable  
605 - containing 'false' otherwise your servers will not run. You may also use the primitive  
606 - 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.  
607 -  
608 -  
609 -  
610 -  
611 -  
612 -  
613 - * (3) The HTML interface.  
614 -  
615 - We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination  
616 - of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more  
617 - reactive and attractive on the client side.  
618 -  
619 -  
620 - ** (3.1) Types used by the HTML interface.  
621 -  
622 - For easy reference, we gather below the definitions of all the types used by the HTML  
623 - interface, and we comment them immediately.  
624 -  
625 -  
626 -public type HTML_Size:  
627 - absolute(Int32), // in pixels  
628 - percentage(Int32).  
629 -  
630 -  
631 -public type Text_Option:  
632 - size(Int32), // size of character font to use  
633 - font(String), // name of character font to use (such as "helvetica",...)  
634 - color(RGB), // color to be used for characters  
635 - italic,  
636 - oblique,  
637 - small_capitals,  
638 - bold,  
639 - underlined,  
640 - left_justified,  
641 - right_justified,  
642 - justified, // justified on both sides  
643 - line_through,  
644 - nowrap,  
645 - class(String). //CSS class  
646 -  
647 - A list of 'Text_Option' must be given with each text you want to put in your page.  
648 -  
649 - This indicate the way of reading text.  
650 -public type Reading_Way:  
651 - ltr, //the text is readable from "Left To Right" like english  
652 - rtl. //the text is readable from "Right To Left" like arabic  
653 -  
654 -  
655 -  
656 -public type CoreAttrs:  
657 - id (String),  
658 - class (String),  
659 - style (String),  
660 - title (String).  
661 -  
662 -public type I18n:  
663 - lang (String),  
664 - dir (Reading_Way).  
665 -  
666 -public type DIV_Option:  
667 - id (String),  
668 - class (String),  
669 - style (String),  
670 - title (String),  
671 - lang (String),  
672 - dir (Reading_Way).  
673 -  
674 -  
675 - A list of 'DIV_Option' must be given with each DIV you want to put in your page.  
676 -  
677 -  
678 -public type Table_Option:  
679 - background_color(RGB), // applied to all cells in the table  
680 - background_image(String url),  
681 - border(Int32 width_of_outer_edge, // if not present, all values are 0  
682 - Int32 width_of_top_of_relief,  
683 - Int32 width_of_inner_edge,  
684 - RGB border_color),  
685 - width(Int32), // sets a minimal width for the table  
686 - percentage_width(Int32).  
687 -  
688 -  
689 -public define Table_Option nude = border(0,0,0,rgb(0,0,0)).  
690 -  
691 -  
692 - A list of 'Table_Option' must be given with each table.  
693 -  
694 -  
695 -public type BackgroundOption:  
696 - repeat, // repeat the background in both directions  
697 - repeat_horizontal, // repeat the background only horizontally  
698 - repeat_vertical, // repeat the background only verticall  
699 - no_repeat, // don't repeat the background  
700 - center.  
701 -  
702 -  
703 -public type Cell_Option:  
704 - left, // put the content of the cell on the left  
705 - h_center, // center the content of the cell horizontally  
706 - right, // put the content of the cell on the right  
707 - top, // put the content of the cell upwards  
708 - v_center, // center the content of tye cell vertically,  
709 - bottom, // put the content of the cell downwards  
710 - base_line, // align the content vertically according to base lines  
711 - background_color(RGB),  
712 - background_image(String url, BackgroundOption),  
713 - width(Int32), // sets a minimal width for the cell  
714 - percentage_width(Int32),  
715 - height(Int32), // sets a minimal height for the cell  
716 - columns(Int32), // lets the cell span over several columns  
717 - rows(Int32), // lets the cell span over several rows  
718 - nowrap. // do not allow text wrapping within the cell  
719 -  
720 - A list of 'Cell_Option' must be given with each cell and each row in a table. Options  
721 - given with a row apply to all the cells in the row, but are superseded by options given  
722 - with cells, which apply only to the cell they are given with.  
723 -  
724 -  
725 -public type HTML_Cell($T):  
726 - cell(List(Cell_Option) options, $T content).  
727 -  
728 - The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',  
729 - depending on where you put your table (within a form or not within a form). For your  
730 - convenience, we define the following particular case:  
731 -  
732 -public define HTML_Cell($T)  
733 - cell  
734 - (  
735 - $T content  
736 - ) =  
737 - cell([],content).  
738 -  
739 -  
740 -  
741 -public type HTML_Row($T):  
742 - row(List(Cell_Option) options, List(HTML_Cell($T)) cells).  
743 -  
744 - Same remark as for 'HTML_Cell($T)'. We define several convenience functions:  
745 -  
746 -public define HTML_Row($T)  
747 - row  
748 - (  
749 - List(HTML_Cell($T)) cells  
750 - ) =  
751 - row([],cells).  
752 -  
753 -public define HTML_Row($T)  
754 - row  
755 - (  
756 - HTML_Cell($T) cell  
757 - ) =  
758 - row([],[cell]).  
759 -  
760 -public type Actioner_Connection:  
761 - same, // use same type of connection as current page  
762 - http, // use non secured connection  
763 - https. // use secured connection  
764 -  
765 -public type Other_Window_Option:  
766 - resizable, // the new window may be resized by the client  
767 - scrollbars, // the new window has scrollbars  
768 - width(Int32), // the new window has the specified width  
769 - height(Int32). // the new window has the specified height  
770 -  
771 -public type Actioner_Target:  
772 - same,  
773 - same (String label),  
774 - other(String window_name, List(Other_Window_Option)).  
775 -  
776 -public type Actioner_Aspect:  
777 - link (List(Text_Option),String text), // hypertext link  
778 - push_button (List(CoreAttrs),String text),  
779 - button (String url_off, String url_on), // rollover button  
780 - button (String url_off, String url_on, Int32 w, Int32 h), // idem with size  
781 - immediate_selector (String name, Int32 size, List(String) choices).  
782 -  
783 -  
784 -public type Actioner_Local_Action:  
785 - close_window.  
786 -  
787 -  
788 -public define Actioner_Aspect  
789 - link  
790 - (  
791 - String text  
792 - ) =  
793 - link([],text).  
794 -  
795 -  
796 -public define Actioner_Aspect  
797 - link  
798 - (  
799 - List(Text_Option) options,  
800 - Int32 i  
801 - ) =  
802 - link(options,integer_to_string(i)).  
803 -  
804 -public define Actioner_Aspect  
805 - link  
806 - (  
807 - Int32 i  
808 - ) =  
809 - link([],i).  
810 -  
811 -public define Actioner_Aspect  
812 - button  
813 - (  
814 - String url_img  
815 - ) =  
816 - button(url_img,url_img).  
817 -  
818 -  
819 -  
820 - Actioners are explained in details below.  
821 -  
822 -  
823 -public type TextAreaOption:  
824 - disabled,  
825 - read_only,  
826 - wrap_lines.  
827 -  
828 -public type HTML_In_Form:  
829 - literal_pt (Printable_tree),  
830 - literal (String),  
831 - sequence (List(HTML_In_Form) items),  
832 - text (List(Text_Option), String the_text),  
833 - preformated (List(Text_Option), String),  
834 - paragraph (List(Text_Option), String the_text),  
835 - image (String url),  
836 - image (String url, Int32 width, Int32 height),  
837 - table (List(Table_Option), List(HTML_Row(HTML_In_Form))),  
838 - center (HTML_In_Form),  
839 - mail_to (String email, HTML_In_Form element),  
840 - scroller (Int32 width, Int32 height,  
841 - Int32 content_width, Int32 content_height,  
842 - HTML_In_Form content),  
843 - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,  
844 - String action_name, List((String,String)) extra_ops,  
845 - List(Actioner_Local_Action)),  
846 - foreign_link (List(Text_Option), String url, String name),  
847 - private_download (String abs_path, String name, String extra_ext,  
848 - Maybe((String,List((String,String)))) action),  
849 - text_input (String label_text, String label, String name, String init, Int32 width),  
850 - password_input (String label_text, String label, String name, Int32 width),  
851 - text_area (List(TextAreaOption), String name, String init, Int32 width, Int32 height),  
852 - file_upload (String name, Int32 width),  
853 - selector (String name, Int32 size, List(String) choices),  
854 - selector (String name, Int32 size, List(String) choices, String selected),  
855 - // List((String,String)) = List((code,name)) where :  
856 - // name appears in selector  
857 - // code is the web-arg value  
858 - selector_c (String name, Int32 size, List((String,String)) choices),  
859 - selector_c (String name, Int32 size, List((String,String)) choices, String selected),  
860 - radio_button (String label_text, String label, String name, String value, Bool checked),  
861 - check_box (String label_text, String label, String name, Bool checked),  
862 - div (List(DIV_Option), HTML_In_Form content),  
863 - div_empty (List(DIV_Option)),  
864 - hidden (String name, String value).  
865 -  
866 -  
867 - 'HTML_In_Form' defines all the elements you may put within a form. We define a  
868 - convenience function:  
869 -  
870 -public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).  
871 -  
872 -public define HTML_In_Form  
873 - foreign_link  
874 - (  
875 - Int32 tsize,  
876 - String url,  
877 - String name  
878 - ) =  
879 - foreign_link([size(tsize)],url,name).  
880 -  
881 -public define HTML_In_Form  
882 - actioner  
883 - (  
884 - Actioner_Connection conn,  
885 - Actioner_Target targ,  
886 - Actioner_Aspect asp,  
887 - String action_name,  
888 - List((String,String)) extra_ops  
889 - ) =  
890 - actioner(conn,targ,asp,action_name,extra_ops,[]).  
891 -  
892 -  
893 -  
894 -public define HTML_In_Form  
895 - text_area  
896 - (  
897 - String name,  
898 - String init,  
899 - Int32 width,  
900 - Int32 height  
901 - ) =  
902 - text_area([],name,init,width,height).  
903 -  
904 -public define HTML_In_Form  
905 - table  
906 - (  
907 - List(HTML_Row(HTML_In_Form)) rows  
908 - ) =  
909 - table([],rows).  
910 -  
911 -  
912 -public define HTML_In_Form  
913 - private_download  
914 - (  
915 - String abs_path,  
916 - String name,  
917 - String extra_ext  
918 - ) =  
919 - private_download(abs_path,name,extra_ext,failure).  
920 -  
921 -public define HTML_In_Form  
922 - private_download  
923 - (  
924 - String abs_path,  
925 - String name,  
926 - String extra_ext,  
927 - String action_name,  
928 - List((String,String)) args  
929 - ) =  
930 - private_download(abs_path,name,extra_ext,success((action_name,args))).  
931 -  
932 -public define HTML_In_Form  
933 - text  
934 - (  
935 - String s  
936 - ) =  
937 - text([],s).  
938 -  
939 -  
940 -  
941 -  
942 -public type HTML_Off_Form:  
943 - literal_pt (Printable_tree),  
944 - literal (String),  
945 - sequence (List(HTML_Off_Form) items),  
946 - text (List(Text_Option), String the_text),  
947 - preformated (List(Text_Option), String),  
948 - paragraph (List(Text_Option), String the_text),  
949 - image (String url),  
950 - image (String url, Int32 width, Int32 height),  
951 - table (List(Table_Option), List(HTML_Row(HTML_Off_Form))),  
952 - center (HTML_Off_Form),  
953 - mail_to (String email, HTML_Off_Form element),  
954 - scroller (Int32 width, Int32 height,  
955 - Int32 content_width, Int32 content_height,  
956 - HTML_Off_Form content),  
957 - fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),  
958 - fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),  
959 - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,  
960 - String action_name, List((String,String)) extra_ops,  
961 - List(Actioner_Local_Action)),  
962 - actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,  
963 - String action_name, List((String,String)) extra_ops,  
964 - List(Actioner_Local_Action), String form_name),  
965 - foreign_link (List(Text_Option), String url, String name),  
966 - private_download (String abs_path, String name, String extra_ext,  
967 - Maybe((String,List((String,String)))) action),  
968 - label (String name),  
969 - form (String form_name, List(CoreAttrs), HTML_In_Form content),  
970 - div (List(DIV_Option), HTML_Off_Form content),  
971 - div_empty (List(DIV_Option)).  
972 -  
973 - 'HTML_Off_Form' defines all the elements you may put outside any form.  
974 -  
975 -  
976 -public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).  
977 -public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)  
978 - = fixed_size_2(width,height,name_of_HTML_file).  
979 -  
980 -  
981 -public define HTML_Off_Form  
982 - foreign_link  
983 - (  
984 - Int32 tsize,  
985 - String url,  
986 - String name  
987 - ) =  
988 - foreign_link([size(tsize)],url,name).  
989 -  
990 -  
991 -public define HTML_Off_Form  
992 - actioner  
993 - (  
994 - Actioner_Connection conn,  
995 - Actioner_Target targ,  
996 - Actioner_Aspect asp,  
997 - String action_name,  
998 - List((String,String)) extra_ops  
999 - ) =  
1000 - actioner(conn,targ,asp,action_name,extra_ops,[]).  
1001 -  
1002 -public define HTML_Off_Form  
1003 - table  
1004 - (  
1005 - List(HTML_Row(HTML_Off_Form)) rows  
1006 - ) =  
1007 - table([],rows).  
1008 -  
1009 -  
1010 -  
1011 - We add two convenience functions for 'row'. The reason why we add two functions, one  
1012 - for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an  
1013 - arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,  
1014 - the arguments of the function do not refer to any of the types defined here.  
1015 -  
1016 -public define HTML_Row(HTML_In_Form)  
1017 - row  
1018 - (  
1019 - HTML_In_Form content  
1020 - ) =  
1021 - row([],[cell([],content)]).  
1022 -  
1023 -public define HTML_Row(HTML_Off_Form)  
1024 - row  
1025 - (  
1026 - HTML_Off_Form content  
1027 - ) =  
1028 - row([],[cell([],content)]).  
1029 -  
1030 -  
1031 -public define HTML_Off_Form  
1032 - private_download  
1033 - (  
1034 - String abs_path,  
1035 - String name,  
1036 - String extra_ext  
1037 - ) =  
1038 - private_download(abs_path,name,extra_ext,failure).  
1039 -  
1040 -  
1041 -public define HTML_Off_Form  
1042 - private_download  
1043 - (  
1044 - String abs_path,  
1045 - String name,  
1046 - String extra_ext,  
1047 - String action_name,  
1048 - List((String,String)) args  
1049 - ) =  
1050 - private_download(abs_path,name,extra_ext,success((action_name,args))).  
1051 -  
1052 -public define HTML_Off_Form  
1053 - text  
1054 - (  
1055 - List(Text_Option) lto,  
1056 - Int32 i  
1057 - ) =  
1058 - text(lto,integer_to_string(i)).  
1059 -  
1060 -  
1061 -public define HTML_Off_Form  
1062 - text  
1063 - (  
1064 - Int32 i  
1065 - ) =  
1066 - text([],i).  
1067 -  
1068 -public define HTML_Off_Form  
1069 - text  
1070 - (  
1071 - String s  
1072 - ) =  
1073 - text([],s).  
1074 -  
1075 - - Cell a gap between two other cells :  
1076 -  
1077 -public define HTML_Cell(HTML_Off_Form)  
1078 - width_gap  
1079 - (  
1080 - Int32 w  
1081 - ) =  
1082 - cell([width(w)],text([],"")).  
1083 -  
1084 -public define HTML_Cell(HTML_In_Form)  
1085 - width_gap  
1086 - (  
1087 - Int32 w  
1088 - ) =  
1089 - cell([width(w)],text([],"")).  
1090 -  
1091 -  
1092 - - Row a gap between two other rows :  
1093 -  
1094 -public define HTML_Row(HTML_Off_Form)  
1095 - height_gap  
1096 - (  
1097 - Int32 h  
1098 - ) =  
1099 - row([],[cell([height(h)],text([],""))]).  
1100 -  
1101 -public define HTML_Row(HTML_In_Form)  
1102 - height_gap  
1103 - (  
1104 - Int32 h  
1105 - ) =  
1106 - row([],[cell([height(h)],text([],""))]).  
1107 -  
1108 -  
1109 - Notice that the two types have alternatives in common (same name, same arguments types,  
1110 - up to the value of the parameter $T), which correspond to elements which may be put  
1111 - anywhere in the page.  
1112 -  
1113 -  
1114 -  
1115 -public type CSS_Style:  
1116 - text_options(List(Text_Option)).  
1117 -  
1118 -public type CSS_File:  
1119 - css_file(String file_name).  
1120 -  
1121 -define String  
1122 - format  
1123 - (  
1124 - List(Text_Option) l  
1125 - ).  
1126 -  
1127 -  
1128 -  
1129 -  
1130 -define Printable_tree  
1131 - format_css_styles  
1132 - (  
1133 - List(CSS_Style) l  
1134 - ) =  
1135 - if l is  
1136 - {  
1137 - [ ] then [ ],  
1138 - [h . t] then  
1139 - [ if h is  
1140 - {  
1141 - text_options(tos) then  
1142 - [" body, span, p { ", format(tos), " }\n" ]  
1143 - }  
1144 - . format_css_styles(t)]  
1145 - }.  
1146 -  
1147 -  
1148 -  
1149 -public type HTML_Meta:  
1150 - keywords (List(String)),  
1151 - refresh (Actioner_Connection connection,  
1152 - Actioner_Target target,  
1153 - String action_name,  
1154 - Int32 delay), // in seconds  
1155 - meta (String name, String content),  
1156 - http_equiv (String name, String content),  
1157 - generic_meta (List((String,String))),  
1158 - literal (String).  
1159 -  
1160 - Meta tags are put in the 'head' of the HTML page.  
1161 -  
1162 -  
1163 -public type Body_Option:  
1164 - background_color (RGB),  
1165 - background_image (String url),  
1166 - background_image (String url, List(BackgroundOption)).  
1167 -  
1168 -public type HTML_Body:  
1169 - body(List(Body_Option) options, HTML_Off_Form content).  
1170 -  
1171 -  
1172 -public type HTML_Page:  
1173 - html_page(String title,  
1174 - List(HTML_Meta) meta_tags,  
1175 - List(CSS_Style) styles,  
1176 - List(CSS_File) css_files,  
1177 - HTML_Body body).  
1178 -  
1179 -public define HTML_Page  
1180 - html_page  
1181 - (  
1182 - String title,  
1183 - List(HTML_Meta) metas,  
1184 - HTML_Body body  
1185 - ) =  
1186 - html_page(title,metas,[],[],body).  
1187 -  
1188 -public define HTML_Page  
1189 - html_page  
1190 - (  
1191 - String title,  
1192 - List(HTML_Meta) metas,  
1193 - List(CSS_Style) styles,  
1194 - HTML_Body body  
1195 - ) =  
1196 - html_page(title, metas, styles, [], body).  
1197 -  
1198 - 'HTML_Page' represents the final product of the construction of a web page.  
1199 -  
1200 -  
1201 -  
1202 -  
1203 - *** (3.2) ``in form'' versus ``off form''.  
1204 -  
1205 - There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some  
1206 - of them may have a content, which is yet another HTML element (or several). Hence, it  
1207 - is meaningful to say that an element is 'within' another one. Now, putting any element  
1208 - within any other one may be meaningless. For example, an input element must be put  
1209 - within a form (otherwise, it is useless), and a from within another form has no precise  
1210 - meaning (and is forbidden by the HTML specification).  
1211 -  
1212 - Actually, the main criterium is ``within a form or not within a form''. So, HTML  
1213 - elements in a given page are separated into two categories: those who are within a  
1214 - form, and the others. Nevertheless, there are elements which may belong to both  
1215 - categories, like images and texts. We want to make use of the strong typing mecanism of  
1216 - Anubis in order to forbid non meaningful placement of elements.  
1217 -  
1218 - The type 'HTML_In_Form' defines elements to be put within forms. Similarly,  
1219 - 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,  
1220 - and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),  
1221 - but the two types are not cross recursive. This is the reason why it is impossible to  
1222 - put a form within a form. In order to construct a web page, you essentially have to  
1223 - produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').  
1224 -  
1225 - In practice, you don't have to worry so much about these two types, because elements  
1226 - which may be put anywhere are constructed for both types by functions with the same  
1227 - name and the same arguments. Hence, for both types, you just write the same thing. You  
1228 - are warned by the compiler only when you try to put an element at a place it is not  
1229 - allowed.  
1230 -  
1231 -  
1232 -  
1233 - *** (3.3) Defining your own style.  
1234 -  
1235 - We provide generic tools for constructing HTML elements. However, your web site needs  
1236 - to have a ``style''.  
1237 -  
1238 - To that end, you need to write down a set of ``styling functions'', using the tools  
1239 - defined here. These styling functions allow the introduction of your colors and other  
1240 - visual characteristics into the constructed elements once and for all. For example, you  
1241 - may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using  
1242 - some 'text_color'. You may write something like this:  
1243 -  
1244 - define RGB text_color = rgb(10,40,40).  
1245 -  
1246 - define HTML_Off_Form  
1247 - text  
1248 - (  
1249 - String the_text  
1250 - ) =  
1251 - text([font("helvetica"),size(14),color(text_color)],  
1252 - the_text).  
1253 -  
1254 - (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a  
1255 - page, you just write:  
1256 -  
1257 - text("... some text ...")  
1258 -  
1259 - and you don't have to provide the font, size and color for each text. If you want to  
1260 - have several styles of text presentation, you just write several sets of such  
1261 - convenience functions. This also suggests a trick. You may want for example different  
1262 - colors for 'in form' texts and 'off form' texts. This may be achieved automatically by  
1263 - defining two functions as above, with the same name and same argument type, but  
1264 - returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.  
1265 -  
1266 - If this preliminary work is well done, you will not waste your time later when you  
1267 - concentrate on the actual informational content of your pages.  
1268 -  
1269 - This general principle should be applied to all sorts of elements. This is the best  
1270 - thing to do in order to separate the functions defining the visual style from the  
1271 - functions defining the informational content itself, so that changing the style without  
1272 - changing the content becomes easy. This is also the best way for having a clean and  
1273 - easily readable source for your web site.  
1274 -  
1275 -  
1276 -  
1277 -  
1278 -  
1279 - *** (3.4) Actioners and forms.  
1280 -  
1281 - We have gathered several notions from HTML into that of an 'actioner'. An actioner is  
1282 - an HTML element which opens a connection to our server when clicked upon. Actioners may  
1283 - have different visual aspects. They may look like hypertext links or like buttons  
1284 - (rollovers), or even like selectors (with immediate action). In any case, their  
1285 - behavior is the same: they open a connection to our server, and send a set of 'web  
1286 - arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes  
1287 - the action to be performed, and the others should be considered as operands for this  
1288 - action. Actually, the precise behavior of the actioner has several variants.  
1289 -  
1290 - The connection with the server may be secured (HTTPS) or non secured (HTTP). See the  
1291 - type 'Actioner_Connection' above.  
1292 -  
1293 - You must also choose where the answer must be rendered. This may be in the same window  
1294 - or in another window (or frame). If it is in another window, the name of that window  
1295 - must be given. If the window does not exist, the browser will create it. Optionally,  
1296 - you may give the dimensions of the new window and other characteristics. See the type  
1297 - 'Actioner_Target' above.  
1298 -  
1299 - The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the  
1300 - case of a rollover button, you provide the URLs of two images (of the same size)  
1301 - representing the button:  
1302 -  
1303 - url_off: to be used when the mouse is not over the button,  
1304 - url_on: to be used when the mouse is over the button.  
1305 -  
1306 - You can also create rollover buttons without creating images. Just use the second  
1307 - alternative named 'button'. The server creates the images automatically.  
1308 -  
1309 - The purpose of forms is just to give operands to actioners. If the actioner is placed  
1310 - within a form, all the input elements which are within this form provide operands to  
1311 - the actioner (except sometimes when they are not set by the client). If it is not put  
1312 - within a form, the actioner gets no operand, except if the name of a form is explicitly  
1313 - given, in which case the actioner gets all the inputs from that form as  
1314 - operands. Furthermore, you may want to give extra operands to the actioner. This may be  
1315 - useful for separating families of actioners with the same action name. Extra operands  
1316 - 'name=value' must be given in the form of pairs '(name,value)'.  
1317 -  
1318 - Notice that the name of a form may be used by an actioner which is off the form, so as  
1319 - to get the operands provided by this form. Also notice that several actioners may refer  
1320 - to the same form, being either in the form, or referring to the form from the  
1321 - outside. These actioners simply get the same set of operands, even if they correspond  
1322 - to distinct actions.  
1323 -  
1324 - Input elements may be put only within a form.  
1325 -  
1326 -  
1327 -  
1328 - *** (3.5) Local popup.  
1329 -  
1330 - This element looks like a link or a rollover button. When this button is clicked upon,  
1331 - a 'popup window' appears. Actually, this popup window is just a layer in the same HTML  
1332 - page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In  
1333 - particular, clicking on the button does not open any connection. This is why it is  
1334 - called 'local'. The arguments have the following roles:  
1335 -  
1336 - Actioner_Aspect aspect of the button (same semantics as for actioners)  
1337 - content content of the popup window  
1338 - x, y, position of the popup window on the HTML page (not relative  
1339 - to the button but to the page itself)  
1340 - title title of the popup window  
1341 - color color of the title bar and close button in the popup window.  
1342 - A lightened version of this color is used for the background  
1343 - of the popup window.  
1344 - width width of the title bar  
1345 -  
1346 -  
1347 -  
1348 -  
1349 -  
1350 - --- That's all for the public part ! --------------------------------------------------  
1351 -  
1352 -  
1353 -  
1354 -  
1355 -  
1356 - ----------------------------------- Table of Contents ---------------------------------  
1357 -  
1358 - *** [1] States.  
1359 - *** [1.1] Saving and retrieving states.  
1360 - *** [1.2] Deleting out of date states.  
1361 -  
1362 - *** [2] Tools.  
1363 - *** [2.1] Directories.  
1364 - *** [2.2] Secondary documents.  
1365 -  
1366 - *** [3] Managing web arguments.  
1367 - *** [3.1] Prefixing web arguments names.  
1368 - *** [3.2] Separating web arguments.  
1369 - *** [3.3] Applying an action.  
1370 -  
1371 - *** [4] Web site descriptions and the 'awp handlers'.  
1372 - *** [4.1] The type 'Web_Site'.  
1373 - *** [4.2] Making a web site description.  
1374 - *** [4.3] Starting the servers.  
1375 -  
1376 - *** [5] HTML Formating.  
1377 - *** [5.1] The type 'HTML_Any($T)'.  
1378 - *** [5.2] Formating a color.  
1379 - *** [5.3] Creating buttons.  
1380 - *** [5.4] Formating an actioner.  
1381 - *** [5.5] Formating a private download link.  
1382 - *** [5.6] Formating rows and cells in a table.  
1383 - *** [5.7] Formating elements which may be put anywhere.  
1384 - *** [5.8] Formating 'in form' elements.  
1385 - *** [5.9] Formating 'off form' elements.  
1386 - *** [5.10] Formating meta-tags.  
1387 -  
1388 - ---------------------------------------------------------------------------------------  
1389 -  
1390 -  
1391 -  
1392 -  
1393 -public define String  
1394 - doctype_w3c_header  
1395 - =  
1396 - "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "+  
1397 - "\"http://www.w3c.org/TR/html4/loose.dtd\">\n".  
1398 -  
1399 -  
1400 -  
1401 -  
1402 - *** [1] States.  
1403 -  
1404 - We have to define functions for saving a state, retrieving a state, deleting out of  
1405 - date states. We need one such function per web site. The types of the first two  
1406 - functions depend on the parameter $State. This is not the case of the third one. The  
1407 - fact that the instance of $State is variable from one web sites to the other implies  
1408 - rather subtle manipulations using full functionality.  
1409 -  
1410 -  
1411 - *** [1.1] Saving and retrieving states.  
1412 -  
1413 - Each state is saved into a file on the server's disk (in the directory represented by  
1414 - the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The  
1415 - state is saved together with a time stamp whose value is obtained by adding the current  
1416 - time to the given timeout for states. The state receives a name obtained by hashing  
1417 - (using sha1) the content of the file itself, and then encoding the hash with  
1418 - 'web_arg_encode'. The name of the file into which the state is saved is the  
1419 - concatenation of "s" and the name of the state.  
1420 -  
1421 -read CXM_web_arg_encode.anubis  
1422 -  
1423 - The tool below constructs the function which is able to save a state on the server's  
1424 - disk.  
1425 -  
1426 -define (Maybe($State) s) -> String // the function constructed returns the name of the state  
1427 - make_save_state_function  
1428 - (  
1429 - Int32 timeout,  
1430 - String state_directory  
1431 - ) =  
1432 - (Maybe($State) mbs) |->  
1433 - if mbs is  
1434 - {  
1435 - failure then "",  
1436 - success(s) then  
1437 - with time_stamp = now+timeout,  
1438 - to_be_saved = (time_stamp,s),  
1439 - state_name = web_arg_encode(sha1(s)),  
1440 - if save(to_be_saved,state_directory+"/s"+state_name) is ok  
1441 - then state_name  
1442 - else (print("Cannot create state file in '"+state_directory+"'.\n"); "")  
1443 - }.  
1444 -  
1445 -  
1446 - When a request arrives, we need to retrieve the previous state from the server's  
1447 - disk. We receive the name of that state. If the state is out of date, the state file is  
1448 - kept 3 days, and then deleted.  
1449 -  
1450 -type PreviousState($State):  
1451 - not_found, // cannot retrieve the previous state  
1452 - out_of_date($State), // the previous state is out of date  
1453 - still_valid($State). // the previous state is still valid  
1454 -  
1455 -define (String state_name) -> PreviousState($State)  
1456 - make_retrieve_state_function  
1457 - (  
1458 - String state_directory  
1459 - ) =  
1460 - (String state_name) |->  
1461 - with file_path = state_directory+"/s"+state_name,  
1462 - if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)  
1463 - then (  
1464 - if d is (time_stamp,s) then  
1465 - if time_stamp < now  
1466 - then (  
1467 - forget(remove(file_path));  
1468 - out_of_date(s)  
1469 - )  
1470 - else still_valid(s) // state has been successfully retrieved  
1471 - )  
1472 - else not_found.  
1473 -  
1474 -  
1475 -  
1476 - *** [1.2] Deleting out of date states.  
1477 -  
1478 - We also need to delete states which are out of date and which will never be deleted by  
1479 - the above method. This may be performed by a machine doing this periodically (say once  
1480 - per states life time period).  
1481 -  
1482 -define (List(String) file_names) -> One  
1483 - make_delete_out_of_date_states_function  
1484 - (  
1485 - Maybe($State) dummy,  
1486 - String state_directory  
1487 - ) =  
1488 - (List(String) file_names) |-df->  
1489 - if file_names is  
1490 - {  
1491 - [ ] then unique,  
1492 - [h . t] then  
1493 - with file_path = state_directory+"/"+h,  
1494 - if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)  
1495 - then (  
1496 - if d is (time_stamp,data) then  
1497 - if time_stamp < now  
1498 - then (forget(remove(file_path)); df(t))  
1499 - else df(t)  
1500 - )  
1501 - else (forget(remove(file_path)); df(t))  
1502 - }.  
1503 -  
1504 -  
1505 - The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.  
1506 -  
1507 - Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the  
1508 - function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler  
1509 - does not accept a parameter in the body of a function (here the parameter is required  
1510 - by the use of 'retrieve') if this parameter does not appear in the type of the  
1511 - function. This is because this would create ambiguities that no explicit typing may  
1512 - ever resolve. If you put a double slash in front of the declaration of 'dummy' above,  
1513 - and if you compile this file, you will get a message like this one:  
1514 -  
1515 - Error in 'making_a_web_site.anubis', line 1300, column 7:  
1516 - A definition may not contain parameters which are not present  
1517 - in the declaration part (hidden parameters):  
1518 - $State  
1519 -  
1520 - The type of the function constructed by 'make_delete_out_of_date_states_function' is  
1521 - independant of the parameter $State. This is important because this allows to create  
1522 - the list of such functions for all web sites. From this list, it is possible to call  
1523 - the functions one after the other, so deleting out of date states for all web  
1524 - sites. Actually, the next function receives a list of pairs (state_directory,function),  
1525 - one for each web site.  
1526 -  
1527 -define One  
1528 - delete_out_of_date_states // for all web sites  
1529 - (  
1530 - List((String, List(String) -> One)) directories_and_functions  
1531 - ) =  
1532 - if directories_and_functions is  
1533 - {  
1534 - [ ] then unique,  
1535 - [h . t] then if h is (state_directory,function) then  
1536 - function(directory_list(state_directory,"s*"));  
1537 - delete_out_of_date_states(t)  
1538 - }.  
1539 -  
1540 -  
1541 - The above function must be called periodically in a separate virtual machine. The  
1542 - period we have choosen is (rather logically) the life time of states itself. This may  
1543 - be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not  
1544 - be really infinite, because the servers may be shutdown. Hence, our loop must test  
1545 - (rather frequently; say every second) if the servers are down. If they are, the loop  
1546 - must be exited.  
1547 -  
1548 -define One  
1549 - delete_states_loop  
1550 - (  
1551 - List((String,List(String) -> One)) directories_and_functions,  
1552 - Int32 timeout,  
1553 - Int32 next_time,  
1554 - Server http_server,  
1555 - Server https_server,  
1556 - Var(Bool) shutdown_required  
1557 - ) =  
1558 - if *shutdown_required  
1559 - then (shutdown(http_server); shutdown(https_server))  
1560 - else unique;  
1561 - if (is_down(http_server) & is_down(https_server))  
1562 - then unique  
1563 - else if now > next_time  
1564 - then  
1565 - (  
1566 - delete_out_of_date_states(directories_and_functions);  
1567 - delete_states_loop(directories_and_functions,  
1568 - timeout,  
1569 - now+timeout,  
1570 - http_server,  
1571 - https_server,  
1572 - shutdown_required)  
1573 - )  
1574 - else  
1575 - (  
1576 - sleep(1000); // sleep just one second and try again  
1577 - delete_states_loop(directories_and_functions,  
1578 - timeout,  
1579 - next_time,  
1580 - http_server,  
1581 - https_server,  
1582 - shutdown_required)  
1583 - ).  
1584 -  
1585 - The above loop must be run in a separate virtual machine. This will be done just after  
1586 - the two servers are started.  
1587 -  
1588 -  
1589 -  
1590 -  
1591 -  
1592 - *** [2] Tools.  
1593 -  
1594 - *** [2.1] Directories.  
1595 -  
1596 - We need a tool for creating directories (if needed).  
1597 -  
1598 - (This tool has been moved to 'tools/basis.anubis').  
1599 -  
1600 -  
1601 -  
1602 -  
1603 - *** [2.2] Secondary documents.  
1604 -  
1605 - Some HTML elements (like '<object>', '<frame>') cannot receive their content directly  
1606 - from the current document, but only through an URL. For this reason, we implement a  
1607 - mecanism for creating secondary documents on the fly. To that end we use the 'private  
1608 - download' mecanism.  
1609 -  
1610 - A secondary document is formated by the same functions as the main document itself. The  
1611 - next function takes an 'off form' element, creates the file containing the secondary  
1612 - document in HTML format, and returns the URL at which the document will be available.  
1613 -  
1614 -define String  
1615 - create_secondary_document  
1616 - (  
1617 - String sd, // site directory  
1618 - String as, // authorization_secret  
1619 - String sn, // state name  
1620 - $T -> Printable_tree format_element,  
1621 - $T content,  
1622 - HTML_Size width  
1623 - ) =  
1624 - with private_download_directory = sd+"/private_download",  
1625 - hash = web_arg_encode(sha1(content)),  
1626 - file_content = (Printable_tree)  
1627 - [doctype_w3c_header,  
1628 - "<html><body><table ",  
1629 - if width is  
1630 - {  
1631 - absolute(w) then ["width=\"",w-25],  
1632 - percentage(w) then ["width=\"95%\""]  
1633 - },"\"><tr><td align=right>",  
1634 - format_element(content),  
1635 - "</td></tr></table></body></html>"  
1636 - ],  
1637 - file_name = "sd"+hash+".html",  
1638 - file_path = private_download_directory+"/"+file_name,  
1639 - if write_to_file(file_path,file_content) is  
1640 - {  
1641 - cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",  
1642 - write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",  
1643 - ok then file_name+"?zauth="+  
1644 - make_authorization(sd,as,private_download_directory+"/"+file_name)  
1645 - }.  
1646 -  
1647 -  
1648 -  
1649 -  
1650 - *** [2.3] Generating unique ids.  
1651 -  
1652 - In order to uniquely name object for JavaScript we generate unique ids from a counter.  
1653 -  
1654 -define Int32  
1655 - new_idnum  
1656 - (  
1657 - Var(Int32) ic_v // 'idnum' counter variable  
1658 - ) =  
1659 - protect  
1660 - with result = *ic_v+1,  
1661 - ic_v <- result;  
1662 - result.  
1663 -  
1664 -  
1665 -  
1666 -  
1667 -  
1668 -  
1669 - *** [3] Managing web arguments.  
1670 -  
1671 - Web arguments are those pairs 'name=value' which are transmitted through the HTTP  
1672 - protocol. We need precise naming conventions for these web arguments.  
1673 -  
1674 -  
1675 -  
1676 - *** [3.1] Prefixing web arguments names.  
1677 -  
1678 - We want to assign different roles to web arguments, and we also want to be able to  
1679 - recognize its role directly from the name of a web argument. The name "s" is reserved  
1680 - for the web argument whose value is the name of the current state. The name "a" is  
1681 - reserved for the web argument whose value is the name of the action to be  
1682 - performed. Other web arguments receive arbitrary names, and in order to avoid clashes,  
1683 - these names are prefixed by:  
1684 -  
1685 - "p" for names of password inputs,  
1686 - "o" for other web arguments  
1687 -  
1688 - The reason why password input names have a distinct prefix is that this allows the HTTP  
1689 - server to hide the passwords on the console of the server and in the journal.  
1690 -  
1691 -  
1692 -  
1693 -  
1694 - *** [3.2] Separating web arguments.  
1695 -  
1696 - When a new request arrives, we need to separate the web arguments, that is to say:  
1697 -  
1698 - - find the value of "s", and recover the corresponding state,  
1699 - - find the value of "a", which is the name of the action to be performed,  
1700 - - get the list of all the remaining web arguments (operands of the action).  
1701 -  
1702 - We must also determine if the previous state may be recovered. If it is not the case  
1703 - (either because the previous state name is invalid, or the previous state is out of  
1704 - date), we must check if there is an action name. Indeed, the presence of an action name  
1705 - indicates that the user has clicked on one of our buttons or links. If on the contrary  
1706 - there is no action name the user has just entered our address in his browser. In this  
1707 - last case, we must send the first page of our site (maybe a 'login' page), but if there  
1708 - is an action, we must send a page just saying that the session ticket has expired. If  
1709 - the previous state is recovered and there is no action, the new state is the same as  
1710 - the previous state.  
1711 -  
1712 - The result of the separation of the web arguments is of type:  
1713 -  
1714 -type Separated_Web_Args($State):  
1715 - swa(Maybe(PreviousState($State)) previous_state,  
1716 - Maybe(String) action_name,  
1717 - List(Web_arg) operands).  
1718 -  
1719 -  
1720 -  
1721 - The next function constructs the function which separates the web arguments.  
1722 -  
1723 -define (List(Web_arg) lwa) -> Separated_Web_Args($State)  
1724 - make_separate_web_args_function  
1725 - (  
1726 - String state_directory,  
1727 - String -> PreviousState($State) retrieve_state  
1728 - ) =  
1729 - (List(Web_arg) lwa) |-swaf->  
1730 - if lwa is  
1731 - {  
1732 - [ ] then  
1733 - //  
1734 - // no web arg found => no previous state and no action  
1735 - //  
1736 - swa(failure,failure,[]),  
1737 -  
1738 - [wa_1 . wa_others] then  
1739 - //  
1740 - // at least one web arg =>  
1741 - // separate other web args, and insert the first one as needed  
1742 - //  
1743 - if (Separated_Web_Args($State))swaf(wa_others) is  
1744 - {  
1745 - swa(ps1, // possible previous state  
1746 - an1, // maybe an action name  
1747 - op1) // operands so far  
1748 - then  
1749 - if wa_1 is  
1750 - {  
1751 - web_arg(n,v) then  
1752 - with prefix = if substr(n,0,4) = "amp;" then substr(n,4,1) else substr(n,0,1),  
1753 - name_start = (Int32)(if substr(n,0,4) = "amp;" then 5 else 1),  
1754 - if prefix = "s" then  
1755 - swa(success(retrieve_state(v)),an1,op1) else  
1756 - if prefix = "a" then  
1757 - swa(ps1,success(v),op1) else  
1758 - if prefix = "t" then  
1759 - swa(ps1,an1,[web_arg("target",v) . op1]) else  
1760 - if prefix = "p" then  
1761 - swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else  
1762 - if prefix = "o" then  
1763 - swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else  
1764 - swa(ps1,an1,op1),  
1765 -  
1766 - upload(n,v,t) then  
1767 - swa(ps1,an1,[upload(substr(n,1,length(n)-1),v,t) . op1])  
1768 - }}  
1769 - }.  
1770 -  
1771 -  
1772 -  
1773 -  
1774 -  
1775 -  
1776 - *** [3.3] Applying an action.  
1777 -  
1778 - When the web arguments are separated (and their names cleaned up from prefixes), we may  
1779 - apply the action to the operands and the current state. We search for the action to be  
1780 - applied in the list of actions. If no action is found, the new state is the same as  
1781 - the previous state. Also, we deny the application of an HTTP action if the request  
1782 - arrives through the HTTPS channel and conversely.  
1783 -  
1784 -  
1785 -define (Maybe($State) previous,  
1786 - String action_name,  
1787 - HTTP_Info http_info,  
1788 - List(Web_arg) lwa,  
1789 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header))  
1790 - make_apply_action_function  
1791 - (  
1792 - List(Web_Action($SessionTicket, $State)) actions  
1793 - ) =  
1794 - with f =  
1795 - (Maybe($State) previous,  
1796 - String action_name,  
1797 - HTTP_Info http_info,  
1798 - List(Web_arg) lwa,  
1799 - Bool is_https,  
1800 - List(Web_Action($SessionTicket, $State)) actions) |-f->  
1801 - if actions is  
1802 - {  
1803 - [ ] then (print("action '"+action_name+  
1804 - "' not found.\n"); (failure, previous, [])),  
1805 - [ac1 . others] then if ac1 is  
1806 - {  
1807 - http_action(an,allow,do_it) then  
1808 - if an = action_name  
1809 - then if is_https  
1810 - then (print("HTTP action '"+an+  
1811 - "' called through HTTPS (denied).\n");  
1812 - (failure, previous, []))  
1813 - else if allow(previous)  
1814 - then do_it(http_info,lwa,previous)  
1815 - else (failure, previous, [])  
1816 - else f(previous,action_name,http_info,lwa,is_https,others),  
1817 -  
1818 - https_action(an,allow,do_it) then  
1819 - if an = action_name  
1820 - then if is_https  
1821 - then if allow(previous)  
1822 - then do_it(http_info,lwa,previous)  
1823 - else (failure, previous, [])  
1824 - else (print("HTTPS action '"+an+  
1825 - "' called through HTTP (denied).\n");  
1826 - (failure, previous, []))  
1827 - else f(previous,action_name,http_info,lwa,is_https,others),  
1828 -  
1829 - http_https_action(an,allow,do_it) then  
1830 - if an = action_name  
1831 - then if allow(previous)  
1832 - then do_it(http_info,lwa,previous)  
1833 - else (failure, previous, [])  
1834 - else f(previous,action_name,http_info,lwa,is_https,others),  
1835 -  
1836 - }  
1837 - },  
1838 - (Maybe($State) previous,  
1839 - String action_name,  
1840 - HTTP_Info http_info,  
1841 - List(Web_arg) lwa,  
1842 - Bool is_https) |->  
1843 - f(previous,action_name,http_info,lwa,is_https,actions).  
1844 -  
1845 -  
1846 -  
1847 -  
1848 -  
1849 -  
1850 -  
1851 -  
1852 - *** [4] Web site descriptions and the 'awp handlers'.  
1853 -  
1854 - *** [4.1] The type 'Web_Site'.  
1855 -  
1856 - The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We  
1857 - need another one, because, we have some extra informations to record for each site.  
1858 -  
1859 -public type Web_Site:  
1860 - web_site((Int32,Int32) -> Web_Site_Description description,  
1861 - List(String) -> One delete_out_of_date).  
1862 -  
1863 -  
1864 -  
1865 -  
1866 - *** [4.2] Making a web site description.  
1867 -  
1868 - Below is the function which creates a web site description. It first creates (if  
1869 - needed) the directories for the site, then constructs the tool functions for the site,  
1870 - and the site handler. Finally, it constructs the web site description.  
1871 -  
1872 - We gather common (constant) informations in the following type:  
1873 -  
1874 -type CommonInfo:  
1875 - info(String common_name,  
1876 - Int32 http_port,  
1877 - Int32 https_port,  
1878 - String site_directory,  
1879 - String authorization_secret  
1880 - ).  
1881 -  
1882 - We need a forward declaration.  
1883 -  
1884 -public define Printable_tree  
1885 - format  
1886 - (  
1887 - CommonInfo cinfo,  
1888 - String state_name,  
1889 - HTML_Page page,  
1890 - Bool is_https,  
1891 - String charset  
1892 - ).  
1893 -  
1894 -  
1895 -define Printable_tree  
1896 - format  
1897 - (  
1898 - HTML_Size s  
1899 - ) =  
1900 - if s is  
1901 - {  
1902 - absolute(x) then ["\"",x,"\""],  
1903 - percentage(x) then ["\"",x,"%\""]  
1904 - }.  
1905 -  
1906 -define Printable_tree  
1907 - top_redirection_page  
1908 - (  
1909 - String common_name  
1910 - ) =  
1911 - [ doctype_w3c_header,  
1912 - "<html><head>",  
1913 - "<meta http-equiv=\"Refresh\" content=\"0; URL=javascript:void window.open('/?t=','_top')\">",  
1914 - "</head><body></body></html>"  
1915 - ].  
1916 -  
1917 -public define Web_Site  
1918 - make_web_site_description  
1919 - (  
1920 - List(String) common_names, // for example: ["www.our-business.com"]  
1921 - String site_directory,  
1922 - One -> One init,  
1923 - (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,  
1924 - ($State expired,  
1925 - HTTP_Info,  
1926 - List(Web_arg),  
1927 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,  
1928 - (HTTP_Info,  
1929 - List(Web_arg),  
1930 - Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,  
1931 - List(Web_Action($SessionTicket, $State)) actions,  
1932 - (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,  
1933 - Int32 timeout,  
1934 - List(Redirection) redirections,  
1935 - String charset,  
1936 - List(String) journal_extensions,  
1937 - List(String) journal_headers,  
1938 - String secret,  
1939 - List(MIME) known_mime_types,  
1940 - (String action_name,  
1941 - List(Web_arg) args) -> One before_send_file  
1942 - ) =  
1943 - init(unique);  
1944 -  
1945 -  
1946 - //  
1947 - // make required directories (if needed)  
1948 - //  
1949 - with web_sites_directory = make_directory(my_anubis_directory+"/web_sites"),  
1950 - base_directory = make_directory(site_directory),  
1951 - state_directory = make_directory(site_directory+"/states"),  
1952 - forget(make_directory(site_directory+"/public"));  
1953 - //  
1954 - // construct tool functions  
1955 - //  
1956 - with save_state = make_save_state_function(timeout,state_directory),  
1957 - retrieve_state = make_retrieve_state_function(state_directory),  
1958 - separate_web_args = make_separate_web_args_function(state_directory,retrieve_state),  
1959 - apply_action = make_apply_action_function(actions),  
1960 - //  
1961 - // construct the site handler  
1962 - //  
1963 - site_handler = (Int32 http_port, Int32 https_port) |->  
1964 - ((String host_name,  
1965 - HTTP_Info http_info,  
1966 - List(Web_arg) lwa,  
1967 - Bool is_https) |->  
1968 - ((List(HTTP_header),Printable_tree))  
1969 - if separate_web_args(lwa) is  
1970 - {  
1971 - swa(mb_previous_state,mb_action_name,operands) then  
1972 - with state_and_headers = if mb_previous_state is  
1973 - {  
1974 - failure then  
1975 - if mb_action_name is  
1976 - {  
1977 - failure then initial_state(http_info),  
1978 - success(action_name) then  
1979 - apply_action(failure,action_name,http_info,operands,is_https)  
1980 - },  
1981 - success(previous_state) then if previous_state is  
1982 - {  
1983 - not_found then  
1984 - if mb_action_name is  
1985 - {  
1986 - failure then initial_state(http_info),  
1987 - success(_) then  
1988 - ticket_lost_state(http_info,lwa,is_https)  
1989 - },  
1990 -  
1991 - out_of_date(state) then  
1992 - ticket_expired_state(state,http_info,lwa,is_https),  
1993 -  
1994 - still_valid(state) then  
1995 - if mb_action_name is  
1996 - {  
1997 - failure then (failure, success(state), []),  
1998 - success(action_name) then  
1999 - apply_action(success(state),action_name,http_info,operands,is_https)  
2000 - }  
2001 - }  
2002 - },  
2003 - if state_and_headers is (session_ticket, mb_new_state, headers) then  
2004 - with state_name = save_state(mb_new_state),  
2005 - (headers,  
2006 - format(info(host_name, http_port, https_port, site_directory, secret),  
2007 - state_name,  
2008 - compute_page(session_ticket, mb_new_state),  
2009 - is_https,  
2010 - charset))  
2011 - }),  
2012 - //  
2013 - // make the delete_out_of_date function  
2014 - //  
2015 - delete_out_of_date =  
2016 - make_delete_out_of_date_states_function((Maybe($State))failure,  
2017 - site_directory+"/states"),  
2018 - //  
2019 - // construct the web site description  
2020 - //  
2021 - web_site((Int32 http_port, Int32 https_port) |->  
2022 - web_site_description(common_names,  
2023 - site_directory,  
2024 - redirections,  
2025 - charset,  
2026 - journal_extensions,  
2027 - journal_headers,  
2028 - secret,  
2029 - known_mime_types,  
2030 - site_handler(http_port,https_port),  
2031 - (List(Web_arg) lwa) |-> if separate_web_args(lwa) is  
2032 - swa(mb_previous_state,mb_action_name,operands) then  
2033 - if mb_action_name is  
2034 - {  
2035 - failure then unique  
2036 - success(an) then before_send_file(an,operands)  
2037 - }),  
2038 - delete_out_of_date).  
2039 -  
2040 -  
2041 -  
2042 -  
2043 - *** [4.3] Starting the servers.  
2044 -  
2045 -public define Start_Web_Sites_Result  
2046 - start_web_sites  
2047 - (  
2048 - Int32 ip_address, // the IP address shared by the web sites  
2049 - Int32 http_port, // usually: 80  
2050 - Int32 https_port, // usually: 443  
2051 - String ssl_certificate_common_name,  
2052 - List(Web_Site) web_sites, // web sites to be started  
2053 - Var(Bool) shutdown_required  
2054 - ) =  
2055 - with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),  
2056 - with http_server_r =  
2057 - start_http_server(ip_address,http_port,  
2058 - map(get_description,web_sites),  
2059 - load_denial_of_service_info),  
2060 - with https_server_r =  
2061 - start_https_server(ip_address,https_port,  
2062 - ssl_certificate_common_name,  
2063 - map(get_description,web_sites),  
2064 - load_denial_of_service_info),  
2065 - if http_server_r is ok(http_server)  
2066 - then  
2067 - (  
2068 - if https_server_r is ok(https_server)  
2069 - then  
2070 - (  
2071 - start_http_servers_tasks(map(get_description,web_sites),  
2072 - [http_server,https_server],  
2073 - 600); // period of 10 minutes  
2074 - delegate  
2075 - delete_states_loop(  
2076 - map((Web_Site ws) |->  
2077 - (site_directory(description(ws)(http_port,https_port))+  
2078 - "/states",delete_out_of_date(ws)),  
2079 - web_sites),  
2080 - 3600*24*3, // keep out of date states 3 days  
2081 - now,  
2082 - http_server,  
2083 - https_server,  
2084 - shutdown_required),  
2085 - ok(http_server,https_server)  
2086 - )  
2087 - else cannot_bind_to_port(https_port)  
2088 - )  
2089 - else  
2090 - (  
2091 - if https_server_r is ok(https_server)  
2092 - then cannot_bind_to_port(http_port)  
2093 - else cannot_bind_to_port(http_port,https_port)  
2094 - ).  
2095 -  
2096 -  
2097 -public define One  
2098 - start_web_sites  
2099 - (  
2100 - Int32 ip_address, // the IP address shared by the web sites  
2101 - Int32 http_port, // usually: 80  
2102 - Int32 https_port, // usually: 443  
2103 - String ssl_certificate_common_name,  
2104 - List(Web_Site) web_sites, // web sites to be started  
2105 - Var(Bool) shutdown_required  
2106 - ) =  
2107 - if (Start_Web_Sites_Result)start_web_sites(ip_address,  
2108 - http_port,  
2109 - https_port,  
2110 - ssl_certificate_common_name,  
2111 - web_sites,  
2112 - shutdown_required) is  
2113 - {  
2114 - cannot_bind_to_port(n) then print("Cannot bind to port: "+n+"\n"),  
2115 - cannot_bind_to_port(n,m) then print("Cannot bind to ports: "+n+", "+m+"\n"),  
2116 - ok(s1,s2) then print("Servers started.\n")  
2117 - }.  
2118 -  
2119 -  
2120 -  
2121 -  
2122 -  
2123 - *** [5] HTML Formating.  
2124 -  
2125 - We need to translate HTML elements as defined above into actual HTML text.  
2126 -  
2127 - Actioners require special informations, which must be transmitted when needed by the  
2128 - 'format' functions:  
2129 -  
2130 - - the 'common name', which is used for URLs,  
2131 - - the HTTP/HTTPS port number,  
2132 - - the 'state name', which must be transmitted when the actioner is clicked upon,  
2133 - - the 'form name' (if any) to which the actioner refers.  
2134 -  
2135 - If the actioner is off form, and if it refers to a form, the name of that form is  
2136 - already known by the actioner. On the contrary, if the actioner is 'in form', it refers  
2137 - implicitly to the form containing it. The name of that form is transmitted to the  
2138 - 'format' functions called from within the formating of that form.  
2139 -  
2140 -  
2141 -  
2142 -  
2143 - *** [5.1] The type 'HTML_Any($T)'.  
2144 -  
2145 - The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The  
2146 - parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.  
2147 -  
2148 -type HTML_Any($T):  
2149 - any_text (List(Text_Option), String the_text),  
2150 - any_preformated (List(Text_Option), String),  
2151 - any_paragraph (List(Text_Option), String the_text),  
2152 - any_image (String url),  
2153 - any_image (String url, Int32 width, Int32 height),  
2154 - any_table (List(Table_Option), List(HTML_Row($T))),  
2155 - any_center ($T),  
2156 - any_mail_to (String email, $T element),  
2157 - any_scroller (Int32 width, Int32 height,  
2158 - Int32 content_width, Int32 content_height,  
2159 - $T content),  
2160 - any_fixed_size (HTML_Size width, HTML_Size height, $T content),  
2161 - any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),  
2162 - any_actioner (Actioner_Connection,  
2163 - Actioner_Target,  
2164 - Actioner_Aspect,  
2165 - String action_name,  
2166 - List((String,String)) extra_ops,  
2167 - List(Actioner_Local_Action),  
2168 - Maybe(String) form_name),  
2169 - any_foreign_link (List(Text_Option), String url, String name),  
2170 - any_private_download (String abs_path, String name, String extra_ext,  
2171 - Maybe((String,List((String,String))))),  
2172 - any_div (List(DIV_Option), $T element),  
2173 - any_div_empty (List(DIV_Option)),  
2174 - any_coreattrs (List(CoreAttrs)).  
2175 -  
2176 -  
2177 -  
2178 - *** [5.2] Formating a color.  
2179 -  
2180 - RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters  
2181 - hexadecimal values.  
2182 -  
2183 -define String  
2184 - html_format  
2185 - (  
2186 - RGB color  
2187 - ) =  
2188 - if color is rgb(r,g,b) then  
2189 - "#" + hexadecimal(word8_to_int32(r),2)  
2190 - + hexadecimal(word8_to_int32(g),2)  
2191 - + hexadecimal(word8_to_int32(b),2).  
2192 -  
2193 -  
2194 - The following is a very arbitrary definition of the opposite color. The thing which is  
2195 - important is that it is far from the original, so that characters in 'opposite' color  
2196 - are clearly visible over the original.  
2197 -  
2198 -define RGB  
2199 - opposite  
2200 - (  
2201 - RGB color  
2202 - ) =  
2203 - if color is rgb(r,g,b) then  
2204 - with r1 = word8_to_int32(r),  
2205 - with g1 = word8_to_int32(g),  
2206 - with b1 = word8_to_int32(b),  
2207 - rgb(truncate_to_word8(255-r1),  
2208 - truncate_to_word8(255-g1),  
2209 - truncate_to_word8(255-b1)).  
2210 -  
2211 -  
2212 -  
2213 -  
2214 - *** [5.3] Creating buttons.  
2215 -  
2216 - We want to be able to create buttons in the form of a pair of images (rollovers)  
2217 - automatically. We use the JPEG interface, because for the time being Anubis cannot  
2218 - handle other kinds of images.  
2219 -  
2220 -  
2221 - Computing printed text length.  
2222 -  
2223 - define Int32  
2224 - printed_text_width  
2225 - (  
2226 - Word8 -> Int32 char_size,  
2227 - List(Word8) l  
2228 - ) =  
2229 - if l is  
2230 - {  
2231 - [] then (Int32) 0,  
2232 - [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)  
2233 - }.  
2234 -  
2235 - define Int32  
2236 - printed_text_width  
2237 - (  
2238 - SystemFont font,  
2239 - String s  
2240 - ) =  
2241 - printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),  
2242 - explode(s)).  
2243 -  
2244 -  
2245 -  
2246 - Converting RGB to RGBA.  
2247 -  
2248 -define RGBA  
2249 - to_rgba  
2250 - (  
2251 - RGB color  
2252 - ) =  
2253 - if color is rgb(r,g,b) then rgba(r,g,b,255).  
2254 -  
2255 -  
2256 - Drawing a 'relief'.  
2257 -  
2258 - define One  
2259 - draw_relief  
2260 - (  
2261 - RGBAImage dest,  
2262 - RGBA color,  
2263 - Int32 contrast,  
2264 - Int32 x,  
2265 - Int32 y,  
2266 - Int32 width,  
2267 - Int32 height  
2268 - ) =  
2269 - with l = lighten(color,contrast),  
2270 - d = darken(color,contrast),  
2271 - draw_rectangle(dest,rect(x,y,x+width,y+1),l);  
2272 - draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);  
2273 - draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);  
2274 - draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).  
2275 -  
2276 -  
2277 - Creating a button background.  
2278 -  
2279 - define RGBAImage  
2280 - create_button_background  
2281 - (  
2282 - RGBA color,  
2283 - Int32 width,  
2284 - Int32 height  
2285 - ) =  
2286 - with result = create_rgba_image(width,height,color),  
2287 - draw_relief(result,color,100,0,0,width,height);  
2288 - draw_relief(result,color,70,1,1,width-2,height-2);  
2289 - draw_relief(result,color,55,2,2,width-4,height-4);  
2290 - draw_relief(result,color,35,3,3,width-6,height-6);  
2291 - draw_relief(result,color,20,4,4,width-8,height-8);  
2292 - draw_relief(result,color,10,5,5,width-10,height-10);  
2293 - draw_relief(result,color,5,6,6,width-12,height-12);  
2294 - result.  
2295 -  
2296 -  
2297 - Drawing the text over the background.  
2298 -  
2299 - define One  
2300 - draw_button_text  
2301 - (  
2302 - RGBAImage image,  
2303 - String text,  
2304 - Int32 text_index,  
2305 - Int32 pixel_x,  
2306 - Int32 y,  
2307 - Rectangle clip,  
2308 - RGBA color,  
2309 - SystemFont font,  
2310 - ) =  
2311 - if nth(text_index,text) is  
2312 - {  
2313 - failure then unique,  
2314 - success(c) then  
2315 - with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),  
2316 - draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)  
2317 - }.  
2318 -  
2319 - define One  
2320 - draw_button_text  
2321 - (  
2322 - RGBAImage image,  
2323 - String text,  
2324 - Int32 text_width,  
2325 - RGBA light_color,  
2326 - RGBA dark_color,  
2327 - SystemFont font  
2328 - ) =  
2329 - with image_width = width(image),  
2330 - image_height = height(image),  
2331 - x_pos = (image_width-text_width)>>1,  
2332 - clip = rect(0,0,image_width,image_height),  
2333 - new_light_color = lighten(light_color,150),  
2334 - new_dark_color = darken(dark_color,40),  
2335 - draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);  
2336 - draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).  
2337 -  
2338 -  
2339 - The next function creates the two images for a button. The information given is the  
2340 - main color of the button, the text of the button and the minimal width (in pixels) of  
2341 - the button. The function does not create the button if the images already exist. The  
2342 - two images are stored in the directory 'site_directory/buttons'. The names of the files  
2343 - are of the form:  
2344 -  
2345 - bxxxx_off.jpg  
2346 - bxxxx_on.jpg  
2347 -  
2348 - where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like  
2349 - 'rm'), and where 'xxxx' is created from the given informations by the formula:  
2350 -  
2351 - xxxx = web_arg_encode(sha1((color,text,width)))  
2352 -  
2353 - Hence, distinct informations give distinct file names.  
2354 -  
2355 -  
2356 - define String // returns xxxx  
2357 - create_button_images  
2358 - (  
2359 - String site_directory,  
2360 - RGBA color,  
2361 - String text,  
2362 - Int32 width,  
2363 - SystemFont font  
2364 - ) =  
2365 - with xxxx = web_arg_encode(sha1((color,text,width))),  
2366 - buttons_dir = site_directory+"/public/buttons",  
2367 - off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",  
2368 - on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",  
2369 - if file_exists(on_filepath)  
2370 - then xxxx  
2371 - else with  
2372 - text_width = printed_text_width(font,text),  
2373 - button_width = max(width,text_width+12),  
2374 - button_height = (Int32)20,  
2375 - light_color = lighten(color,60),  
2376 - very_light_color = lighten(light_color,30),  
2377 - dark_color = darken(color,40),  
2378 - background_off =  
2379 - create_button_background(color,button_width,button_height),  
2380 - background_on =  
2381 - create_button_background(light_color,button_width,button_height),  
2382 -  
2383 - draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);  
2384 - draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);  
2385 - forget(write_image_to_JPEG_file(to_JPEG(background_off),  
2386 - off_filepath,  
2387 - 100));  
2388 - forget(write_image_to_JPEG_file(to_JPEG(background_on),  
2389 - on_filepath,  
2390 - 100));  
2391 - xxxx.  
2392 -  
2393 -  
2394 -  
2395 -  
2396 -  
2397 - *** [5.4] Formating an actioner.  
2398 -  
2399 - An actioner works as follows. Assume first that it refers to a form. When it is clicked  
2400 - upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the  
2401 - form, and submits the form, using the JavaScript command 'form_name.submit()'. If the  
2402 - actioner does not refer to a form, it fires the URL directly via 'href', because in  
2403 - that case, the actioner is always an <a> tag.  
2404 -  
2405 - The URL itself is composed using the connection sort (same, http or https), the common  
2406 - name and port number (if needed), the state name, the action name, and the extra  
2407 - operands, which are put into a query string. It may look like this:  
2408 -  
2409 - http://common_name:port/?s=state_name&a=action_name&oname=value...  
2410 -  
2411 - Each extra operand is a pair of strings: (name,value). It is formated as:  
2412 -  
2413 - &oname=value  
2414 -  
2415 -  
2416 -define String  
2417 - format_extra_operands  
2418 - (  
2419 - List((String,String)) l  
2420 - ) =  
2421 - if l is  
2422 - {  
2423 - [ ] then "",  
2424 - [h . t] then if h is (n,v) then  
2425 - "&amp;o"+n+"="+v+format_extra_operands(t)  
2426 - }.  
2427 -  
2428 -  
2429 - It seams that the standard requires "&amp;" instead of "&" !  
2430 -  
2431 - In case the target is another window, we need to format the options for this window.  
2432 -  
2433 -define String  
2434 - format  
2435 - (  
2436 - List(Other_Window_Option) l  
2437 - ) =  
2438 - if l is  
2439 - {  
2440 - [ ] then "",  
2441 - [h . t] then if h is  
2442 - {  
2443 - resizable then "resizable",  
2444 - scrollbars then "scrollbars",  
2445 - width(w) then "width="+w,  
2446 - height(h) then "height="+h  
2447 - } + if t is [ ] then "" else (","+format(t))  
2448 - }.  
2449 -  
2450 -  
2451 -  
2452 - Formating choices for a <select> tag.  
2453 -  
2454 -public define Printable_tree  
2455 - format_choices  
2456 - (  
2457 - List(String) l  
2458 - ) =  
2459 - if l is  
2460 - {  
2461 - [ ] then [ ],  
2462 - [h . t] then ["<option>",h . format_choices(t)]  
2463 - }.  
2464 -  
2465 -  
2466 -public define Printable_tree  
2467 - format_choices  
2468 - (  
2469 - List(String) l,  
2470 - String selected  
2471 - ) =  
2472 - if l is  
2473 - {  
2474 - [ ] then [ ],  
2475 - [h . t] then if h = selected  
2476 - then ["<option selected>",h . format_choices(t)]  
2477 - else ["<option>",h . format_choices(t,selected)]  
2478 - }.  
2479 -  
2480 -  
2481 -public define Printable_tree  
2482 - format_choices  
2483 - (  
2484 - List((String,String)) l  
2485 - ) =  
2486 - if l is  
2487 - {  
2488 - [ ] then [ ],  
2489 - [h . t] then  
2490 - if h is (val,item)  
2491 - then ["<option value=\""+val+"\">",item . format_choices(t)]  
2492 - }.  
2493 -  
2494 -public define Printable_tree  
2495 - format_choices  
2496 - (  
2497 - List((String,String)) l,  
2498 - String selected  
2499 - ) =  
2500 - if l is  
2501 - {  
2502 - [ ] then [ ],  
2503 - [h . t] then  
2504 - if h is (val,item) then  
2505 - if item = selected  
2506 - then ["<option value=\""+val+"\" selected>",item . format_choices(t)]  
2507 - else ["<option value=\""+val+"\">",item . format_choices(t,selected)]  
2508 - }.  
2509 -  
2510 -  
2511 -  
2512 -variable Int32 count = 0.  
2513 -  
2514 - Note: this counter is private to the virtual machine, hence there is one counter by  
2515 - client.  
2516 -  
2517 -define Int32  
2518 - new_count  
2519 - =  
2520 - count <- *count+1;  
2521 - *count.  
2522 -  
2523 -  
2524 - The next function composes the URL. It is a JavaScript URL when the target is another  
2525 - window.  
2526 -  
2527 -define String  
2528 - make_actioner_url  
2529 - (  
2530 - CommonInfo cinfo,  
2531 - Actioner_Connection connection,  
2532 - Actioner_Target target,  
2533 - String state_name,  
2534 - String action_name,  
2535 - List((String,String)) extra_ops,  
2536 - Bool is_https  
2537 - ) =  
2538 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
2539 - with strict_url =  
2540 - if connection is  
2541 - {  
2542 - same then "/",  
2543 - /*  
2544 - same then if is_https  
2545 - then "https://"+common_name+":"+https_port+"/"  
2546 - else "http://"+common_name+":"+https_port+"/",  
2547 - */  
2548 -  
2549 - http then "http://"+common_name+":"+http_port+"/",  
2550 - https then "https://"+common_name+":"+https_port+"/",  
2551 - } +  
2552 - "?s=" + state_name + "&amp;a=" + action_name +  
2553 - format_extra_operands(extra_ops),  
2554 - if target is  
2555 - {  
2556 - same then strict_url,  
2557 - same(label) then strict_url+"#"+label,  
2558 - other(wn,ops) then  
2559 - "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+  
2560 - "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"  
2561 - }.  
2562 -  
2563 -  
2564 - Depending on the fact that the actioner refers to a form or not, the URL is used in two  
2565 - different ways. If the actioner does not refer to a form, it is realized by a '<a>'  
2566 - tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'  
2567 - tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'  
2568 - event handler. The handler calls a JavaScript function which puts the URL as the value  
2569 - of the 'action' attribute of the form, and submits the form.  
2570 -  
2571 -type URL_or_JavaScript:  
2572 - url (String),  
2573 - javascript (Printable_tree script, Printable_tree handler).  
2574 -  
2575 - define URL_or_JavaScript  
2576 - format_action  
2577 - (  
2578 - String the_url,  
2579 - Maybe(String) mb_form_name  
2580 - ) =  
2581 - url(the_url).  
2582 -  
2583 -  
2584 - It was as shown below, in order to allow submission of a form from outside the form,  
2585 - but this makes problems:  
2586 -  
2587 -define URL_or_JavaScript  
2588 - format_action  
2589 - (  
2590 - String the_url,  
2591 - Maybe(String) mb_form_name  
2592 - ) =  
2593 - if mb_form_name is  
2594 - {  
2595 - failure then  
2596 - url(the_url),  
2597 - success(form_name) then  
2598 - with n = new_count,  
2599 - javascript(  
2600 - [  
2601 - "<script type=\"text/javascript\" language =\"JavaScript\">\n",  
2602 - "function pfu",form_name,n,"() {\n",  
2603 - " var fa = document.forms.f",form_name,";\n",  
2604 - " var u = \"",the_url,"\";\n",  
2605 - " fa.action = u;\n",  
2606 - " fa.submit(); }",  
2607 - "</script>"  
2608 - ],  
2609 - ["pfu",form_name,n,"();"]  
2610 - )  
2611 - }.  
2612 -  
2613 -  
2614 -  
2615 - Now, we format the actioner according to its aspect.  
2616 -  
2617 -define List(Text_Option)  
2618 - get_text_options  
2619 - (  
2620 - List(Text_Option) l  
2621 - )  
2622 - =  
2623 - if l is  
2624 - {  
2625 - [] then [],  
2626 - [h . t ] then  
2627 - if h is class(class) then  
2628 - get_text_options(t)  
2629 - else  
2630 - [ h . get_text_options(t) ]  
2631 -  
2632 - }.  
2633 -  
2634 - /**  
2635 - * Extract the CSS class list from the list of Text_Option  
2636 - */  
2637 -define List(Text_Option)  
2638 - get_css_class  
2639 - (  
2640 - List(Text_Option) l  
2641 - )  
2642 - =  
2643 - if l is  
2644 - {  
2645 - [] then [],  
2646 - [h . t ] then  
2647 -  
2648 -  
2649 - if h is class(class) then  
2650 - [ h . get_css_class(t) ]  
2651 - else  
2652 - get_css_class(t)  
2653 - }  
2654 - .  
2655 -  
2656 -define String  
2657 - format_text_options  
2658 - (  
2659 - List(Text_Option) l  
2660 - )  
2661 - =  
2662 - with text_options = get_text_options(l),  
2663 - css_classes = get_css_class(l),  
2664 - if text_options is  
2665 - {  
2666 - [] then "",  
2667 - [_._] then " style=\"" + format(text_options) + "\" "  
2668 - }  
2669 - +  
2670 - if css_classes is  
2671 - {  
2672 - [] then "",  
2673 - [_._] then format(css_classes)  
2674 - }.  
2675 -  
2676 -define Printable_tree  
2677 - format  
2678 - (  
2679 - List(Actioner_Local_Action) l  
2680 - ) =  
2681 - if l is  
2682 - {  
2683 - [ ] then [ ],  
2684 - [h . t] then [if h is  
2685 - {  
2686 - close_window then [" window.close(); "]  
2687 - }  
2688 - . format(t)]  
2689 - }.  
2690 -  
2691 -define String  
2692 - format_coreattrs  
2693 - (  
2694 - List(CoreAttrs) attributs  
2695 - )=  
2696 - if attributs is  
2697 - {  
2698 - [] then "",  
2699 - [h .t] then  
2700 - with current = if h is  
2701 - {  
2702 - id(id_name) then  
2703 - " id=\"" + id_name + "\"",  
2704 - class(class_name) then  
2705 - " class=\"" + class_name + "\"",  
2706 - style(style_string) then  
2707 - " style=\"" + style_string + "\"",  
2708 -  
2709 - title(title_string) then  
2710 - " title=\"" + title_string + "\"",  
2711 - },  
2712 - current + format_coreattrs(t)  
2713 - }.  
2714 -  
2715 -define String  
2716 - _format  
2717 - (  
2718 - List(DIV_Option) opt  
2719 - )=  
2720 - if opt is  
2721 - {  
2722 - [] then "",  
2723 - [h .t] then  
2724 - with current = if h is  
2725 - {  
2726 - id(id_name) then  
2727 - " id=\"" + id_name + "\"",  
2728 - class(class_name) then  
2729 - " class=\"" + class_name + "\"",  
2730 - style(style_string) then  
2731 - " style=\"" + style_string + "\"",  
2732 -  
2733 - title(title_string) then  
2734 - " title=\"" + title_string + "\"",  
2735 - lang(lang) then  
2736 - " xml:lang=" + lang,  
2737 - dir(reading_Way) then  
2738 - if reading_Way is  
2739 - {  
2740 - ltr then " dir=ltr",  
2741 - rtl then " dir=rtl"  
2742 - }  
2743 - },  
2744 - current + _format(t)  
2745 - }  
2746 - .  
2747 -  
2748 -define Printable_tree  
2749 - format_div_option  
2750 - (  
2751 - List(DIV_Option) options  
2752 - )=  
2753 - ["<DIV" + _format(options) + ">"] .  
2754 -  
2755 -  
2756 -define Printable_tree  
2757 - format_actioner  
2758 - (  
2759 - CommonInfo cinfo,  
2760 - String state_name,  
2761 - Actioner_Connection connection,  
2762 - Actioner_Target target,  
2763 - Actioner_Aspect aspect,  
2764 - String action_name,  
2765 - List((String,String)) extra_ops,  
2766 - List(Actioner_Local_Action) local_actions,  
2767 - Maybe(String) mb_form_name,  
2768 - Bool is_https,  
2769 - ) =  
2770 - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then  
2771 - with url = make_actioner_url(cinfo,connection,target,  
2772 - state_name,action_name,extra_ops,is_https),  
2773 - with action = format_action(url,mb_form_name),  
2774 - if aspect is  
2775 - {  
2776 -  
2777 - link(opt, text) then [  
2778 - if action is  
2779 - {  
2780 - url(u) then  
2781 - ["<a href=\"",u,"\"", format_text_options(opt), ">"],  
2782 -// ["<a href=\"",u,"\" style=\"",format(reverse(opt)),"\">"],  
2783 - javascript(s,h) then  
2784 - [s,"<a href=\"javascript: ",h,"\">"]  
2785 - },  
2786 - text,  
2787 - "</a>"  
2788 - ],  
2789 -  
2790 - push_button(options, text) then  
2791 - [  
2792 - if action is  
2793 - {  
2794 - url(u) then ["<a href=\"",u,"\"", format_coreattrs(options), ">",  
2795 - text, "</a>"  
2796 - ],  
2797 - javascript(s,h) then  
2798 - [s,"<INPUT TYPE =\"button\" Value=\"",text,"\"", format_coreattrs(options), " onClick=\"",h,"\">"]  
2799 - }  
2800 - ],  
2801 -  
2802 - button(url_off,url_on) then  
2803 - [ if action is  
2804 - {  
2805 - url(u) then ["<a href=\"",u],  
2806 - javascript(s,h) then [s,"<a onMouseDown=\"",h]  
2807 - },  
2808 - "\" style=\"text-decoration:none\">",  
2809 - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",  
2810 - " onMouseOver=\"this.src='",url_on,"'\" ",  
2811 - " onMouseOut=\"this.src='",url_off,"'\">",  
2812 - "</a>"  
2813 - ],  
2814 -  
2815 - button(url_off,url_on,w,h) then  
2816 - [ if action is  
2817 - {  
2818 - url(u) then ["<a href=\"",u],  
2819 - javascript(s,h) then [s,"<a onMouseDown=\"",h]  
2820 - },  
2821 - "\" style=\"text-decoration:none\">",  
2822 - "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",  
2823 - " onMouseOver=\"this.src='",url_on,"'\" ",  
2824 - " onMouseOut=\"this.src='",url_off,"'\">",  
2825 - "</a>"  
2826 - ],  
2827 -  
2828 - immediate_selector(name,size,choices) then  
2829 - [ if action is  
2830 - {  
2831 - url(u) then ["<select href=\"",u]  
2832 - javascript(s,h) then [s,"<select onchange=\"",h]  
2833 - },  
2834 - "\" name=o",name," size=",size,">",  
2835 - format_choices(choices),"</select>"  
2836 - ]  
2837 - }.  
2838 -  
2839 -  
2840 -define Printable_tree  
2841 - format_local_popup_button  
2842 - (  
2843 - CommonInfo cinfo,  
2844 - Actioner_Aspect aspect,  
2845 - Int32 n,  
2846 - ) =  
2847 - if cinfo is info(common_name,http_port,https_port,site_dir,secret) then  
2848 - [ "<script type=\"text/javascript\" language=\"JavaScript\">",  
2849 - " var lpust_",n," = new Array(); ",  
2850 - " lpust_",n,"[0] = 0; ",  
2851 - "</script>",  
2852 - "<a href=\"javascript:show_local_popup('lpu_",n,"','lpust_",n,"');\">",  
2853 - if aspect is  
2854 - {  
2855 - link(opt,text) then [text],  
2856 - push_button(opt, text) then [text],  
2857 - button(url_off,url_on) then  
2858 - [  
2859 - "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",  
2860 - " onMouseOver=\"this.src='",url_on,"'\" ",  
2861 - " onMouseOut=\"this.src='",url_off,"'\">",  
2862 - ],  
2863 -  
2864 - button(url_off,url_on,w,h) then  
2865 - [  
2866 - "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",  
2867 - " onMouseOver=\"this.src='",url_on,"'\" ",  
2868 - " onMouseOut=\"this.src='",url_off,"'\">",  
2869 - ],  
2870 -  
2871 - immediate_selector(name,size,choices) then alert,  
2872 -  
2873 - },  
2874 - "</a>"].  
2875 -  
2876 -  
2877 -  
2878 -  
2879 -  
2880 - *** [5.5] Formating a private download link.  
2881 -  
2882 - We get the absolute path of the file to be downloaded, and the name under which it  
2883 - should appear to the client. The function 'format_private_download' creates an  
2884 - hypertext link for downloading the file. The secured mecanism of private download is  
2885 - used. This function is called by the function which formats HTML_Any($T) elements.  
2886 -  
2887 -  
2888 -define Printable_tree  
2889 - format_private_download  
2890 - (  
2891 - CommonInfo cinfo,  
2892 - String sn, // state name  
2893 - String abs_path, // absolute file path on server  
2894 - String name, // name of file as it appears in the browser  
2895 - String extra, // extra extension  
2896 - Maybe((String,List((String,String)))) action  
2897 -  
2898 - ) =  
2899 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
2900 - with private_download_directory = site_directory+"/private_download",  
2901 - with auth = make_authorization(site_directory,secret,abs_path),  
2902 - [  
2903 - "<a href=\"",name,extra,"?zauth=",auth,  
2904 - if action is  
2905 - {  
2906 - failure then [ ]  
2907 - success(a) then if a is (an,args) then  
2908 - ["&amp;a=",an,format_extra_operands(args)]  
2909 - },  
2910 - "\">",  
2911 - name,  
2912 - "</a>"  
2913 - ].  
2914 -  
2915 -  
2916 -  
2917 -  
2918 - *** [5.6] Formating rows and cells in a table.  
2919 -  
2920 -define Int32  
2921 - percent  
2922 - (  
2923 - Int32 p  
2924 - ) =  
2925 - if p < 0 then 0 else if p > 100 then 100 else p.  
2926 -  
2927 -  
2928 -  
2929 -  
2930 - Formating cell options.  
2931 -  
2932 -  
2933 -  
2934 -define String  
2935 - format  
2936 - (  
2937 - BackgroundOption o  
2938 - ) =  
2939 - if o is  
2940 - {  
2941 - repeat then "",  
2942 - repeat_horizontal then "; background-repeat: repeat-x",  
2943 - repeat_vertical then "; background-repeat: repeat-y",  
2944 - no_repeat then "; background-repeat: no-repeat",  
2945 - center then "; background-position: center top"  
2946 - }.  
2947 -  
2948 -define String  
2949 - format  
2950 - (  
2951 - List(BackgroundOption) l  
2952 - ) =  
2953 - if l is  
2954 - {  
2955 - [ ] then "",  
2956 - [h . t] then format(h)+format(t)  
2957 - }.  
2958 -  
2959 -  
2960 -define String  
2961 - format  
2962 - (  
2963 - List(Cell_Option) options  
2964 - ) =  
2965 - if options is  
2966 - {  
2967 - [ ] then "",  
2968 - [h . t] then  
2969 - if h is  
2970 - {  
2971 - left then " align=left",  
2972 - h_center then " align=center",  
2973 - right then " align=right",  
2974 - top then " valign=top",  
2975 - v_center then " valign=middle",  
2976 - bottom then " valign=bottom",  
2977 - base_line then " valign=baseline",  
2978 - background_color(c) then " bgcolor=\""+html_format(c)+"\"",  
2979 - background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",  
2980 - width(w) then " width=\""+w+"\"",  
2981 - percentage_width(n) then " width=\""+percent(n)+"%\"",  
2982 - height(h) then " height="+h,  
2983 - columns(n) then " colspan="+n,  
2984 - rows(n) then " rowspan="+n,  
2985 - nowrap then " nowrap"  
2986 - }  
2987 - + format(t)  
2988 - }.  
2989 -  
2990 -  
2991 - Normalizing a list of cell options (horizontal position must be specified; the default  
2992 - is 'left').  
2993 -  
2994 -define List(Cell_Option)  
2995 - normalize  
2996 - (  
2997 - List(Cell_Option) l  
2998 - ) =  
2999 - if member(l,left) then l else  
3000 - if member(l,h_center) then l else  
3001 - if member(l,right) then l else  
3002 - [left . l].  
3003 -  
3004 -  
3005 - Formating cells in a row.  
3006 -  
3007 -define Printable_tree  
3008 - format  
3009 - (  
3010 - List(HTML_Cell($T)) cells,  
3011 - $T -> Printable_tree format_element  
3012 - ) =  
3013 - if cells is  
3014 - {  
3015 - [ ] then [ ],  
3016 - [h . t] then if h is cell(options,element) then  
3017 - ["<td ",format(reverse(normalize(options))),">",  
3018 - format_element(element),  
3019 - "</td>"  
3020 - . format(t,format_element)]  
3021 - }.  
3022 -  
3023 -  
3024 - Formating the rows in a table.  
3025 -  
3026 -define Printable_tree  
3027 - format  
3028 - (  
3029 - List(HTML_Row($T)) rows,  
3030 - $T -> Printable_tree format_element,  
3031 - ) =  
3032 - if rows is  
3033 - {  
3034 - [ ] then [ ],  
3035 - [h . t] then if h is row(options,cells) then  
3036 - ["<tr ",format(reverse(options)),">",  
3037 - format(cells,format_element),  
3038 - "</tr>"  
3039 - . format(t,format_element)]  
3040 - }.  
3041 -  
3042 -  
3043 -  
3044 -define Printable_tree  
3045 - format1  
3046 - (  
3047 - List(TextAreaOption) l  
3048 - ) =  
3049 - if l is  
3050 - {  
3051 - [] then [],  
3052 - [h . t] then if h is  
3053 - {  
3054 - disabled then [" disabled " . format1(t)]  
3055 - read_only then [" readonly " . format1(t)]  
3056 - wrap_lines then [" wrap " . format1(t)]  
3057 - }  
3058 - }.  
3059 -  
3060 -define Printable_tree  
3061 - format  
3062 - (  
3063 - List(TextAreaOption) l  
3064 - ) =  
3065 - if member(l,wrap_lines)  
3066 - then format1(l)  
3067 - else [" wrap=off " . format1(l)].  
3068 -  
3069 -  
3070 - *** [5.7] Formating elements which may be put anywhere.  
3071 -  
3072 - The function below involves the parameter $T which is later instantiated as  
3073 - 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions  
3074 - for these two types, and because formating of tables requires recursive calls of such  
3075 - functions, it is necessary to provide the 'format' function to be called recursively as  
3076 - an argument. Putting naively a call to 'format' will not work, because the compiler  
3077 - will look for a function able to format data of type $T (which is at that time distinct  
3078 - from any other type, including our two types). Such a function does not exist. Hence  
3079 - the function to be called for formating elements must be passed as a functional  
3080 - argument (called 'format_element' below). Actually, what we pass is a function taking  
3081 - a unique argument of type $T. Other informations (like the name of the state) are  
3082 - already in the function by way of full functionality.  
3083 -  
3084 -  
3085 - Formating text options. They are formated in CSS syntax, to be used within a  
3086 - 'style=...'.  
3087 -  
3088 -define String  
3089 - format  
3090 - (  
3091 - List(Text_Option) l  
3092 - ) =  
3093 - if l is  
3094 - {  
3095 - [ ] then "",  
3096 - [h . t] then if h is  
3097 - {  
3098 - size(n) then "font-size:"+n+"pt",  
3099 - font(fn) then "font-family:"+fn,  
3100 - color(c) then if c is rgb(r,g,b) then  
3101 - "color:rgb("+word8_to_int32(r)+","+word8_to_int32(g)+","+word8_to_int32(b)+")",  
3102 - italic then "font-style:italic",  
3103 - oblique then "font-style:oblique",  
3104 - small_capitals then "font-variant:small-caps",  
3105 - bold then "font-weight:bold",  
3106 - underlined then "text-decoration:underline",  
3107 - left_justified then "text-align:left",  
3108 - right_justified then "text-align:right",  
3109 - justified then "text-align:justify",  
3110 - line_through then "text-decoration:line-through",  
3111 - nowrap then "white-space:nowrap",  
3112 - class(class_name)then " class=\"" +class_name +"\""  
3113 - } + if t is [ ] then "" else ("; "+format(t))  
3114 - }.  
3115 -  
3116 -  
3117 -  
3118 - Formating table options.  
3119 -  
3120 -define String  
3121 - format  
3122 - (  
3123 - List(Table_Option) l,  
3124 - Bool border_seen  
3125 - ) =  
3126 - if l is  
3127 - {  
3128 - [ ] then if border_seen then "" else " border=0 cellspacing=0 cellpadding=0",  
3129 - [h . t] then if h is  
3130 - {  
3131 - background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),  
3132 - background_image(url) then " background="+url+format(t,border_seen),  
3133 - border(o,top,i,c) then " border="+o+" cellspacing="+top+" cellpadding="+i+  
3134 - //" bordercolor="+format(c)+  
3135 - format(t,true),  
3136 - width(w) then " width=\""+w+"\""+format(t,border_seen),  
3137 - percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),  
3138 - }  
3139 - }.  
3140 -  
3141 -  
3142 -  
3143 -  
3144 -  
3145 -define Printable_tree  
3146 - format_scroller  
3147 - (  
3148 - String sn,  
3149 - Int32 width,  
3150 - Int32 height,  
3151 - Int32 content_width,  
3152 - Int32 content_height,  
3153 - Int32 idnum, // identifying the scroller  
3154 - $T content,  
3155 - $T -> Printable_tree format_element  
3156 - ) =  
3157 - [  
3158 - "<script type = \"text/javascript\" language=\"JavaScript\">",  
3159 - "function doscroll_",idnum,"(dx,dy) {\n",  
3160 - " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",  
3161 - " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",  
3162 - idnum,"').style\"); } else\n",  
3163 - " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",  
3164 - " var x_",idnum," = parseInt(c_",idnum,".left);\n",  
3165 - " var y_",idnum," = parseInt(c_",idnum,".top);\n",  
3166 - " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",  
3167 - " { x_",idnum," += dx; }\n",  
3168 - " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",  
3169 - " { y_",idnum," += dy; }\n",  
3170 - " c_",idnum,".left = x_",idnum,";\n",  
3171 - " c_",idnum,".top = y_",idnum,";\n",  
3172 - " }\n",  
3173 - "</script>",  
3174 - "<table>",  
3175 - "<tr>",  
3176 - "<td align=left valign=top",  
3177 - " width=",width,  
3178 - " height=",height,  
3179 - ">",  
3180 - "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",  
3181 - " clip:rect(0px ",width,"px ",height,"px 0px)\">",  
3182 - "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",  
3183 - format_element(content),  
3184 - "</div>",  
3185 - "</div>",  
3186 - "</td>",  
3187 - "<td valign=bottom>",  
3188 - "<table>",  
3189 - "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onMouseDown=\"doscroll_",  
3190 - idnum,"(0,20);\"></td></tr>",  
3191 - "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onMouseDown=\"doscroll_",  
3192 - idnum,"(0,-20);\"></td></tr>",  
3193 - "</table>",  
3194 - "</td>",  
3195 - "</tr>",  
3196 - (if content_width > width then  
3197 - [  
3198 - "<tr>",  
3199 - "<td align=right>",  
3200 - "<table>",  
3201 - "<tr>",  
3202 - "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onMouseDown=\"doscroll_",  
3203 - idnum,"(20,0);\"></td>",  
3204 - "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onMouseDown=\"doscroll_",  
3205 - idnum,"(-20,0);\"></td>",  
3206 - "</tr>",  
3207 - "</table>",  
3208 - "</td>",  
3209 - "</tr>",  
3210 - ] else [ ]),  
3211 - "</table>"  
3212 - ].  
3213 -  
3214 -  
3215 -  
3216 - define Printable_tree  
3217 - popup_topbar  
3218 - (  
3219 - CommonInfo cinfo,  
3220 - String title,  
3221 - RGB color,  
3222 - Int32 width,  
3223 - ) =  
3224 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3225 - with xxxx = web_arg_encode(sha1((title,color,width))),  
3226 - path = site_directory+"/public/buttons/t"+xxxx+".jpg",  
3227 - result = (Printable_tree)["<img alt=\"button\" src=\"buttons/t"+xxxx+".jpg\">"],  
3228 - if file_exists(path) then result else  
3229 - with col = to_rgba(color),  
3230 - bg = create_button_background(col,width,20),  
3231 - very_light_color = lighten(col,70),  
3232 - dark_color = darken(col,40),  
3233 - title_width = printed_text_width(font,title),  
3234 - draw_button_text(bg,title,title_width,very_light_color,dark_color,font);  
3235 - forget(write_image_to_JPEG_file(to_JPEG(bg),path,100));  
3236 - result.  
3237 -  
3238 -  
3239 - define Printable_tree  
3240 - popup_close_button  
3241 - (  
3242 - CommonInfo cinfo,  
3243 - RGB color,  
3244 - String div_name,  
3245 - String state_var_name,  
3246 - ) =  
3247 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3248 - with xxxx = web_arg_encode(sha1(color)),  
3249 - path_on = site_directory+"/public/buttons/c"+xxxx+"_on.jpg",  
3250 - path_off = site_directory+"/public/buttons/c"+xxxx+"_off.jpg",  
3251 - result = (Printable_tree)["<img alt=\"button\" src=\"buttons/c"+xxxx+"_off.jpg\"",  
3252 - " onMouseOver=\"this.src='buttons/c"+xxxx+"_on.jpg'\"",  
3253 - " onMouseOut=\"this.src='buttons/c"+xxxx+"_off.jpg'\"",  
3254 - " onMouseDown=\"show_local_popup('",div_name,"','",state_var_name,"')\">"],  
3255 - if file_exists(path_on) then result else  
3256 - with col = to_rgba(color),  
3257 - title = "x",  
3258 - title_width = printed_text_width(font,title),  
3259 - bg_on = create_button_background(lighten(col,30),20,20),  
3260 - bg_off = create_button_background(col,20,20),  
3261 - very_light_color = lighten(col,70),  
3262 - dark_color = darken(col,40),  
3263 - draw_button_text(bg_on,title,title_width,very_light_color,dark_color,font);  
3264 - draw_button_text(bg_off,title,title_width,very_light_color,dark_color,font);  
3265 - forget(write_image_to_JPEG_file(to_JPEG(bg_on),path_on,100));  
3266 - forget(write_image_to_JPEG_file(to_JPEG(bg_off),path_off,100));  
3267 - result.  
3268 -  
3269 -  
3270 -  
3271 -  
3272 -  
3273 - // The function below formats a datum of type 'HTML_Any($T)'.  
3274 -  
3275 -define Printable_tree  
3276 - format  
3277 - (  
3278 - CommonInfo cinfo,  
3279 - String sn, // state_name  
3280 - Var(Int32) ic_v,  
3281 - HTML_Any($T) element,  
3282 - $T -> Printable_tree format_element, // able to format a datum of type $T  
3283 - Bool is_https,  
3284 - ) =  
3285 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3286 - if element is  
3287 - {  
3288 - any_text(opts,t) then  
3289 - ["<span ", format_text_options(opts), ">",t,"</span>"],  
3290 - any_preformated(opts,s) then  
3291 - ["<span ", format_text_options(opts), "><pre>",s,"</pre></span>"],  
3292 - //["<pre>",s,"</pre>"],  
3293 - any_paragraph(opts,t) then  
3294 - ["<p ", format_text_options(opts), ">",t,"</p>"],  
3295 - any_image(url) then  
3296 - ["<img alt=\"",url,"\" src=\"",url,"\">"],  
3297 - any_image(url,w,h) then  
3298 - ["<img alt=\"",url,"\" src=\"",url,"\" width=",w," height=",h,">"],  
3299 - any_table(opts,rows) then  
3300 - ["<table ",format(reverse(opts),false),">",format(rows,format_element),"</table>"],  
3301 - any_center(e) then  
3302 - ["<center>",format_element(e),"</center>"],  
3303 - any_mail_to(email,elem) then  
3304 - ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],  
3305 - any_scroller(w,h,cw,ch,c) then  
3306 - format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),  
3307 - any_fixed_size(w,h,c) then  
3308 - with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),  
3309 - ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",  
3310 - "secondary document",  
3311 - "</object>"],  
3312 - any_fixed_size_2(w,h,fn) then  
3313 - with url = fn+"?zauth="+make_authorization(site_directory,secret,  
3314 - fn),  
3315 - ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",  
3316 - "secondary document",  
3317 - "</object>"],  
3318 - any_actioner(c,t,a,an,eo,ja,fn) then  
3319 - format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https),  
3320 - any_foreign_link(options,url,name) then  
3321 - ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],  
3322 - any_private_download(url,name,extra_ext,action) then  
3323 - format_private_download(cinfo,sn,url,name,extra_ext,action),  
3324 - any_div(options, e) then  
3325 - [format_div_option(options), format_element(e),"</DIV>"],  
3326 - any_div_empty(options) then  
3327 - [format_div_option(options), "</DIV>"],  
3328 - any_coreattrs(attributs) then  
3329 - [format_coreattrs(attributs)]  
3330 - }.  
3331 -  
3332 -  
3333 -  
3334 -  
3335 - // *** [5.8] Formating 'in form' elements.  
3336 -  
3337 -  
3338 -  
3339 -  
3340 -  
3341 -define Printable_tree  
3342 - format  
3343 - (  
3344 - CommonInfo cinfo,  
3345 - String fn, // form_name  
3346 - String sn, // state_name  
3347 - Var(Int32) ic_v, // idnum counter variable  
3348 - HTML_In_Form element,  
3349 - Bool is_https,  
3350 - ) =  
3351 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3352 - with format_element = (HTML_In_Form e) |-> format(cinfo,fn,sn,ic_v,e,is_https),  
3353 - if element is  
3354 - {  
3355 - literal_pt(t) then t,  
3356 - literal(t) then [t],  
3357 - sequence(l) then flat(map(format_element,l))  
3358 - text(opts,t) then  
3359 - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),  
3360 - preformated(o,s) then  
3361 - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),  
3362 - paragraph(opts,t) then  
3363 - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),  
3364 - image(url) then  
3365 - format(cinfo,sn,ic_v,any_image(url),format_element,is_https),  
3366 - image(url,w,h) then  
3367 - format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),  
3368 - table(opts,rows) then  
3369 - format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),  
3370 - center(e) then  
3371 - format(cinfo,sn,ic_v,any_center(e),format_element,is_https),  
3372 - mail_to(a,e) then  
3373 - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),  
3374 - scroller(w,h,cw,ch,c) then  
3375 - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),  
3376 - actioner(c,t,a,an,eo,ja) then  
3377 - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),  
3378 - foreign_link(options,url,name) then  
3379 - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),  
3380 - private_download(url,name,extra,action) then  
3381 - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),  
3382 - text_input(label_text, label, name,i,w) then  
3383 - [ "<label for=\"",label,"\">",label_text,"</label>",  
3384 - "<input type=text name=o",name," id=",label," size=",w," value=\"",i,"\">"],  
3385 - //["&nbsp; <input type=text name=o",n," size=",w," value=\"",i,"\">"],  
3386 - password_input(label_text, label, name,w) then  
3387 - [ "<label for=\"",label,"\">",label_text,"</label>",  
3388 - "<input type=password name=p",name," id=",label," size=",w,">"],  
3389 - //["&nbsp; <input type=password name=p",n," size=",w,">"],  
3390 - text_area(opts,n,i,w,h) then  
3391 - ["<textarea ",format(opts)," name=o",n," cols=",w," rows=",h,">",i,"</textarea>"],  
3392 - file_upload(n,w) then  
3393 - ["<input type=file size=",w," name=o",n,">"],  
3394 - selector(n,s,cs) then  
3395 - ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],  
3396 - selector(n,s,cs,sd) then  
3397 - ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],  
3398 - selector_c(n,s,cs) then  
3399 - ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],  
3400 - selector_c(n,s,cs,sd) then  
3401 - ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],  
3402 -  
3403 - radio_button(label_text,label,n,v,c) then  
3404 - [ "<label for=\"",label,"\">",label_text,"</label>",  
3405 - "<input type=radio name=o",n," id=",label," value=\"",v,"\"",(if c then " checked" else ""),">"],  
3406 - check_box(label_text, label,n,c) then  
3407 - [ "<label for=\"",label,"\">",label_text,"</label>",  
3408 - "<input type=checkbox name=o",n," id=\"",label,"\""+(if c then " checked " else ""),">"]  
3409 - div(options, e) then  
3410 - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),  
3411 - div_empty(options) then  
3412 - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),  
3413 - hidden(name, value) then  
3414 - ["<input type=hidden name=o",name," value=\"",value,"\">"],  
3415 -  
3416 - }.  
3417 -  
3418 -  
3419 -  
3420 -  
3421 -  
3422 -  
3423 - *** [5.9] Formating 'off form' elements.  
3424 -  
3425 - The encryption type 'multipart/form-data' is required for a form containing an upload.  
3426 -  
3427 -  
3428 -define Bool  
3429 - contains_an_upload  
3430 - (  
3431 - HTML_In_Form form_content  
3432 - ).  
3433 -  
3434 -define Bool  
3435 - contains_an_upload  
3436 - (  
3437 - HTML_Row(HTML_In_Form) row  
3438 - ) =  
3439 - mapor(contains_an_upload,  
3440 - map(content,cells(row))).  
3441 -  
3442 -  
3443 -define Bool  
3444 - contains_an_upload  
3445 - (  
3446 - HTML_In_Form form_content  
3447 - ) =  
3448 - if form_content is  
3449 - {  
3450 - literal_pt(t) then false,  
3451 - literal(t) then false,  
3452 - sequence(l) then mapor(contains_an_upload,l)  
3453 - text(o,t) then false,  
3454 - preformated(o,s) then false,  
3455 - paragraph(o,t) then false,  
3456 - image(u) then false,  
3457 - image(u,w,h) then false,  
3458 - table(o,rows) then mapor(contains_an_upload,rows),  
3459 - center(e) then contains_an_upload(e),  
3460 - mail_to(m,e) then false, // 'e' may but should not contain an upload  
3461 - scroller(w,h,cw,ch,e) then contains_an_upload(e),  
3462 - actioner(c,t,a,an,eo,ja) then false,  
3463 - foreign_link(o,u,n) then false,  
3464 - private_download(p,n,e,a) then false,  
3465 - text_input(lt,l,n,i,w) then false,  
3466 - password_input(lt,l,n,w) then false,  
3467 - text_area(o,n,i,w,h) then false,  
3468 - file_upload(n,w) then true,  
3469 - selector(n,s,c) then false,  
3470 - selector(n,s,c,p) then false,  
3471 - selector_c(n,s,c) then false,  
3472 - selector_c(n,s,c,p) then false,  
3473 - radio_button(_,_,n,v,c) then false,  
3474 - check_box(_,_,n,c) then false,  
3475 - div(o,c) then false,  
3476 - div_empty(o) then false,  
3477 - hidden(_,_) then false  
3478 - }.  
3479 -  
3480 -define String  
3481 - enctype  
3482 - (  
3483 - HTML_In_Form form_content  
3484 - ) =  
3485 - if contains_an_upload(form_content)  
3486 - then " enctype=multipart/form-data"  
3487 - else "".  
3488 -  
3489 -  
3490 -  
3491 -define Printable_tree  
3492 - format  
3493 - (  
3494 - CommonInfo cinfo,  
3495 - String sn, // state_name  
3496 - Var(Int32) ic_v, // 'idnum' counter variable  
3497 - HTML_Off_Form element,  
3498 - Bool is_https,  
3499 - ) =  
3500 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3501 - with format_element = (HTML_Off_Form e) |-> format(cinfo,sn,ic_v,e,is_https),  
3502 - if element is  
3503 - {  
3504 - literal_pt(t) then t,  
3505 - literal(t) then [t],  
3506 - sequence(l) then flat(map(format_element,l)),  
3507 - text(opts,t) then  
3508 - format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),  
3509 - preformated(o,s) then  
3510 - format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),  
3511 - paragraph(opts,t) then  
3512 - format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),  
3513 - image(url) then  
3514 - format(cinfo,sn,ic_v,any_image(url),format_element,is_https),  
3515 - image(url,w,h) then  
3516 - format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),  
3517 - table(opts,rows) then  
3518 - format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),  
3519 - center(e) then  
3520 - format(cinfo,sn,ic_v,any_center(e),format_element,is_https),  
3521 - mail_to(a,e) then  
3522 - format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),  
3523 - scroller(w,h,cw,ch,c) then  
3524 - format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),  
3525 - fixed_size(w,h,c) then  
3526 - format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https),  
3527 - fixed_size_2(w,h,fn) then  
3528 - format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https),  
3529 - actioner(c,t,a,an,eo,ja) then  
3530 - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https),  
3531 - actioner(c,t,a,an,eo,ja,fn) then  
3532 - format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),  
3533 - foreign_link(options,url,name) then  
3534 - format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),  
3535 - private_download(url,name,extra,action) then  
3536 - format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),  
3537 - label(n) then ["<a name=\"",n,"\">"],  
3538 - form(fn,attributs, c) then  
3539 - [  
3540 - "<form name=\"f",fn,"\"",  
3541 - format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),  
3542 - " method=POST",  
3543 - enctype(c),  
3544 - " action=\"http",  
3545 - if is_https then "s" else "",  
3546 - "://",common_name,":",http_port,"/\">",  
3547 - // action is set dynamically by  
3548 - // the actioner using JavaScript  
3549 - format(cinfo,fn,sn,ic_v,c,is_https),  
3550 - "</form>"  
3551 - ]  
3552 - div(options, e) then  
3553 - format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),  
3554 - div_empty(options) then  
3555 - format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),  
3556 -  
3557 - }.  
3558 -  
3559 -  
3560 -  
3561 -  
3562 - *** [5.10] Formating meta-tags.  
3563 -  
3564 -define Printable_tree  
3565 - format_keywords  
3566 - (  
3567 - List(String) l  
3568 - ) =  
3569 - if l is  
3570 - {  
3571 - [] then [ ],  
3572 - [h . t] then if t is []  
3573 - then [h]  
3574 - else [h , ", " . format_keywords(t)]  
3575 - }.  
3576 -  
3577 -  
3578 -define Printable_tree  
3579 - format  
3580 - (  
3581 - CommonInfo cinfo,  
3582 - String state_name,  
3583 - HTML_Meta m,  
3584 - Bool is_https  
3585 - ) =  
3586 - if m is  
3587 - {  
3588 - keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\">"],  
3589 - refresh(co,ta,an,delay) then  
3590 - ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",  
3591 - make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\">"],  
3592 - meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\">"],  
3593 - http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\">"],  
3594 - generic_meta(l) then ["<meta ",  
3595 - flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],  
3596 - l)),  
3597 - ">"],  
3598 - literal(s) then [s]  
3599 - }.  
3600 -  
3601 -  
3602 -define Printable_tree  
3603 - format  
3604 - (  
3605 - CommonInfo cinfo,  
3606 - String state_name,  
3607 - List(HTML_Meta) metas,  
3608 - Bool is_https,  
3609 - String charset  
3610 - ) =  
3611 - if metas is  
3612 - {  
3613 - [] then [format(cinfo,state_name,http_equiv("content-type",  
3614 - "text/html; charset="+charset),is_https)],  
3615 - [h . t] then [format(cinfo,state_name,h,is_https)  
3616 - . format(cinfo,state_name,t,is_https,charset)]  
3617 - }.  
3618 -  
3619 -  
3620 -define Printable_tree  
3621 - format  
3622 - (  
3623 - Body_Option o  
3624 - ) =  
3625 - if o is  
3626 - {  
3627 - background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],  
3628 - background_image(n) then [" background=", n],  
3629 - background_image(n,o) then [" style=\"background: url(",n,")",format(o),"\""]  
3630 -  
3631 - }.  
3632 -  
3633 -  
3634 -  
3635 -define Printable_tree  
3636 - format  
3637 - (  
3638 - List(Body_Option) l  
3639 - ) =  
3640 - if l is  
3641 - {  
3642 - [ ] then [ ],  
3643 - [h . t] then [format(h) . format(t)]  
3644 - }.  
3645 -  
3646 -define Printable_tree  
3647 - add_css_files  
3648 - (  
3649 - List(CSS_File) l  
3650 - ) =  
3651 - if l is  
3652 - {  
3653 - [ ] then [ ],  
3654 - [h . t] then  
3655 - [ ["<LINK rel=\"stylesheet\" type=\"text/css\" href=" + file_name(h) + ">\n" ]  
3656 - . add_css_files(t)]  
3657 - }.  
3658 -  
3659 -define Printable_tree  
3660 - add_css_styles  
3661 - (  
3662 - List(CSS_Style) css_styles  
3663 - ) =  
3664 -  
3665 - if css_styles is  
3666 - {  
3667 - [] then [],  
3668 - [_._] then [ "<style type=\"text/css\"><!--\n",  
3669 - format_css_styles(css_styles),  
3670 - " --></style>"  
3671 - ]  
3672 - }.  
3673 -  
3674 -define Printable_tree  
3675 - format  
3676 - (  
3677 - CommonInfo cinfo,  
3678 - String state_name,  
3679 - HTML_Page page,  
3680 - Bool is_https,  
3681 - String charset  
3682 - ) =  
3683 - if cinfo is info(common_name,http_port,https_port,site_directory,secret) then  
3684 - with ic_v = var((Int32)0),  
3685 - if page is  
3686 - {  
3687 - html_page(title,metas,css_styles, css_files, body) then  
3688 - if body is body(options,element) then  
3689 - [ doctype_w3c_header,  
3690 - "<html>",  
3691 - "<head>",  
3692 - add_css_styles(css_styles),  
3693 - add_css_files(css_files),  
3694 - "<link rel=\"shortcut icon\" href=\"favicon.ico\">",  
3695 - "<script type = \"text/javascript\" language=\"JavaScript\">",  
3696 - " function show_local_popup(divname,stvname) {",  
3697 - " if (document.layers) { var d = eval(document.divname); } else\n",  
3698 - " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",  
3699 - " if (document.all) { var d = eval(document.all.divname.style)};\n",  
3700 - // " alert(typeof(eval(stvname))); ",  
3701 - " var s = eval(stvname); ",  
3702 - " if (s[0]==0) ",  
3703 - " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",  
3704 - " { s[0]=0; d.style.visibility = 'hidden'; }; }",  
3705 - "</script>",  
3706 - "<title>",title,"</title>", // put title  
3707 - format(cinfo,state_name,metas,is_https,charset), // format the metas  
3708 - "</head>",  
3709 - "<body ", format(options), ">", // format body options  
3710 - //"<center>",  
3711 - format(cinfo,state_name,ic_v,element,is_https),  
3712 - //"</center>",  
3713 - "</body>",  
3714 - "</html>"  
3715 - ]  
3716 - }.  
3717 -  
3718 -  
3719 -  
3720 -  
3721 -  
3722 - 1 +
  2 +
  3 + *Project* Anubis
  4 +
  5 + *Title* Making interactive Web sites.
  6 +
  7 + *Copyright* Copyright (c) Alain Prouté 2004-2005.
  8 +
  9 +
  10 + *Author* Alain Prouté
  11 +
  12 + *Revised* January 2005.
  13 +
  14 +
  15 + *Overview*
  16 +
  17 + In this file we propose simple tools for making well structured interactive and secured
  18 + web sites.
  19 +
  20 +
  21 + ----------------------------------- Table of Contents ---------------------------------
  22 +
  23 + * (1) Structure of a web site.
  24 + ** (1.1) Three sorts of data.
  25 + ** (1.2) How requests are handled.
  26 + ** (1.3) What web pages are made of.
  27 + ** (1.4) Actions.
  28 + ** (1.5) States.
  29 +
  30 + * (2) Carrying on.
  31 + ** (2.1) Describing your web sites.
  32 + ** (2.2) Directories on the server's disk.
  33 + ** (2.3) Starting your web sites.
  34 +
  35 + * (3) The HTML interface.
  36 + ** (3.1) Types used by the HTML interface.
  37 + ** (3.2) ``in form'' versus ``off form''.
  38 + ** (3.3) Defining your own style.
  39 + ** (3.4) Actioners and forms.
  40 + ** (3.5) Local popup.
  41 +
  42 + ---------------------------------------------------------------------------------------
  43 +
  44 +
  45 +read tools/basis.anubis
  46 +read CXM_common.anubis
  47 +read CXM_multihost_http_server.anubis
  48 +read CXM_mime.anubis
  49 +
  50 +
  51 +
  52 + * (1) Structure of a web site.
  53 +
  54 + First of all we need to explain what a web site should be made of. Ideally, the
  55 + visitor (also called the 'client') should see the web site working as any other
  56 + interactive computer software. So, it should be clear that a 'session' (i.e. a visit
  57 + to the web site, including the consultation of several pages) is some kind of
  58 + conversation between the visitor and the web site, and that the web site should
  59 + maintain a 'current state' of this conversation. At each new request (click) from the
  60 + visitor, this state must be updated. This whole conversation is called a 'session' and
  61 + should not be confused with a single request.
  62 +
  63 +
  64 +
  65 + ** (1.1) Three sorts of data.
  66 +
  67 + All the data needed for putting a web site at work may be dispatched into three
  68 + categories:
  69 +
  70 + 1. Constant data (data that never change). These data may be hard coded into the
  71 + Anubis source files of the web site.
  72 +
  73 + 2. Permanent data (data which always exist independantly of the users connected to
  74 + the web site). These data are normally recorded into data bases.
  75 +
  76 + 3. Session data (data which depend on a particular visitor and which exist only
  77 + during the time he visits the web site). These data are stored into so-called
  78 + 'states'.
  79 +
  80 +
  81 + It is important to determine which data belongs to which category. This is part of your
  82 + design decisions.
  83 +
  84 +
  85 +
  86 + ** (1.2) How requests are handled.
  87 +
  88 + We want to separate the following two functionalities (which are used at each request
  89 + (click) during a single session):
  90 +
  91 + - computing the new state from the previous state and from the client request, and
  92 + updating the data base,
  93 +
  94 + - computing the page to be sent to the client from the new current state and from
  95 + the informations in the data base.
  96 +
  97 +
  98 + The next picture shows the structure we have in mind:
  99 +
  100 +
  101 + request +---------+ HTML page (with a hidden state name)
  102 + .-------------------| client |<--------------.
  103 + | .-----------------| | |
  104 + | | previous state +---------+ |
  105 + | | name (if any) |
  106 + | | | client side
  107 + ............................................................................
  108 + | | | server side
  109 + | | |
  110 + | | .-------------------. |
  111 + | | | previous state | |
  112 + V V V | |
  113 + +---------------+ +---------------+ +--------------+
  114 + | compute state | | server's disk | | compute page |
  115 + +---------------+ +---------------+ +--------------+
  116 + ^ | | ^ ^ ^ ^ ^
  117 + | | | | | | | |
  118 + | | `--------------------+--------------------' | |
  119 + | | new state | | |
  120 + read | `------------------------+--------------------' |
  121 + write | new state name |
  122 + update V |
  123 + +-----------+ |
  124 + | data base |--------------------------------------------'
  125 + +-----------+ read only
  126 +
  127 +
  128 + When the client begins a session, there is no previous state. In this case, a default
  129 + 'initial state' is used instead.
  130 +
  131 + The data base may be updated by 'compute state' box, but should not be update by the
  132 + 'compute page' box. The 'compute page' box should be allowed only to read the data
  133 + base.
  134 +
  135 + In this file, all the above stuff is defined, except the 'compute state' and 'compute
  136 + page' boxes. You just have to provide the function for computing a new state (compute
  137 + state) and the function for computing the page (compute page) from the new state. You
  138 + don't have to worry about state names, saving and retrieving states and the like.
  139 +
  140 +
  141 +
  142 +
  143 +
  144 + ** (1.3) What web pages are made of.
  145 +
  146 + What the client can see in his browser's window may be called a 'page'. Within a page,
  147 + we have several sorts of components:
  148 +
  149 + - 'local' components, i.e. all components which do not open a connection, like
  150 + texts, images, etc... possibly using JavaScript programmation,
  151 +
  152 + - 'actioners', which, when clicked upon, open a connection with our web site; they
  153 + may appear as links or buttons, etc...
  154 +
  155 + - 'foreign links', which when clicked upon, open a connection with another web site
  156 + (or ours eventually).
  157 +
  158 + Of course, what an actioner does is just ask our web site to perform an action. To that
  159 + end, the actioner essentially sends the name of the action to be performed. However, it
  160 + may be necessary to provide additional informations which may be seen as 'operands' of
  161 + the action. In order to attach operands to an action, HTML provides the notion of
  162 + 'form'. Indeed, a form contains essentially a set of input fields into which the client
  163 + may put values for the required operands of the action, and a submit button, which is
  164 + the actioner itself. Notice that a single form may contain several submit buttons,
  165 + which simply means that there are several distincts actions taking the same set of
  166 + operands.
  167 +
  168 + Restrictions must be put on the use of all theses gadgets. Indeed, for example,
  169 + putting a form within another form is officially meaningless in HTML, and the client's
  170 + browser may be seriously disturbed by this. In this file, we propose an interface to
  171 + the HTML language, which forbids such meaningless things, simply by imposing a strict
  172 + typing of HTML concepts.
  173 +
  174 + Each web site may be accessible through two communication channels:
  175 +
  176 + - a non secured channel (HTTP),
  177 + - a secured channel (HTTPS).
  178 +
  179 + Nevertheless, the whole thing should be considered as a single web site. For example,
  180 + you may have a secured page, obtained through HTTPS, containing public images obtained
  181 + through HTTP. An actioner in a non secured page may open a secured connection, and
  182 + conversely.
  183 +
  184 + Summarizing, a web page is made of local elements, foreign links and actioners.
  185 + Actioners receive operands from forms, and they also choose to communicate through the
  186 + non secured or through the secured channel.
  187 +
  188 +
  189 +
  190 + +-------------------+
  191 + | page |
  192 + | | +---------------+
  193 + | +--------------+ | | next page |
  194 + | | form | | | (non secured) |
  195 + | | +----------+ | | HTTP | |
  196 + | | | actioner |---------------------------->| |
  197 + | | +----------+ | | +---------------+
  198 + | | | |
  199 + | | +----------+ | | +---------------+
  200 + | | | actioner |---------------------------->| next page |
  201 + | | +----------+ | | HTTPS | (secured) |
  202 + | | | | | |
  203 + | +--------------+ | | |
  204 + | | +---------------+
  205 + | |
  206 + +-------------------+
  207 +
  208 +
  209 + Notice that actioners need no be necessarily put into forms. In that case, they work as
  210 + ordinary links, but they still may receive operands as we shall see.
  211 +
  212 +
  213 +
  214 +
  215 + ** (1.4) Actions.
  216 +
  217 + The client opens a new connection with our web site whenever he clicks on an
  218 + actioner. The result is that a request is sent, essentially made of a list of 'web
  219 + arguments'. Each web argument is a pair (name,value). One of these web arguments, the
  220 + 'action' web argument (whose name is "a"), determines the action to be performed. The
  221 + other web arguments (not including "s", used to identify the state) are the operands
  222 + for this action.
  223 +
  224 + Hence, the 'compute state' box in the picture above, splits naturally into as many
  225 + sub-boxes as there are actions. For this reason, we define the following type for
  226 + representing actions (where '$State' is the type representing session informations):
  227 +
  228 +public type Web_Action($SessionTicket, $State):
  229 + http_action (String name, // name of action
  230 + (Maybe($State)) -> Bool allow, // true if action allowed
  231 + (HTTP_Info http_info,
  232 + List(Web_arg) web_args, // actually only 'operands' web arguments
  233 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
  234 + https_action (String name, // name of action
  235 + (Maybe($State)) -> Bool allow, // true if action allowed
  236 + (HTTP_Info http_info,
  237 + List(Web_arg) web_args, // actually only 'operands' web arguments
  238 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
  239 + http_https_action (String name,
  240 + (Maybe($State)) -> Bool allow, // true if action allowed
  241 + (HTTP_Info http_info,
  242 + List(Web_arg) web_args,
  243 + Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).
  244 +
  245 + 'http_action's are executed only under HTTP, and 'https_action's are executed only
  246 + under HTTPS. 'http_https_action's may be executed under both types of connections.
  247 +
  248 + Each action has a name, which is used to identify the action. Each action also has a
  249 + function 'allow' whose job is to verify that the action is allowed in the current
  250 + state, and a function 'do_it' for performing the action. The function 'do_it' receives
  251 + a lot of informations:
  252 +
  253 + - 'HTTP informations':
  254 + - the IP address of the client,
  255 + - the URI requested by the client (after redirection),
  256 + - the list of HTTP headers generated by the client's browser,
  257 + - the list of web arguments sent by the client (except "s" and "a"),
  258 + - the previous state (or the 'initial' or 'ticket expired' state if no previous
  259 + state can be found).
  260 +
  261 + In most cases, HTTP informations are not used. This is the reason why they are gathered
  262 + for simplicity into a unique datum of type 'HTTP_Info'.
  263 +
  264 +// For your convenience, we introduce the following simpler variants:
  265 +//
  266 +//public define Web_Action($State)
  267 +// http_action
  268 +// (
  269 +// String name,
  270 +// $State -> Bool allow,
  271 +// (List(Web_arg),$State) -> $State do_it
  272 +// ) =
  273 +// http_action(name,
  274 +// (Maybe($State) ms) |-> if ms is
  275 +// {
  276 +// failure then true,
  277 +// success(s) then allow(s)
  278 +// },
  279 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  280 +// {
  281 +// failure then (failure, []),
  282 +// success(s2) then (success(do_it(l,s2)), [])
  283 +// }).
  284 +//
  285 +//public define Web_Action($State)
  286 +// https_action
  287 +// (
  288 +// String name,
  289 +// $State -> Bool allow,
  290 +// (List(Web_arg),$State) -> $State do_it
  291 +// ) =
  292 +// https_action(name,
  293 +// (Maybe($State) ms) |-> if ms is
  294 +// {
  295 +// failure then true,
  296 +// success(s) then allow(s)
  297 +// },
  298 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  299 +// {
  300 +// failure then (failure, []),
  301 +// success(s2) then (success(do_it(l,s2)), [])
  302 +// }).
  303 +//
  304 +//public define Web_Action($State)
  305 +// http_https_action
  306 +// (
  307 +// String name,
  308 +// $State -> Bool allow,
  309 +// (List(Web_arg),$State) -> $State do_it
  310 +// ) =
  311 +// http_https_action(name,
  312 +// (Maybe($State) ms) |-> if ms is
  313 +// {
  314 +// failure then true,
  315 +// success(s) then allow(s)
  316 +// },
  317 +// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
  318 +// {
  319 +// failure then (failure, []),
  320 +// success(s2) then (success(do_it(l,s2)), [])
  321 +// }).
  322 +
  323 +
  324 +
  325 + When you define your web site, you must provide the list of all the actions of the
  326 + site. When a new state has been computed, a graphical representation of this state
  327 + must be sent to the client. To that end, you must provide a function (named below
  328 + 'compute_page') of type:
  329 +
  330 + $State -> HTML_Page
  331 +
  332 + where the type 'HTML_Page' (defined below in this file) abstractly represents HTML
  333 + pages.
  334 +
  335 +public type HTML_Page:...
  336 +
  337 + It should be clear that states and pages are deeply linked together. Indeed, we really
  338 + understand the page shown to the client as a representation of the current state of the
  339 + conversation between the client and the web site, but also containing informations
  340 + taken from the data bases.
  341 +
  342 +
  343 +
  344 +
  345 +
  346 + ** (1.5) States.
  347 +
  348 + Now, we explain how you can define the type (say 'State') to be used as an instance of
  349 + the type parameter '$State'. The following is just a suggestion.
  350 +
  351 + Each state determines a page (since 'compute_page' computes a page from a
  352 + state). However, some components of the state may be independant of the page. It may be
  353 + the case for example for the indication of the natural language used by the
  354 + client. Hence, a state should be made of (at least) two parts:
  355 +
  356 + - informations which are the same for all pages,
  357 + - informations which are particular to each page.
  358 +
  359 + For example, you could define:
  360 +
  361 + type Page: // one alternative per page, with particular informations
  362 + login(...), // in the components
  363 + main_page(...),
  364 + ...etc...
  365 +
  366 + Now, the type 'State' could be defined as follows:
  367 +
  368 + type State:
  369 + state(Language, // informations valid for all pages
  370 + ...,
  371 + Page). // informations particular to a page
  372 +
  373 + However, if you are making a secured web site within which clients should be identified
  374 + (by id and password), it may be a good idea to have two sorts of states, one for non
  375 + identified clients and one for identified clients. In this case, define the type
  376 + 'State' as follows (this is just a suggestion):
  377 +
  378 + type State:
  379 + non_identified(Language),
  380 + identified(String id,
  381 + Language,
  382 + Page).
  383 +
  384 + When a request arrives, check if the previous state is 'identified(...)' or
  385 + 'non_identified(...)', and don't provide access to certain pages to non identified
  386 + clients. This is required for security.
  387 +
  388 + Some more words on security. If your site needs to identify clients, define the
  389 + initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
  390 + password of the client. If the id and password are correct, then change the state of
  391 + the client to 'identified(...)'. No other action should be able to do that. Now, be
  392 + confident that clients cannot forge states. The only information they have is the name
  393 + of a state, not the state itself which is never sent over the network, but only stored
  394 + on the server's disk. The name of the state is constructed using strong cryptographical
  395 + methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
  396 + state names cannot be seen by a third party. So, if the system retrieves a previous
  397 + state of the form 'identified(...)', you can be confident that your client is well
  398 + identified, and you can send him confidential informations.
  399 +
  400 + States have a limited life time. It may happen that a client clicks on a button at a
  401 + time its state is out of date. In this case, this system considers that the new state
  402 + is a special state named 'ticket expired'. You must provide a function producing this
  403 + state when you describe your web site. The page corresponding to this state must just
  404 + inform the client that he/she waited a too long time before clicking on a button, and
  405 + has to restart (a new conversation) from the begining.
  406 +
  407 +
  408 +
  409 + * (2) Carrying on.
  410 +
  411 + ** (2.1) Describing your web sites.
  412 +
  413 + Before you may start your web site, you must describe it, i.e. produce a datum of the
  414 + opaque type 'Web_Site'.
  415 +
  416 +public type Web_Site:...
  417 +
  418 + Producing such a datum may be performed by:
  419 +
  420 +public define Web_Site
  421 + make_web_site_description
  422 + (
  423 + List(String) common_names, // for example: ["www.our-business.com",
  424 + // "192.168.0.1"]
  425 + // the second one is just for testing
  426 + String site_directory, // where 'public' and other directories are
  427 + // located (should NOT end with '/')
  428 + One -> One init,
  429 + (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
  430 + ($State expired,
  431 + HTTP_Info,
  432 + List(Web_arg),
  433 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
  434 + (HTTP_Info,
  435 + List(Web_arg),
  436 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
  437 + List(Web_Action($SessionTicket, $State)) actions,
  438 + (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
  439 + Int32 timeout, // seconds (todo: minutes)
  440 + List(Redirection) redirections,
  441 + String charset,
  442 + List(String) journal_extensions,
  443 + List(String) journal_headers,
  444 + String authorization_secret,
  445 + List(MIME) known_mime_types,
  446 + (String action_name,
  447 + List(Web_arg) args)-> One before_send_file
  448 + ).
  449 +
  450 +
  451 + Explanations:
  452 +
  453 + 'common_names' is the list of names of the site (the name the browser must send as the
  454 + value of the 'Host' HTTP header in order to access the site must be in that list). Such
  455 + a name generally looks like this:
  456 +
  457 + www.somewhere.com
  458 +
  459 + If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
  460 + the site must be exactly the same as the name on the certificate (which is precisely
  461 + called the 'common name' in the X.509 jargon). If the two names do not match, the site
  462 + will still work, but the transaction will not be transparent to the client. His browser
  463 + will complain that the name of the certificate does not match the name of the site, and
  464 + he will have to accept the certificate manually.
  465 +
  466 + 'site_directory' is the absolute path to the directory where the files needed by the
  467 + site are located. Usually this directory looks like:
  468 +
  469 + my_anubis/web_sites/www.somewhere.com
  470 +
  471 + However, this information is not computed from 'common_name', so that you can change
  472 + the common name (for example temporarily, for networking reasons) without loosing
  473 + access to the files.
  474 +
  475 + 'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
  476 + whose graphical representation is a page explaining to the user that its 'ticket' (or
  477 + 'session information') has expired, and that he/she must close all popup windows and
  478 + start a new session. The arguments of the function contain the previous (expired)
  479 + state and all current informations concerning the user. This arguments may be useful
  480 + for example for producing the expiration message in the language chosen by the user.
  481 + You can also (and this may be much smarter) send a 'ticket prolongation page'
  482 + (including a new login for example), and resume the same conversation, since you have
  483 + all the pertinent informations at hand. In the case the ticket is definitely lost, the
  484 + second fonction 'ticket_lost_state' is used.
  485 +
  486 + Notice that despite the fact that the parameter $State is involved in the arguments of
  487 + the above function, the type 'Web_Site' does not depend on this parameter. This allows
  488 + to produce lists of web site descriptions, where each description may be constructed
  489 + with a different instance of $State. This is required because distinct sites must have
  490 + distinct types of session informations. This is made possible by the fact that the
  491 + type is obscure, and the constructor replaced by a function which assembles
  492 + 'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
  493 + entity not depending on $State. You should have a look to the private part of this file
  494 + if you want more precisions about this programming technique.
  495 +
  496 + 'charset' is a string which will determine the character encoding to be used by the
  497 + browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
  498 + etc...
  499 +
  500 + 'before_send_file' is a function which is executed just before the HTTP server sends a
  501 + file. It gets an action name and the web arguments received with the request for that
  502 + file. Notice that this action name and these web arguments may be put into a
  503 + 'private_download' element, and will come back to the server at the time of the
  504 + download.
  505 +
  506 +
  507 +
  508 + ** (2.2) Directories on the server's disk.
  509 +
  510 + The description of you site contains the name of the directory within which the
  511 + required files are located. This may be for example:
  512 +
  513 + my_anubis/web_sites/www.our-business.com/
  514 +
  515 + This is called the 'site directory' (for the given site). Within the site directory,
  516 + the following directories are created by this program:
  517 +
  518 + states
  519 + public
  520 + journal
  521 + private_download
  522 + upload_temporary
  523 +
  524 + The directory 'states' is used for storing states (session informations). Out of date
  525 + states are automatically removed after some time.
  526 +
  527 + The tree rooted at 'public' contains files that the server is allowed to send to the
  528 + clients. For security reasons, the server never sends a file which is not within the
  529 + tree whose root is this 'public' directory (except for the 'private download' mecanism;
  530 + see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
  531 + must have been recognized before the file may be sent.
  532 +
  533 + The directory 'journal' contains the jounal files. The roles of the remaining
  534 + directories 'private_download' and 'upload_temporary' is explained in
  535 + 'multihost_http_server.anubis', where you will also find further informations on
  536 + 'public' and 'journal'.
  537 +
  538 +
  539 +
  540 +
  541 + ** (2.3) Web servers parameters.
  542 +
  543 + The web servers have several parameters useful for administration. They are described
  544 + as follows:
  545 +
  546 + public type WebServersParameters:
  547 + wsparms(Var(Bool) shutdown_required,
  548 +
  549 +
  550 +
  551 +
  552 + ** (2.4) Starting your web sites.
  553 +
  554 + When you have described all your web sites (you may want to have several web sites, and
  555 + they are distinguished by their 'common name'), you may start them all together using
  556 + 'start_web_sites' below. This function returns a result of the following type:
  557 +
  558 +public type Start_Web_Sites_Result:
  559 + cannot_bind_to_port(Int32),
  560 + cannot_bind_to_port(Int32,Int32),
  561 + ok(Server http_server,
  562 + Server https_server).
  563 +
  564 + Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
  565 + ports (or to both). The main reason is that another server is already listening on that
  566 + port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
  567 + the 's' bit has not been set for 'user' and 'group' (there is not such problem under
  568 + Windows). Also notice that the Linux kernel may need a rather long time (up to several
  569 + minutes) before liberating a listening port. Now, if the system can bind to the two
  570 + ports, the pair of the two servers is returned. Two tools are useful for manipulating
  571 + servers:
  572 +
  573 + shutdown of type Server -> One
  574 + is_down of type Server -> Bool
  575 +
  576 + They are defined in 'predefined.anubis' (together with the type 'Server').
  577 +
  578 +
  579 +public define Start_Web_Sites_Result
  580 + start_web_sites
  581 + (
  582 + Int32 ip_address, // the IP address shared by the web sites
  583 + Int32 http_port, // usually: 80
  584 + Int32 https_port, // usually: 443
  585 + String ssl_certificate_common_name,
  586 + List(Web_Site) web_sites, // web sites to be started
  587 + Var(Bool) shutdown_required
  588 + ).
  589 +
  590 + 'ip_address' is the IP address on which the two servers listen. If you put 0, the
  591 + servers listen on all the IP addresses of the machine. This may be useful if the
  592 + machine has several network interfaces.
  593 +
  594 + 'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
  595 + loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
  596 + certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
  597 + and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
  598 +
  599 + Notice that the number of servers is always 2, regardless of the number of web sites
  600 + you are starting.
  601 +
  602 + The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
  603 + servers from within the web site (typically the administration part). The servers will
  604 + shutdown as soon as this variable contains 'true'. So you must provide a variable
  605 + containing 'false' otherwise your servers will not run. You may also use the primitive
  606 + 'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
  607 +
  608 +
  609 +
  610 +
  611 +
  612 +
  613 + * (3) The HTML interface.
  614 +
  615 + We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
  616 + of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
  617 + reactive and attractive on the client side.
  618 +
  619 +
  620 + ** (3.1) Types used by the HTML interface.
  621 +
  622 + For easy reference, we gather below the definitions of all the types used by the HTML
  623 + interface, and we comment them immediately.
  624 +
  625 +
  626 +public type HTML_Size:
  627 + absolute(Int32), // in pixels
  628 + percentage(Int32).
  629 +
  630 +
  631 +public type Text_Option:
  632 + size(Int32), // size of character font to use
  633 + font(String), // name of character font to use (such as "helvetica",...)
  634 + color(RGB), // color to be used for characters
  635 + italic,
  636 + oblique,
  637 + small_capitals,
  638 + bold,
  639 + underlined,
  640 + left_justified,
  641 + right_justified,
  642 + justified, // justified on both sides
  643 + line_through,
  644 + nowrap,
  645 + class(String). //CSS class
  646 +
  647 + A list of 'Text_Option' must be given with each text you want to put in your page.
  648 +
  649 + This indicate the way of reading text.
  650 +public type Reading_Way:
  651 + ltr, //the text is readable from "Left To Right" like english
  652 + rtl. //the text is readable from "Right To Left" like arabic
  653 +
  654 +
  655 +
  656 +public type CoreAttrs:
  657 + id (String),
  658 + class (String),
  659 + style (String),
  660 + title (String).
  661 +
  662 +public type I18n:
  663 + lang (String),
  664 + dir (Reading_Way).
  665 +
  666 +public type DIV_Option:
  667 + id (String),
  668 + class (String),
  669 + style (String),
  670 + title (String),
  671 + lang (String),
  672 + dir (Reading_Way).
  673 +
  674 +
  675 + A list of 'DIV_Option' must be given with each DIV you want to put in your page.
  676 +
  677 +
  678 +public type Table_Option:
  679 + background_color(RGB), // applied to all cells in the table
  680 + background_image(String url),
  681 + border(Int32 width_of_outer_edge, // if not present, all values are 0
  682 + Int32 width_of_top_of_relief,
  683 + Int32 width_of_inner_edge,
  684 + RGB border_color),
  685 + width(Int32), // sets a minimal width for the table
  686 + percentage_width(Int32).
  687 +
  688 +
  689 +public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
  690 +
  691 +
  692 + A list of 'Table_Option' must be given with each table.
  693 +
  694 +
  695 +public type BackgroundOption:
  696 + repeat, // repeat the background in both directions
  697 + repeat_horizontal, // repeat the background only horizontally
  698 + repeat_vertical, // repeat the background only verticall
  699 + no_repeat, // don't repeat the background
  700 + center.
  701 +
  702 +
  703 +public type Cell_Option:
  704 + left, // put the content of the cell on the left
  705 + h_center, // center the content of the cell horizontally
  706 + right, // put the content of the cell on the right
  707 + top, // put the content of the cell upwards
  708 + v_center, // center the content of tye cell vertically,
  709 + bottom, // put the content of the cell downwards
  710 + base_line, // align the content vertically according to base lines
  711 + background_color(RGB),
  712 + background_image(String url, BackgroundOption),
  713 + width(Int32), // sets a minimal width for the cell
  714 + percentage_width(Int32),
  715 + height(Int32), // sets a minimal height for the cell
  716 + columns(Int32), // lets the cell span over several columns
  717 + rows(Int32), // lets the cell span over several rows
  718 + nowrap. // do not allow text wrapping within the cell
  719 +
  720 + A list of 'Cell_Option' must be given with each cell and each row in a table. Options
  721 + given with a row apply to all the cells in the row, but are superseded by options given
  722 + with cells, which apply only to the cell they are given with.
  723 +
  724 +
  725 +public type HTML_Cell($T):
  726 + cell(List(Cell_Option) options, $T content).
  727 +
  728 + The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
  729 + depending on where you put your table (within a form or not within a form). For your
  730 + convenience, we define the following particular case:
  731 +
  732 +public define HTML_Cell($T)
  733 + cell
  734 + (
  735 + $T content
  736 + ) =
  737 + cell([],content).
  738 +
  739 +
  740 +
  741 +public type HTML_Row($T):
  742 + row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
  743 +
  744 + Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
  745 +
  746 +public define HTML_Row($T)
  747 + row
  748 + (
  749 + List(HTML_Cell($T)) cells
  750 + ) =
  751 + row([],cells).
  752 +
  753 +public define HTML_Row($T)
  754 + row
  755 + (
  756 + HTML_Cell($T) cell
  757 + ) =
  758 + row([],[cell]).
  759 +
  760 +public type Actioner_Connection:
  761 + same, // use same type of connection as current page
  762 + http, // use non secured connection
  763 + https. // use secured connection
  764 +
  765 +public type Other_Window_Option:
  766 + resizable, // the new window may be resized by the client
  767 + scrollbars, // the new window has scrollbars
  768 + width(Int32), // the new window has the specified width
  769 + height(Int32). // the new window has the specified height
  770 +
  771 +public type Actioner_Target:
  772 + same,
  773 + same (String label),
  774 + other(String window_name, List(Other_Window_Option)).
  775 +
  776 +public type Actioner_Aspect:
  777 + link (List(Text_Option),String text), // hypertext link
  778 + push_button (List(CoreAttrs),String text),
  779 + button (String url_off, String url_on), // rollover button
  780 + button (String url_off, String url_on, Int32 w, Int32 h), // idem with size
  781 + immediate_selector (String name, Int32 size, List(String) choices).
  782 +
  783 +
  784 +public type Actioner_Local_Action:
  785 + close_window.
  786 +
  787 +
  788 +public define Actioner_Aspect
  789 + link
  790 + (
  791 + String text
  792 + ) =
  793 + link([],text).
  794 +
  795 +
  796 +public define Actioner_Aspect
  797 + link
  798 + (
  799 + List(Text_Option) options,
  800 + Int32 i
  801 + ) =
  802 + link(options,integer_to_string(i)).
  803 +
  804 +public define Actioner_Aspect
  805 + link
  806 + (
  807 + Int32 i
  808 + ) =
  809 + link([],i).
  810 +
  811 +public define Actioner_Aspect
  812 + button
  813 + (
  814 + String url_img
  815 + ) =
  816 + button(url_img,url_img).
  817 +
  818 +
  819 +
  820 + Actioners are explained in details below.
  821 +
  822 +
  823 +public type TextAreaOption:
  824 + disabled,
  825 + read_only,
  826 + wrap_lines.
  827 +
  828 +public type HTML_In_Form:
  829 + literal_pt (Printable_tree),
  830 + literal (String),
  831 + sequence (List(HTML_In_Form) items),
  832 + text (List(Text_Option), String the_text),
  833 + preformated (List(Text_Option), String),
  834 + paragraph (List(Text_Option), String the_text),
  835 + image (String url),
  836 + image (String url, Int32 width, Int32 height),
  837 + table (List(Table_Option), List(HTML_Row(HTML_In_Form))),
  838 + center (HTML_In_Form),
  839 + mail_to (String email, HTML_In_Form element),
  840 + scroller (Int32 width, Int32 height,
  841 + Int32 content_width, Int32 content_height,
  842 + HTML_In_Form content),
  843 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  844 + String action_name, List((String,String)) extra_ops,
  845 + List(Actioner_Local_Action)),
  846 + foreign_link (List(Text_Option), String url, String name),
  847 + private_download (String abs_path, String name, String extra_ext,
  848 + Maybe((String,List((String,String)))) action),
  849 + text_input (String label_text, String label, String name, String init, Int32 width),
  850 + password_input (String label_text, String label, String name, Int32 width),
  851 + text_area (List(TextAreaOption), String name, String init, Int32 width, Int32 height),
  852 + file_upload (String name, Int32 width),
  853 + selector (String name, Int32 size, List(String) choices),
  854 + selector (String name, Int32 size, List(String) choices, String selected),
  855 + // List((String,String)) = List((code,name)) where :
  856 + // name appears in selector
  857 + // code is the web-arg value
  858 + selector_c (String name, Int32 size, List((String,String)) choices),
  859 + selector_c (String name, Int32 size, List((String,String)) choices, String selected),
  860 + radio_button (String label_text, String label, String name, String value, Bool checked),
  861 + check_box (String label_text, String label, String name, Bool checked),
  862 + div (List(DIV_Option), HTML_In_Form content),
  863 + div_empty (List(DIV_Option)),
  864 + hidden (String name, String value).
  865 +
  866 +
  867 + 'HTML_In_Form' defines all the elements you may put within a form. We define a
  868 + convenience function:
  869 +
  870 +public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
  871 +
  872 +public define HTML_In_Form
  873 + foreign_link
  874 + (
  875 + Int32 tsize,
  876 + String url,
  877 + String name
  878 + ) =
  879 + foreign_link([size(tsize)],url,name).
  880 +
  881 +public define HTML_In_Form
  882 + actioner
  883 + (
  884 + Actioner_Connection conn,
  885 + Actioner_Target targ,
  886 + Actioner_Aspect asp,
  887 + String action_name,
  888 + List((String,String)) extra_ops
  889 + ) =
  890 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  891 +
  892 +
  893 +
  894 +public define HTML_In_Form
  895 + text_area
  896 + (
  897 + String name,
  898 + String init,
  899 + Int32 width,
  900 + Int32 height
  901 + ) =
  902 + text_area([],name,init,width,height).
  903 +
  904 +public define HTML_In_Form
  905 + table
  906 + (
  907 + List(HTML_Row(HTML_In_Form)) rows
  908 + ) =
  909 + table([],rows).
  910 +
  911 +
  912 +public define HTML_In_Form
  913 + private_download
  914 + (
  915 + String abs_path,
  916 + String name,
  917 + String extra_ext
  918 + ) =
  919 + private_download(abs_path,name,extra_ext,failure).
  920 +
  921 +public define HTML_In_Form
  922 + private_download
  923 + (
  924 + String abs_path,
  925 + String name,
  926 + String extra_ext,
  927 + String action_name,
  928 + List((String,String)) args
  929 + ) =
  930 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  931 +
  932 +public define HTML_In_Form
  933 + text
  934 + (
  935 + String s
  936 + ) =
  937 + text([],s).
  938 +
  939 +
  940 +
  941 +
  942 +public type HTML_Off_Form:
  943 + literal_pt (Printable_tree),
  944 + literal (String),
  945 + sequence (List(HTML_Off_Form) items),
  946 + text (List(Text_Option), String the_text),
  947 + preformated (List(Text_Option), String),
  948 + paragraph (List(Text_Option), String the_text),
  949 + image (String url),
  950 + image (String url, Int32 width, Int32 height),
  951 + table (List(Table_Option), List(HTML_Row(HTML_Off_Form))),
  952 + center (HTML_Off_Form),
  953 + mail_to (String email, HTML_Off_Form element),
  954 + scroller (Int32 width, Int32 height,
  955 + Int32 content_width, Int32 content_height,
  956 + HTML_Off_Form content),
  957 + fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
  958 + fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  959 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  960 + String action_name, List((String,String)) extra_ops,
  961 + List(Actioner_Local_Action)),
  962 + actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
  963 + String action_name, List((String,String)) extra_ops,
  964 + List(Actioner_Local_Action), String form_name),
  965 + foreign_link (List(Text_Option), String url, String name),
  966 + private_download (String abs_path, String name, String extra_ext,
  967 + Maybe((String,List((String,String)))) action),
  968 + label (String name),
  969 + form (String form_name, List(CoreAttrs), HTML_In_Form content),
  970 + div (List(DIV_Option), HTML_Off_Form content),
  971 + div_empty (List(DIV_Option)).
  972 +
  973 + 'HTML_Off_Form' defines all the elements you may put outside any form.
  974 +
  975 +
  976 +public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
  977 +public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
  978 + = fixed_size_2(width,height,name_of_HTML_file).
  979 +
  980 +
  981 +public define HTML_Off_Form
  982 + foreign_link
  983 + (
  984 + Int32 tsize,
  985 + String url,
  986 + String name
  987 + ) =
  988 + foreign_link([size(tsize)],url,name).
  989 +
  990 +
  991 +public define HTML_Off_Form
  992 + actioner
  993 + (
  994 + Actioner_Connection conn,
  995 + Actioner_Target targ,
  996 + Actioner_Aspect asp,
  997 + String action_name,
  998 + List((String,String)) extra_ops
  999 + ) =
  1000 + actioner(conn,targ,asp,action_name,extra_ops,[]).
  1001 +
  1002 +public define HTML_Off_Form
  1003 + table
  1004 + (
  1005 + List(HTML_Row(HTML_Off_Form)) rows
  1006 + ) =
  1007 + table([],rows).
  1008 +
  1009 +
  1010 +
  1011 + We add two convenience functions for 'row'. The reason why we add two functions, one
  1012 + for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
  1013 + arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
  1014 + the arguments of the function do not refer to any of the types defined here.
  1015 +
  1016 +public define HTML_Row(HTML_In_Form)
  1017 + row
  1018 + (
  1019 + HTML_In_Form content
  1020 + ) =
  1021 + row([],[cell([],content)]).
  1022 +
  1023 +public define HTML_Row(HTML_Off_Form)
  1024 + row
  1025 + (
  1026 + HTML_Off_Form content
  1027 + ) =
  1028 + row([],[cell([],content)]).
  1029 +
  1030 +
  1031 +public define HTML_Off_Form
  1032 + private_download
  1033 + (
  1034 + String abs_path,
  1035 + String name,
  1036 + String extra_ext
  1037 + ) =
  1038 + private_download(abs_path,name,extra_ext,failure).
  1039 +
  1040 +
  1041 +public define HTML_Off_Form
  1042 + private_download
  1043 + (
  1044 + String abs_path,
  1045 + String name,
  1046 + String extra_ext,
  1047 + String action_name,
  1048 + List((String,String)) args
  1049 + ) =
  1050 + private_download(abs_path,name,extra_ext,success((action_name,args))).
  1051 +
  1052 +public define HTML_Off_Form
  1053 + text
  1054 + (
  1055 + List(Text_Option) lto,
  1056 + Int32 i
  1057 + ) =
  1058 + text(lto,integer_to_string(i)).
  1059 +
  1060 +
  1061 +public define HTML_Off_Form
  1062 + text
  1063 + (
  1064 + Int32 i
  1065 + ) =
  1066 + text([],i).
  1067 +
  1068 +public define HTML_Off_Form
  1069 + text
  1070 + (
  1071 + String s
  1072 + ) =
  1073 + text([],s).
  1074 +
  1075 + - Cell a gap between two other cells :
  1076 +
  1077 +public define HTML_Cell(HTML_Off_Form)
  1078 + width_gap
  1079 + (
  1080 + Int32 w
  1081 + ) =
  1082 + cell([width(w)],text([],"")).
  1083 +
  1084 +public define HTML_Cell(HTML_In_Form)
  1085 + width_gap
  1086 + (
  1087 + Int32 w
  1088 + ) =
  1089 + cell([width(w)],text([],"")).
  1090 +
  1091 +
  1092 + - Row a gap between two other rows :
  1093 +
  1094 +public define HTML_Row(HTML_Off_Form)
  1095 + height_gap
  1096 + (
  1097 + Int32 h
  1098 + ) =
  1099 + row([],[cell([height(h)],text([],""))]).
  1100 +
  1101 +public define HTML_Row(HTML_In_Form)
  1102 + height_gap
  1103 + (
  1104 + Int32 h
  1105 + ) =
  1106 + row([],[cell([height(h)],text([],""))]).
  1107 +
  1108 +
  1109 + Notice that the two types have alternatives in common (same name, same arguments types,
  1110 + up to the value of the parameter $T), which correspond to elements which may be put
  1111 + anywhere in the page.
  1112 +
  1113 +
  1114 +
  1115 +public type CSS_Style:
  1116 + text_options(List(Text_Option)).
  1117 +
  1118 +public type CSS_File:
  1119 + css_file(String file_name).
  1120 +
  1121 +public type JS_File:
  1122 + js_file(String file_name).
  1123 +
  1124 +define String
  1125 + format
  1126 + (
  1127 + List(Text_Option) l
  1128 + ).
  1129 +
  1130 +
  1131 +
  1132 +
  1133 +define Printable_tree
  1134 + format_css_styles
  1135 + (
  1136 + List(CSS_Style) l
  1137 + ) =
  1138 + if l is
  1139 + {
  1140 + [ ] then [ ],
  1141 + [h . t] then
  1142 + [ if h is
  1143 + {
  1144 + text_options(tos) then
  1145 + [" body, span, p { ", format(tos), " }\n" ]
  1146 + }
  1147 + . format_css_styles(t)]
  1148 + }.
  1149 +
  1150 +
  1151 +
  1152 +public type HTML_Meta:
  1153 + keywords (List(String)),
  1154 + refresh (Actioner_Connection connection,
  1155 + Actioner_Target target,
  1156 + String action_name,
  1157 + Int32 delay), // in seconds
  1158 + meta (String name, String content),
  1159 + http_equiv (String name, String content),
  1160 + generic_meta (List((String,String))),
  1161 + literal (String).
  1162 +
  1163 + Meta tags are put in the 'head' of the HTML page.
  1164 +
  1165 +
  1166 +public type Body_Option:
  1167 + background_color (RGB),
  1168 + background_image (String url),
  1169 + background_image (String url, List(BackgroundOption)).
  1170 +
  1171 +public type HTML_Body:
  1172 + body(List(Body_Option) options, HTML_Off_Form content).
  1173 +
  1174 +
  1175 +public type HTML_Page:
  1176 + html_page(String title,
  1177 + List(HTML_Meta) meta_tags,
  1178 + List(CSS_Style) styles,
  1179 + List(CSS_File) css_files,
  1180 + List(JS_File) js_files,
  1181 + HTML_Body body).
  1182 +
  1183 +public define HTML_Page
  1184 + html_page
  1185 + (
  1186 + String title,
  1187 + List(HTML_Meta) metas,
  1188 + HTML_Body body
  1189 + ) =
  1190 + html_page(title,metas,[],[],[],body).
  1191 +
  1192 +public define HTML_Page
  1193 + html_page
  1194 + (
  1195 + String title,
  1196 + List(HTML_Meta) metas,
  1197 + List(CSS_Style) styles,
  1198 + HTML_Body body
  1199 + ) =
  1200 + html_page(title, metas, styles, [], [], body).
  1201 +
  1202 + 'HTML_Page' represents the final product of the construction of a web page.
  1203 +
  1204 +
  1205 +
  1206 +
  1207 + *** (3.2) ``in form'' versus ``off form''.
  1208 +
  1209 + There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
  1210 + of them may have a content, which is yet another HTML element (or several). Hence, it
  1211 + is meaningful to say that an element is 'within' another one. Now, putting any element
  1212 + within any other one may be meaningless. For example, an input element must be put
  1213 + within a form (otherwise, it is useless), and a from within another form has no precise
  1214 + meaning (and is forbidden by the HTML specification).
  1215 +
  1216 + Actually, the main criterium is ``within a form or not within a form''. So, HTML
  1217 + elements in a given page are separated into two categories: those who are within a
  1218 + form, and the others. Nevertheless, there are elements which may belong to both
  1219 + categories, like images and texts. We want to make use of the strong typing mecanism of
  1220 + Anubis in order to forbid non meaningful placement of elements.
  1221 +
  1222 + The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
  1223 + 'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
  1224 + and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
  1225 + but the two types are not cross recursive. This is the reason why it is impossible to
  1226 + put a form within a form. In order to construct a web page, you essentially have to
  1227 + produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
  1228 +
  1229 + In practice, you don't have to worry so much about these two types, because elements
  1230 + which may be put anywhere are constructed for both types by functions with the same
  1231 + name and the same arguments. Hence, for both types, you just write the same thing. You
  1232 + are warned by the compiler only when you try to put an element at a place it is not
  1233 + allowed.
  1234 +
  1235 +
  1236 +
  1237 + *** (3.3) Defining your own style.
  1238 +
  1239 + We provide generic tools for constructing HTML elements. However, your web site needs
  1240 + to have a ``style''.
  1241 +
  1242 + To that end, you need to write down a set of ``styling functions'', using the tools
  1243 + defined here. These styling functions allow the introduction of your colors and other
  1244 + visual characteristics into the constructed elements once and for all. For example, you
  1245 + may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
  1246 + some 'text_color'. You may write something like this:
  1247 +
  1248 + define RGB text_color = rgb(10,40,40).
  1249 +
  1250 + define HTML_Off_Form
  1251 + text
  1252 + (
  1253 + String the_text
  1254 + ) =
  1255 + text([font("helvetica"),size(14),color(text_color)],
  1256 + the_text).
  1257 +
  1258 + (and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
  1259 + page, you just write:
  1260 +
  1261 + text("... some text ...")
  1262 +
  1263 + and you don't have to provide the font, size and color for each text. If you want to
  1264 + have several styles of text presentation, you just write several sets of such
  1265 + convenience functions. This also suggests a trick. You may want for example different
  1266 + colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
  1267 + defining two functions as above, with the same name and same argument type, but
  1268 + returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
  1269 +
  1270 + If this preliminary work is well done, you will not waste your time later when you
  1271 + concentrate on the actual informational content of your pages.
  1272 +
  1273 + This general principle should be applied to all sorts of elements. This is the best
  1274 + thing to do in order to separate the functions defining the visual style from the
  1275 + functions defining the informational content itself, so that changing the style without
  1276 + changing the content becomes easy. This is also the best way for having a clean and
  1277 + easily readable source for your web site.
  1278 +
  1279 +
  1280 +
  1281 +
  1282 +
  1283 + *** (3.4) Actioners and forms.
  1284 +
  1285 + We have gathered several notions from HTML into that of an 'actioner'. An actioner is
  1286 + an HTML element which opens a connection to our server when clicked upon. Actioners may
  1287 + have different visual aspects. They may look like hypertext links or like buttons
  1288 + (rollovers), or even like selectors (with immediate action). In any case, their
  1289 + behavior is the same: they open a connection to our server, and send a set of 'web
  1290 + arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
  1291 + the action to be performed, and the others should be considered as operands for this
  1292 + action. Actually, the precise behavior of the actioner has several variants.
  1293 +
  1294 + The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
  1295 + type 'Actioner_Connection' above.
  1296 +
  1297 + You must also choose where the answer must be rendered. This may be in the same window
  1298 + or in another window (or frame). If it is in another window, the name of that window
  1299 + must be given. If the window does not exist, the browser will create it. Optionally,
  1300 + you may give the dimensions of the new window and other characteristics. See the type
  1301 + 'Actioner_Target' above.
  1302 +
  1303 + The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
  1304 + case of a rollover button, you provide the URLs of two images (of the same size)
  1305 + representing the button:
  1306 +
  1307 + url_off: to be used when the mouse is not over the button,
  1308 + url_on: to be used when the mouse is over the button.
  1309 +
  1310 + You can also create rollover buttons without creating images. Just use the second
  1311 + alternative named 'button'. The server creates the images automatically.
  1312 +
  1313 + The purpose of forms is just to give operands to actioners. If the actioner is placed
  1314 + within a form, all the input elements which are within this form provide operands to
  1315 + the actioner (except sometimes when they are not set by the client). If it is not put
  1316 + within a form, the actioner gets no operand, except if the name of a form is explicitly
  1317 + given, in which case the actioner gets all the inputs from that form as
  1318 + operands. Furthermore, you may want to give extra operands to the actioner. This may be
  1319 + useful for separating families of actioners with the same action name. Extra operands
  1320 + 'name=value' must be given in the form of pairs '(name,value)'.
  1321 +
  1322 + Notice that the name of a form may be used by an actioner which is off the form, so as
  1323 + to get the operands provided by this form. Also notice that several actioners may refer
  1324 + to the same form, being either in the form, or referring to the form from the
  1325 + outside. These actioners simply get the same set of operands, even if they correspond
  1326 + to distinct actions.
  1327 +
  1328 + Input elements may be put only within a form.
  1329 +
  1330 +
  1331 +
  1332 + *** (3.5) Local popup.
  1333 +
  1334 + This element looks like a link or a rollover button. When this button is clicked upon,
  1335 + a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
  1336 + page, which becomes suddenly visible. It is realized with a '<div>' HTML tag. In
  1337 + particular, clicking on the button does not open any connection. This is why it is
  1338 + called 'local'. The arguments have the following roles:
  1339 +
  1340 + Actioner_Aspect aspect of the button (same semantics as for actioners)
  1341 + content content of the popup window
  1342 + x, y, position of the popup window on the HTML page (not relative
  1343 + to the button but to the page itself)
  1344 + title title of the popup window
  1345 + color color of the title bar and close button in the popup window.
  1346 + A lightened version of this color is used for the background
  1347 + of the popup window.
  1348 + width width of the title bar
  1349 +
  1350 +
  1351 +
  1352 +
  1353 +
  1354 + --- That's all for the public part ! --------------------------------------------------
  1355 +
  1356 +
  1357 +
  1358 +
  1359 +
  1360 + ----------------------------------- Table of Contents ---------------------------------
  1361 +
  1362 + *** [1] States.
  1363 + *** [1.1] Saving and retrieving states.
  1364 + *** [1.2] Deleting out of date states.
  1365 +
  1366 + *** [2] Tools.
  1367 + *** [2.1] Directories.
  1368 + *** [2.2] Secondary documents.
  1369 +
  1370 + *** [3] Managing web arguments.
  1371 + *** [3.1] Prefixing web arguments names.
  1372 + *** [3.2] Separating web arguments.
  1373 + *** [3.3] Applying an action.
  1374 +
  1375 + *** [4] Web site descriptions and the 'awp handlers'.
  1376 + *** [4.1] The type 'Web_Site'.
  1377 + *** [4.2] Making a web site description.
  1378 + *** [4.3] Starting the servers.
  1379 +
  1380 + *** [5] HTML Formating.
  1381 + *** [5.1] The type 'HTML_Any($T)'.
  1382 + *** [5.2] Formating a color.
  1383 + *** [5.3] Creating buttons.
  1384 + *** [5.4] Formating an actioner.
  1385 + *** [5.5] Formating a private download link.
  1386 + *** [5.6] Formating rows and cells in a table.
  1387 + *** [5.7] Formating elements which may be put anywhere.
  1388 + *** [5.8] Formating 'in form' elements.
  1389 + *** [5.9] Formating 'off form' elements.
  1390 + *** [5.10] Formating meta-tags.
  1391 +
  1392 + ---------------------------------------------------------------------------------------
  1393 +
  1394 +
  1395 +
  1396 +
  1397 +public define String
  1398 + doctype_w3c_header
  1399 + =
  1400 + "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "+
  1401 + "\"http://www.w3c.org/TR/html4/loose.dtd\">\n".
  1402 +
  1403 +
  1404 +
  1405 +
  1406 + *** [1] States.
  1407 +
  1408 + We have to define functions for saving a state, retrieving a state, deleting out of
  1409 + date states. We need one such function per web site. The types of the first two
  1410 + functions depend on the parameter $State. This is not the case of the third one. The
  1411 + fact that the instance of $State is variable from one web sites to the other implies
  1412 + rather subtle manipulations using full functionality.
  1413 +
  1414 +
  1415 + *** [1.1] Saving and retrieving states.
  1416 +
  1417 + Each state is saved into a file on the server's disk (in the directory represented by
  1418 + the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
  1419 + state is saved together with a time stamp whose value is obtained by adding the current
  1420 + time to the given timeout for states. The state receives a name obtained by hashing
  1421 + (using sha1) the content of the file itself, and then encoding the hash with
  1422 + 'web_arg_encode'. The name of the file into which the state is saved is the
  1423 + concatenation of "s" and the name of the state.
  1424 +
  1425 +read CXM_web_arg_encode.anubis
  1426 +
  1427 + The tool below constructs the function which is able to save a state on the server's
  1428 + disk.
  1429 +
  1430 +define (Maybe($State) s) -> String // the function constructed returns the name of the state
  1431 + make_save_state_function
  1432 + (
  1433 + Int32 timeout,
  1434 + String state_directory
  1435 + ) =
  1436 + (Maybe($State) mbs) |->
  1437 + if mbs is
  1438 + {
  1439 + failure then "",
  1440 + success(s) then
  1441 + with time_stamp = now+timeout,
  1442 + to_be_saved = (time_stamp,s),
  1443 + state_name = web_arg_encode(sha1(s)),
  1444 + if save(to_be_saved,state_directory+"/s"+state_name) is ok
  1445 + then state_name
  1446 + else (print("Cannot create state file in '"+state_directory+"'.\n"); "")
  1447 + }.
  1448 +
  1449 +
  1450 + When a request arrives, we need to retrieve the previous state from the server's
  1451 + disk. We receive the name of that state. If the state is out of date, the state file is
  1452 + kept 3 days, and then deleted.
  1453 +
  1454 +type PreviousState($State):
  1455 + not_found, // cannot retrieve the previous state
  1456 + out_of_date($State), // the previous state is out of date
  1457 + still_valid($State). // the previous state is still valid
  1458 +
  1459 +define (String state_name) -> PreviousState($State)
  1460 + make_retrieve_state_function
  1461 + (
  1462 + String state_directory
  1463 + ) =
  1464 + (String state_name) |->
  1465 + with file_path = state_directory+"/s"+state_name,
  1466 + if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
  1467 + then (
  1468 + if d is (time_stamp,s) then
  1469 + if time_stamp < now
  1470 + then (
  1471 + forget(remove(file_path));
  1472 + out_of_date(s)
  1473 + )
  1474 + else still_valid(s) // state has been successfully retrieved
  1475 + )
  1476 + else not_found.
  1477 +
  1478 +
  1479 +
  1480 + *** [1.2] Deleting out of date states.
  1481 +
  1482 + We also need to delete states which are out of date and which will never be deleted by
  1483 + the above method. This may be performed by a machine doing this periodically (say once
  1484 + per states life time period).
  1485 +
  1486 +define (List(String) file_names) -> One
  1487 + make_delete_out_of_date_states_function
  1488 + (
  1489 + Maybe($State) dummy,
  1490 + String state_directory
  1491 + ) =
  1492 + (List(String) file_names) |-df->
  1493 + if file_names is
  1494 + {
  1495 + [ ] then unique,
  1496 + [h . t] then
  1497 + with file_path = state_directory+"/"+h,
  1498 + if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
  1499 + then (
  1500 + if d is (time_stamp,data) then
  1501 + if time_stamp < now
  1502 + then (forget(remove(file_path)); df(t))
  1503 + else df(t)
  1504 + )
  1505 + else (forget(remove(file_path)); df(t))
  1506 + }.
  1507 +
  1508 +
  1509 + The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
  1510 +
  1511 + Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
  1512 + function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
  1513 + does not accept a parameter in the body of a function (here the parameter is required
  1514 + by the use of 'retrieve') if this parameter does not appear in the type of the
  1515 + function. This is because this would create ambiguities that no explicit typing may
  1516 + ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
  1517 + and if you compile this file, you will get a message like this one:
  1518 +
  1519 + Error in 'making_a_web_site.anubis', line 1300, column 7:
  1520 + A definition may not contain parameters which are not present
  1521 + in the declaration part (hidden parameters):
  1522 + $State
  1523 +
  1524 + The type of the function constructed by 'make_delete_out_of_date_states_function' is
  1525 + independant of the parameter $State. This is important because this allows to create
  1526 + the list of such functions for all web sites. From this list, it is possible to call
  1527 + the functions one after the other, so deleting out of date states for all web
  1528 + sites. Actually, the next function receives a list of pairs (state_directory,function),
  1529 + one for each web site.
  1530 +
  1531 +define One
  1532 + delete_out_of_date_states // for all web sites
  1533 + (
  1534 + List((String, List(String) -> One)) directories_and_functions
  1535 + ) =
  1536 + if directories_and_functions is
  1537 + {
  1538 + [ ] then unique,
  1539 + [h . t] then if h is (state_directory,function) then
  1540 + function(directory_list(state_directory,"s*"));
  1541 + delete_out_of_date_states(t)
  1542 + }.
  1543 +
  1544 +
  1545 + The above function must be called periodically in a separate virtual machine. The
  1546 + period we have choosen is (rather logically) the life time of states itself. This may
  1547 + be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
  1548 + be really infinite, because the servers may be shutdown. Hence, our loop must test
  1549 + (rather frequently; say every second) if the servers are down. If they are, the loop
  1550 + must be exited.
  1551 +
  1552 +define One
  1553 + delete_states_loop
  1554 + (
  1555 + List((String,List(String) -> One)) directories_and_functions,
  1556 + Int32 timeout,
  1557 + Int32 next_time,
  1558 + Server http_server,
  1559 + Server https_server,
  1560 + Var(Bool) shutdown_required
  1561 + ) =
  1562 + if *shutdown_required
  1563 + then (shutdown(http_server); shutdown(https_server))
  1564 + else unique;
  1565 + if (is_down(http_server) & is_down(https_server))
  1566 + then unique
  1567 + else if now > next_time
  1568 + then
  1569 + (
  1570 + delete_out_of_date_states(directories_and_functions);
  1571 + delete_states_loop(directories_and_functions,
  1572 + timeout,
  1573 + now+timeout,
  1574 + http_server,
  1575 + https_server,
  1576 + shutdown_required)
  1577 + )
  1578 + else
  1579 + (
  1580 + sleep(1000); // sleep just one second and try again
  1581 + delete_states_loop(directories_and_functions,
  1582 + timeout,
  1583 + next_time,
  1584 + http_server,
  1585 + https_server,
  1586 + shutdown_required)
  1587 + ).
  1588 +
  1589 + The above loop must be run in a separate virtual machine. This will be done just after
  1590 + the two servers are started.
  1591 +
  1592 +
  1593 +
  1594 +
  1595 +
  1596 + *** [2] Tools.
  1597 +
  1598 + *** [2.1] Directories.
  1599 +
  1600 + We need a tool for creating directories (if needed).
  1601 +
  1602 + (This tool has been moved to 'tools/basis.anubis').
  1603 +
  1604 +
  1605 +
  1606 +
  1607 + *** [2.2] Secondary documents.
  1608 +
  1609 + Some HTML elements (like '<object>', '<frame>') cannot receive their content directly
  1610 + from the current document, but only through an URL. For this reason, we implement a
  1611 + mecanism for creating secondary documents on the fly. To that end we use the 'private
  1612 + download' mecanism.
  1613 +
  1614 + A secondary document is formated by the same functions as the main document itself. The
  1615 + next function takes an 'off form' element, creates the file containing the secondary
  1616 + document in HTML format, and returns the URL at which the document will be available.
  1617 +
  1618 +define String
  1619 + create_secondary_document
  1620 + (
  1621 + String sd, // site directory
  1622 + String as, // authorization_secret
  1623 + String sn, // state name
  1624 + $T -> Printable_tree format_element,
  1625 + $T content,
  1626 + HTML_Size width
  1627 + ) =
  1628 + with private_download_directory = sd+"/private_download",
  1629 + hash = web_arg_encode(sha1(content)),
  1630 + file_content = (Printable_tree)
  1631 + [doctype_w3c_header,
  1632 + "<html><body><table ",
  1633 + if width is
  1634 + {
  1635 + absolute(w) then ["width=\"",w-25],
  1636 + percentage(w) then ["width=\"95%\""]
  1637 + },"\"><tr><td align=right>",
  1638 + format_element(content),
  1639 + "</td></tr></table></body></html>"
  1640 + ],
  1641 + file_name = "sd"+hash+".html",
  1642 + file_path = private_download_directory+"/"+file_name,
  1643 + if write_to_file(file_path,file_content) is
  1644 + {
  1645 + cannot_open_file then print("Cannot open file '"+file_path+"'.\n"); "",
  1646 + write_error(n) then print("Error writing file '"+file_path+"'.\n"); "",
  1647 + ok then file_name+"?zauth="+
  1648 + make_authorization(sd,as,private_download_directory+"/"+file_name)
  1649 + }.
  1650 +
  1651 +
  1652 +
  1653 +
  1654 + *** [2.3] Generating unique ids.
  1655 +
  1656 + In order to uniquely name object for JavaScript we generate unique ids from a counter.
  1657 +
  1658 +define Int32
  1659 + new_idnum
  1660 + (
  1661 + Var(Int32) ic_v // 'idnum' counter variable
  1662 + ) =
  1663 + protect
  1664 + with result = *ic_v+1,
  1665 + ic_v <- result;
  1666 + result.
  1667 +
  1668 +
  1669 +
  1670 +
  1671 +
  1672 +
  1673 + *** [3] Managing web arguments.
  1674 +
  1675 + Web arguments are those pairs 'name=value' which are transmitted through the HTTP
  1676 + protocol. We need precise naming conventions for these web arguments.
  1677 +
  1678 +
  1679 +
  1680 + *** [3.1] Prefixing web arguments names.
  1681 +
  1682 + We want to assign different roles to web arguments, and we also want to be able to
  1683 + recognize its role directly from the name of a web argument. The name "s" is reserved
  1684 + for the web argument whose value is the name of the current state. The name "a" is
  1685 + reserved for the web argument whose value is the name of the action to be
  1686 + performed. Other web arguments receive arbitrary names, and in order to avoid clashes,
  1687 + these names are prefixed by:
  1688 +
  1689 + "p" for names of password inputs,
  1690 + "o" for other web arguments
  1691 +
  1692 + The reason why password input names have a distinct prefix is that this allows the HTTP
  1693 + server to hide the passwords on the console of the server and in the journal.
  1694 +
  1695 +
  1696 +
  1697 +
  1698 + *** [3.2] Separating web arguments.
  1699 +
  1700 + When a new request arrives, we need to separate the web arguments, that is to say:
  1701 +
  1702 + - find the value of "s", and recover the corresponding state,
  1703 + - find the value of "a", which is the name of the action to be performed,
  1704 + - get the list of all the remaining web arguments (operands of the action).
  1705 +
  1706 + We must also determine if the previous state may be recovered. If it is not the case
  1707 + (either because the previous state name is invalid, or the previous state is out of
  1708 + date), we must check if there is an action name. Indeed, the presence of an action name
  1709 + indicates that the user has clicked on one of our buttons or links. If on the contrary
  1710 + there is no action name the user has just entered our address in his browser. In this
  1711 + last case, we must send the first page of our site (maybe a 'login' page), but if there
  1712 + is an action, we must send a page just saying that the session ticket has expired. If
  1713 + the previous state is recovered and there is no action, the new state is the same as
  1714 + the previous state.
  1715 +
  1716 + The result of the separation of the web arguments is of type:
  1717 +
  1718 +type Separated_Web_Args($State):
  1719 + swa(Maybe(PreviousState($State)) previous_state,
  1720 + Maybe(String) action_name,
  1721 + List(Web_arg) operands).
  1722 +
  1723 +
  1724 +
  1725 + The next function constructs the function which separates the web arguments.
  1726 +
  1727 +define (List(Web_arg) lwa) -> Separated_Web_Args($State)
  1728 + make_separate_web_args_function
  1729 + (
  1730 + String state_directory,
  1731 + String -> PreviousState($State) retrieve_state
  1732 + ) =
  1733 + (List(Web_arg) lwa) |-swaf->
  1734 + if lwa is
  1735 + {
  1736 + [ ] then
  1737 + //
  1738 + // no web arg found => no previous state and no action
  1739 + //
  1740 + swa(failure,failure,[]),
  1741 +
  1742 + [wa_1 . wa_others] then
  1743 + //
  1744 + // at least one web arg =>
  1745 + // separate other web args, and insert the first one as needed
  1746 + //
  1747 + if (Separated_Web_Args($State))swaf(wa_others) is
  1748 + {
  1749 + swa(ps1, // possible previous state
  1750 + an1, // maybe an action name
  1751 + op1) // operands so far
  1752 + then
  1753 + if wa_1 is
  1754 + {
  1755 + web_arg(n,v) then
  1756 + with prefix = if substr(n,0,4) = "amp;" then substr(n,4,1) else substr(n,0,1),
  1757 + name_start = (Int32)(if substr(n,0,4) = "amp;" then 5 else 1),
  1758 + if prefix = "s" then
  1759 + swa(success(retrieve_state(v)),an1,op1) else
  1760 + if prefix = "a" then
  1761 + swa(ps1,success(v),op1) else
  1762 + if prefix = "t" then
  1763 + swa(ps1,an1,[web_arg("target",v) . op1]) else
  1764 + if prefix = "p" then
  1765 + swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
  1766 + if prefix = "o" then
  1767 + swa(ps1,an1,[web_arg(substr(n,name_start,length(n)-name_start),v) . op1]) else
  1768 + swa(ps1,an1,op1),
  1769 +
  1770 + upload(n,v,t) then
  1771 + swa(ps1,an1,[upload(substr(n,1,length(n)-1),v,t) . op1])
  1772 + }}
  1773 + }.
  1774 +
  1775 +
  1776 +
  1777 +
  1778 +
  1779 +
  1780 + *** [3.3] Applying an action.
  1781 +
  1782 + When the web arguments are separated (and their names cleaned up from prefixes), we may
  1783 + apply the action to the operands and the current state. We search for the action to be
  1784 + applied in the list of actions. If no action is found, the new state is the same as
  1785 + the previous state. Also, we deny the application of an HTTP action if the request
  1786 + arrives through the HTTPS channel and conversely.
  1787 +
  1788 +
  1789 +define (Maybe($State) previous,
  1790 + String action_name,
  1791 + HTTP_Info http_info,
  1792 + List(Web_arg) lwa,
  1793 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header))
  1794 + make_apply_action_function
  1795 + (
  1796 + List(Web_Action($SessionTicket, $State)) actions
  1797 + ) =
  1798 + with f =
  1799 + (Maybe($State) previous,
  1800 + String action_name,
  1801 + HTTP_Info http_info,
  1802 + List(Web_arg) lwa,
  1803 + Bool is_https,
  1804 + List(Web_Action($SessionTicket, $State)) actions) |-f->
  1805 + if actions is
  1806 + {
  1807 + [ ] then (print("action '"+action_name+
  1808 + "' not found.\n"); (failure, previous, [])),
  1809 + [ac1 . others] then if ac1 is
  1810 + {
  1811 + http_action(an,allow,do_it) then
  1812 + if an = action_name
  1813 + then if is_https
  1814 + then (print("HTTP action '"+an+
  1815 + "' called through HTTPS (denied).\n");
  1816 + (failure, previous, []))
  1817 + else if allow(previous)
  1818 + then do_it(http_info,lwa,previous)
  1819 + else (failure, previous, [])
  1820 + else f(previous,action_name,http_info,lwa,is_https,others),
  1821 +
  1822 + https_action(an,allow,do_it) then
  1823 + if an = action_name
  1824 + then if is_https
  1825 + then if allow(previous)
  1826 + then do_it(http_info,lwa,previous)
  1827 + else (failure, previous, [])
  1828 + else (print("HTTPS action '"+an+
  1829 + "' called through HTTP (denied).\n");
  1830 + (failure, previous, []))
  1831 + else f(previous,action_name,http_info,lwa,is_https,others),
  1832 +
  1833 + http_https_action(an,allow,do_it) then
  1834 + if an = action_name
  1835 + then if allow(previous)
  1836 + then do_it(http_info,lwa,previous)
  1837 + else (failure, previous, [])
  1838 + else f(previous,action_name,http_info,lwa,is_https,others),
  1839 +
  1840 + }
  1841 + },
  1842 + (Maybe($State) previous,
  1843 + String action_name,
  1844 + HTTP_Info http_info,
  1845 + List(Web_arg) lwa,
  1846 + Bool is_https) |->
  1847 + f(previous,action_name,http_info,lwa,is_https,actions).
  1848 +
  1849 +
  1850 +
  1851 +
  1852 +
  1853 +
  1854 +
  1855 +
  1856 + *** [4] Web site descriptions and the 'awp handlers'.
  1857 +
  1858 + *** [4.1] The type 'Web_Site'.
  1859 +
  1860 + The type 'Web_Site_Description' is defined in 'web/multihost_http_server.anubis'. We
  1861 + need another one, because, we have some extra informations to record for each site.
  1862 +
  1863 +public type Web_Site:
  1864 + web_site((Int32,Int32) -> Web_Site_Description description,
  1865 + List(String) -> One delete_out_of_date).
  1866 +
  1867 +
  1868 +
  1869 +
  1870 + *** [4.2] Making a web site description.
  1871 +
  1872 + Below is the function which creates a web site description. It first creates (if
  1873 + needed) the directories for the site, then constructs the tool functions for the site,
  1874 + and the site handler. Finally, it constructs the web site description.
  1875 +
  1876 + We gather common (constant) informations in the following type:
  1877 +
  1878 +type CommonInfo:
  1879 + info(String common_name,
  1880 + Int32 http_port,
  1881 + Int32 https_port,
  1882 + String site_directory,
  1883 + String authorization_secret
  1884 + ).
  1885 +
  1886 + We need a forward declaration.
  1887 +
  1888 +public define Printable_tree
  1889 + format
  1890 + (
  1891 + CommonInfo cinfo,
  1892 + String state_name,
  1893 + HTML_Page page,
  1894 + Bool is_https,
  1895 + String charset
  1896 + ).
  1897 +
  1898 +
  1899 +define Printable_tree
  1900 + format
  1901 + (
  1902 + HTML_Size s
  1903 + ) =
  1904 + if s is
  1905 + {
  1906 + absolute(x) then ["\"",x,"\""],
  1907 + percentage(x) then ["\"",x,"%\""]
  1908 + }.
  1909 +
  1910 +define Printable_tree
  1911 + top_redirection_page
  1912 + (
  1913 + String common_name
  1914 + ) =
  1915 + [ doctype_w3c_header,
  1916 + "<html><head>",
  1917 + "<meta http-equiv=\"Refresh\" content=\"0; URL=javascript:void window.open('/?t=','_top')\">",
  1918 + "</head><body></body></html>"
  1919 + ].
  1920 +
  1921 +public define Web_Site
  1922 + make_web_site_description
  1923 + (
  1924 + List(String) common_names, // for example: ["www.our-business.com"]
  1925 + String site_directory,
  1926 + One -> One init,
  1927 + (HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
  1928 + ($State expired,
  1929 + HTTP_Info,
  1930 + List(Web_arg),
  1931 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
  1932 + (HTTP_Info,
  1933 + List(Web_arg),
  1934 + Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
  1935 + List(Web_Action($SessionTicket, $State)) actions,
  1936 + (Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
  1937 + Int32 timeout,
  1938 + List(Redirection) redirections,
  1939 + String charset,
  1940 + List(String) journal_extensions,
  1941 + List(String) journal_headers,
  1942 + String secret,
  1943 + List(MIME) known_mime_types,
  1944 + (String action_name,
  1945 + List(Web_arg) args) -> One before_send_file
  1946 + ) =
  1947 + init(unique);
  1948 +
  1949 +
  1950 + //
  1951 + // make required directories (if needed)
  1952 + //
  1953 + with web_sites_directory = make_directory(my_anubis_directory+"/web_sites"),
  1954 + base_directory = make_directory(site_directory),
  1955 + state_directory = make_directory(site_directory+"/states"),
  1956 + forget(make_directory(site_directory+"/public"));
  1957 + //
  1958 + // construct tool functions
  1959 + //
  1960 + with save_state = make_save_state_function(timeout,state_directory),
  1961 + retrieve_state = make_retrieve_state_function(state_directory),
  1962 + separate_web_args = make_separate_web_args_function(state_directory,retrieve_state),
  1963 + apply_action = make_apply_action_function(actions),
  1964 + //
  1965 + // construct the site handler
  1966 + //
  1967 + site_handler = (Int32 http_port, Int32 https_port) |->
  1968 + ((String host_name,
  1969 + HTTP_Info http_info,
  1970 + List(Web_arg) lwa,
  1971 + Bool is_https) |->
  1972 + ((List(HTTP_header),Printable_tree))
  1973 + if separate_web_args(lwa) is
  1974 + {
  1975 + swa(mb_previous_state,mb_action_name,operands) then
  1976 + with state_and_headers = if mb_previous_state is
  1977 + {
  1978 + failure then
  1979 + if mb_action_name is
  1980 + {
  1981 + failure then initial_state(http_info),
  1982 + success(action_name) then
  1983 + apply_action(failure,action_name,http_info,operands,is_https)
  1984 + },
  1985 + success(previous_state) then if previous_state is
  1986 + {
  1987 + not_found then
  1988 + if mb_action_name is
  1989 + {
  1990 + failure then initial_state(http_info),
  1991 + success(_) then
  1992 + ticket_lost_state(http_info,lwa,is_https)
  1993 + },
  1994 +
  1995 + out_of_date(state) then
  1996 + ticket_expired_state(state,http_info,lwa,is_https),
  1997 +
  1998 + still_valid(state) then
  1999 + if mb_action_name is
  2000 + {
  2001 + failure then (failure, success(state), []),
  2002 + success(action_name) then
  2003 + apply_action(success(state),action_name,http_info,operands,is_https)
  2004 + }
  2005 + }
  2006 + },
  2007 + if state_and_headers is (session_ticket, mb_new_state, headers) then
  2008 + with state_name = save_state(mb_new_state),
  2009 + (headers,
  2010 + format(info(host_name, http_port, https_port, site_directory, secret),
  2011 + state_name,
  2012 + compute_page(session_ticket, mb_new_state),
  2013 + is_https,
  2014 + charset))
  2015 + }),
  2016 + //
  2017 + // make the delete_out_of_date function
  2018 + //
  2019 + delete_out_of_date =
  2020 + make_delete_out_of_date_states_function((Maybe($State))failure,
  2021 + site_directory+"/states"),
  2022 + //
  2023 + // construct the web site description
  2024 + //
  2025 + web_site((Int32 http_port, Int32 https_port) |->
  2026 + web_site_description(common_names,
  2027 + site_directory,
  2028 + redirections,
  2029 + charset,
  2030 + journal_extensions,
  2031 + journal_headers,
  2032 + secret,
  2033 + known_mime_types,
  2034 + site_handler(http_port,https_port),
  2035 + (List(Web_arg) lwa) |-> if separate_web_args(lwa) is
  2036 + swa(mb_previous_state,mb_action_name,operands) then
  2037 + if mb_action_name is
  2038 + {
  2039 + failure then unique
  2040 + success(an) then before_send_file(an,operands)
  2041 + }),
  2042 + delete_out_of_date).
  2043 +
  2044 +
  2045 +
  2046 +
  2047 + *** [4.3] Starting the servers.
  2048 +
  2049 +public define Start_Web_Sites_Result
  2050 + start_web_sites
  2051 + (
  2052 + Int32 ip_address, // the IP address shared by the web sites
  2053 + Int32 http_port, // usually: 80
  2054 + Int32 https_port, // usually: 443
  2055 + String ssl_certificate_common_name,
  2056 + List(Web_Site) web_sites, // web sites to be started
  2057 + Var(Bool) shutdown_required
  2058 + ) =
  2059 + with get_description = (Web_Site ws) |-> description(ws)(http_port,https_port),
  2060 + with http_server_r =
  2061 + start_http_server(ip_address,http_port,
  2062 + map(get_description,web_sites),
  2063 + load_denial_of_service_info),
  2064 + with https_server_r =
  2065 + start_https_server(ip_address,https_port,
  2066 + ssl_certificate_common_name,
  2067 + map(get_description,web_sites),
  2068 + load_denial_of_service_info),
  2069 + if http_server_r is ok(http_server)
  2070 + then
  2071 + (
  2072 + if https_server_r is ok(https_server)
  2073 + then
  2074 + (
  2075 + start_http_servers_tasks(map(get_description,web_sites),
  2076 + [http_server,https_server],
  2077 + 600); // period of 10 minutes
  2078 + delegate
  2079 + delete_states_loop(
  2080 + map((Web_Site ws) |->
  2081 + (site_directory(description(ws)(http_port,https_port))+
  2082 + "/states",delete_out_of_date(ws)),
  2083 + web_sites),
  2084 + 3600*24*3, // keep out of date states 3 days
  2085 + now,
  2086 + http_server,
  2087 + https_server,
  2088 + shutdown_required),
  2089 + ok(http_server,https_server)
  2090 + )
  2091 + else cannot_bind_to_port(https_port)
  2092 + )
  2093 + else
  2094 + (
  2095 + if https_server_r is ok(https_server)
  2096 + then cannot_bind_to_port(http_port)
  2097 + else cannot_bind_to_port(http_port,https_port)
  2098 + ).
  2099 +
  2100 +
  2101 +public define One
  2102 + start_web_sites
  2103 + (
  2104 + Int32 ip_address, // the IP address shared by the web sites
  2105 + Int32 http_port, // usually: 80
  2106 + Int32 https_port, // usually: 443
  2107 + String ssl_certificate_common_name,
  2108 + List(Web_Site) web_sites, // web sites to be started
  2109 + Var(Bool) shutdown_required
  2110 + ) =
  2111 + if (Start_Web_Sites_Result)start_web_sites(ip_address,
  2112 + http_port,
  2113 + https_port,
  2114 + ssl_certificate_common_name,
  2115 + web_sites,
  2116 + shutdown_required) is
  2117 + {
  2118 + cannot_bind_to_port(n) then print("Cannot bind to port: "+n+"\n"),
  2119 + cannot_bind_to_port(n,m) then print("Cannot bind to ports: "+n+", "+m+"\n"),
  2120 + ok(s1,s2) then print("Servers started.\n")
  2121 + }.
  2122 +
  2123 +
  2124 +
  2125 +
  2126 +
  2127 + *** [5] HTML Formating.
  2128 +
  2129 + We need to translate HTML elements as defined above into actual HTML text.
  2130 +
  2131 + Actioners require special informations, which must be transmitted when needed by the
  2132 + 'format' functions:
  2133 +
  2134 + - the 'common name', which is used for URLs,
  2135 + - the HTTP/HTTPS port number,
  2136 + - the 'state name', which must be transmitted when the actioner is clicked upon,
  2137 + - the 'form name' (if any) to which the actioner refers.
  2138 +
  2139 + If the actioner is off form, and if it refers to a form, the name of that form is
  2140 + already known by the actioner. On the contrary, if the actioner is 'in form', it refers
  2141 + implicitly to the form containing it. The name of that form is transmitted to the
  2142 + 'format' functions called from within the formating of that form.
  2143 +
  2144 +
  2145 +
  2146 +
  2147 + *** [5.1] The type 'HTML_Any($T)'.
  2148 +
  2149 + The type 'HTML_Any($T)' gathers elements which may be put anywhere in the page. The
  2150 + parameter $T becomes either 'HTML_Off_Form' or 'HTML_In_Form'.
  2151 +
  2152 +type HTML_Any($T):
  2153 + any_text (List(Text_Option), String the_text),
  2154 + any_preformated (List(Text_Option), String),
  2155 + any_paragraph (List(Text_Option), String the_text),
  2156 + any_image (String url),
  2157 + any_image (String url, Int32 width, Int32 height),
  2158 + any_table (List(Table_Option), List(HTML_Row($T))),
  2159 + any_center ($T),
  2160 + any_mail_to (String email, $T element),
  2161 + any_scroller (Int32 width, Int32 height,
  2162 + Int32 content_width, Int32 content_height,
  2163 + $T content),
  2164 + any_fixed_size (HTML_Size width, HTML_Size height, $T content),
  2165 + any_fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
  2166 + any_actioner (Actioner_Connection,
  2167 + Actioner_Target,
  2168 + Actioner_Aspect,
  2169 + String action_name,
  2170 + List((String,String)) extra_ops,
  2171 + List(Actioner_Local_Action),
  2172 + Maybe(String) form_name),
  2173 + any_foreign_link (List(Text_Option), String url, String name),
  2174 + any_private_download (String abs_path, String name, String extra_ext,
  2175 + Maybe((String,List((String,String))))),
  2176 + any_div (List(DIV_Option), $T element),
  2177 + any_div_empty (List(DIV_Option)),
  2178 + any_coreattrs (List(CoreAttrs)).
  2179 +
  2180 +
  2181 +
  2182 + *** [5.2] Formating a color.
  2183 +
  2184 + RGB colors are formatted as '#rrggbb' where rr, gg and bb are two characters
  2185 + hexadecimal values.
  2186 +
  2187 +define String
  2188 + html_format
  2189 + (
  2190 + RGB color
  2191 + ) =
  2192 + if color is rgb(r,g,b) then
  2193 + "#" + hexadecimal(word8_to_int32(r),2)
  2194 + + hexadecimal(word8_to_int32(g),2)
  2195 + + hexadecimal(word8_to_int32(b),2).
  2196 +
  2197 +
  2198 + The following is a very arbitrary definition of the opposite color. The thing which is
  2199 + important is that it is far from the original, so that characters in 'opposite' color
  2200 + are clearly visible over the original.
  2201 +
  2202 +define RGB
  2203 + opposite
  2204 + (
  2205 + RGB color
  2206 + ) =
  2207 + if color is rgb(r,g,b) then
  2208 + with r1 = word8_to_int32(r),
  2209 + with g1 = word8_to_int32(g),
  2210 + with b1 = word8_to_int32(b),
  2211 + rgb(truncate_to_word8(255-r1),
  2212 + truncate_to_word8(255-g1),
  2213 + truncate_to_word8(255-b1)).
  2214 +
  2215 +
  2216 +
  2217 +
  2218 + *** [5.3] Creating buttons.
  2219 +
  2220 + We want to be able to create buttons in the form of a pair of images (rollovers)
  2221 + automatically. We use the JPEG interface, because for the time being Anubis cannot
  2222 + handle other kinds of images.
  2223 +
  2224 +
  2225 + Computing printed text length.
  2226 +
  2227 + define Int32
  2228 + printed_text_width
  2229 + (
  2230 + Word8 -> Int32 char_size,
  2231 + List(Word8) l
  2232 + ) =
  2233 + if l is
  2234 + {
  2235 + [] then (Int32) 0,
  2236 + [h . t] then char_size(h) + 1+ printed_text_width(char_size,t)
  2237 + }.
  2238 +
  2239 + define Int32
  2240 + printed_text_width
  2241 + (
  2242 + SystemFont font,
  2243 + String s
  2244 + ) =
  2245 + printed_text_width((Word8 c) |-> word8_to_int32(width(get_char_info(font,c))),
  2246 + explode(s)).
  2247 +
  2248 +
  2249 +
  2250 + Converting RGB to RGBA.
  2251 +
  2252 +define RGBA
  2253 + to_rgba
  2254 + (
  2255 + RGB color
  2256 + ) =
  2257 + if color is rgb(r,g,b) then rgba(r,g,b,255).
  2258 +
  2259 +
  2260 + Drawing a 'relief'.
  2261 +
  2262 + define One
  2263 + draw_relief
  2264 + (
  2265 + RGBAImage dest,
  2266 + RGBA color,
  2267 + Int32 contrast,
  2268 + Int32 x,
  2269 + Int32 y,
  2270 + Int32 width,
  2271 + Int32 height
  2272 + ) =
  2273 + with l = lighten(color,contrast),
  2274 + d = darken(color,contrast),
  2275 + draw_rectangle(dest,rect(x,y,x+width,y+1),l);
  2276 + draw_rectangle(dest,rect(x,y+1,x+1,y+height),l);
  2277 + draw_rectangle(dest,rect(x+width-1,y+1,x+width,y+height),d);
  2278 + draw_rectangle(dest,rect(x+1,y+height-1,x+width-1,y+height),d).
  2279 +
  2280 +
  2281 + Creating a button background.
  2282 +
  2283 + define RGBAImage
  2284 + create_button_background
  2285 + (
  2286 + RGBA color,
  2287 + Int32 width,
  2288 + Int32 height
  2289 + ) =
  2290 + with result = create_rgba_image(width,height,color),
  2291 + draw_relief(result,color,100,0,0,width,height);
  2292 + draw_relief(result,color,70,1,1,width-2,height-2);
  2293 + draw_relief(result,color,55,2,2,width-4,height-4);
  2294 + draw_relief(result,color,35,3,3,width-6,height-6);
  2295 + draw_relief(result,color,20,4,4,width-8,height-8);
  2296 + draw_relief(result,color,10,5,5,width-10,height-10);
  2297 + draw_relief(result,color,5,6,6,width-12,height-12);
  2298 + result.
  2299 +
  2300 +
  2301 + Drawing the text over the background.
  2302 +
  2303 + define One
  2304 + draw_button_text
  2305 + (
  2306 + RGBAImage image,
  2307 + String text,
  2308 + Int32 text_index,
  2309 + Int32 pixel_x,
  2310 + Int32 y,
  2311 + Rectangle clip,
  2312 + RGBA color,
  2313 + SystemFont font,
  2314 + ) =
  2315 + if nth(text_index,text) is
  2316 + {
  2317 + failure then unique,
  2318 + success(c) then
  2319 + with cw = draw_system_character(image,clip,pixel_x,y,font,word8_to_int32(c),color),
  2320 + draw_button_text(image,text,text_index+1,pixel_x+cw+1,y,clip,color,font)
  2321 + }.
  2322 +
  2323 + define One
  2324 + draw_button_text
  2325 + (
  2326 + RGBAImage image,
  2327 + String text,
  2328 + Int32 text_width,
  2329 + RGBA light_color,
  2330 + RGBA dark_color,
  2331 + SystemFont font
  2332 + ) =
  2333 + with image_width = width(image),
  2334 + image_height = height(image),
  2335 + x_pos = (image_width-text_width)>>1,
  2336 + clip = rect(0,0,image_width,image_height),
  2337 + new_light_color = lighten(light_color,150),
  2338 + new_dark_color = darken(dark_color,40),
  2339 + draw_button_text(image, text, 0, x_pos+2, 16, clip, new_dark_color, font);
  2340 + draw_button_text(image, text, 0, x_pos, 14, clip, new_light_color, font).
  2341 +
  2342 +
  2343 + The next function creates the two images for a button. The information given is the
  2344 + main color of the button, the text of the button and the minimal width (in pixels) of
  2345 + the button. The function does not create the button if the images already exist. The
  2346 + two images are stored in the directory 'site_directory/buttons'. The names of the files
  2347 + are of the form:
  2348 +
  2349 + bxxxx_off.jpg
  2350 + bxxxx_on.jpg
  2351 +
  2352 + where the prefix 'b' is to avoid leading '-' which may perturb UNIX commands (like
  2353 + 'rm'), and where 'xxxx' is created from the given informations by the formula:
  2354 +
  2355 + xxxx = web_arg_encode(sha1((color,text,width)))
  2356 +
  2357 + Hence, distinct informations give distinct file names.
  2358 +
  2359 +
  2360 + define String // returns xxxx
  2361 + create_button_images
  2362 + (
  2363 + String site_directory,
  2364 + RGBA color,
  2365 + String text,
  2366 + Int32 width,
  2367 + SystemFont font
  2368 + ) =
  2369 + with xxxx = web_arg_encode(sha1((color,text,width))),
  2370 + buttons_dir = site_directory+"/public/buttons",
  2371 + off_filepath = buttons_dir+"/b"+xxxx+"_off.jpg",
  2372 + on_filepath = buttons_dir+"/b"+xxxx+"_on.jpg",
  2373 + if file_exists(on_filepath)
  2374 + then xxxx
  2375 + else with
  2376 + text_width = printed_text_width(font,text),
  2377 + button_width = max(width,text_width+12),
  2378 + button_height = (Int32)20,
  2379 + light_color = lighten(color,60),
  2380 + very_light_color = lighten(light_color,30),
  2381 + dark_color = darken(color,40),
  2382 + background_off =
  2383 + create_button_background(color,button_width,button_height),
  2384 + background_on =
  2385 + create_button_background(light_color,button_width,button_height),
  2386 +
  2387 + draw_button_text(background_off,text,text_width,very_light_color,dark_color,font);
  2388 + draw_button_text(background_on, text,text_width,very_light_color,dark_color,font);
  2389 + forget(write_image_to_JPEG_file(to_JPEG(background_off),
  2390 + off_filepath,
  2391 + 100));
  2392 + forget(write_image_to_JPEG_file(to_JPEG(background_on),
  2393 + on_filepath,
  2394 + 100));
  2395 + xxxx.
  2396 +
  2397 +
  2398 +
  2399 +
  2400 +
  2401 + *** [5.4] Formating an actioner.
  2402 +
  2403 + An actioner works as follows. Assume first that it refers to a form. When it is clicked
  2404 + upon, the actioner puts (via 'onMouseDown') the URL into the 'action' attribute of the
  2405 + form, and submits the form, using the JavaScript command 'form_name.submit()'. If the
  2406 + actioner does not refer to a form, it fires the URL directly via 'href', because in
  2407 + that case, the actioner is always an <a> tag.
  2408 +
  2409 + The URL itself is composed using the connection sort (same, http or https), the common
  2410 + name and port number (if needed), the state name, the action name, and the extra
  2411 + operands, which are put into a query string. It may look like this:
  2412 +
  2413 + http://common_name:port/?s=state_name&a=action_name&oname=value...
  2414 +
  2415 + Each extra operand is a pair of strings: (name,value). It is formated as:
  2416 +
  2417 + &oname=value
  2418 +
  2419 +
  2420 +define String
  2421 + format_extra_operands
  2422 + (
  2423 + List((String,String)) l
  2424 + ) =
  2425 + if l is
  2426 + {
  2427 + [ ] then "",
  2428 + [h . t] then if h is (n,v) then
  2429 + "&amp;o"+n+"="+v+format_extra_operands(t)
  2430 + }.
  2431 +
  2432 +
  2433 + It seams that the standard requires "&amp;" instead of "&" !
  2434 +
  2435 + In case the target is another window, we need to format the options for this window.
  2436 +
  2437 +define String
  2438 + format
  2439 + (
  2440 + List(Other_Window_Option) l
  2441 + ) =
  2442 + if l is
  2443 + {
  2444 + [ ] then "",
  2445 + [h . t] then if h is
  2446 + {
  2447 + resizable then "resizable",
  2448 + scrollbars then "scrollbars",
  2449 + width(w) then "width="+w,
  2450 + height(h) then "height="+h
  2451 + } + if t is [ ] then "" else (","+format(t))
  2452 + }.
  2453 +
  2454 +
  2455 +
  2456 + Formating choices for a <select> tag.
  2457 +
  2458 +public define Printable_tree
  2459 + format_choices
  2460 + (
  2461 + List(String) l
  2462 + ) =
  2463 + if l is
  2464 + {
  2465 + [ ] then [ ],
  2466 + [h . t] then ["<option>",h . format_choices(t)]
  2467 + }.
  2468 +
  2469 +
  2470 +public define Printable_tree
  2471 + format_choices
  2472 + (
  2473 + List(String) l,
  2474 + String selected
  2475 + ) =
  2476 + if l is
  2477 + {
  2478 + [ ] then [ ],
  2479 + [h . t] then if h = selected
  2480 + then ["<option selected>",h . format_choices(t)]
  2481 + else ["<option>",h . format_choices(t,selected)]
  2482 + }.
  2483 +
  2484 +
  2485 +public define Printable_tree
  2486 + format_choices
  2487 + (
  2488 + List((String,String)) l
  2489 + ) =
  2490 + if l is
  2491 + {
  2492 + [ ] then [ ],
  2493 + [h . t] then
  2494 + if h is (val,item)
  2495 + then ["<option value=\""+val+"\">",item . format_choices(t)]
  2496 + }.
  2497 +
  2498 +public define Printable_tree
  2499 + format_choices
  2500 + (
  2501 + List((String,String)) l,
  2502 + String selected
  2503 + ) =
  2504 + if l is
  2505 + {
  2506 + [ ] then [ ],
  2507 + [h . t] then
  2508 + if h is (val,item) then
  2509 + if item = selected
  2510 + then ["<option value=\""+val+"\" selected>",item . format_choices(t)]
  2511 + else ["<option value=\""+val+"\">",item . format_choices(t,selected)]
  2512 + }.
  2513 +
  2514 +
  2515 +
  2516 +variable Int32 count = 0.
  2517 +
  2518 + Note: this counter is private to the virtual machine, hence there is one counter by
  2519 + client.
  2520 +
  2521 +define Int32
  2522 + new_count
  2523 + =
  2524 + count <- *count+1;
  2525 + *count.
  2526 +
  2527 +
  2528 + The next function composes the URL. It is a JavaScript URL when the target is another
  2529 + window.
  2530 +
  2531 +define String
  2532 + make_actioner_url
  2533 + (
  2534 + CommonInfo cinfo,
  2535 + Actioner_Connection connection,
  2536 + Actioner_Target target,
  2537 + String state_name,
  2538 + String action_name,
  2539 + List((String,String)) extra_ops,
  2540 + Bool is_https
  2541 + ) =
  2542 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  2543 + with strict_url =
  2544 + if connection is
  2545 + {
  2546 + same then "/",
  2547 + /*
  2548 + same then if is_https
  2549 + then "https://"+common_name+":"+https_port+"/"
  2550 + else "http://"+common_name+":"+https_port+"/",
  2551 + */
  2552 +
  2553 + http then "http://"+common_name+":"+http_port+"/",
  2554 + https then "https://"+common_name+":"+https_port+"/",
  2555 + } +
  2556 + "?s=" + state_name + "&amp;a=" + action_name +
  2557 + format_extra_operands(extra_ops),
  2558 + if target is
  2559 + {
  2560 + same then strict_url,
  2561 + same(label) then strict_url+"#"+label,
  2562 + other(wn,ops) then
  2563 + "javascript:void window.open('"+strict_url+"&amp;t="+wn+"','"+
  2564 + "w"+to_ascii(sha1(wn))+"','"+format(ops)+"')"
  2565 + }.
  2566 +
  2567 +
  2568 + Depending on the fact that the actioner refers to a form or not, the URL is used in two
  2569 + different ways. If the actioner does not refer to a form, it is realized by a '<a>'
  2570 + tag, with a 'href' attribute. If it refers to a form, it is still realized by a '<a>'
  2571 + tag, but with no href attribute. In this case, we use the 'onMouseDown' or 'onChange'
  2572 + event handler. The handler calls a JavaScript function which puts the URL as the value
  2573 + of the 'action' attribute of the form, and submits the form.
  2574 +
  2575 +type URL_or_JavaScript:
  2576 + url (String),
  2577 + javascript (Printable_tree script, Printable_tree handler).
  2578 +
  2579 + define URL_or_JavaScript
  2580 + format_action
  2581 + (
  2582 + String the_url,
  2583 + Maybe(String) mb_form_name
  2584 + ) =
  2585 + url(the_url).
  2586 +
  2587 +
  2588 + It was as shown below, in order to allow submission of a form from outside the form,
  2589 + but this makes problems:
  2590 +
  2591 +define URL_or_JavaScript
  2592 + format_action
  2593 + (
  2594 + String the_url,
  2595 + Maybe(String) mb_form_name
  2596 + ) =
  2597 + if mb_form_name is
  2598 + {
  2599 + failure then
  2600 + url(the_url),
  2601 + success(form_name) then
  2602 + with n = new_count,
  2603 + javascript(
  2604 + [
  2605 + "<script type=\"text/javascript\" language =\"JavaScript\">\n",
  2606 + "function pfu",form_name,n,"() {\n",
  2607 + " var fa = document.forms.f",form_name,";\n",
  2608 + " var u = \"",the_url,"\";\n",
  2609 + " fa.action = u;\n",
  2610 + " fa.submit(); }",
  2611 + "</script>"
  2612 + ],
  2613 + ["pfu",form_name,n,"();"]
  2614 + )
  2615 + }.
  2616 +
  2617 +
  2618 +
  2619 + Now, we format the actioner according to its aspect.
  2620 +
  2621 +define List(Text_Option)
  2622 + get_text_options
  2623 + (
  2624 + List(Text_Option) l
  2625 + )
  2626 + =
  2627 + if l is
  2628 + {
  2629 + [] then [],
  2630 + [h . t ] then
  2631 + if h is class(class) then
  2632 + get_text_options(t)
  2633 + else
  2634 + [ h . get_text_options(t) ]
  2635 +
  2636 + }.
  2637 +
  2638 + /**
  2639 + * Extract the CSS class list from the list of Text_Option
  2640 + */
  2641 +define List(Text_Option)
  2642 + get_css_class
  2643 + (
  2644 + List(Text_Option) l
  2645 + )
  2646 + =
  2647 + if l is
  2648 + {
  2649 + [] then [],
  2650 + [h . t ] then
  2651 +
  2652 +
  2653 + if h is class(class) then
  2654 + [ h . get_css_class(t) ]
  2655 + else
  2656 + get_css_class(t)
  2657 + }
  2658 + .
  2659 +
  2660 +define String
  2661 + format_text_options
  2662 + (
  2663 + List(Text_Option) l
  2664 + )
  2665 + =
  2666 + with text_options = get_text_options(l),
  2667 + css_classes = get_css_class(l),
  2668 + if text_options is
  2669 + {
  2670 + [] then "",
  2671 + [_._] then " style=\"" + format(text_options) + "\" "
  2672 + }
  2673 + +
  2674 + if css_classes is
  2675 + {
  2676 + [] then "",
  2677 + [_._] then format(css_classes)
  2678 + }.
  2679 +
  2680 +define Printable_tree
  2681 + format
  2682 + (
  2683 + List(Actioner_Local_Action) l
  2684 + ) =
  2685 + if l is
  2686 + {
  2687 + [ ] then [ ],
  2688 + [h . t] then [if h is
  2689 + {
  2690 + close_window then [" window.close(); "]
  2691 + }
  2692 + . format(t)]
  2693 + }.
  2694 +
  2695 +define String
  2696 + format_coreattrs
  2697 + (
  2698 + List(CoreAttrs) attributs
  2699 + )=
  2700 + if attributs is
  2701 + {
  2702 + [] then "",
  2703 + [h .t] then
  2704 + with current = if h is
  2705 + {
  2706 + id(id_name) then
  2707 + " id=\"" + id_name + "\"",
  2708 + class(class_name) then
  2709 + " class=\"" + class_name + "\"",
  2710 + style(style_string) then
  2711 + " style=\"" + style_string + "\"",
  2712 +
  2713 + title(title_string) then
  2714 + " title=\"" + title_string + "\"",
  2715 + },
  2716 + current + format_coreattrs(t)
  2717 + }.
  2718 +
  2719 +define String
  2720 + _format
  2721 + (
  2722 + List(DIV_Option) opt
  2723 + )=
  2724 + if opt is
  2725 + {
  2726 + [] then "",
  2727 + [h .t] then
  2728 + with current = if h is
  2729 + {
  2730 + id(id_name) then
  2731 + " id=\"" + id_name + "\"",
  2732 + class(class_name) then
  2733 + " class=\"" + class_name + "\"",
  2734 + style(style_string) then
  2735 + " style=\"" + style_string + "\"",
  2736 +
  2737 + title(title_string) then
  2738 + " title=\"" + title_string + "\"",
  2739 + lang(lang) then
  2740 + " xml:lang=" + lang,
  2741 + dir(reading_Way) then
  2742 + if reading_Way is
  2743 + {
  2744 + ltr then " dir=ltr",
  2745 + rtl then " dir=rtl"
  2746 + }
  2747 + },
  2748 + current + _format(t)
  2749 + }
  2750 + .
  2751 +
  2752 +define Printable_tree
  2753 + format_div_option
  2754 + (
  2755 + List(DIV_Option) options
  2756 + )=
  2757 + ["<DIV" + _format(options) + ">"] .
  2758 +
  2759 +
  2760 +define Printable_tree
  2761 + format_actioner
  2762 + (
  2763 + CommonInfo cinfo,
  2764 + String state_name,
  2765 + Actioner_Connection connection,
  2766 + Actioner_Target target,
  2767 + Actioner_Aspect aspect,
  2768 + String action_name,
  2769 + List((String,String)) extra_ops,
  2770 + List(Actioner_Local_Action) local_actions,
  2771 + Maybe(String) mb_form_name,
  2772 + Bool is_https,
  2773 + ) =
  2774 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  2775 + with url = make_actioner_url(cinfo,connection,target,
  2776 + state_name,action_name,extra_ops,is_https),
  2777 + with action = format_action(url,mb_form_name),
  2778 + if aspect is
  2779 + {
  2780 +
  2781 + link(opt, text) then [
  2782 + if action is
  2783 + {
  2784 + url(u) then
  2785 + ["<a href=\"",u,"\"", format_text_options(opt), ">"],
  2786 +// ["<a href=\"",u,"\" style=\"",format(reverse(opt)),"\">"],
  2787 + javascript(s,h) then
  2788 + [s,"<a href=\"javascript: ",h,"\">"]
  2789 + },
  2790 + text,
  2791 + "</a>"
  2792 + ],
  2793 +
  2794 + push_button(options, text) then
  2795 + [
  2796 + if action is
  2797 + {
  2798 + url(u) then ["<a href=\"",u,"\"", format_coreattrs(options), ">",
  2799 + text, "</a>"
  2800 + ],
  2801 + javascript(s,h) then
  2802 + [s,"<INPUT TYPE =\"button\" Value=\"",text,"\"", format_coreattrs(options), " onClick=\"",h,"\">"]
  2803 + }
  2804 + ],
  2805 +
  2806 + button(url_off,url_on) then
  2807 + [ if action is
  2808 + {
  2809 + url(u) then ["<a href=\"",u],
  2810 + javascript(s,h) then [s,"<a onMouseDown=\"",h]
  2811 + },
  2812 + "\" style=\"text-decoration:none\">",
  2813 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2814 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2815 + " onMouseOut=\"this.src='",url_off,"'\">",
  2816 + "</a>"
  2817 + ],
  2818 +
  2819 + button(url_off,url_on,w,h) then
  2820 + [ if action is
  2821 + {
  2822 + url(u) then ["<a href=\"",u],
  2823 + javascript(s,h) then [s,"<a onMouseDown=\"",h]
  2824 + },
  2825 + "\" style=\"text-decoration:none\">",
  2826 + "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2827 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2828 + " onMouseOut=\"this.src='",url_off,"'\">",
  2829 + "</a>"
  2830 + ],
  2831 +
  2832 + immediate_selector(name,size,choices) then
  2833 + [ if action is
  2834 + {
  2835 + url(u) then ["<select href=\"",u]
  2836 + javascript(s,h) then [s,"<select onchange=\"",h]
  2837 + },
  2838 + "\" name=o",name," size=",size,">",
  2839 + format_choices(choices),"</select>"
  2840 + ]
  2841 + }.
  2842 +
  2843 +
  2844 +define Printable_tree
  2845 + format_local_popup_button
  2846 + (
  2847 + CommonInfo cinfo,
  2848 + Actioner_Aspect aspect,
  2849 + Int32 n,
  2850 + ) =
  2851 + if cinfo is info(common_name,http_port,https_port,site_dir,secret) then
  2852 + [ "<script type=\"text/javascript\" language=\"JavaScript\">",
  2853 + " var lpust_",n," = new Array(); ",
  2854 + " lpust_",n,"[0] = 0; ",
  2855 + "</script>",
  2856 + "<a href=\"javascript:show_local_popup('lpu_",n,"','lpust_",n,"');\">",
  2857 + if aspect is
  2858 + {
  2859 + link(opt,text) then [text],
  2860 + push_button(opt, text) then [text],
  2861 + button(url_off,url_on) then
  2862 + [
  2863 + "<img alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2864 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2865 + " onMouseOut=\"this.src='",url_off,"'\">",
  2866 + ],
  2867 +
  2868 + button(url_off,url_on,w,h) then
  2869 + [
  2870 + "<img width=",w," height=",h," alt=\"",url_off,"\" src=\"",url_off,"\" border=0",
  2871 + " onMouseOver=\"this.src='",url_on,"'\" ",
  2872 + " onMouseOut=\"this.src='",url_off,"'\">",
  2873 + ],
  2874 +
  2875 + immediate_selector(name,size,choices) then alert,
  2876 +
  2877 + },
  2878 + "</a>"].
  2879 +
  2880 +
  2881 +
  2882 +
  2883 +
  2884 + *** [5.5] Formating a private download link.
  2885 +
  2886 + We get the absolute path of the file to be downloaded, and the name under which it
  2887 + should appear to the client. The function 'format_private_download' creates an
  2888 + hypertext link for downloading the file. The secured mecanism of private download is
  2889 + used. This function is called by the function which formats HTML_Any($T) elements.
  2890 +
  2891 +
  2892 +define Printable_tree
  2893 + format_private_download
  2894 + (
  2895 + CommonInfo cinfo,
  2896 + String sn, // state name
  2897 + String abs_path, // absolute file path on server
  2898 + String name, // name of file as it appears in the browser
  2899 + String extra, // extra extension
  2900 + Maybe((String,List((String,String)))) action
  2901 +
  2902 + ) =
  2903 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  2904 + with private_download_directory = site_directory+"/private_download",
  2905 + with auth = make_authorization(site_directory,secret,abs_path),
  2906 + [
  2907 + "<a href=\"",name,extra,"?zauth=",auth,
  2908 + if action is
  2909 + {
  2910 + failure then [ ]
  2911 + success(a) then if a is (an,args) then
  2912 + ["&amp;a=",an,format_extra_operands(args)]
  2913 + },
  2914 + "\">",
  2915 + name,
  2916 + "</a>"
  2917 + ].
  2918 +
  2919 +
  2920 +
  2921 +
  2922 + *** [5.6] Formating rows and cells in a table.
  2923 +
  2924 +define Int32
  2925 + percent
  2926 + (
  2927 + Int32 p
  2928 + ) =
  2929 + if p < 0 then 0 else if p > 100 then 100 else p.
  2930 +
  2931 +
  2932 +
  2933 +
  2934 + Formating cell options.
  2935 +
  2936 +
  2937 +
  2938 +define String
  2939 + format
  2940 + (
  2941 + BackgroundOption o
  2942 + ) =
  2943 + if o is
  2944 + {
  2945 + repeat then "",
  2946 + repeat_horizontal then "; background-repeat: repeat-x",
  2947 + repeat_vertical then "; background-repeat: repeat-y",
  2948 + no_repeat then "; background-repeat: no-repeat",
  2949 + center then "; background-position: center top"
  2950 + }.
  2951 +
  2952 +define String
  2953 + format
  2954 + (
  2955 + List(BackgroundOption) l
  2956 + ) =
  2957 + if l is
  2958 + {
  2959 + [ ] then "",
  2960 + [h . t] then format(h)+format(t)
  2961 + }.
  2962 +
  2963 +
  2964 +define String
  2965 + format
  2966 + (
  2967 + List(Cell_Option) options
  2968 + ) =
  2969 + if options is
  2970 + {
  2971 + [ ] then "",
  2972 + [h . t] then
  2973 + if h is
  2974 + {
  2975 + left then " align=left",
  2976 + h_center then " align=center",
  2977 + right then " align=right",
  2978 + top then " valign=top",
  2979 + v_center then " valign=middle",
  2980 + bottom then " valign=bottom",
  2981 + base_line then " valign=baseline",
  2982 + background_color(c) then " bgcolor=\""+html_format(c)+"\"",
  2983 + background_image(n,o) then " style=\"background: url("+n+")"+format(o)+"\"",
  2984 + width(w) then " width=\""+w+"\"",
  2985 + percentage_width(n) then " width=\""+percent(n)+"%\"",
  2986 + height(h) then " height="+h,
  2987 + columns(n) then " colspan="+n,
  2988 + rows(n) then " rowspan="+n,
  2989 + nowrap then " nowrap"
  2990 + }
  2991 + + format(t)
  2992 + }.
  2993 +
  2994 +
  2995 + Normalizing a list of cell options (horizontal position must be specified; the default
  2996 + is 'left').
  2997 +
  2998 +define List(Cell_Option)
  2999 + normalize
  3000 + (
  3001 + List(Cell_Option) l
  3002 + ) =
  3003 + if member(l,left) then l else
  3004 + if member(l,h_center) then l else
  3005 + if member(l,right) then l else
  3006 + [left . l].
  3007 +
  3008 +
  3009 + Formating cells in a row.
  3010 +
  3011 +define Printable_tree
  3012 + format
  3013 + (
  3014 + List(HTML_Cell($T)) cells,
  3015 + $T -> Printable_tree format_element
  3016 + ) =
  3017 + if cells is
  3018 + {
  3019 + [ ] then [ ],
  3020 + [h . t] then if h is cell(options,element) then
  3021 + ["<td ",format(reverse(normalize(options))),">",
  3022 + format_element(element),
  3023 + "</td>"
  3024 + . format(t,format_element)]
  3025 + }.
  3026 +
  3027 +
  3028 + Formating the rows in a table.
  3029 +
  3030 +define Printable_tree
  3031 + format
  3032 + (
  3033 + List(HTML_Row($T)) rows,
  3034 + $T -> Printable_tree format_element,
  3035 + ) =
  3036 + if rows is
  3037 + {
  3038 + [ ] then [ ],
  3039 + [h . t] then if h is row(options,cells) then
  3040 + ["<tr ",format(reverse(options)),">",
  3041 + format(cells,format_element),
  3042 + "</tr>"
  3043 + . format(t,format_element)]
  3044 + }.
  3045 +
  3046 +
  3047 +
  3048 +define Printable_tree
  3049 + format1
  3050 + (
  3051 + List(TextAreaOption) l
  3052 + ) =
  3053 + if l is
  3054 + {
  3055 + [] then [],
  3056 + [h . t] then if h is
  3057 + {
  3058 + disabled then [" disabled " . format1(t)]
  3059 + read_only then [" readonly " . format1(t)]
  3060 + wrap_lines then [" wrap " . format1(t)]
  3061 + }
  3062 + }.
  3063 +
  3064 +define Printable_tree
  3065 + format
  3066 + (
  3067 + List(TextAreaOption) l
  3068 + ) =
  3069 + if member(l,wrap_lines)
  3070 + then format1(l)
  3071 + else [" wrap=off " . format1(l)].
  3072 +
  3073 +
  3074 + *** [5.7] Formating elements which may be put anywhere.
  3075 +
  3076 + The function below involves the parameter $T which is later instantiated as
  3077 + 'HTML_In_Form' or as 'HTML_Off_Form'. Now, since there are dictinct 'format' functions
  3078 + for these two types, and because formating of tables requires recursive calls of such
  3079 + functions, it is necessary to provide the 'format' function to be called recursively as
  3080 + an argument. Putting naively a call to 'format' will not work, because the compiler
  3081 + will look for a function able to format data of type $T (which is at that time distinct
  3082 + from any other type, including our two types). Such a function does not exist. Hence
  3083 + the function to be called for formating elements must be passed as a functional
  3084 + argument (called 'format_element' below). Actually, what we pass is a function taking
  3085 + a unique argument of type $T. Other informations (like the name of the state) are
  3086 + already in the function by way of full functionality.
  3087 +
  3088 +
  3089 + Formating text options. They are formated in CSS syntax, to be used within a
  3090 + 'style=...'.
  3091 +
  3092 +define String
  3093 + format
  3094 + (
  3095 + List(Text_Option) l
  3096 + ) =
  3097 + if l is
  3098 + {
  3099 + [ ] then "",
  3100 + [h . t] then if h is
  3101 + {
  3102 + size(n) then "font-size:"+n+"pt",
  3103 + font(fn) then "font-family:"+fn,
  3104 + color(c) then if c is rgb(r,g,b) then
  3105 + "color:rgb("+word8_to_int32(r)+","+word8_to_int32(g)+","+word8_to_int32(b)+")",
  3106 + italic then "font-style:italic",
  3107 + oblique then "font-style:oblique",
  3108 + small_capitals then "font-variant:small-caps",
  3109 + bold then "font-weight:bold",
  3110 + underlined then "text-decoration:underline",
  3111 + left_justified then "text-align:left",
  3112 + right_justified then "text-align:right",
  3113 + justified then "text-align:justify",
  3114 + line_through then "text-decoration:line-through",
  3115 + nowrap then "white-space:nowrap",
  3116 + class(class_name)then " class=\"" +class_name +"\""
  3117 + } + if t is [ ] then "" else ("; "+format(t))
  3118 + }.
  3119 +
  3120 +
  3121 +
  3122 + Formating table options.
  3123 +
  3124 +define String
  3125 + format
  3126 + (
  3127 + List(Table_Option) l,
  3128 + Bool border_seen
  3129 + ) =
  3130 + if l is
  3131 + {
  3132 + [ ] then if border_seen then "" else " border=0 cellspacing=0 cellpadding=0",
  3133 + [h . t] then if h is
  3134 + {
  3135 + background_color(c) then " bgcolor=\""+html_format(c)+"\""+format(t,border_seen),
  3136 + background_image(url) then " background="+url+format(t,border_seen),
  3137 + border(o,top,i,c) then " border="+o+" cellspacing="+top+" cellpadding="+i+
  3138 + //" bordercolor="+format(c)+
  3139 + format(t,true),
  3140 + width(w) then " width=\""+w+"\""+format(t,border_seen),
  3141 + percentage_width(p) then " width=\""+percent(p)+"%\""+format(t,border_seen),
  3142 + }
  3143 + }.
  3144 +
  3145 +
  3146 +
  3147 +
  3148 +
  3149 +define Printable_tree
  3150 + format_scroller
  3151 + (
  3152 + String sn,
  3153 + Int32 width,
  3154 + Int32 height,
  3155 + Int32 content_width,
  3156 + Int32 content_height,
  3157 + Int32 idnum, // identifying the scroller
  3158 + $T content,
  3159 + $T -> Printable_tree format_element
  3160 + ) =
  3161 + [
  3162 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  3163 + "function doscroll_",idnum,"(dx,dy) {\n",
  3164 + " if (document.layers) { var c_",idnum," = eval(document.cs_",idnum,"); } else\n",
  3165 + " if (document.getElementById) {var c_",idnum," = eval(\"document.getElementById('cs_",
  3166 + idnum,"').style\"); } else\n",
  3167 + " if (document.all) { var c_",idnum," = eval(document.all.cs_",idnum,".style); };\n",
  3168 + " var x_",idnum," = parseInt(c_",idnum,".left);\n",
  3169 + " var y_",idnum," = parseInt(c_",idnum,".top);\n",
  3170 + " if ((x_",idnum,"+dx <= 0) && (x_",idnum,"+dx > ",width-content_width,"))\n",
  3171 + " { x_",idnum," += dx; }\n",
  3172 + " if ((y_",idnum,"+dy <= 0) && (y_",idnum,"+dy > ",height-content_height,"))\n",
  3173 + " { y_",idnum," += dy; }\n",
  3174 + " c_",idnum,".left = x_",idnum,";\n",
  3175 + " c_",idnum,".top = y_",idnum,";\n",
  3176 + " }\n",
  3177 + "</script>",
  3178 + "<table>",
  3179 + "<tr>",
  3180 + "<td align=left valign=top",
  3181 + " width=",width,
  3182 + " height=",height,
  3183 + ">",
  3184 + "<div id=\"ws_",idnum,"\" style=\"position:absolute; width:",width,"px; height:",height,"px;",
  3185 + " clip:rect(0px ",width,"px ",height,"px 0px)\">",
  3186 + "<div id=\"cs_",idnum,"\" style=\"position:absolute; left:0px; top:0px\">",
  3187 + format_element(content),
  3188 + "</div>",
  3189 + "</div>",
  3190 + "</td>",
  3191 + "<td valign=bottom>",
  3192 + "<table>",
  3193 + "<tr><td><img alt=\"sroll up\" src=\"scrollup.gif\" onMouseDown=\"doscroll_",
  3194 + idnum,"(0,20);\"></td></tr>",
  3195 + "<tr><td><img alt=\"scroll down\" src=\"scrolldown.gif\" onMouseDown=\"doscroll_",
  3196 + idnum,"(0,-20);\"></td></tr>",
  3197 + "</table>",
  3198 + "</td>",
  3199 + "</tr>",
  3200 + (if content_width > width then
  3201 + [
  3202 + "<tr>",
  3203 + "<td align=right>",
  3204 + "<table>",
  3205 + "<tr>",
  3206 + "<td><img alt=\"scroll left\" src=\"scrollleft.gif\" onMouseDown=\"doscroll_",
  3207 + idnum,"(20,0);\"></td>",
  3208 + "<td><img alt=\"scroll right\" src=\"scrollright.gif\" onMouseDown=\"doscroll_",
  3209 + idnum,"(-20,0);\"></td>",
  3210 + "</tr>",
  3211 + "</table>",
  3212 + "</td>",
  3213 + "</tr>",
  3214 + ] else [ ]),
  3215 + "</table>"
  3216 + ].
  3217 +
  3218 +
  3219 +
  3220 + define Printable_tree
  3221 + popup_topbar
  3222 + (
  3223 + CommonInfo cinfo,
  3224 + String title,
  3225 + RGB color,
  3226 + Int32 width,
  3227 + ) =
  3228 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3229 + with xxxx = web_arg_encode(sha1((title,color,width))),
  3230 + path = site_directory+"/public/buttons/t"+xxxx+".jpg",
  3231 + result = (Printable_tree)["<img alt=\"button\" src=\"buttons/t"+xxxx+".jpg\">"],
  3232 + if file_exists(path) then result else
  3233 + with col = to_rgba(color),
  3234 + bg = create_button_background(col,width,20),
  3235 + very_light_color = lighten(col,70),
  3236 + dark_color = darken(col,40),
  3237 + title_width = printed_text_width(font,title),
  3238 + draw_button_text(bg,title,title_width,very_light_color,dark_color,font);
  3239 + forget(write_image_to_JPEG_file(to_JPEG(bg),path,100));
  3240 + result.
  3241 +
  3242 +
  3243 + define Printable_tree
  3244 + popup_close_button
  3245 + (
  3246 + CommonInfo cinfo,
  3247 + RGB color,
  3248 + String div_name,
  3249 + String state_var_name,
  3250 + ) =
  3251 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3252 + with xxxx = web_arg_encode(sha1(color)),
  3253 + path_on = site_directory+"/public/buttons/c"+xxxx+"_on.jpg",
  3254 + path_off = site_directory+"/public/buttons/c"+xxxx+"_off.jpg",
  3255 + result = (Printable_tree)["<img alt=\"button\" src=\"buttons/c"+xxxx+"_off.jpg\"",
  3256 + " onMouseOver=\"this.src='buttons/c"+xxxx+"_on.jpg'\"",
  3257 + " onMouseOut=\"this.src='buttons/c"+xxxx+"_off.jpg'\"",
  3258 + " onMouseDown=\"show_local_popup('",div_name,"','",state_var_name,"')\">"],
  3259 + if file_exists(path_on) then result else
  3260 + with col = to_rgba(color),
  3261 + title = "x",
  3262 + title_width = printed_text_width(font,title),
  3263 + bg_on = create_button_background(lighten(col,30),20,20),
  3264 + bg_off = create_button_background(col,20,20),
  3265 + very_light_color = lighten(col,70),
  3266 + dark_color = darken(col,40),
  3267 + draw_button_text(bg_on,title,title_width,very_light_color,dark_color,font);
  3268 + draw_button_text(bg_off,title,title_width,very_light_color,dark_color,font);
  3269 + forget(write_image_to_JPEG_file(to_JPEG(bg_on),path_on,100));
  3270 + forget(write_image_to_JPEG_file(to_JPEG(bg_off),path_off,100));
  3271 + result.
  3272 +
  3273 +
  3274 +
  3275 +
  3276 +
  3277 + // The function below formats a datum of type 'HTML_Any($T)'.
  3278 +
  3279 +define Printable_tree
  3280 + format
  3281 + (
  3282 + CommonInfo cinfo,
  3283 + String sn, // state_name
  3284 + Var(Int32) ic_v,
  3285 + HTML_Any($T) element,
  3286 + $T -> Printable_tree format_element, // able to format a datum of type $T
  3287 + Bool is_https,
  3288 + ) =
  3289 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3290 + if element is
  3291 + {
  3292 + any_text(opts,t) then
  3293 + ["<span ", format_text_options(opts), ">",t,"</span>"],
  3294 + any_preformated(opts,s) then
  3295 + ["<span ", format_text_options(opts), "><pre>",s,"</pre></span>"],
  3296 + //["<pre>",s,"</pre>"],
  3297 + any_paragraph(opts,t) then
  3298 + ["<p ", format_text_options(opts), ">",t,"</p>"],
  3299 + any_image(url) then
  3300 + ["<img alt=\"",url,"\" src=\"",url,"\">"],
  3301 + any_image(url,w,h) then
  3302 + ["<img alt=\"",url,"\" src=\"",url,"\" width=",w," height=",h,">"],
  3303 + any_table(opts,rows) then
  3304 + ["<table ",format(reverse(opts),false),">",format(rows,format_element),"</table>"],
  3305 + any_center(e) then
  3306 + ["<center>",format_element(e),"</center>"],
  3307 + any_mail_to(email,elem) then
  3308 + ["<a href=\"mailto:",email,"\">",format_element(elem),"</a>"],
  3309 + any_scroller(w,h,cw,ch,c) then
  3310 + format_scroller(sn,w,h,cw,ch,new_idnum(ic_v),c,format_element),
  3311 + any_fixed_size(w,h,c) then
  3312 + with url = create_secondary_document(site_directory,secret,sn,format_element,c,w),
  3313 + ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",
  3314 + "secondary document",
  3315 + "</object>"],
  3316 + any_fixed_size_2(w,h,fn) then
  3317 + with url = fn+"?zauth="+make_authorization(site_directory,secret,
  3318 + fn),
  3319 + ["<object data=\"",url,"\" type=\"text/html\" width=",format(w)," height=",format(h)," >",
  3320 + "secondary document",
  3321 + "</object>"],
  3322 + any_actioner(c,t,a,an,eo,ja,fn) then
  3323 + format_actioner(cinfo,sn,c,t,a,an,eo,ja,fn,is_https),
  3324 + any_foreign_link(options,url,name) then
  3325 + ["<a href=\"",url,"\"><span ", format_text_options(options), ">",name,"</span></a>"],
  3326 + any_private_download(url,name,extra_ext,action) then
  3327 + format_private_download(cinfo,sn,url,name,extra_ext,action),
  3328 + any_div(options, e) then
  3329 + [format_div_option(options), format_element(e),"</DIV>"],
  3330 + any_div_empty(options) then
  3331 + [format_div_option(options), "</DIV>"],
  3332 + any_coreattrs(attributs) then
  3333 + [format_coreattrs(attributs)]
  3334 + }.
  3335 +
  3336 +
  3337 +
  3338 +
  3339 + // *** [5.8] Formating 'in form' elements.
  3340 +
  3341 +
  3342 +
  3343 +
  3344 +
  3345 +define Printable_tree
  3346 + format
  3347 + (
  3348 + CommonInfo cinfo,
  3349 + String fn, // form_name
  3350 + String sn, // state_name
  3351 + Var(Int32) ic_v, // idnum counter variable
  3352 + HTML_In_Form element,
  3353 + Bool is_https,
  3354 + ) =
  3355 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3356 + with format_element = (HTML_In_Form e) |-> format(cinfo,fn,sn,ic_v,e,is_https),
  3357 + if element is
  3358 + {
  3359 + literal_pt(t) then t,
  3360 + literal(t) then [t],
  3361 + sequence(l) then flat(map(format_element,l))
  3362 + text(opts,t) then
  3363 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
  3364 + preformated(o,s) then
  3365 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
  3366 + paragraph(opts,t) then
  3367 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
  3368 + image(url) then
  3369 + format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
  3370 + image(url,w,h) then
  3371 + format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
  3372 + table(opts,rows) then
  3373 + format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),
  3374 + center(e) then
  3375 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
  3376 + mail_to(a,e) then
  3377 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
  3378 + scroller(w,h,cw,ch,c) then
  3379 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
  3380 + actioner(c,t,a,an,eo,ja) then
  3381 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
  3382 + foreign_link(options,url,name) then
  3383 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
  3384 + private_download(url,name,extra,action) then
  3385 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
  3386 + text_input(label_text, label, name,i,w) then
  3387 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3388 + "<input type=text name=o",name," id=",label," size=",w," value=\"",i,"\">"],
  3389 + //["&nbsp; <input type=text name=o",n," size=",w," value=\"",i,"\">"],
  3390 + password_input(label_text, label, name,w) then
  3391 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3392 + "<input type=password name=p",name," id=",label," size=",w,">"],
  3393 + //["&nbsp; <input type=password name=p",n," size=",w,">"],
  3394 + text_area(opts,n,i,w,h) then
  3395 + ["<textarea ",format(opts)," name=o",n," cols=",w," rows=",h,">",i,"</textarea>"],
  3396 + file_upload(n,w) then
  3397 + ["<input type=file size=",w," name=o",n,">"],
  3398 + selector(n,s,cs) then
  3399 + ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],
  3400 + selector(n,s,cs,sd) then
  3401 + ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],
  3402 + selector_c(n,s,cs) then
  3403 + ["<select name=o",n," size=",s,">",format_choices(cs),"</select>"],
  3404 + selector_c(n,s,cs,sd) then
  3405 + ["<select name=o",n," size=",s,">",format_choices(cs,sd),"</select>"],
  3406 +
  3407 + radio_button(label_text,label,n,v,c) then
  3408 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3409 + "<input type=radio name=o",n," id=",label," value=\"",v,"\"",(if c then " checked" else ""),">"],
  3410 + check_box(label_text, label,n,c) then
  3411 + [ "<label for=\"",label,"\">",label_text,"</label>",
  3412 + "<input type=checkbox name=o",n," id=\"",label,"\""+(if c then " checked " else ""),">"]
  3413 + div(options, e) then
  3414 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
  3415 + div_empty(options) then
  3416 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
  3417 + hidden(name, value) then
  3418 + ["<input type=hidden name=o",name," value=\"",value,"\">"],
  3419 +
  3420 + }.
  3421 +
  3422 +
  3423 +
  3424 +
  3425 +
  3426 +
  3427 + *** [5.9] Formating 'off form' elements.
  3428 +
  3429 + The encryption type 'multipart/form-data' is required for a form containing an upload.
  3430 +
  3431 +
  3432 +define Bool
  3433 + contains_an_upload
  3434 + (
  3435 + HTML_In_Form form_content
  3436 + ).
  3437 +
  3438 +define Bool
  3439 + contains_an_upload
  3440 + (
  3441 + HTML_Row(HTML_In_Form) row
  3442 + ) =
  3443 + mapor(contains_an_upload,
  3444 + map(content,cells(row))).
  3445 +
  3446 +
  3447 +define Bool
  3448 + contains_an_upload
  3449 + (
  3450 + HTML_In_Form form_content
  3451 + ) =
  3452 + if form_content is
  3453 + {
  3454 + literal_pt(t) then false,
  3455 + literal(t) then false,
  3456 + sequence(l) then mapor(contains_an_upload,l)
  3457 + text(o,t) then false,
  3458 + preformated(o,s) then false,
  3459 + paragraph(o,t) then false,
  3460 + image(u) then false,
  3461 + image(u,w,h) then false,
  3462 + table(o,rows) then mapor(contains_an_upload,rows),
  3463 + center(e) then contains_an_upload(e),
  3464 + mail_to(m,e) then false, // 'e' may but should not contain an upload
  3465 + scroller(w,h,cw,ch,e) then contains_an_upload(e),
  3466 + actioner(c,t,a,an,eo,ja) then false,
  3467 + foreign_link(o,u,n) then false,
  3468 + private_download(p,n,e,a) then false,
  3469 + text_input(lt,l,n,i,w) then false,
  3470 + password_input(lt,l,n,w) then false,
  3471 + text_area(o,n,i,w,h) then false,
  3472 + file_upload(n,w) then true,
  3473 + selector(n,s,c) then false,
  3474 + selector(n,s,c,p) then false,
  3475 + selector_c(n,s,c) then false,
  3476 + selector_c(n,s,c,p) then false,
  3477 + radio_button(_,_,n,v,c) then false,
  3478 + check_box(_,_,n,c) then false,
  3479 + div(o,c) then false,
  3480 + div_empty(o) then false,
  3481 + hidden(_,_) then false
  3482 + }.
  3483 +
  3484 +define String
  3485 + enctype
  3486 + (
  3487 + HTML_In_Form form_content
  3488 + ) =
  3489 + if contains_an_upload(form_content)
  3490 + then " enctype=multipart/form-data"
  3491 + else "".
  3492 +
  3493 +
  3494 +
  3495 +define Printable_tree
  3496 + format
  3497 + (
  3498 + CommonInfo cinfo,
  3499 + String sn, // state_name
  3500 + Var(Int32) ic_v, // 'idnum' counter variable
  3501 + HTML_Off_Form element,
  3502 + Bool is_https,
  3503 + ) =
  3504 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3505 + with format_element = (HTML_Off_Form e) |-> format(cinfo,sn,ic_v,e,is_https),
  3506 + if element is
  3507 + {
  3508 + literal_pt(t) then t,
  3509 + literal(t) then [t],
  3510 + sequence(l) then flat(map(format_element,l)),
  3511 + text(opts,t) then
  3512 + format(cinfo,sn,ic_v,any_text(opts,t),format_element,is_https),
  3513 + preformated(o,s) then
  3514 + format(cinfo,sn,ic_v,any_preformated(o,s),format_element,is_https),
  3515 + paragraph(opts,t) then
  3516 + format(cinfo,sn,ic_v,any_paragraph(opts,t),format_element,is_https),
  3517 + image(url) then
  3518 + format(cinfo,sn,ic_v,any_image(url),format_element,is_https),
  3519 + image(url,w,h) then
  3520 + format(cinfo,sn,ic_v,any_image(url,w,h),format_element,is_https),
  3521 + table(opts,rows) then
  3522 + format(cinfo,sn,ic_v,any_table(opts,rows),format_element,is_https),
  3523 + center(e) then
  3524 + format(cinfo,sn,ic_v,any_center(e),format_element,is_https),
  3525 + mail_to(a,e) then
  3526 + format(cinfo,sn,ic_v,any_mail_to(a,e),format_element,is_https),
  3527 + scroller(w,h,cw,ch,c) then
  3528 + format(cinfo,sn,ic_v,any_scroller(w,h,cw,ch,c),format_element,is_https),
  3529 + fixed_size(w,h,c) then
  3530 + format(cinfo,sn,ic_v,any_fixed_size(w,h,c),format_element,is_https),
  3531 + fixed_size_2(w,h,fn) then
  3532 + format(cinfo,sn,ic_v,any_fixed_size_2(w,h,fn),format_element,is_https),
  3533 + actioner(c,t,a,an,eo,ja) then
  3534 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,failure),format_element,is_https),
  3535 + actioner(c,t,a,an,eo,ja,fn) then
  3536 + format(cinfo,sn,ic_v,any_actioner(c,t,a,an,eo,ja,success(fn)),format_element,is_https),
  3537 + foreign_link(options,url,name) then
  3538 + format(cinfo,sn,ic_v,any_foreign_link(options,url,name),format_element,is_https),
  3539 + private_download(url,name,extra,action) then
  3540 + format(cinfo,sn,ic_v,any_private_download(url,name,extra,action),format_element,is_https),
  3541 + label(n) then ["<a name=\"",n,"\">"],
  3542 + form(fn,attributs, c) then
  3543 + [
  3544 + "<form name=\"f",fn,"\"",
  3545 + format(cinfo,sn,ic_v,any_coreattrs(attributs),format_element,is_https),
  3546 + " method=POST",
  3547 + enctype(c),
  3548 + " action=\"http",
  3549 + if is_https then "s" else "",
  3550 + "://",common_name,":",http_port,"/\">",
  3551 + // action is set dynamically by
  3552 + // the actioner using JavaScript
  3553 + format(cinfo,fn,sn,ic_v,c,is_https),
  3554 + "</form>"
  3555 + ]
  3556 + div(options, e) then
  3557 + format(cinfo,sn,ic_v,any_div(options, e),format_element,is_https),
  3558 + div_empty(options) then
  3559 + format(cinfo,sn,ic_v,any_div_empty(options),format_element,is_https),
  3560 +
  3561 + }.
  3562 +
  3563 +
  3564 +
  3565 +
  3566 + *** [5.10] Formating meta-tags.
  3567 +
  3568 +define Printable_tree
  3569 + format_keywords
  3570 + (
  3571 + List(String) l
  3572 + ) =
  3573 + if l is
  3574 + {
  3575 + [] then [ ],
  3576 + [h . t] then if t is []
  3577 + then [h]
  3578 + else [h , ", " . format_keywords(t)]
  3579 + }.
  3580 +
  3581 +
  3582 +define Printable_tree
  3583 + format
  3584 + (
  3585 + CommonInfo cinfo,
  3586 + String state_name,
  3587 + HTML_Meta m,
  3588 + Bool is_https
  3589 + ) =
  3590 + if m is
  3591 + {
  3592 + keywords(l) then ["<meta name=\"keywords\" content=\"",format_keywords(l),"\">"],
  3593 + refresh(co,ta,an,delay) then
  3594 + ["<meta http-equiv=\"Refresh\" content=\"",delay,"; URL=",
  3595 + make_actioner_url(cinfo,co,ta,state_name,an,[],is_https),"\">"],
  3596 + meta(n,c) then ["<meta name=\"",n,"\" content=\"",c,"\">"],
  3597 + http_equiv(n,c) then ["<meta http-equiv=\"",n,"\" content=\"",c,"\">"],
  3598 + generic_meta(l) then ["<meta ",
  3599 + flat(map(((String,String) p) |-> if p is (n,v) then [n,"=\"",v,"\" "],
  3600 + l)),
  3601 + ">"],
  3602 + literal(s) then [s]
  3603 + }.
  3604 +
  3605 +
  3606 +define Printable_tree
  3607 + format
  3608 + (
  3609 + CommonInfo cinfo,
  3610 + String state_name,
  3611 + List(HTML_Meta) metas,
  3612 + Bool is_https,
  3613 + String charset
  3614 + ) =
  3615 + if metas is
  3616 + {
  3617 + [] then [format(cinfo,state_name,http_equiv("content-type",
  3618 + "text/html; charset="+charset),is_https)],
  3619 + [h . t] then [format(cinfo,state_name,h,is_https)
  3620 + . format(cinfo,state_name,t,is_https,charset)]
  3621 + }.
  3622 +
  3623 +
  3624 +define Printable_tree
  3625 + format
  3626 + (
  3627 + Body_Option o
  3628 + ) =
  3629 + if o is
  3630 + {
  3631 + background_color(c) then [" bgcolor=\"" , (String)html_format(c), "\""],
  3632 + background_image(n) then [" background=", n],
  3633 + background_image(n,o) then [" style=\"background: url(",n,")",format(o),"\""]
  3634 +
  3635 + }.
  3636 +
  3637 +
  3638 +
  3639 +define Printable_tree
  3640 + format
  3641 + (
  3642 + List(Body_Option) l
  3643 + ) =
  3644 + if l is
  3645 + {
  3646 + [ ] then [ ],
  3647 + [h . t] then [format(h) . format(t)]
  3648 + }.
  3649 +
  3650 +define Printable_tree
  3651 + add_css_files
  3652 + (
  3653 + List(CSS_File) l
  3654 + ) =
  3655 + if l is
  3656 + {
  3657 + [ ] then [ ],
  3658 + [h . t] then
  3659 + [ ["<LINK rel=\"stylesheet\" type=\"text/css\" href=" + file_name(h) + ">\n" ]
  3660 + . add_css_files(t)]
  3661 + }.
  3662 +
  3663 +define Printable_tree
  3664 + add_js_files
  3665 + (
  3666 + List(JS_File) l
  3667 + ) =
  3668 + if l is
  3669 + {
  3670 + [ ] then [ ],
  3671 + [h . t] then
  3672 + [ ["<script src=\""+ file_name(h) +"\" type=\"text/javascript\"></script>\n" ]
  3673 + . add_js_files(t)]
  3674 + }.
  3675 +
  3676 +define Printable_tree
  3677 + add_css_styles
  3678 + (
  3679 + List(CSS_Style) css_styles
  3680 + ) =
  3681 +
  3682 + if css_styles is
  3683 + {
  3684 + [] then [],
  3685 + [_._] then [ "<style type=\"text/css\"><!--\n",
  3686 + format_css_styles(css_styles),
  3687 + " --></style>"
  3688 + ]
  3689 + }.
  3690 +
  3691 +define Printable_tree
  3692 + format
  3693 + (
  3694 + CommonInfo cinfo,
  3695 + String state_name,
  3696 + HTML_Page page,
  3697 + Bool is_https,
  3698 + String charset
  3699 + ) =
  3700 + if cinfo is info(common_name,http_port,https_port,site_directory,secret) then
  3701 + with ic_v = var((Int32)0),
  3702 + if page is
  3703 + {
  3704 + html_page(title,metas,css_styles, css_files, js_files, body) then
  3705 + if body is body(options,element) then
  3706 + [ doctype_w3c_header,
  3707 + "<html>",
  3708 + "<head>",
  3709 + add_css_styles(css_styles),
  3710 + add_css_files(css_files),
  3711 + add_js_files(js_files),
  3712 + "<link rel=\"shortcut icon\" href=\"favicon.ico\">",
  3713 + "<script type = \"text/javascript\" language=\"JavaScript\">",
  3714 + " function show_local_popup(divname,stvname) {",
  3715 + " if (document.layers) { var d = eval(document.divname); } else\n",
  3716 + " if (document.getElementById) { var d = eval(\"document.getElementById(divname)\"); } else\n",
  3717 + " if (document.all) { var d = eval(document.all.divname.style)};\n",
  3718 + // " alert(typeof(eval(stvname))); ",
  3719 + " var s = eval(stvname); ",
  3720 + " if (s[0]==0) ",
  3721 + " { s[0]=1; d.style.visibility = 'visible'; d.zIndex = 100; } else\n",
  3722 + " { s[0]=0; d.style.visibility = 'hidden'; }; }",
  3723 + "</script>",
  3724 + "<title>",title,"</title>", // put title
  3725 + format(cinfo,state_name,metas,is_https,charset), // format the metas
  3726 + "</head>",
  3727 + "<body ", format(options), ">", // format body options
  3728 + //"<center>",
  3729 + format(cinfo,state_name,ic_v,element,is_https),
  3730 + //"</center>",
  3731 + "</body>",
  3732 + "</html>"
  3733 + ]
  3734 + }.
  3735 +
  3736 +
  3737 +
  3738 +
  3739 +
  3740 +