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 n--;
108 src_ptr++;
109 dest_ptr++;
110 *dest_ptr = 0;
111 } /* while (*src_ptr != 0) */
113 assert (*dest_ptr == 0);
114 return (dest);
115 } /* }}} char *uri_unescape_copy */
117 static char *uri_unescape (const char *string) /* {{{ */
118 {
119 char buffer[4096];
121 if (string == NULL)
122 return (NULL);
124 uri_unescape_copy (buffer, string, sizeof (buffer));
126 return (strdup (buffer));
127 } /* }}} char *uri_unescape */
129 static int param_parse_keyval (param_list_t *pl, char *keyval) /* {{{ */
130 {
131 char *key_raw;
132 char *value_raw;
133 char *key;
134 char *value;
136 key_raw = keyval;
137 value_raw = strchr (key_raw, '=');
138 if (value_raw == NULL)
139 return (EINVAL);
140 *value_raw = 0;
141 value_raw++;
143 key = uri_unescape (key_raw);
144 if (key == NULL)
145 return (ENOMEM);
147 value = uri_unescape (value_raw);
148 if (value == NULL)
149 {
150 free (key);
151 return (ENOMEM);
152 }
154 param_set (pl, key, value);
156 free (key);
157 free (value);
159 return (0);
160 } /* }}} int param_parse_keyval */
162 static int parse_query_string (param_list_t *pl, /* {{{ */
163 char *query_string)
164 {
165 char *dummy;
166 char *keyval;
168 if ((pl == NULL) || (query_string == NULL))
169 return (EINVAL);
171 dummy = query_string;
172 while ((keyval = strtok (dummy, ";&")) != NULL)
173 {
174 dummy = NULL;
175 param_parse_keyval (pl, keyval);
176 }
178 return (0);
179 } /* }}} int parse_query_string */
181 int param_init (void) /* {{{ */
182 {
183 if (pl_global != NULL)
184 return (0);
186 pl_global = param_create (/* query string = */ NULL);
187 if (pl_global == NULL)
188 return (ENOMEM);
190 return (0);
191 } /* }}} int param_init */
193 void param_finish (void) /* {{{ */
194 {
195 param_destroy (pl_global);
196 pl_global = NULL;
197 } /* }}} void param_finish */
199 const char *param (const char *key) /* {{{ */
200 {
201 param_init ();
203 return (param_get (pl_global, key));
204 } /* }}} const char *param */
206 param_list_t *param_create (const char *query_string) /* {{{ */
207 {
208 char *tmp;
209 param_list_t *pl;
211 if (query_string == NULL)
212 query_string = getenv ("QUERY_STRING");
214 if (query_string == NULL)
215 return (NULL);
217 tmp = strdup (query_string);
218 if (tmp == NULL)
219 return (NULL);
221 pl = malloc (sizeof (*pl));
222 if (pl == NULL)
223 {
224 free (tmp);
225 return (NULL);
226 }
227 memset (pl, 0, sizeof (*pl));
229 parse_query_string (pl, tmp);
231 free (tmp);
232 return (pl);
233 } /* }}} param_list_t *param_create */
235 param_list_t *param_clone (__attribute__((unused)) param_list_t *pl) /* {{{ */
236 {
237 /* FIXME: To be implemented. */
238 assert (23 == 42);
239 return (NULL);
240 } /* }}} param_list_t *param_clone */
242 void param_destroy (param_list_t *pl) /* {{{ */
243 {
244 size_t i;
246 if (pl == NULL)
247 return;
249 for (i = 0; i < pl->parameters_num; i++)
250 {
251 free (pl->parameters[i].key);
252 free (pl->parameters[i].value);
253 }
254 free (pl->parameters);
255 free (pl);
256 } /* }}} void param_destroy */
258 const char *param_get (param_list_t *pl, const char *name) /* {{{ */
259 {
260 size_t i;
262 if ((pl == NULL) || (name == NULL))
263 return (NULL);
265 for (i = 0; i < pl->parameters_num; i++)
266 {
267 if ((name == NULL) && (pl->parameters[i].key == NULL))
268 return (pl->parameters[i].value);
269 else if ((name != NULL) && (pl->parameters[i].key != NULL)
270 && (strcmp (name, pl->parameters[i].key) == 0))
271 return (pl->parameters[i].value);
272 }
274 return (NULL);
275 } /* }}} char *param_get */
277 static int param_add (param_list_t *pl, /* {{{ */
278 const char *key, const char *value)
279 {
280 parameter_t *tmp;
282 tmp = realloc (pl->parameters,
283 sizeof (*pl->parameters) * (pl->parameters_num + 1));
284 if (tmp == NULL)
285 return (ENOMEM);
286 pl->parameters = tmp;
287 tmp = pl->parameters + pl->parameters_num;
289 memset (tmp, 0, sizeof (*tmp));
290 tmp->key = strdup (key);
291 if (tmp->key == NULL)
292 return (ENOMEM);
294 tmp->value = strdup (value);
295 if (tmp->value == NULL)
296 {
297 free (tmp->key);
298 return (ENOMEM);
299 }
301 pl->parameters_num++;
303 return (0);
304 } /* }}} int param_add */
306 static int param_delete (param_list_t *pl, /* {{{ */
307 const char *name)
308 {
309 size_t i;
311 if ((pl == NULL) || (name == NULL))
312 return (EINVAL);
314 for (i = 0; i < pl->parameters_num; i++)
315 if (strcasecmp (pl->parameters[i].key, name) == 0)
316 break;
318 if (i >= pl->parameters_num)
319 return (ENOENT);
321 if (i < (pl->parameters_num - 1))
322 {
323 parameter_t p;
325 p = pl->parameters[i];
326 pl->parameters[i] = pl->parameters[pl->parameters_num - 1];
327 pl->parameters[pl->parameters_num - 1] = p;
328 }
330 pl->parameters_num--;
331 free (pl->parameters[pl->parameters_num].key);
332 free (pl->parameters[pl->parameters_num].value);
334 return (0);
335 } /* }}} int param_delete */
337 int param_set (param_list_t *pl, const char *name, /* {{{ */
338 const char *value)
339 {
340 parameter_t *p;
341 char *value_copy;
342 size_t i;
344 if ((pl == NULL) || (name == NULL))
345 return (EINVAL);
347 if (value == NULL)
348 return (param_delete (pl, name));
350 p = NULL;
351 for (i = 0; i < pl->parameters_num; i++)
352 {
353 if (strcasecmp (pl->parameters[i].key, name) == 0)
354 {
355 p = pl->parameters + i;
356 break;
357 }
358 }
360 if (p == NULL)
361 return (param_add (pl, name, value));
363 value_copy = strdup (value);
364 if (value_copy == NULL)
365 return (ENOMEM);
367 free (p->value);
368 p->value = value_copy;
370 return (0);
371 } /* }}} int param_set */
373 char *param_as_string (param_list_t *pl) /* {{{ */
374 {
375 char buffer[4096];
376 char key[2048];
377 char value[2048];
378 size_t i;
380 if (pl == NULL)
381 return (NULL);
383 buffer[0] = 0;
384 for (i = 0; i < pl->parameters_num; i++)
385 {
386 uri_escape_copy (key, pl->parameters[i].key, sizeof (key));
387 uri_escape_copy (value, pl->parameters[i].value, sizeof (value));
389 if (i != 0)
390 strlcat (buffer, ";", sizeof (buffer));
391 strlcat (buffer, key, sizeof (buffer));
392 strlcat (buffer, "=", sizeof (buffer));
393 strlcat (buffer, value, sizeof (buffer));
394 }
396 return (strdup (buffer));
397 } /* }}} char *param_as_string */
399 int param_print_hidden (param_list_t *pl) /* {{{ */
400 {
401 char key[2048];
402 char value[2048];
403 size_t i;
405 if (pl == NULL)
406 return (EINVAL);
408 for (i = 0; i < pl->parameters_num; i++)
409 {
410 html_escape_copy (key, pl->parameters[i].key, sizeof (key));
411 html_escape_copy (value, pl->parameters[i].value, sizeof (value));
413 printf (" <input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
414 key, value);
415 }
417 return (0);
418 } /* }}} int param_print_hidden */
420 char *uri_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
421 {
422 size_t in;
423 size_t out;
425 in = 0;
426 out = 0;
427 while (42)
428 {
429 if (src[in] == 0)
430 {
431 dest[out] = 0;
432 return (dest);
433 }
434 else if ((((unsigned char) src[in]) < 32)
435 || (src[in] == ' ')
436 || (src[in] == '&')
437 || (src[in] == ';')
438 || (src[in] == '?')
439 || (src[in] == '/')
440 || (((unsigned char) src[in]) >= 128))
441 {
442 char esc[4];
444 if ((n - out) < 4)
445 break;
447 snprintf (esc, sizeof (esc), "%%%02x", (unsigned int) src[in]);
448 dest[out] = esc[0];
449 dest[out+1] = esc[1];
450 dest[out+2] = esc[2];
452 out += 3;
453 in++;
454 }
455 else
456 {
457 dest[out] = src[in];
458 out++;
459 in++;
460 }
461 } /* while (42) */
463 return (dest);
464 } /* }}} char *uri_escape_copy */
466 char *uri_escape_buffer (char *buffer, size_t buffer_size) /* {{{ */
467 {
468 char temp[buffer_size];
470 uri_escape_copy (temp, buffer, buffer_size);
471 memcpy (buffer, temp, buffer_size);
473 return (&buffer[0]);
474 } /* }}} char *uri_escape_buffer */
476 char *uri_escape (const char *string) /* {{{ */
477 {
478 char buffer[4096];
480 if (string == NULL)
481 return (NULL);
483 uri_escape_copy (buffer, string, sizeof (buffer));
485 return (strdup (buffer));
486 } /* }}} char *uri_escape */
488 #define COPY_ENTITY(e) do { \
489 size_t len = strlen (e); \
490 if (dest_size < (len + 1)) \
491 break; \
492 strcpy (dest_ptr, (e)); \
493 dest_ptr += len; \
494 dest_size -= len; \
495 } while (0)
497 char *json_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
498 {
499 char *dest_ptr;
500 size_t dest_size;
501 size_t pos;
503 dest[0] = 0;
504 dest_ptr = dest;
505 dest_size = n;
506 for (pos = 0; src[pos] != 0; pos++)
507 {
508 if (src[pos] == '"')
509 COPY_ENTITY ("\\\"");
510 else if (src[pos] == '\\')
511 COPY_ENTITY ("\\\\");
512 else if (((uint8_t) src[pos]) < 32)
513 {
514 if (src[pos] == '\n')
515 COPY_ENTITY ("\\n");
516 else if (src[pos] == '\r')
517 COPY_ENTITY ("\\r");
518 else if (src[pos] == '\t')
519 COPY_ENTITY ("\\t");
520 else if (src[pos] == '\b')
521 COPY_ENTITY ("\\b");
522 else if (src[pos] == '\f')
523 COPY_ENTITY ("\\f");
524 else
525 {
526 char buffer[8];
527 sprintf (buffer, "\\u%04"PRIx8, (uint8_t) src[pos]);
528 buffer[sizeof (buffer) - 1] = 0;
529 COPY_ENTITY (buffer);
530 }
531 }
532 else
533 {
534 *dest_ptr = src[pos];
535 dest_ptr++;
536 dest_size--;
537 *dest_ptr = 0;
538 }
540 if (dest_size <= 1)
541 break;
542 }
544 return (dest);
545 } /* }}} char *json_escape_copy */
547 #undef COPY_ENTITY
549 char *json_escape_buffer (char *buffer, size_t buffer_size)
550 {
551 char temp[buffer_size];
553 json_escape_copy (temp, buffer, buffer_size);
554 memcpy (buffer, temp, buffer_size);
556 return (buffer);
557 } /* }}} char *json_escape_buffer */
559 char *json_escape (const char *string) /* {{{ */
560 {
561 char buffer[4096];
563 if (string == NULL)
564 return (NULL);
566 json_escape_copy (buffer, string, sizeof (buffer));
568 return (strdup (buffer));
569 } /* }}} char *json_escape */
571 const char *script_name (void) /* {{{ */
572 {
573 char *ret;
575 ret = getenv ("SCRIPT_NAME");
576 if (ret == NULL)
577 ret = "collection4.fcgi";
579 return (ret);
580 } /* }}} char *script_name */
582 int time_to_rfc1123 (time_t t, char *buffer, size_t buffer_size) /* {{{ */
583 {
584 struct tm tm_tmp;
585 size_t status;
587 /* RFC 1123 *requires* the time to be GMT and the "GMT" timezone string.
588 * Apache will ignore the timezone if "localtime_r" and "%z" is used,
589 * resulting in weird behavior. */
590 if (gmtime_r (&t, &tm_tmp) == NULL)
591 return (errno);
593 status = strftime (buffer, buffer_size, "%a, %d %b %Y %T GMT", &tm_tmp);
594 if (status == 0)
595 return (errno);
597 return (0);
598 } /* }}} int time_to_rfc1123 */
600 #define COPY_ENTITY(e) do { \
601 size_t len = strlen (e); \
602 if (dest_size < (len + 1)) \
603 break; \
604 strcpy (dest_ptr, (e)); \
605 dest_ptr += len; \
606 dest_size -= len; \
607 } while (0)
609 char *html_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
610 {
611 char *dest_ptr;
612 size_t dest_size;
613 size_t pos;
615 dest[0] = 0;
616 dest_ptr = dest;
617 dest_size = n;
618 for (pos = 0; src[pos] != 0; pos++)
619 {
620 if (src[pos] == '"')
621 COPY_ENTITY (""");
622 else if (src[pos] == '<')
623 COPY_ENTITY ("<");
624 else if (src[pos] == '>')
625 COPY_ENTITY (">");
626 else if (src[pos] == '&')
627 COPY_ENTITY ("&");
628 else
629 {
630 *dest_ptr = src[pos];
631 dest_ptr++;
632 dest_size--;
633 *dest_ptr = 0;
634 }
636 if (dest_size <= 1)
637 break;
638 }
640 return (dest);
641 } /* }}} char *html_escape_copy */
643 #undef COPY_ENTITY
645 char *html_escape_buffer (char *buffer, size_t buffer_size) /* {{{ */
646 {
647 char tmp[buffer_size];
649 html_escape_copy (tmp, buffer, sizeof (tmp));
650 memcpy (buffer, tmp, buffer_size);
652 return (buffer);
653 } /* }}} char *html_escape_buffer */
655 char *html_escape (const char *string) /* {{{ */
656 {
657 char buffer[4096];
659 if (string == NULL)
660 return (NULL);
662 html_escape_copy (buffer, string, sizeof (buffer));
664 return (strdup (buffer));
665 } /* }}} char *html_escape */
667 int html_print_page (const char *title, /* {{{ */
668 const page_callbacks_t *cb, void *user_data)
669 {
670 char *title_html;
672 printf ("Content-Type: text/html\n"
673 "X-Generator: "PACKAGE_STRING"\n"
674 "\n\n");
676 if (title == NULL)
677 title = "C₄: collection4 graph interface";
679 title_html = html_escape (title);
681 printf ("<html>\n"
682 " <head>\n"
683 " <title>%s</title>\n"
684 " <link rel=\"stylesheet\" type=\"text/css\" href=\"../share/style.css\" />\n"
685 " <script type=\"text/javascript\" src=\"../share/jquery-1.4.2.min.js\">\n"
686 " </script>\n"
687 " <script type=\"text/javascript\" src=\"../share/collection.js\">\n"
688 " </script>\n"
689 " </head>\n",
690 title_html);
692 printf (" <body>\n"
693 " <table id=\"layout-table\">\n"
694 " <tr id=\"layout-top\">\n"
695 " <td id=\"layout-top-left\">");
696 if (cb->top_left != NULL)
697 (*cb->top_left) (user_data);
698 else
699 html_print_logo (NULL);
700 printf ("</td>\n"
701 " <td id=\"layout-top-center\">");
702 if (cb->top_center != NULL)
703 (*cb->top_center) (user_data);
704 else
705 printf ("<h1>%s</h1>", title_html);
706 printf ("</td>\n"
707 " <td id=\"layout-top-right\">");
708 if (cb->top_right != NULL)
709 (*cb->top_right) (user_data);
710 printf ("</td>\n"
711 " </tr>\n"
712 " <tr id=\"layout-middle\">\n"
713 " <td id=\"layout-middle-left\">");
714 if (cb->middle_left != NULL)
715 (*cb->middle_left) (user_data);
716 printf ("</td>\n"
717 " <td id=\"layout-middle-center\">");
718 if (cb->middle_center != NULL)
719 (*cb->middle_center) (user_data);
720 printf ("</td>\n"
721 " <td id=\"layout-middle-right\">");
722 if (cb->middle_right != NULL)
723 (*cb->middle_right) (user_data);
724 printf ("</td>\n"
725 " </tr>\n"
726 " <tr id=\"layout-bottom\">\n"
727 " <td id=\"layout-bottom-left\">");
728 if (cb->bottom_left != NULL)
729 (*cb->bottom_left) (user_data);
730 printf ("</td>\n"
731 " <td id=\"layout-bottom-center\">");
732 if (cb->bottom_center != NULL)
733 (*cb->bottom_center) (user_data);
734 printf ("</td>\n"
735 " <td id=\"layout-bottom-right\">");
736 if (cb->bottom_right != NULL)
737 (*cb->bottom_right) (user_data);
738 printf ("</td>\n"
739 " </tr>\n"
740 " </table>\n"
741 " <div class=\"footer\">"PACKAGE_STRING"</div>\n"
742 " </body>\n"
743 "</html>\n");
745 free (title_html);
746 return (0);
747 } /* }}} int html_print_page */
749 int html_print_logo (__attribute__((unused)) void *user_data) /* {{{ */
750 {
751 printf ("<a href=\"%s?action=list_graphs\" id=\"logo-canvas\">\n"
752 " <h1>C<sub>4</sub></h1>\n"
753 " <div id=\"logo-subscript\">collection 4</div>\n"
754 "</a>\n");
756 return (0);
757 } /* }}} int html_print_search_box */
759 int html_print_search_box (__attribute__((unused)) void *user_data) /* {{{ */
760 {
761 char *term_html;
763 term_html = html_escape (param ("q"));
765 printf ("<form action=\"%s\" method=\"get\" id=\"search-form\">\n"
766 " <input type=\"hidden\" name=\"action\" value=\"search\" />\n"
767 " <input type=\"text\" name=\"q\" value=\"%s\" id=\"search-input\" />\n"
768 " <input type=\"submit\" name=\"button\" value=\"Search\" />\n"
769 "</form>\n",
770 script_name (),
771 (term_html != NULL) ? term_html : "");
773 free (term_html);
775 return (0);
776 } /* }}} int html_print_search_box */
778 /* vim: set sw=2 sts=2 et fdm=marker : */