2 * collection4 - utils_cgi.c
3 * Copyright (C) 2010 Florian octo Forster
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.
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.
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
21 * Florian octo Forster <ff at octo.it>
34 #include "utils_cgi.h"
38 #include <fcgi_stdio.h>
45 typedef struct parameter_s parameter_t;
49 parameter_t *parameters;
50 size_t parameters_num;
53 static param_list_t *pl_global = NULL;
55 static char *uri_unescape_copy (char *dest, const char *src, size_t n) /* {{{ */
60 if ((dest == NULL) || (src == NULL) || (n < 1))
77 else if ((src_ptr[0] == '%')
78 && isxdigit ((int) src_ptr[1]) && isxdigit ((int) src_ptr[2]))
84 tmpstr[0] = src_ptr[1];
85 tmpstr[1] = src_ptr[2];
90 value = strtol (tmpstr, &endptr, /* base = */ 16);
91 if ((endptr == tmpstr) || (errno != 0))
97 *dest_ptr = (char) value;
104 *dest_ptr = *src_ptr;
110 } /* while (*src_ptr != 0) */
112 assert (*dest_ptr == 0);
114 } /* }}} char *uri_unescape */
116 static char *uri_unescape (const char *string) /* {{{ */
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) /* {{{ */
136 value_raw = strchr (key_raw, '=');
137 if (value_raw == NULL)
142 key = uri_unescape (key_raw);
146 value = uri_unescape (value_raw);
153 param_set (pl, key, value);
159 } /* }}} int param_parse_keyval */
161 static int parse_query_string (param_list_t *pl, /* {{{ */
167 if ((pl == NULL) || (query_string == NULL))
170 dummy = query_string;
171 while ((keyval = strtok (dummy, ";&")) != NULL)
174 param_parse_keyval (pl, keyval);
178 } /* }}} int parse_query_string */
180 int param_init (void) /* {{{ */
182 if (pl_global != NULL)
185 pl_global = param_create (/* query string = */ NULL);
186 if (pl_global == NULL)
190 } /* }}} int param_init */
192 void param_finish (void) /* {{{ */
194 param_destroy (pl_global);
196 } /* }}} void param_finish */
198 const char *param (const char *key) /* {{{ */
202 return (param_get (pl_global, key));
203 } /* }}} const char *param */
205 param_list_t *param_create (const char *query_string) /* {{{ */
210 if (query_string == NULL)
211 query_string = getenv ("QUERY_STRING");
213 if (query_string == NULL)
216 tmp = strdup (query_string);
220 pl = malloc (sizeof (*pl));
226 memset (pl, 0, sizeof (*pl));
228 parse_query_string (pl, tmp);
232 } /* }}} param_list_t *param_create */
234 param_list_t *param_clone (__attribute__((unused)) param_list_t *pl) /* {{{ */
236 /* FIXME: To be implemented. */
239 } /* }}} param_list_t *param_clone */
241 void param_destroy (param_list_t *pl) /* {{{ */
248 for (i = 0; i < pl->parameters_num; i++)
250 free (pl->parameters[i].key);
251 free (pl->parameters[i].value);
253 free (pl->parameters);
255 } /* }}} void param_destroy */
257 const char *param_get (param_list_t *pl, const char *name) /* {{{ */
261 if ((pl == NULL) || (name == NULL))
264 for (i = 0; i < pl->parameters_num; i++)
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);
274 } /* }}} char *param_get */
276 static int param_add (param_list_t *pl, /* {{{ */
277 const char *key, const char *value)
281 tmp = realloc (pl->parameters,
282 sizeof (*pl->parameters) * (pl->parameters_num + 1));
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)
293 tmp->value = strdup (value);
294 if (tmp->value == NULL)
300 pl->parameters_num++;
303 } /* }}} int param_add */
305 static int param_delete (param_list_t *pl, /* {{{ */
310 if ((pl == NULL) || (name == NULL))
313 for (i = 0; i < pl->parameters_num; i++)
314 if (strcasecmp (pl->parameters[i].key, name) == 0)
317 if (i >= pl->parameters_num)
320 if (i < (pl->parameters_num - 1))
324 p = pl->parameters[i];
325 pl->parameters[i] = pl->parameters[pl->parameters_num - 1];
326 pl->parameters[pl->parameters_num - 1] = p;
329 pl->parameters_num--;
330 free (pl->parameters[pl->parameters_num].key);
331 free (pl->parameters[pl->parameters_num].value);
334 } /* }}} int param_delete */
336 int param_set (param_list_t *pl, const char *name, /* {{{ */
343 if ((pl == NULL) || (name == NULL))
347 return (param_delete (pl, name));
350 for (i = 0; i < pl->parameters_num; i++)
352 if (strcasecmp (pl->parameters[i].key, name) == 0)
354 p = pl->parameters + i;
360 return (param_add (pl, name, value));
362 value_copy = strdup (value);
363 if (value_copy == NULL)
367 p->value = value_copy;
370 } /* }}} int param_set */
372 char *param_as_string (param_list_t *pl) /* {{{ */
383 for (i = 0; i < pl->parameters_num; i++)
385 uri_escape_copy (key, pl->parameters[i].key, sizeof (key));
386 uri_escape_copy (value, pl->parameters[i].value, sizeof (value));
389 strlcat (buffer, ";", sizeof (buffer));
390 strlcat (buffer, key, sizeof (buffer));
391 strlcat (buffer, "=", sizeof (buffer));
392 strlcat (buffer, value, sizeof (buffer));
395 return (strdup (buffer));
396 } /* }}} char *param_as_string */
398 int param_print_hidden (param_list_t *pl) /* {{{ */
407 for (i = 0; i < pl->parameters_num; i++)
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",
417 } /* }}} int param_print_hidden */
419 char *uri_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
433 else if ((((unsigned char) src[in]) < 32)
438 || (((unsigned char) src[in]) >= 128))
445 snprintf (esc, sizeof (esc), "%%%02x", (unsigned int) src[in]);
447 dest[out+1] = esc[1];
448 dest[out+2] = esc[2];
462 } /* }}} char *uri_escape_copy */
464 char *uri_escape (const char *string) /* {{{ */
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)) \
480 strcpy (dest_ptr, (e)); \
485 char *json_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
494 for (pos = 0; src[pos] != 0; pos++)
497 COPY_ENTITY ("\\\"");
498 else if (src[pos] == '\\')
499 COPY_ENTITY ("\\\\");
500 else if (((uint8_t) src[pos]) < 32)
502 if (src[pos] == '\n')
504 else if (src[pos] == '\r')
506 else if (src[pos] == '\t')
508 else if (src[pos] == '\b')
510 else if (src[pos] == '\f')
515 sprintf (buffer, "\\u%04"PRIx8, (uint8_t) src[pos]);
516 buffer[sizeof (buffer) - 1] = 0;
517 COPY_ENTITY (buffer);
522 *dest_ptr = src[pos];
533 } /* }}} char *json_escape_copy */
537 char *json_escape_buffer (char *buffer, size_t buffer_size)
539 char temp[buffer_size];
541 json_escape_copy (temp, buffer, buffer_size);
542 memcpy (buffer, temp, buffer_size);
545 } /* }}} char *json_escape_buffer */
547 char *json_escape (const char *string) /* {{{ */
554 json_escape_copy (buffer, string, sizeof (buffer));
556 return (strdup (buffer));
557 } /* }}} char *json_escape */
559 const char *script_name (void) /* {{{ */
563 ret = getenv ("SCRIPT_NAME");
565 ret = "collection4.fcgi";
568 } /* }}} char *script_name */
570 int time_to_rfc1123 (time_t t, char *buffer, size_t buffer_size) /* {{{ */
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)
581 status = strftime (buffer, buffer_size, "%a, %d %b %Y %T GMT", &tm_tmp);
586 } /* }}} int time_to_rfc1123 */
588 #define COPY_ENTITY(e) do { \
589 size_t len = strlen (e); \
590 if (dest_size < (len + 1)) \
592 strcpy (dest_ptr, (e)); \
597 char *html_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
606 for (pos = 0; src[pos] != 0; 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 ("&");
618 *dest_ptr = src[pos];
629 } /* }}} char *html_escape_copy */
633 char *html_escape_buffer (char *buffer, size_t buffer_size) /* {{{ */
635 char tmp[buffer_size];
637 html_escape_copy (tmp, buffer, sizeof (tmp));
638 memcpy (buffer, tmp, buffer_size);
641 } /* }}} char *html_escape_buffer */
643 char *html_escape (const char *string) /* {{{ */
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)
660 printf ("Content-Type: text/html\n"
661 "X-Generator: "PACKAGE_STRING"\n"
665 title = "C₄: collection4 graph interface";
667 title_html = html_escape (title);
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"
675 " <script type=\"text/javascript\" src=\"../share/collection.js\">\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);
687 html_print_logo (NULL);
689 " <td id=\"layout-top-center\">");
690 if (cb->top_center != NULL)
691 (*cb->top_center) (user_data);
693 printf ("<h1>%s</h1>", title_html);
695 " <td id=\"layout-top-right\">");
696 if (cb->top_right != NULL)
697 (*cb->top_right) (user_data);
700 " <tr id=\"layout-middle\">\n"
701 " <td id=\"layout-middle-left\">");
702 if (cb->middle_left != NULL)
703 (*cb->middle_left) (user_data);
705 " <td id=\"layout-middle-center\">");
706 if (cb->middle_center != NULL)
707 (*cb->middle_center) (user_data);
709 " <td id=\"layout-middle-right\">");
710 if (cb->middle_right != NULL)
711 (*cb->middle_right) (user_data);
714 " <tr id=\"layout-bottom\">\n"
715 " <td id=\"layout-bottom-left\">");
716 if (cb->bottom_left != NULL)
717 (*cb->bottom_left) (user_data);
719 " <td id=\"layout-bottom-center\">");
720 if (cb->bottom_center != NULL)
721 (*cb->bottom_center) (user_data);
723 " <td id=\"layout-bottom-right\">");
724 if (cb->bottom_right != NULL)
725 (*cb->bottom_right) (user_data);
729 " <div class=\"footer\">"PACKAGE_STRING"</div>\n"
735 } /* }}} int html_print_page */
737 int html_print_logo (__attribute__((unused)) void *user_data) /* {{{ */
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"
745 } /* }}} int html_print_search_box */
747 int html_print_search_box (__attribute__((unused)) void *user_data) /* {{{ */
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=\"list_graphs\" />\n"
755 " <input type=\"text\" name=\"q\" value=\"%s\" id=\"search-input\" />\n"
756 " <input type=\"submit\" name=\"button\" value=\"Search\" />\n"
759 (term_html != NULL) ? term_html : "");
764 } /* }}} int html_print_search_box */
766 /* vim: set sw=2 sts=2 et fdm=marker : */