2 * - by Richard W.M. Jones <rich@annexia.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: monolith.h,v 1.19 2003/02/22 15:34:32 rich Exp $
26 #include <pthr_pseudothread.h>
29 #include <rws_request.h>
31 #include <stdlib.h> /* For size_t */
33 /* Just like the semi-standard "offsetof" function. */
35 #define ml_offsetof(type,member) offset(type,member)
37 #define ml_offsetof(type,member) ((size_t) &((type *)0)->member)
40 #include <sys/socket.h>
42 #include <ml_window.h>
46 typedef struct ml_session *ml_session;
48 /* Function: ml_entry_point - entry point into monolith from handle_request
50 * @code{ml_entry_point} is the entry point into the monolith core
51 * library code, called from @code{handle_request}. The application's
52 * @code{handle_request} function should contain just a single
53 * call to @code{ml_entry_point} like this:
55 * @code{return ml_entry_point (rq, app_main);}
57 * where @code{rq} is the @code{rws_request} object passed by the
58 * web server, and @code{app_main} is the application's main function.
60 * See @code{examples/01_label_and_button.c} for a simple monolith
63 * See also: @ref{ml_session_pool(3)},
64 * @ref{rws_request_pool(3)}, @ref{new_ml_window(3)},
65 * @ref{ml_cfg_get_string(3)}.
67 extern int ml_entry_point (rws_request rq, void (*app_main) (ml_session));
69 /* Function: ml_session_pool - get monolith per-session information
70 * Function: ml_session_args
71 * Function: ml_session_sessionid
72 * Function: ml_session_host_header
73 * Function: ml_session_canonical_path
74 * Function: ml_session_script_name
75 * Function: ml_session_user_agent
76 * Function: ml_session_set_main_window
77 * Function: ml_session_get_main_window
78 * Function: ml_session_get_peername
79 * Function: ml_session_get_peernamestr
81 * These functions extract the information from the opaque @code{ml_session}
82 * object passed to most application functions and callbacks. Each separate
83 * user session is tied to a separate @code{ml_session} object which
84 * contains standard fields.
86 * @code{ml_session_pool} returns the session pool, which is a pool which
87 * has the lifetime of the whole session.
89 * @code{ml_session_args} returns the arguments (a @code{cgi} object) which
90 * were passed to the application when it started running. This is kind of
91 * equivalent to @code{argc} and @code{argv} for a traditional application.
93 * @code{ml_session_sessionid} returns the session ID, a 32 character
94 * string of random hex digits which also happens to be the cookie
95 * passed by the browser.
97 * @code{ml_session_host_header} returns the Host: header of the
98 * monolith application. For example @code{www.annexia.org}.
100 * @code{ml_session_canonical_path} returns the canonical path of the
101 * monolith application, that is, the full path of the application as
102 * it appears to the browser. For example @code{/so-bin/app.so}.
104 * @code{ml_session_script_name} returns just the name of the application
105 * as it appears to the browser. For example @code{app.so}.
107 * @code{ml_session_user_agent} returns the identifying string for
108 * the browser, sent in the HTTP @code{User-Agent} header. This can
111 * @code{ml_session_(set|get)_main_window} are a pair of esoteric
112 * functions which you will not need to use in most applications. They
113 * are useful, however, when you are building a website. They nominate
114 * a particular window which will be the window displayed if the user
115 * types the URL of the application directly into their browser
116 * location bar, without the normally mandatory @code{ml_window}
119 * @code{ml_session_get_peername} returns the peer address of the
120 * socket. The peer address is the IP address of the user's browser
121 * or proxy. This function returns @code{0} on success or @code{-1}
122 * on failure (with the appropriate error code in @code{errno}).
123 * @code{ml_session_get_peernamestr} returns the same
124 * converted to a string, normally in "dotted quad" format, for example:
125 * @code{"10.0.0.137"}. On failure, @code{ml_session_get_peernamestr}
126 * returns @code{NULL}.
128 * See also: @ref{ml_entry_point(3)}, @ref{ml_die(3)}, @ref{new_cgi(3)}
130 extern pool ml_session_pool (ml_session);
131 extern cgi ml_session_args (ml_session);
132 extern const char *ml_session_sessionid (ml_session);
133 extern const char *ml_session_host_header (ml_session);
134 extern const char *ml_session_canonical_path (ml_session);
135 extern const char *ml_session_script_name (ml_session);
136 extern const char *ml_session_user_agent (ml_session);
137 extern void ml_session_set_main_window (ml_session, ml_window);
138 extern ml_window ml_session_get_main_window (ml_session);
139 extern int ml_session_get_peername (ml_session, struct sockaddr *name, socklen_t *namelen);
140 extern const char *ml_session_get_peernamestr (ml_session);
142 /* Function: ml_session_release_lock
143 * Function: ml_session_acquire_lock
145 * These are advanced functions which you will probably never need to use.
147 * Monolith maintains a single session structure for each session. If
148 * the browser were to try and fetch two pages with the same session ID
149 * at the same time, then rws could create two threads running at the
150 * same time, and these two threads could access the same session
151 * structure. The result of this, under some circumstances, would be
152 * that the session structure would be corrupted. There is obviously
153 * potential for malicious abuse here, but this can also happen under
154 * normal conditions, particularly in framesets (the browser fetches
155 * each frame at the same time).
157 * To avoid this situation, monolith maintains a mutex lock on each
158 * session structure, so that no two threads can try to access the
159 * same session structure at the same time. (Different sessions can
160 * execute concurrently, of course).
162 * Monolith's session locking is normally transparent to programmers
163 * and users, but these functions allow you to bypass the session
164 * lock under certain circumstances. Of course, when a programmer
165 * does this, they can no longer rely on monolith to keep the session
166 * structure consistent, and must instead guarantee this themselves.
168 * @code{ml_session_release_lock} releases the session lock. This
169 * potentially allows other threads to run, overwriting parts of the
172 * @code{ml_session_acquire_lock} reacquires the session lock, possibly
173 * sleeping to do so. This does not restore the session structure which
174 * will still be in a semi-corrupted state.
176 * In future we will add functions to allow @code{ml_session_acquire_lock}
177 * to restore the session structure to a non-corrupt state, perhaps by
178 * having a stack of session structures (or some parts of the session
181 * See also: @ref{new_pseudothread(3)}, @ref{new_mutex(3)}.
183 extern void ml_session_release_lock (ml_session);
184 extern void ml_session_acquire_lock (ml_session);
186 /* Function: ml_session_login - user authentication, log in, log out
187 * Function: ml_session_logout
188 * Function: ml_session_userid
190 * These functions provide a low-level, database-independent,
191 * authentication-method-agnostic way to handle the problem
192 * of user authentication in monolith applications.
194 * All of these functions require the schema in
195 * @code{sql/monolith_auth_create.sql}. This schema contains a
196 * table which stores a mapping from cookies to user IDs. The
197 * @code{rws} configuration file must contain a line which allows
198 * monolith to find the database containing this table:
200 * @code{monolith user database: CONNINFO}
202 * (@code{CONNINFO} is the PostgreSQL connection info string).
204 * Each user must be identified by a unique numeric userid >= 1.
206 * Once a user has logged in, a cookie (@code{ml_auth}) is sent back
207 * to their browser. The reason for sending back a cookie, rather than
208 * just storing the userid in the session state, is that it might be
209 * necessary for the user to be automatically logged in to other
210 * applications on the same server. By sending the cookie back to,
211 * say, the @code{/} path, a user effectively logs into all
212 * applications whenever they log into any one application. Also,
213 * by controlling the expiry time of the cookie, it is possible
214 * to allow a user to remain logged in across several browser
217 * The session stores the currently logged in user (as a userid)
218 * or @code{0} if no user is currently logged in. @code{ml_session_userid}
219 * retrieves the currently logged in userid or @code{0}. Note that
220 * the currently logged in user can change unexpectedly, so you
221 * must NOT stash the userid away. Call @code{ml_session_userid}
222 * every time you want to check the currently logged in user. See
223 * below for more details.
225 * When a user logs in to a particular application, the application
226 * should call @code{ml_session_login}. The actual method of login
227 * is not defined at this level (but monolith provides some higher-level
228 * widgets which you may use). The @code{userid} parameter is the
229 * user who has logged in. The @code{path} and @code{expires} parameters
230 * are used to control the @code{ml_auth} cookie sent back to the
231 * browser. If @code{path} is @code{NULL}, then the cookie only applies
232 * to the current application. If @code{path} is @code{"/"}, then the
233 * user will be automatically logged into all applications running
234 * at the same address. Other @code{path}s may also be specified to
235 * restrict authentication to some subset of the path space. If
236 * @code{expires} is @code{NULL}, then the cookie will expire at
237 * the end of the current browser session (when the user closes their
238 * browser). This means that the next time they visit the site, they
239 * will need to log in again, if they have closed their browser in
240 * the meantime. @code{expires} may also be set to a date in
241 * RFC 2616 compliant format, or to a relative time such as
242 * @code{"+1y"} (meaning 1 year from now). Relative times are
243 * specified in exactly the same way as the @code{rws} @code{expires}
244 * configuration option.
246 * There are various browser-related problems which mean that you
247 * should not mix and match different @code{path}s unless you really
248 * know what you are doing. In general, you should always set @code{path}
249 * to either @code{"/"} everywhere, or to @code{NULL} everywhere. The
250 * simplest thing is to always set @code{path} to @code{"/"} everywhere.
252 * If @code{path} is not @code{NULL}, then calling @code{ml_session_login}
253 * may also cause other monolith applications to become logged in.
254 * This is because they notice the @code{ml_auth} cookie, verify it
255 * against the database, and if it is verified, change their state
256 * to logged in. For this reason, applications should not stash away
257 * the result of @code{ml_session_userid}, but should check it each
258 * time they need it, because it can change unexpectedly.
260 * @code{ml_session_logout} logs the current user out. @code{path} should
261 * be the same as above.
263 * As before, if @code{path} is not @code{NULL}, then calling
264 * @code{ml_session_logout} may cause other monolith applications to
265 * become logged out (@code{ml_session_userid} will start to return
266 * @code{0}). This is because monolith overwrites the @code{ml_auth}
267 * cookie with a "poisoned" cookie which other applications may notice.
269 extern void ml_session_login (ml_session, int userid, const char *path, const char *expires);
270 extern void ml_session_logout (ml_session, const char *path);
271 extern int ml_session_userid (ml_session);
273 /* Function: ml_cfg_get_string - get values from the configuration file
274 * Function: ml_cfg_get_int
275 * Function: ml_cfg_get_bool
277 * @code{rws_request_cfg_get_string} returns the configuration file
278 * string for @code{key}. If there is no entry in the configuration
279 * file, this returns @code{default_value}.
281 * @code{rws_request_cfg_get_int} returns the string converted to
284 * @code{rws_request_cfg_get_bool} returns the string converted to
287 * See also: @ref{ml_entry_point(3)}, @ref{rws_request_cfg_get_string(3)}.
290 extern const char *ml_cfg_get_string (ml_session, const char *key, const char *default_value);
291 extern int ml_cfg_get_int (ml_session, const char *key, int default_value);
292 extern int ml_cfg_get_bool (ml_session, const char *key, int default_value);
294 /* Function: ml_register_action - register callbacks
295 * Function: ml_unregister_action
297 * Widgets such as buttons may register actions (callback functions)
298 * to be run when a user clicks a button or submits a form. Each
299 * action is associated with a unique action ID (a string). A separate
300 * set of actions is registered within each session. The callback
301 * function is invoked as @code{void callback_fn (ml_session
302 * session, void *data)}.
304 * @code{ml_register_action} registers an action within a given
305 * session and returns the action ID string.
307 * @code{ml_unregister_action} unregisters an action.
309 * See also: @ref{new_ml_button(3)}.
311 extern const char *ml_register_action (ml_session session, void (*callback_fn) (ml_session, void *), void *data);
312 extern void ml_unregister_action (ml_session session, const char *action_id);
314 /* Private function used by forms to get the arguments passed just to
315 * this request. The returned value is on the short-lived thread pool,
316 * so interesting stuff must be copied to the session pool.
318 extern cgi _ml_session_submitted_args (ml_session);
320 /* Private function used by ml_window to register the current window. */
321 extern void _ml_session_set_current_window (ml_session, ml_window, const char *windowid);
323 /* Some private functions used by the stats package to inspect the internals
324 * of monolith. These functions are subject to change and should not be used
325 * in ordinary applications.
327 extern const vector _ml_get_sessions (pool);
328 extern ml_session _ml_get_session (const char *sessionid);
329 extern int _ml_session_get_hits (ml_session);
330 extern reactor_time_t _ml_session_get_last_access (ml_session);
331 extern reactor_time_t _ml_session_get_created (ml_session);
332 extern struct sockaddr_in _ml_session_get_original_ip (ml_session);
333 extern void *_ml_session_get_app_main (ml_session);
334 extern const vector _ml_session_get_windows (ml_session, pool);
335 extern ml_window _ml_session_get_window (ml_session, const char *windowid);
336 extern const vector _ml_session_get_actions (ml_session, pool);
337 extern int _ml_session_get_action (ml_session, const char *actionid, void **fn_rtn, void **data_rtn);
339 #endif /* MONOLITH_H */