1 /* Monolith server-parsed pages (.msp's).
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: ml_msp.c,v 1.10 2003/02/22 15:34:33 rich Exp $
50 #include <pthr_iolib.h>
53 #include "ml_widget.h"
54 #include "ml_flow_layout.h"
58 static void repaint (void *, ml_session, const char *, io_handle);
60 struct ml_widget_operations msp_ops =
67 struct ml_widget_operations *ops;
68 pool pool; /* Pool for allocations. */
69 ml_session session; /* Current session. */
70 const char *conninfo; /* Database connection. */
71 const char *rootdir; /* Document root. */
72 const char *filename; /* MSP file, relative to rootdir. */
73 const char *pathname; /* Full path to file. */
74 vector widgetpath; /* Path when loading widgets (may be NULL). */
75 ml_flow_layout w; /* MSP is really just a flow layout. */
78 static void init_msp (void) __attribute__((constructor));
79 static void free_msp (void) __attribute__((destructor));
80 static int parse_file (ml_msp w, int fd);
81 static int verify_relative_path (const char *s);
82 static int verify_filename (const char *s);
84 /* Global variables. */
86 static const pcre *re_openclose, *re_ws;
88 /* Initialise the library. */
92 msp_pool = new_subpool (global_pool);
93 re_openclose = precomp (msp_pool, "<%|%>", 0);
94 re_ws = precomp (msp_pool, "[ \t]+", 0);
97 /* Free up global memory used by the library. */
101 delete_pool (msp_pool);
105 new_ml_msp (pool pool, ml_session session, const char *conninfo,
106 const char *rootdir, const char *filename)
108 ml_msp w = pmalloc (pool, sizeof *w);
111 /* Security checks on the rootdir and filename. */
112 if (rootdir[0] != '/')
113 pth_die ("ml_msp.c: rootdir must start with '/'\n");
115 if (!verify_relative_path (filename))
120 w->session = session;
121 w->conninfo = conninfo;
122 w->rootdir = rootdir;
123 w->filename = filename;
125 w->w = new_ml_flow_layout (pool);
127 /* Create the full path to the file. */
128 w->pathname = psprintf (pool, "%s/%s", rootdir, filename);
131 fd = open (w->pathname, O_RDONLY);
134 perror (psprintf (pool, "ml_msp.c: %s", w->pathname));
138 /* Read and parse the file. */
139 if (parse_file (w, fd) == -1)
142 return 0; /* parse_file prints an error. */
150 static int parse_directive (ml_msp w, const char *directive);
151 static const char *load_file (pool tmp, int fd);
154 parse_file (ml_msp w, int fd)
156 pool pool = w->pool, tmp = new_subpool (pool);
163 /* Load the file into a temporary buffer. */
164 file = load_file (tmp, fd);
165 if (!file) return -1;
167 /* Using pstrresplit2 we can split up the file into units like
168 * this: [ "some HTML", "<%", "directive", "%>", "some more HTML", ... ]
169 * This makes parsing the file much simpler.
171 v = pstrresplit2 (pool, file, re_openclose);
173 for (i = 0; i < vector_size (v); ++i)
178 vector_get (v, i, s);
181 fprintf (stderr, "ml_msp.c: reading %s\n", pstrndup (tmp, s, 20));
184 if (strcmp (s, "<%") == 0) type = '(';
185 else if (strcmp (s, "%>") == 0) type = ')';
190 case 'o': /* Waiting for opening <% */
192 /* Found it. We are now inside a directive. */
196 /* Must be HTML. Output it as a label. */
197 label = new_ml_label (pool, s);
198 ml_flow_layout_push_back (w->w, label);
201 case 'i': /* Inside a directive. */
204 /* Parse the directive. */
205 if (parse_directive (w, s) == -1)
209 else if (type == ')') /* Allow <%%> - just ignore it. */
211 else if (type != ')')
215 "ml_msp.c: %s: unexpected '%s' inside directive.\n",
220 case 'c': /* Expecting %>. */
222 /* Found it. We are now back in HTML. */
227 "ml_msp.c: %s: unexpected '%s' inside directive.\n",
231 } /* switch (state) */
234 /* Check our final state, which must be 'o'. */
237 fprintf (stderr, "ml_msp.c: %s: unclosed '<%%' in file.\n", w->filename);
246 do_include (ml_msp w, const char *include_file)
252 /* Verify that this is a plain, ordinary filename. */
253 if (!verify_filename (include_file))
256 /* Locate the included file, relative to the current filename. Never leave
257 * the current root, however.
259 dir = pstrdup (pool, w->filename);
260 while (strlen (dir) > 0)
262 t = strrchr (dir, '/');
266 try = psprintf (pool, "%s/%s/%s", w->rootdir, dir, include_file);
271 try = psprintf (pool, "%s/%s", w->rootdir, include_file);
274 fd = open (try, O_RDONLY);
280 fprintf (stderr, "ml_msp.c: include: %s: file not found.\n", include_file);
284 /* Parse the included file. */
285 if (parse_file (w, fd) == -1)
288 return -1; /* parse_file prints an error. */
297 do_widget (ml_msp w, const char *libfile, const char *new_fn,
301 const char *filename, *error, *arg[5];
305 if (strcmp (libfile, "-") == 0)
306 filename = 0; /* Search in the current executable. */
307 else if (libfile[0] != '/') /* Relative to the widget path. */
312 for (i = 0; i < vector_size (w->widgetpath); ++i)
317 vector_get (w->widgetpath, i, path);
318 try = psprintf (w->pool, "%s/%s", path, libfile);
319 if (access (try, X_OK) == 0)
326 else /* Absolute path. */
329 lib = dlopen (filename,
338 fprintf (stderr, "ml_msp.c: %s: %s\n", libfile, dlerror ());
342 /* Does the new function exist? */
343 new_sym = dlsym (lib, new_fn);
344 if ((error = dlerror ()) != 0)
346 fprintf (stderr, "ml_msp.c: %s: %s: %s\n", libfile, new_fn, error);
351 /* Make sure we close this library when the widget is deleted. */
352 pool_register_cleanup_fn (w->pool, (void (*)(void *)) dlclose, lib);
354 /* Formulate our call.
355 * XXX There needs to be a generic method for doing this in c2lib XXX
357 arg[0] = arg[1] = arg[2] = arg[3] = arg[4] = 0;
358 if (vector_size (args) >= 1) vector_get (args, 0, arg[0]);
359 if (vector_size (args) >= 2) vector_get (args, 1, arg[1]);
360 if (vector_size (args) >= 3) vector_get (args, 2, arg[2]);
361 if (vector_size (args) >= 4) vector_get (args, 3, arg[3]);
362 if (vector_size (args) >= 5) vector_get (args, 4, arg[4]);
364 if (vector_size (args) == 1 && strcmp (arg[0], "pool") == 0)
366 ml_widget (*fn) (pool) = (ml_widget (*) (pool)) new_sym;
368 widget = fn (w->pool);
369 if (widget) ml_flow_layout_push_back (w->w, widget);
371 else if (vector_size (args) == 2 && strcmp (arg[0], "pool") == 0 &&
372 strcmp (arg[1], "session") == 0)
374 ml_widget (*fn) (pool, ml_session) =
375 (ml_widget (*) (pool, ml_session)) new_sym;
377 widget = fn (w->pool, w->session);
378 if (widget) ml_flow_layout_push_back (w->w, widget);
380 else if (vector_size (args) == 3 && strcmp (arg[0], "pool") == 0 &&
381 strcmp (arg[1], "session") == 0 &&
382 strcmp (arg[2], "conninfo") == 0)
384 ml_widget (*fn) (pool, ml_session, const char *) =
385 (ml_widget (*) (pool, ml_session, const char *)) new_sym;
387 widget = fn (w->pool, w->session, w->conninfo);
388 if (widget) ml_flow_layout_push_back (w->w, widget);
390 else if (vector_size (args) == 4 && strcmp (arg[0], "pool") == 0 &&
391 strcmp (arg[1], "session") == 0 &&
392 strcmp (arg[2], "conninfo") == 0)
394 ml_widget (*fn) (pool, ml_session, const char *, const char *) =
395 (ml_widget (*) (pool, ml_session, const char *, const char *)) new_sym;
397 widget = fn (w->pool, w->session, w->conninfo, arg[3]);
398 if (widget) ml_flow_layout_push_back (w->w, widget);
400 else if (vector_size (args) == 5 && strcmp (arg[0], "pool") == 0 &&
401 strcmp (arg[1], "session") == 0 &&
402 strcmp (arg[2], "conninfo") == 0)
404 ml_widget (*fn) (pool, ml_session, const char *, const char *, const char *) =
405 (ml_widget (*) (pool, ml_session, const char *, const char *, const char *)) new_sym;
407 widget = fn (w->pool, w->session, w->conninfo, arg[3], arg[4]);
408 if (widget) ml_flow_layout_push_back (w->w, widget);
411 abort (); /* XXX Not yet implemented */
417 do_widgetpath (ml_msp w, const vector dirs)
419 fprintf (stderr, "XXX not impl XXX\n");
424 parse_directive (ml_msp w, const char *directive)
430 /* Split the directive up into tokens.
431 * XXX This is presently not very intelligent. It will split
432 * string arguments. There is a proposed solution for this in
433 * the form of a 'pstrtok' function in c2lib. At the moment this
436 tokens = pstrresplit (pool, directive, re_ws);
437 if (vector_size (tokens) < 1)
438 return 0; /* Just ignore it. */
440 /* Get the command. */
441 vector_pop_front (tokens, command);
443 if (strcasecmp (command, "include") == 0)
447 if (vector_size (tokens) != 1)
449 fprintf (stderr, "ml_msp.c: %s: include: needs one filename\n",
453 vector_pop_front (tokens, file);
454 return do_include (w, file);
456 else if (strcasecmp (command, "widget") == 0)
461 if (vector_size (tokens) < 3)
463 fprintf (stderr, "ml_msp.c: %s: widget: bad parameters\n",
467 vector_pop_front (tokens, file);
468 vector_pop_front (tokens, new_fn);
469 return do_widget (w, file, new_fn, tokens);
471 else if (strcasecmp (command, "widgetpath") == 0)
473 return do_widgetpath (w, tokens);
477 fprintf (stderr, "ml_msp.c: %s: %s: unknown directive\n",
478 w->filename, command);
483 /* Load a file into memory from fd, allocating space from the
484 * temporary pool tmp.
487 load_file (pool tmp, int fd)
491 const int n = 16385; /* [sic] - see comment at end */
495 file = prealloc (tmp, file, sz + n);
496 r = read (fd, file + sz, n-1);
503 if (r == 0) break; /* end of file */
508 /* Since tmp is a temporary pool, don't bother rounding the size of the
509 * buffer down to match the file size. Just make sure it's null-terminated.
510 * Note that one extra byte we reserved above.
518 verify_relative_path (const char *s)
521 strncmp (s, "..", 2) == 0 ||
526 "ml_msp.c: security error: string is not a relative path.\n");
533 verify_filename (const char *s)
535 if (strchr (s, '/') != 0 ||
540 "ml_msp.c: security error: string is not a plain filename.\n");
547 repaint (void *vw, ml_session session, const char *windowid, io_handle io)
549 ml_msp w = (ml_msp) vw;
551 /* Repaint the flow layout. */
553 ml_widget_repaint (w->w, session, windowid, io);