"show graph" action: Implement displaying _graphs_ (rather than instances).
[collection4.git] / src / utils_cgi.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <errno.h>
6 #include <time.h>
7
8 #include "utils_cgi.h"
9
10 #include <fcgiapp.h>
11 #include <fcgi_stdio.h>
12
13 struct parameter_s
14 {
15   char *key;
16   char *value;
17 };
18 typedef struct parameter_s parameter_t;
19
20 static parameter_t *parameters = NULL;
21 static size_t parameters_num = 0;
22 static _Bool parameters_init = 0;
23
24 static int parameter_add (const char *key, const char *value) /* {{{ */
25 {
26   parameter_t *ptr;
27
28   if (value == NULL)
29     return (EINVAL);
30
31   ptr = realloc (parameters, sizeof (*parameters) * (parameters_num + 1));
32   if (ptr == NULL)
33     return (ENOMEM);
34   parameters = ptr;
35
36   ptr = parameters + parameters_num;
37   if (key == NULL)
38   {
39     ptr->key = NULL;
40   }
41   else
42   {
43     ptr->key = strdup (key);
44     if (ptr->key == NULL)
45       return (ENOMEM);
46   }
47
48   ptr->value = strdup (value);
49   if (ptr->value == NULL)
50   {
51     free (ptr->key);
52     return (ENOMEM);
53   }
54
55   parameters_num++;
56   return (0);
57 } /* }}} int parameter_add */
58
59 static char *parameter_lookup (const char *key) /* {{{ */
60 {
61   size_t i;
62
63   for (i = 0; i < parameters_num; i++)
64   {
65     if ((key == NULL) && (parameters[i].key == NULL))
66       return (parameters[i].value);
67     else if ((key != NULL) && (parameters[i].key != NULL)
68         && (strcmp (key, parameters[i].key) == 0))
69       return (parameters[i].value);
70   }
71
72   return (NULL);
73 } /* }}} char *parameter_lookup */
74
75 static char *uri_unescape (char *string) /* {{{ */
76 {
77   char *in;
78   char *out;
79
80   if (string == NULL)
81     return (NULL);
82
83   in = string;
84   out = string;
85
86   while (*in != 0)
87   {
88     if (*in == '+')
89     {
90       *out = ' ';
91     }
92     else if ((in[0] == '%')
93         && isxdigit ((int) in[1]) && isxdigit ((int) in[2]))
94     {
95       char tmpstr[3];
96       char *endptr;
97       long value;
98
99       tmpstr[0] = in[1];
100       tmpstr[1] = in[2];
101       tmpstr[2] = 0;
102
103       errno = 0;
104       endptr = NULL;
105       value = strtol (tmpstr, &endptr, /* base = */ 16);
106       if ((endptr == tmpstr) || (errno != 0))
107       {
108         *out = '?';
109       }
110       else
111       {
112         *out = (char) value;
113       }
114
115       in += 2;
116     }
117     else
118     {
119       *out = *in;
120     }
121
122     in++;
123     out++;
124   } /* while (*in != 0) */
125
126   *out = 0;
127   return (string);
128 } /* }}} char *uri_unescape */
129
130 static int parse_keyval (char *keyval) /* {{{ */
131 {
132   char *key;
133   char *val;
134
135   val = strchr (keyval, '=');
136   if (val == NULL)
137   {
138     key = NULL;
139     val = keyval;
140   }
141   else
142   {
143     key = keyval;
144     *val = 0;
145     val++;
146   }
147
148   parameter_add (uri_unescape (key), uri_unescape (val));
149
150   return (0);
151 } /* }}} int parse_keyval */
152
153 static int parse_query_string (char *query_string) /* {{{ */
154 {
155   char *dummy;
156   char *keyval;
157
158   if (query_string == NULL)
159     return (EINVAL);
160
161   dummy = query_string;
162   while ((keyval = strtok (dummy, ";&")) != NULL)
163   {
164     dummy = NULL;
165     parse_keyval (keyval);
166   }
167
168   return (0);
169 } /* }}} int parse_query_string */
170
171 int param_init (void) /* {{{ */
172 {
173   const char *query_string;
174   char *copy;
175   int status;
176
177   if (parameters_init)
178     return (0);
179
180   query_string = getenv ("QUERY_STRING");
181   if (query_string == NULL)
182     return (ENOENT);
183
184   copy = strdup (query_string);
185   if (copy == NULL)
186     return (ENOMEM);
187
188   status = parse_query_string (copy);
189   free (copy);
190
191   parameters_init = 1;
192
193   return (status);
194 } /* }}} int param_init */
195
196 void param_finish (void) /* {{{ */
197 {
198   size_t i;
199
200   if (!parameters_init)
201     return;
202
203   for (i = 0; i < parameters_num; i++)
204   {
205     free (parameters[i].key);
206     free (parameters[i].value);
207   }
208   free (parameters);
209
210   parameters = NULL;
211   parameters_num = 0;
212   parameters_init = 0;
213 } /* }}} void param_finish */
214
215 const char *param (const char *key) /* {{{ */
216 {
217   param_init ();
218
219   return (parameter_lookup (key));
220 } /* }}} const char *param */
221
222 int uri_escape (char *dst, const char *src, size_t size) /* {{{ */
223 {
224   size_t in;
225   size_t out;
226
227   in = 0;
228   out = 0;
229   while (42)
230   {
231     if (src[in] == 0)
232     {
233       dst[out] = 0;
234       return (0);
235     }
236     else if ((src[in] < 32)
237         || (src[in] == '&')
238         || (src[in] == ';')
239         || (((unsigned char) src[in]) >= 128))
240     {
241       char esc[4];
242
243       if ((size - out) < 4)
244         break;
245       
246       snprintf (esc, sizeof (esc), "%%%02x", (unsigned int) src[in]);
247       dst[out] = esc[0];
248       dst[out+1] = esc[1];
249       dst[out+2] = esc[2];
250
251       out += 3;
252       in++;
253     }
254     else
255     {
256       dst[out] = src[in];
257       out++;
258       in++;
259     }
260   } /* while (42) */
261
262   return (0);
263 } /* }}} int uri_escape */
264
265 const char *script_name (void) /* {{{ */
266 {
267   char *ret;
268
269   ret = getenv ("SCRIPT_NAME");
270   if (ret == NULL)
271     ret = "collection4.fcgi";
272
273   return (ret);
274 } /* }}} char *script_name */
275
276 int time_to_rfc1123 (time_t t, char *buffer, size_t buffer_size) /* {{{ */
277 {
278   struct tm tm_tmp;
279   size_t status;
280
281   /* RFC 1123 *requires* the time to be GMT and the "GMT" timezone string.
282    * Apache will ignore the timezone if "localtime_r" and "%z" is used,
283    * resulting in weird behavior. */
284   if (gmtime_r (&t, &tm_tmp) == NULL)
285     return (errno);
286
287   status = strftime (buffer, buffer_size, "%a, %d %b %Y %T GMT", &tm_tmp);
288   if (status == 0)
289     return (errno);
290
291   return (0);
292 } /* }}} int time_to_rfc1123 */
293
294 #define COPY_ENTITY(e) do {    \
295   size_t len = strlen (e);     \
296   if (dest_size < (len + 1)) \
297     break;                     \
298   strcpy (dest_ptr, (e));    \
299   dest_ptr += len;           \
300   dest_size -= len;          \
301 } while (0)
302
303 char *html_escape_copy (char *dest, const char *src, size_t n) /* {{{ */
304 {
305   char *dest_ptr;
306   size_t dest_size;
307   size_t pos;
308
309   dest[0] = 0;
310   dest_ptr = dest;
311   dest_size = n;
312   for (pos = 0; src[pos] != 0; pos++)
313   {
314     if (src[pos] == '"')
315       COPY_ENTITY ("&quot;");
316     else if (src[pos] == '<')
317       COPY_ENTITY ("&lt;");
318     else if (src[pos] == '>')
319       COPY_ENTITY ("&gt;");
320     else if (src[pos] == '&')
321       COPY_ENTITY ("&amp;");
322     else
323     {
324       *dest_ptr = src[pos];
325       dest_ptr++;
326       dest_size--;
327       *dest_ptr = 0;
328     }
329
330     if (dest_size <= 1)
331       break;
332   }
333
334   return (dest);
335 } /* }}} char *html_escape_copy */
336
337 #undef COPY_ENTITY
338
339 char *html_escape_buffer (char *buffer, size_t buffer_size) /* {{{ */
340 {
341   char tmp[buffer_size];
342
343   html_escape_copy (tmp, buffer, sizeof (tmp));
344   memcpy (buffer, tmp, buffer_size);
345
346   return (buffer);
347 } /* }}} char *html_escape_buffer */
348
349 char *html_escape (const char *string) /* {{{ */
350 {
351   char buffer[4096];
352
353   if (string == NULL)
354     return (NULL);
355
356   html_escape_copy (buffer, string, sizeof (buffer));
357
358   return (strdup (buffer));
359 } /* }}} char *html_escape */
360
361 int html_print_page (const char *title, /* {{{ */
362     const page_callbacks_t *cb, void *user_data)
363 {
364   char *title_html;
365
366   printf ("Content-Type: text/html\n\n");
367
368   if (title == NULL)
369     title = "c4: collection4 graph interface";
370
371   title_html = html_escape (title);
372
373   printf ("<html>\n"
374       "  <head>\n"
375       "    <title>%s</title>\n"
376       "    <link rel=\"stylesheet\" type=\"text/css\" href=\"../share/style.css\" />\n"
377       "    <script type=\"text/javascript\" src=\"../share/jquery-1.4.2.min.js\">\n"
378       "    </script>\n"
379       "    <script type=\"text/javascript\" src=\"../share/collection.js\">\n"
380       "    </script>\n"
381       "  </head>\n",
382       title_html);
383
384   printf ("  <body>\n"
385       "    <table id=\"layout-table\">\n"
386       "      <tr id=\"layout-top\">\n"
387       "        <td id=\"layout-top-left\">");
388   if (cb->top_left != NULL)
389     (*cb->top_left) (user_data);
390   printf ("</td>\n"
391       "        <td id=\"layout-top-center\">");
392   if (cb->top_center != NULL)
393     (*cb->top_center) (user_data);
394   else
395     printf ("<h1>%s</h1>", title_html);
396   printf ("</td>\n"
397       "        <td id=\"layout-top-right\">");
398   if (cb->top_right != NULL)
399     (*cb->top_right) (user_data);
400   printf ("</td>\n"
401       "      </tr>\n"
402       "      <tr id=\"layout-middle\">\n"
403       "        <td id=\"layout-middle-left\">");
404   if (cb->middle_left != NULL)
405     (*cb->middle_left) (user_data);
406   printf ("</td>\n"
407       "        <td id=\"layout-middle-center\">");
408   if (cb->middle_center != NULL)
409     (*cb->middle_center) (user_data);
410   printf ("</td>\n"
411       "        <td id=\"layout-middle-right\">");
412   if (cb->middle_right != NULL)
413     (*cb->middle_right) (user_data);
414   printf ("</td>\n"
415       "      </tr>\n"
416       "      <tr id=\"layout-bottom\">\n"
417       "        <td id=\"layout-bottom-left\">");
418   if (cb->bottom_left != NULL)
419     (*cb->bottom_left) (user_data);
420   printf ("</td>\n"
421       "        <td id=\"layout-bottom-center\">");
422   if (cb->bottom_center != NULL)
423     (*cb->bottom_center) (user_data);
424   printf ("</td>\n"
425       "        <td id=\"layout-bottom-right\">");
426   if (cb->bottom_right != NULL)
427     (*cb->bottom_right) (user_data);
428   printf ("</td>\n"
429       "      </tr>\n"
430       "    </table>\n"
431       "  </body>\n"
432       "</html>\n");
433
434   free (title_html);
435   return (0);
436 } /* }}} int html_print_page */
437
438 int html_print_search_box (__attribute__((unused)) void *user_data) /* {{{ */
439 {
440   char *term_html;
441
442   term_html = html_escape (param ("search"));
443
444   printf ("<form action=\"%s\" method=\"get\">\n"
445       "  <input type=\"hidden\" name=\"action\" value=\"list_graphs\" />\n"
446       "  <input type=\"text\" name=\"search\" value=\"%s\" id=\"search-input\" />\n"
447       "  <input type=\"submit\" name=\"button\" value=\"Search\" />\n"
448       "</form>\n",
449       script_name (),
450       (term_html != NULL) ? term_html : "");
451
452   free (term_html);
453
454   return (0);
455 } /* }}} int html_print_search_box */
456
457 /* vim: set sw=2 sts=2 et fdm=marker : */