Commit e281d82ed75b007ea0148654519fbfdf43743df0

Authored by Cédric RICARD
1 parent aadc3df9

Adding 'attr' alternative on DIV_Option type

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