f4abec2dac3be3196a1b4009d47860e1fbbdabe9
1 /**
2 * collection4 - utils_cgi.c
3 * Copyright (C) 2010 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 *
20 * Authors:
21 * Florian octo Forster <ff at octo.it>
22 **/
24 #include "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <time.h>
32 #include <assert.h>
34 #include "utils_cgi.h"
35 #include "common.h"
37 #include <fcgiapp.h>
38 #include <fcgi_stdio.h>
40 struct parameter_s
41 {
42 char *key;
43 char *value;
44 };
45 typedef struct parameter_s parameter_t;
47 struct param_list_s
48 {
49 parameter_t *parameters;
50 size_t parameters_num;
51 };
53 static param_list_t *pl_global = NULL;
55 static char *uri_unescape_copy (char *dest, const char *src, size_t n) /* {{{ */
56 {
57 const char *src_ptr;
58 char *dest_ptr;
60 if ((dest == NULL) || (src == NULL) || (n < 1))
61 return (NULL);
63 src_ptr = src;
64 dest_ptr = dest;
66 *dest_ptr = 0;
68 while (*src_ptr != 0)
69 {
70 if (n < 2)
71 break;
73 if (*src_ptr == '+')
74 {
75 *dest_ptr = ' ';
76 }
77 else if ((src_ptr[0] == '%')
78 && isxdigit ((int) src_ptr[1]) && isxdigit ((int) src_ptr[2]))
79 {
80 char tmpstr[3];
81 char *endptr;
82 long value;
84 tmpstr[0] = src_ptr[1];
85 tmpstr[1] = src_ptr[2];
86 tmpstr[2] = 0;
88 errno = 0;
89 endptr = NULL;
90 value = strtol (tmpstr, &endptr, /* base = */ 16);
91 if ((endptr == tmpstr) || (errno != 0))
92 {
93 *dest_ptr = '?';
94 }
95 else
96 {
97 *dest_ptr = (char) value;
98 }
100 src_ptr += 2;
101 }
102 else
103 {
104 *dest_ptr = *src_ptr;
105 }
107 src_ptr++;
108 dest_ptr++;
109 *dest_ptr = 0;
110 } /* while (*src_ptr != 0) */
112 assert (*dest_ptr == 0);
113 return (dest);
114 } /* }}} char *uri_unescape */
116 static char *uri_unescape (const char *string) /* {{{ */
117 {
118 char buffer[4096];
120 if (string == NULL)
121 return (NULL);
123 uri_unescape_copy (buffer, string, sizeof (buffer));
125 return (strdup (buffer));
126 } /* }}} char *uri_unescape */
128 static int param_parse_keyval (param_list_t *pl, char *keyval) /* {{{ */
129 {
130 char *key_raw;
131 char *value_raw;
132 char *key;
133 char *value;
135 key_raw = keyval;
136 value_raw = strchr (key_raw, '=');
137 if (value_raw == NULL)
138 return (EINVAL);
139 *value_raw = 0;
140 value_raw++;
142 key = uri_unescape (key_raw);
143 if (key == NULL)
144 return (ENOMEM);
146 value = uri_unescape (value_raw);
147 if (value == NULL)
148 {
149 free (key);
150 return (ENOMEM);
151 }
153 param_set (pl, key, value);
155 free (key);
156 free (value);
158 return (0);
159 } /* }}} int param_parse_keyval */
161 static int parse_query_string (param_list_t *pl, /* {{{ */
162 char *query_string)
163 {
164 char *dummy;
165 char *keyval;
167 if ((pl == NULL) || (query_string == NULL))
168 return (EINVAL);
170 dummy = query_string;
171 while ((keyval = strtok (dummy, ";&")) != NULL)
172 {
173 dummy = NULL;
174 param_parse_keyval (pl, keyval);
175 }
177 return (0);
178 } /* }}} int parse_query_string */
180 int param_init (void) /* {{{ */
181 {
182 if (pl_global != NULL)
183 return (0);
185 pl_global = param_create (/* query string = */ NULL);
186 if (pl_global == NULL)
187 return (ENOMEM);
189 return (0);
190 } /* }}} int param_init */
192 void param_finish (void) /* {{{ */
193 {
194 param_destroy (pl_global);
195 pl_global = NULL;
196 } /* }}} void param_finish */
198 const char *param (const char *key) /* {{{ */
199 {
200 param_init ();
202 return (param_get (pl_global, key));
203 } /* }}} const char *param */
205 param_list_t *param_create (const char *query_string) /* {{{ */
206 {
207 char *tmp;
208 param_list_t *pl;
210 if (query_string == NULL)
211 query_string = getenv ("QUERY_STRING");
213 if (query_string == NULL)
214 return (NULL);
216 tmp = strdup (query_string);
217 if (tmp == NULL)
218 return (NULL);
220 pl = malloc (sizeof (*pl));
221 if (pl == NULL)
222 {
223 free (tmp);
224 return (NULL);
225 }
226 memset (pl, 0, sizeof (*pl));
228 parse_query_string (pl, tmp);
230 free (tmp);
231 return (pl);
232 } /* }}} param_list_t *param_create */
234 param_list_t *param_clone (__attribute__((unused)) param_list_t *pl) /* {{{ */
235 {
236 /* FIXME: To be implemented. */
237 assert (23 == 42);
238 return (NULL);
239 } /* }}} param_list_t *param_clone */
241 void param_destroy (param_list_t *pl) /* {{{ */
242 {
243 size_t i;
245 if (pl == NULL)
246 return;
248 for (i = 0; i < pl->parameters_num; i++)
249 {
250 free (pl->parameters[i].key);
251 free (pl->parameters[i].value);
252 }
253 free (pl->parameters);
254 free (pl);
255 } /* }}} void param_destroy */
257 const char *param_get (param_list_t *pl, const char *name) /* {{{ */
258 {
259 size_t i;
261 if ((pl == NULL) || (name == NULL))
262 return (NULL);
264 for (i = 0; i < pl->parameters_num; i++)
265 {
266 if ((name == NULL) && (pl->parameters[i].key == NULL))
267 return (pl->parameters[i].value);
268 else if ((name != NULL) && (pl->parameters[i].key != NULL)
269 && (strcmp (name, pl->parameters[i].key) == 0))
270 return (pl->parameters[i].value);
271 }
273 return (NULL);
274 } /* }}} char *param_get */
276 static int param_add (param_list_t *pl, /* {{{ */
277 const char *key, const char *value)
278 {
279 parameter_t *tmp;
281 tmp = realloc (pl->parameters,
282 sizeof (*pl->parameters) * (pl->parameters_num + 1));
283 if (tmp == NULL)
284 return (ENOMEM);
285 pl->parameters = tmp;
286 tmp = pl->parameters + pl->parameters_num;
288 memset (tmp, 0, sizeof (*tmp));
289 tmp->key = strdup (key);
290 if (tmp->key == NULL)
291 return (ENOMEM);
293 tmp->value = strdup (value);
294 if (tmp->value == NULL)
295 {
296 free (tmp->key);
297 return (ENOMEM);
298 }
300 pl->parameters_num++;
302 return (0);
303 } /* }}} int param_add */
305 static int param_delete (param_list_t *pl, /* {{{ */
306 const char *name)
307 {
308 size_t i;
310 if ((pl == NULL) || (name == NULL))
311 return (EINVAL);
313 for (i = 0; i < pl->parameters_num; i++)
314 if (strcasecmp (pl->parameters[i].key, name) == 0)
315 break;
317 if (i >= pl->parameters_num)
318 return (ENOENT);
320 if (i < (pl->parameters_num - 1))
321 {
322 parameter_t p;
324 p = pl->parameters[i];
325 pl->parameters[i] = pl->parameters[pl->parameters_num - 1];
326 pl->parameters[pl->parameters_num - 1] = p;
327 }
329 pl->parameters_num--;
330 free (pl->parameters[pl->parameters_num].key);
331 free (pl->parameters[pl->parameters_num].value);
333 return (0);
334 } /* }}} int param_delete */
336 int param_set (param_list_t *pl, const char *name, /* {{{ */
337 const char *value)
338 {
339 parameter_t *p;
340 char *value_copy;
341 size_t i;
343 if ((pl == NULL) || (name == NULL))
344 return (EINVAL);
346 if (value == NULL)
347 return (param_delete (pl, name));
349 p = NULL;
350 for (i = 0; i < pl->parameters_num; i++)
351 {
352 if (strcasecmp (pl->parameters[i].key, name) == 0)
353 {
354 p = pl->parameters + i;
355 break;
356 }
357 }
359 if (p == NULL)
360 return (param_add (pl, name, value));
362 value_copy = strdup (value);
363 if (value_copy == NULL)
364 return (ENOMEM);
366 free (p->value);
367 p->value = value_copy;
369 return (0);
370 } /* }}} int param_set */
372 char *param_as_string (param_list_t *pl) /* {{{ */
373 {
374 char buffer[4096];
375 char key[2048];
376 char value[2048];
377 size_t i;
379 if (pl == NULL)
380 return (NULL);
382 buffer[0] = 0;
383 for (i = 0; i < pl->parameters_num; i++)
384 {
385 uri_escape_copy (key, pl->parameters[i].key, sizeof (key));
386 uri_escape_copy (value, pl->parameters[i].value, sizeof (value));
388 if (i != 0)
389 strlcat (buffer, ";", sizeof (buffer));
390 strlcat (buffer, key, sizeof (buffer));
391 strlcat (buffer, "=", sizeof (buffer));
392 strlcat (buffer, value, sizeof (buffer));
393 }
395 return (strdup (buffer));
396 } /* }}} char *param_as_string */
398 int param_print_hidden (param_list_t *pl) /* {{{ */
399 {
400 char key[2048];
401 char value[2048];
402 size_t i;
404 if (pl == NULL)
405 return (EINVAL);
407 for (i = 0; i < pl->parameters_num; i++)
408 {
409 html_escape_copy (key, pl->parameters[i].key, sizeof (key));
410 html_escape_copy (value, pl->parameters[i].value, sizeof (value));
412 printf (" <input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
413 key, value);
414 }
416 return (0);
417 } /* }}} int param_print_hidden */
419 char *uri_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
420 {
421 size_t in;
422 size_t out;
424 in = 0;
425 out = 0;
426 while (42)
427 {
428 if (src[in] == 0)
429 {
430 dest[out] = 0;
431 return (dest);
432 }
433 else if ((((unsigned char) src[in]) < 32)
434 || (src[in] == '&')
435 || (src[in] == ';')
436 || (src[in] == '?')
437 || (src[in] == '/')
438 || (((unsigned char) src[in]) >= 128))
439 {
440 char esc[4];
442 if ((n - out) < 4)
443 break;
445 snprintf (esc, sizeof (esc), "%%%02x", (unsigned int) src[in]);
446 dest[out] = esc[0];
447 dest[out+1] = esc[1];
448 dest[out+2] = esc[2];
450 out += 3;
451 in++;
452 }
453 else
454 {
455 dest[out] = src[in];
456 out++;
457 in++;
458 }
459 } /* while (42) */
461 return (dest);
462 } /* }}} char *uri_escape_copy */
464 char *uri_escape (const char *string) /* {{{ */
465 {
466 char buffer[4096];
468 if (string == NULL)
469 return (NULL);
471 uri_escape_copy (buffer, string, sizeof (buffer));
473 return (strdup (buffer));
474 } /* }}} char *uri_escape */
476 #define COPY_ENTITY(e) do { \
477 size_t len = strlen (e); \
478 if (dest_size < (len + 1)) \
479 break; \
480 strcpy (dest_ptr, (e)); \
481 dest_ptr += len; \
482 dest_size -= len; \
483 } while (0)
485 char *json_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
486 {
487 char *dest_ptr;
488 size_t dest_size;
489 size_t pos;
491 dest[0] = 0;
492 dest_ptr = dest;
493 dest_size = n;
494 for (pos = 0; src[pos] != 0; pos++)
495 {
496 if (src[pos] == '"')
497 COPY_ENTITY ("\\\"");
498 else if (src[pos] == '\\')
499 COPY_ENTITY ("\\\\");
500 else if (((uint8_t) src[pos]) < 32)
501 {
502 if (src[pos] == '\n')
503 COPY_ENTITY ("\\n");
504 else if (src[pos] == '\r')
505 COPY_ENTITY ("\\r");
506 else if (src[pos] == '\t')
507 COPY_ENTITY ("\\t");
508 else if (src[pos] == '\b')
509 COPY_ENTITY ("\\b");
510 else if (src[pos] == '\f')
511 COPY_ENTITY ("\\f");
512 else
513 {
514 char buffer[8];
515 sprintf (buffer, "\\u%04"PRIx8, (uint8_t) src[pos]);
516 buffer[sizeof (buffer) - 1] = 0;
517 COPY_ENTITY (buffer);
518 }
519 }
520 else
521 {
522 *dest_ptr = src[pos];
523 dest_ptr++;
524 dest_size--;
525 *dest_ptr = 0;
526 }
528 if (dest_size <= 1)
529 break;
530 }
532 return (dest);
533 } /* }}} char *json_escape_copy */
535 #undef COPY_ENTITY
537 char *json_escape_buffer (char *buffer, size_t buffer_size)
538 {
539 char temp[buffer_size];
541 json_escape_copy (temp, buffer, buffer_size);
542 memcpy (buffer, temp, buffer_size);
544 return (buffer);
545 } /* }}} char *json_escape_buffer */
547 char *json_escape (const char *string) /* {{{ */
548 {
549 char buffer[4096];
551 if (string == NULL)
552 return (NULL);
554 json_escape_copy (buffer, string, sizeof (buffer));
556 return (strdup (buffer));
557 } /* }}} char *json_escape */
559 const char *script_name (void) /* {{{ */
560 {
561 char *ret;
563 ret = getenv ("SCRIPT_NAME");
564 if (ret == NULL)
565 ret = "collection4.fcgi";
567 return (ret);
568 } /* }}} char *script_name */
570 int time_to_rfc1123 (time_t t, char *buffer, size_t buffer_size) /* {{{ */
571 {
572 struct tm tm_tmp;
573 size_t status;
575 /* RFC 1123 *requires* the time to be GMT and the "GMT" timezone string.
576 * Apache will ignore the timezone if "localtime_r" and "%z" is used,
577 * resulting in weird behavior. */
578 if (gmtime_r (&t, &tm_tmp) == NULL)
579 return (errno);
581 status = strftime (buffer, buffer_size, "%a, %d %b %Y %T GMT", &tm_tmp);
582 if (status == 0)
583 return (errno);
585 return (0);
586 } /* }}} int time_to_rfc1123 */
588 #define COPY_ENTITY(e) do { \
589 size_t len = strlen (e); \
590 if (dest_size < (len + 1)) \
591 break; \
592 strcpy (dest_ptr, (e)); \
593 dest_ptr += len; \
594 dest_size -= len; \
595 } while (0)
597 char *html_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
598 {
599 char *dest_ptr;
600 size_t dest_size;
601 size_t pos;
603 dest[0] = 0;
604 dest_ptr = dest;
605 dest_size = n;
606 for (pos = 0; src[pos] != 0; pos++)
607 {
608 if (src[pos] == '"')
609 COPY_ENTITY (""");
610 else if (src[pos] == '<')
611 COPY_ENTITY ("<");
612 else if (src[pos] == '>')
613 COPY_ENTITY (">");
614 else if (src[pos] == '&')
615 COPY_ENTITY ("&");
616 else
617 {
618 *dest_ptr = src[pos];
619 dest_ptr++;
620 dest_size--;
621 *dest_ptr = 0;
622 }
624 if (dest_size <= 1)
625 break;
626 }
628 return (dest);
629 } /* }}} char *html_escape_copy */
631 #undef COPY_ENTITY
633 char *html_escape_buffer (char *buffer, size_t buffer_size) /* {{{ */
634 {
635 char tmp[buffer_size];
637 html_escape_copy (tmp, buffer, sizeof (tmp));
638 memcpy (buffer, tmp, buffer_size);
640 return (buffer);
641 } /* }}} char *html_escape_buffer */
643 char *html_escape (const char *string) /* {{{ */
644 {
645 char buffer[4096];
647 if (string == NULL)
648 return (NULL);
650 html_escape_copy (buffer, string, sizeof (buffer));
652 return (strdup (buffer));
653 } /* }}} char *html_escape */
655 int html_print_page (const char *title, /* {{{ */
656 const page_callbacks_t *cb, void *user_data)
657 {
658 char *title_html;
660 printf ("Content-Type: text/html\n"
661 "X-Generator: "PACKAGE_STRING"\n"
662 "\n\n");
664 if (title == NULL)
665 title = "C₄: collection4 graph interface";
667 title_html = html_escape (title);
669 printf ("<html>\n"
670 " <head>\n"
671 " <title>%s</title>\n"
672 " <link rel=\"stylesheet\" type=\"text/css\" href=\"../share/style.css\" />\n"
673 " <script type=\"text/javascript\" src=\"../share/jquery-1.4.2.min.js\">\n"
674 " </script>\n"
675 " <script type=\"text/javascript\" src=\"../share/collection.js\">\n"
676 " </script>\n"
677 " </head>\n",
678 title_html);
680 printf (" <body>\n"
681 " <table id=\"layout-table\">\n"
682 " <tr id=\"layout-top\">\n"
683 " <td id=\"layout-top-left\">");
684 if (cb->top_left != NULL)
685 (*cb->top_left) (user_data);
686 else
687 html_print_logo (NULL);
688 printf ("</td>\n"
689 " <td id=\"layout-top-center\">");
690 if (cb->top_center != NULL)
691 (*cb->top_center) (user_data);
692 else
693 printf ("<h1>%s</h1>", title_html);
694 printf ("</td>\n"
695 " <td id=\"layout-top-right\">");
696 if (cb->top_right != NULL)
697 (*cb->top_right) (user_data);
698 printf ("</td>\n"
699 " </tr>\n"
700 " <tr id=\"layout-middle\">\n"
701 " <td id=\"layout-middle-left\">");
702 if (cb->middle_left != NULL)
703 (*cb->middle_left) (user_data);
704 printf ("</td>\n"
705 " <td id=\"layout-middle-center\">");
706 if (cb->middle_center != NULL)
707 (*cb->middle_center) (user_data);
708 printf ("</td>\n"
709 " <td id=\"layout-middle-right\">");
710 if (cb->middle_right != NULL)
711 (*cb->middle_right) (user_data);
712 printf ("</td>\n"
713 " </tr>\n"
714 " <tr id=\"layout-bottom\">\n"
715 " <td id=\"layout-bottom-left\">");
716 if (cb->bottom_left != NULL)
717 (*cb->bottom_left) (user_data);
718 printf ("</td>\n"
719 " <td id=\"layout-bottom-center\">");
720 if (cb->bottom_center != NULL)
721 (*cb->bottom_center) (user_data);
722 printf ("</td>\n"
723 " <td id=\"layout-bottom-right\">");
724 if (cb->bottom_right != NULL)
725 (*cb->bottom_right) (user_data);
726 printf ("</td>\n"
727 " </tr>\n"
728 " </table>\n"
729 " <div class=\"footer\">"PACKAGE_STRING"</div>\n"
730 " </body>\n"
731 "</html>\n");
733 free (title_html);
734 return (0);
735 } /* }}} int html_print_page */
737 int html_print_logo (__attribute__((unused)) void *user_data) /* {{{ */
738 {
739 printf ("<a href=\"%s?action=list_graphs\" id=\"logo-canvas\">\n"
740 " <h1>C<sub>4</sub></h1>\n"
741 " <div id=\"logo-subscript\">collection 4</div>\n"
742 "</a>\n");
744 return (0);
745 } /* }}} int html_print_search_box */
747 int html_print_search_box (__attribute__((unused)) void *user_data) /* {{{ */
748 {
749 char *term_html;
751 term_html = html_escape (param ("q"));
753 printf ("<form action=\"%s\" method=\"get\" id=\"search-form\">\n"
754 " <input type=\"hidden\" name=\"action\" value=\"search\" />\n"
755 " <input type=\"text\" name=\"q\" value=\"%s\" id=\"search-input\" />\n"
756 " <input type=\"submit\" name=\"button\" value=\"Search\" />\n"
757 "</form>\n",
758 script_name (),
759 (term_html != NULL) ? term_html : "");
761 free (term_html);
763 return (0);
764 } /* }}} int html_print_search_box */
766 /* vim: set sw=2 sts=2 et fdm=marker : */