1 #include "config.h"
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #include <time.h>
9 #include <assert.h>
11 #include "utils_cgi.h"
12 #include "common.h"
14 #include <fcgiapp.h>
15 #include <fcgi_stdio.h>
17 struct parameter_s
18 {
19 char *key;
20 char *value;
21 };
22 typedef struct parameter_s parameter_t;
24 struct param_list_s
25 {
26 parameter_t *parameters;
27 size_t parameters_num;
28 };
30 static param_list_t *pl_global = NULL;
32 static char *uri_unescape_copy (char *dest, const char *src, size_t n) /* {{{ */
33 {
34 const char *src_ptr;
35 char *dest_ptr;
37 if ((dest == NULL) || (src == NULL) || (n < 1))
38 return (NULL);
40 src_ptr = src;
41 dest_ptr = dest;
43 *dest_ptr = 0;
45 while (*src_ptr != 0)
46 {
47 if (n < 2)
48 break;
50 if (*src_ptr == '+')
51 {
52 *dest_ptr = ' ';
53 }
54 else if ((src_ptr[0] == '%')
55 && isxdigit ((int) src_ptr[1]) && isxdigit ((int) src_ptr[2]))
56 {
57 char tmpstr[3];
58 char *endptr;
59 long value;
61 tmpstr[0] = src_ptr[1];
62 tmpstr[1] = src_ptr[2];
63 tmpstr[2] = 0;
65 errno = 0;
66 endptr = NULL;
67 value = strtol (tmpstr, &endptr, /* base = */ 16);
68 if ((endptr == tmpstr) || (errno != 0))
69 {
70 *dest_ptr = '?';
71 }
72 else
73 {
74 *dest_ptr = (char) value;
75 }
77 src_ptr += 2;
78 }
79 else
80 {
81 *dest_ptr = *src_ptr;
82 }
84 src_ptr++;
85 dest_ptr++;
86 *dest_ptr = 0;
87 } /* while (*src_ptr != 0) */
89 assert (*dest_ptr == 0);
90 return (dest);
91 } /* }}} char *uri_unescape */
93 static char *uri_unescape (const char *string) /* {{{ */
94 {
95 char buffer[4096];
97 if (string == NULL)
98 return (NULL);
100 uri_unescape_copy (buffer, string, sizeof (buffer));
102 return (strdup (buffer));
103 } /* }}} char *uri_unescape */
105 static int param_parse_keyval (param_list_t *pl, char *keyval) /* {{{ */
106 {
107 char *key_raw;
108 char *value_raw;
109 char *key;
110 char *value;
112 key_raw = keyval;
113 value_raw = strchr (key_raw, '=');
114 if (value_raw == NULL)
115 return (EINVAL);
116 *value_raw = 0;
117 value_raw++;
119 key = uri_unescape (key_raw);
120 if (key == NULL)
121 return (ENOMEM);
123 value = uri_unescape (value_raw);
124 if (value == NULL)
125 {
126 free (key);
127 return (ENOMEM);
128 }
130 param_set (pl, key, value);
132 free (key);
133 free (value);
135 return (0);
136 } /* }}} int param_parse_keyval */
138 static int parse_query_string (param_list_t *pl, /* {{{ */
139 char *query_string)
140 {
141 char *dummy;
142 char *keyval;
144 if ((pl == NULL) || (query_string == NULL))
145 return (EINVAL);
147 dummy = query_string;
148 while ((keyval = strtok (dummy, ";&")) != NULL)
149 {
150 dummy = NULL;
151 param_parse_keyval (pl, keyval);
152 }
154 return (0);
155 } /* }}} int parse_query_string */
157 int param_init (void) /* {{{ */
158 {
159 if (pl_global != NULL)
160 return (0);
162 pl_global = param_create (/* query string = */ NULL);
163 if (pl_global == NULL)
164 return (ENOMEM);
166 return (0);
167 } /* }}} int param_init */
169 void param_finish (void) /* {{{ */
170 {
171 param_destroy (pl_global);
172 pl_global = NULL;
173 } /* }}} void param_finish */
175 const char *param (const char *key) /* {{{ */
176 {
177 param_init ();
179 return (param_get (pl_global, key));
180 } /* }}} const char *param */
182 param_list_t *param_create (const char *query_string) /* {{{ */
183 {
184 char *tmp;
185 param_list_t *pl;
187 if (query_string == NULL)
188 query_string = getenv ("QUERY_STRING");
190 if (query_string == NULL)
191 return (NULL);
193 tmp = strdup (query_string);
194 if (tmp == NULL)
195 return (NULL);
197 pl = malloc (sizeof (*pl));
198 if (pl == NULL)
199 {
200 free (tmp);
201 return (NULL);
202 }
203 memset (pl, 0, sizeof (*pl));
205 parse_query_string (pl, tmp);
207 free (tmp);
208 return (pl);
209 } /* }}} param_list_t *param_create */
211 param_list_t *param_clone (__attribute__((unused)) param_list_t *pl) /* {{{ */
212 {
213 /* FIXME: To be implemented. */
214 assert (23 == 42);
215 return (NULL);
216 } /* }}} param_list_t *param_clone */
218 void param_destroy (param_list_t *pl) /* {{{ */
219 {
220 size_t i;
222 if (pl == NULL)
223 return;
225 for (i = 0; i < pl->parameters_num; i++)
226 {
227 free (pl->parameters[i].key);
228 free (pl->parameters[i].value);
229 }
230 free (pl->parameters);
231 free (pl);
232 } /* }}} void param_destroy */
234 const char *param_get (param_list_t *pl, const char *name) /* {{{ */
235 {
236 size_t i;
238 if ((pl == NULL) || (name == NULL))
239 return (NULL);
241 for (i = 0; i < pl->parameters_num; i++)
242 {
243 if ((name == NULL) && (pl->parameters[i].key == NULL))
244 return (pl->parameters[i].value);
245 else if ((name != NULL) && (pl->parameters[i].key != NULL)
246 && (strcmp (name, pl->parameters[i].key) == 0))
247 return (pl->parameters[i].value);
248 }
250 return (NULL);
251 } /* }}} char *param_get */
253 static int param_add (param_list_t *pl, /* {{{ */
254 const char *key, const char *value)
255 {
256 parameter_t *tmp;
258 tmp = realloc (pl->parameters,
259 sizeof (*pl->parameters) * (pl->parameters_num + 1));
260 if (tmp == NULL)
261 return (ENOMEM);
262 pl->parameters = tmp;
263 tmp = pl->parameters + pl->parameters_num;
265 memset (tmp, 0, sizeof (*tmp));
266 tmp->key = strdup (key);
267 if (tmp->key == NULL)
268 return (ENOMEM);
270 tmp->value = strdup (value);
271 if (tmp->value == NULL)
272 {
273 free (tmp->key);
274 return (ENOMEM);
275 }
277 pl->parameters_num++;
279 return (0);
280 } /* }}} int param_add */
282 static int param_delete (param_list_t *pl, /* {{{ */
283 const char *name)
284 {
285 size_t i;
287 if ((pl == NULL) || (name == NULL))
288 return (EINVAL);
290 for (i = 0; i < pl->parameters_num; i++)
291 if (strcasecmp (pl->parameters[i].key, name) == 0)
292 break;
294 if (i >= pl->parameters_num)
295 return (ENOENT);
297 if (i < (pl->parameters_num - 1))
298 {
299 parameter_t p;
301 p = pl->parameters[i];
302 pl->parameters[i] = pl->parameters[pl->parameters_num - 1];
303 pl->parameters[pl->parameters_num - 1] = p;
304 }
306 pl->parameters_num--;
307 free (pl->parameters[pl->parameters_num].key);
308 free (pl->parameters[pl->parameters_num].value);
310 return (0);
311 } /* }}} int param_delete */
313 int param_set (param_list_t *pl, const char *name, /* {{{ */
314 const char *value)
315 {
316 parameter_t *p;
317 char *value_copy;
318 size_t i;
320 if ((pl == NULL) || (name == NULL))
321 return (EINVAL);
323 if (value == NULL)
324 return (param_delete (pl, name));
326 p = NULL;
327 for (i = 0; i < pl->parameters_num; i++)
328 {
329 if (strcasecmp (pl->parameters[i].key, name) == 0)
330 {
331 p = pl->parameters + i;
332 break;
333 }
334 }
336 if (p == NULL)
337 return (param_add (pl, name, value));
339 value_copy = strdup (value);
340 if (value_copy == NULL)
341 return (ENOMEM);
343 free (p->value);
344 p->value = value_copy;
346 return (0);
347 } /* }}} int param_set */
349 char *param_as_string (param_list_t *pl) /* {{{ */
350 {
351 char buffer[4096];
352 char key[2048];
353 char value[2048];
354 size_t i;
356 if (pl == NULL)
357 return (NULL);
359 buffer[0] = 0;
360 for (i = 0; i < pl->parameters_num; i++)
361 {
362 uri_escape_copy (key, pl->parameters[i].key, sizeof (key));
363 uri_escape_copy (value, pl->parameters[i].value, sizeof (value));
365 if (i != 0)
366 strlcat (buffer, ";", sizeof (buffer));
367 strlcat (buffer, key, sizeof (buffer));
368 strlcat (buffer, "=", sizeof (buffer));
369 strlcat (buffer, value, sizeof (buffer));
370 }
372 return (strdup (buffer));
373 } /* }}} char *param_as_string */
375 int param_print_hidden (param_list_t *pl) /* {{{ */
376 {
377 char key[2048];
378 char value[2048];
379 size_t i;
381 if (pl == NULL)
382 return (EINVAL);
384 for (i = 0; i < pl->parameters_num; i++)
385 {
386 html_escape_copy (key, pl->parameters[i].key, sizeof (key));
387 html_escape_copy (value, pl->parameters[i].value, sizeof (value));
389 printf (" <input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
390 key, value);
391 }
393 return (0);
394 } /* }}} int param_print_hidden */
396 char *uri_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
397 {
398 size_t in;
399 size_t out;
401 in = 0;
402 out = 0;
403 while (42)
404 {
405 if (src[in] == 0)
406 {
407 dest[out] = 0;
408 return (dest);
409 }
410 else if ((((unsigned char) src[in]) < 32)
411 || (src[in] == '&')
412 || (src[in] == ';')
413 || (src[in] == '?')
414 || (src[in] == '/')
415 || (((unsigned char) src[in]) >= 128))
416 {
417 char esc[4];
419 if ((n - out) < 4)
420 break;
422 snprintf (esc, sizeof (esc), "%%%02x", (unsigned int) src[in]);
423 dest[out] = esc[0];
424 dest[out+1] = esc[1];
425 dest[out+2] = esc[2];
427 out += 3;
428 in++;
429 }
430 else
431 {
432 dest[out] = src[in];
433 out++;
434 in++;
435 }
436 } /* while (42) */
438 return (dest);
439 } /* }}} char *uri_escape_copy */
441 char *uri_escape (const char *string) /* {{{ */
442 {
443 char buffer[4096];
445 if (string == NULL)
446 return (NULL);
448 uri_escape_copy (buffer, string, sizeof (buffer));
450 return (strdup (buffer));
451 } /* }}} char *uri_escape */
453 const char *script_name (void) /* {{{ */
454 {
455 char *ret;
457 ret = getenv ("SCRIPT_NAME");
458 if (ret == NULL)
459 ret = "collection4.fcgi";
461 return (ret);
462 } /* }}} char *script_name */
464 int time_to_rfc1123 (time_t t, char *buffer, size_t buffer_size) /* {{{ */
465 {
466 struct tm tm_tmp;
467 size_t status;
469 /* RFC 1123 *requires* the time to be GMT and the "GMT" timezone string.
470 * Apache will ignore the timezone if "localtime_r" and "%z" is used,
471 * resulting in weird behavior. */
472 if (gmtime_r (&t, &tm_tmp) == NULL)
473 return (errno);
475 status = strftime (buffer, buffer_size, "%a, %d %b %Y %T GMT", &tm_tmp);
476 if (status == 0)
477 return (errno);
479 return (0);
480 } /* }}} int time_to_rfc1123 */
482 #define COPY_ENTITY(e) do { \
483 size_t len = strlen (e); \
484 if (dest_size < (len + 1)) \
485 break; \
486 strcpy (dest_ptr, (e)); \
487 dest_ptr += len; \
488 dest_size -= len; \
489 } while (0)
491 char *html_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
492 {
493 char *dest_ptr;
494 size_t dest_size;
495 size_t pos;
497 dest[0] = 0;
498 dest_ptr = dest;
499 dest_size = n;
500 for (pos = 0; src[pos] != 0; pos++)
501 {
502 if (src[pos] == '"')
503 COPY_ENTITY (""");
504 else if (src[pos] == '<')
505 COPY_ENTITY ("<");
506 else if (src[pos] == '>')
507 COPY_ENTITY (">");
508 else if (src[pos] == '&')
509 COPY_ENTITY ("&");
510 else
511 {
512 *dest_ptr = src[pos];
513 dest_ptr++;
514 dest_size--;
515 *dest_ptr = 0;
516 }
518 if (dest_size <= 1)
519 break;
520 }
522 return (dest);
523 } /* }}} char *html_escape_copy */
525 #undef COPY_ENTITY
527 char *html_escape_buffer (char *buffer, size_t buffer_size) /* {{{ */
528 {
529 char tmp[buffer_size];
531 html_escape_copy (tmp, buffer, sizeof (tmp));
532 memcpy (buffer, tmp, buffer_size);
534 return (buffer);
535 } /* }}} char *html_escape_buffer */
537 char *html_escape (const char *string) /* {{{ */
538 {
539 char buffer[4096];
541 if (string == NULL)
542 return (NULL);
544 html_escape_copy (buffer, string, sizeof (buffer));
546 return (strdup (buffer));
547 } /* }}} char *html_escape */
549 int html_print_page (const char *title, /* {{{ */
550 const page_callbacks_t *cb, void *user_data)
551 {
552 char *title_html;
554 printf ("Content-Type: text/html\n"
555 "X-Generator: "PACKAGE_STRING"\n"
556 "\n\n");
558 if (title == NULL)
559 title = "c4: collection4 graph interface";
561 title_html = html_escape (title);
563 printf ("<html>\n"
564 " <head>\n"
565 " <title>%s</title>\n"
566 " <link rel=\"stylesheet\" type=\"text/css\" href=\"../share/style.css\" />\n"
567 " <script type=\"text/javascript\" src=\"../share/jquery-1.4.2.min.js\">\n"
568 " </script>\n"
569 " <script type=\"text/javascript\" src=\"../share/collection.js\">\n"
570 " </script>\n"
571 " </head>\n",
572 title_html);
574 printf (" <body>\n"
575 " <table id=\"layout-table\">\n"
576 " <tr id=\"layout-top\">\n"
577 " <td id=\"layout-top-left\">");
578 if (cb->top_left != NULL)
579 (*cb->top_left) (user_data);
580 else
581 html_print_logo (NULL);
582 printf ("</td>\n"
583 " <td id=\"layout-top-center\">");
584 if (cb->top_center != NULL)
585 (*cb->top_center) (user_data);
586 else
587 printf ("<h1>%s</h1>", title_html);
588 printf ("</td>\n"
589 " <td id=\"layout-top-right\">");
590 if (cb->top_right != NULL)
591 (*cb->top_right) (user_data);
592 printf ("</td>\n"
593 " </tr>\n"
594 " <tr id=\"layout-middle\">\n"
595 " <td id=\"layout-middle-left\">");
596 if (cb->middle_left != NULL)
597 (*cb->middle_left) (user_data);
598 printf ("</td>\n"
599 " <td id=\"layout-middle-center\">");
600 if (cb->middle_center != NULL)
601 (*cb->middle_center) (user_data);
602 printf ("</td>\n"
603 " <td id=\"layout-middle-right\">");
604 if (cb->middle_right != NULL)
605 (*cb->middle_right) (user_data);
606 printf ("</td>\n"
607 " </tr>\n"
608 " <tr id=\"layout-bottom\">\n"
609 " <td id=\"layout-bottom-left\">");
610 if (cb->bottom_left != NULL)
611 (*cb->bottom_left) (user_data);
612 printf ("</td>\n"
613 " <td id=\"layout-bottom-center\">");
614 if (cb->bottom_center != NULL)
615 (*cb->bottom_center) (user_data);
616 printf ("</td>\n"
617 " <td id=\"layout-bottom-right\">");
618 if (cb->bottom_right != NULL)
619 (*cb->bottom_right) (user_data);
620 printf ("</td>\n"
621 " </tr>\n"
622 " </table>\n"
623 " </body>\n"
624 "</html>\n");
626 free (title_html);
627 return (0);
628 } /* }}} int html_print_page */
630 int html_print_logo (__attribute__((unused)) void *user_data) /* {{{ */
631 {
632 printf ("<a href=\"%s?action=list_graphs\" id=\"logo-canvas\">\n"
633 " <h1>c<sup>4</sup></h1>\n"
634 " <div id=\"logo-subscript\">collection 4</div>\n"
635 "</a>\n");
637 return (0);
638 } /* }}} int html_print_search_box */
640 int html_print_search_box (__attribute__((unused)) void *user_data) /* {{{ */
641 {
642 char *term_html;
644 term_html = html_escape (param ("q"));
646 printf ("<form action=\"%s\" method=\"get\" id=\"search-form\">\n"
647 " <input type=\"hidden\" name=\"action\" value=\"list_graphs\" />\n"
648 " <input type=\"text\" name=\"q\" value=\"%s\" id=\"search-input\" />\n"
649 " <input type=\"submit\" name=\"button\" value=\"Search\" />\n"
650 "</form>\n",
651 script_name (),
652 (term_html != NULL) ? term_html : "");
654 free (term_html);
656 return (0);
657 } /* }}} int html_print_search_box */
659 /* vim: set sw=2 sts=2 et fdm=marker : */