First version of CGI-based edit interface. This is very much a
[privoxy.git] / cgiedit.c
1 const char cgiedit_rcs[] = "$Id: cgi.c,v 1.25 2001/09/16 15:02:35 jongfoster Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
5  *
6  * Purpose     :  CGI-based actionsfile editor.
7  *                
8  *                Functions declared include:
9  * 
10  *
11  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
12  *                IJBSWA team.  http://ijbswa.sourceforge.net
13  *
14  *                Based on the Internet Junkbuster originally written
15  *                by and Copyright (C) 1997 Anonymous Coders and 
16  *                Junkbusters Corporation.  http://www.junkbusters.com
17  *
18  *                This program is free software; you can redistribute it 
19  *                and/or modify it under the terms of the GNU General
20  *                Public License as published by the Free Software
21  *                Foundation; either version 2 of the License, or (at
22  *                your option) any later version.
23  *
24  *                This program is distributed in the hope that it will
25  *                be useful, but WITHOUT ANY WARRANTY; without even the
26  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
27  *                PARTICULAR PURPOSE.  See the GNU General Public
28  *                License for more details.
29  *
30  *                The GNU General Public License should be included with
31  *                this file.  If not, you can view it at
32  *                http://www.gnu.org/copyleft/gpl.html
33  *                or write to the Free Software Foundation, Inc., 59
34  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
35  *
36  * Revisions   :
37  *    $Log: cgi.c,v $
38  *
39  **********************************************************************/
40 \f
41
42 #include "config.h"
43
44 /*
45  * FIXME: Following includes copied from cgi.c - which are actually needed?
46  */
47
48 #include <stdio.h>
49 #include <sys/types.h>
50 #include <stdlib.h>
51 #include <ctype.h>
52 #include <string.h>
53 #include <assert.h>
54
55 #ifdef _WIN32
56 #define snprintf _snprintf
57 #endif /* def _WIN32 */
58
59 #include "project.h"
60 #include "cgi.h"
61 #include "cgiedit.h"
62 #include "list.h"
63 #include "encode.h"
64 #include "ssplit.h"
65 #include "jcc.h"
66 #include "filters.h"
67 #include "actions.h"
68 #include "errlog.h"
69 #include "miscutil.h"
70 #include "showargs.h"
71 #include "loadcfg.h"
72
73 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
74
75
76 #ifdef FEATURE_CGI_EDIT_ACTIONS
77
78
79 /*********************************************************************
80  *
81  * Function    :  cgi_edit_actions_list
82  *
83  * Description :  CGI function that edits the actions list.
84  *
85  * Parameters  :
86  *           1 :  csp = Current client state (buffers, headers, etc...)
87  *           2 :  rsp = http_response data structure for output
88  *           3 :  parameters = map of cgi parameters
89  *
90  * CGI Parameters : None
91  *
92  * Returns     :  0
93  *
94  *********************************************************************/
95 int cgi_edit_actions_list(struct client_state *csp, struct http_response *rsp,
96                                  struct map *parameters)
97 {
98    struct file_list *fl;
99    struct url_actions *actions;
100    char * actions_html;
101    char * next_actions_html;
102    char * section_template;
103    char * url_template;
104    char * sections;
105    char * urls;
106    struct map * exports = default_exports(csp, NULL);
107    struct map * section_exports;
108    struct map * url_exports;
109    int urlid;
110    char buf[50];
111    char * s;
112    int url_1_2;
113
114    if (((fl = csp->actions_list) == NULL) || ((actions = fl->f) == NULL))
115    {
116       /* FIXME: Oops, no file to edit */
117       free_map(exports);
118       return cgi_default(csp, rsp, parameters);
119    }
120
121    /* Should do all global exports above this point */
122
123    section_template = template_load(csp, "edit-actions-list-section");
124    url_template = template_load(csp, "edit-actions-list-url");
125
126    template_fill(&section_template, exports);
127    template_fill(&url_template, exports);
128
129    urlid = 0;
130    sections = strdup("");
131
132    ++urlid;
133    actions = actions->next;
134    if (actions != NULL)
135    {
136       actions_html = actions_to_html(actions->action);
137    }
138
139    while (actions != NULL)
140    {
141       section_exports = new_map();
142
143       snprintf(buf, 50, "%d", urlid);
144       map(section_exports, "sectionid", 1, buf, 1);
145
146       map(section_exports, "actions", 1, actions_html, 1);
147
148       /* Should do all section-specific exports above this point */
149
150       urls = strdup("");
151       url_1_2 = 2;
152
153       next_actions_html = NULL;
154       do
155       {
156          freez(next_actions_html);
157
158          url_exports = new_map();
159
160          snprintf(buf, 50, "%d", urlid);
161          map(url_exports, "urlid", 1, buf, 1);
162
163          snprintf(buf, 50, "%d", url_1_2);
164          map(url_exports, "url-1-2", 1, buf, 1);
165
166          s = html_encode(actions->url->spec);
167          map(url_exports, "url", 1, s, 1);
168
169          s = strdup(url_template);
170          template_fill(&s, section_exports);
171          template_fill(&s, url_exports);
172          urls = strsav(urls, s);
173          free_map(url_exports);
174
175          ++urlid;
176          url_1_2 = 3 - url_1_2;
177          actions = actions->next;
178          if (actions)
179          {
180             next_actions_html = actions_to_html(actions->action);
181          }
182       }
183       while (actions && (0 == strcmp(actions_html, next_actions_html)));
184
185       map(section_exports, "urls", 1, urls, 0);
186
187       /* Could also do section-specific exports here, but it wouldn't be as fast */
188
189       s = strdup(section_template);
190       template_fill(&s, section_exports);
191       sections = strsav(sections, s);
192       free_map(section_exports);
193
194       freez(actions_html);
195       actions_html = next_actions_html;
196    }
197
198    map(exports, "sections", 1, sections, 0);
199
200    /* Could also do global exports here, but it wouldn't be as fast */
201
202    rsp->body = template_load(csp, "edit-actions-list");
203    template_fill(&rsp->body, exports);
204    free_map(exports);
205
206    return(0);
207 }
208
209
210 /*********************************************************************
211  *
212  * Function    :  map_radio
213  *
214  * Description :  Map a set of radio button values.  E.g. if you have
215  *                3 radio buttons, declare them as:
216  *                  <option type="radio" name="xyz" @xyz-a@>
217  *                  <option type="radio" name="xyz" @xyz-b@>
218  *                  <option type="radio" name="xyz" @xyz-c@>
219  *                Then map one of the @xyz-?@ variables to "checked"
220  *                and all the others to empty by calling:
221  *                map_radio(exports, "xyz", "abc", sel)
222  *                Where 'sel' is 'a', 'b', or 'c'.
223  *
224  * Parameters  :
225  *           1 :  exports = Exports map to modify.
226  *           2 :  optionname = name for map
227  *           3 :  values = null-terminated list of values;
228  *           4 :  value = Selected value.
229  *
230  * CGI Parameters : None
231  *
232  * Returns     :  0 on success, nonzero on error.
233  *
234  *********************************************************************/
235 static int map_radio(struct map * exports, const char * optionname, 
236                      const char * values, char value)
237 {
238    int len;
239    char * buf;
240    char * p;
241    char c;
242    
243    assert(exports);
244    assert(optionname);
245    assert(values);
246
247    len = strlen(optionname);
248    buf = malloc(len + 3);
249    if (buf == NULL)
250    {
251       return 1;
252    }
253
254    strcpy(buf, optionname);
255    p = buf + len;
256    *p++ = '-';
257    p[1] = '\0';
258
259    while ((c = *values++) != '\0')
260    {
261       if (c != value)
262       {
263          *p = c;
264          if (map(exports, buf, 1, "", 1))
265          {
266             free(buf);
267             return 1;
268          }
269       }
270    }
271
272    *p = value;
273    if (map(exports, buf, 0, "checked", 1))
274    {
275       free(buf);
276       return 1;
277    }
278
279    return 0;
280 }
281
282
283 /*********************************************************************
284  *
285  * Function    :  actions_to_radio
286  *
287  * Description :  Converts a actionsfile entry FIXME
288  *
289  * Parameters  :
290  *          1  :  exports = FIXME
291  *          2  :  action  = FIXME
292  *
293  * Returns     :  0 on success, nonzero on error.
294  *
295  *********************************************************************/
296 static int actions_to_radio(struct map * exports, struct action_spec *action)
297 {
298    unsigned mask = action->mask;
299    unsigned add  = action->add;
300    int mapped_param;
301    int checked;
302
303    /* sanity - prevents "-feature +feature" */
304    mask |= add;
305
306
307 #define DEFINE_ACTION_BOOL(name, bit)       \
308    if (!(mask & bit))                       \
309    {                                        \
310       map_radio(exports, name, "ynx", 'n'); \
311    }                                        \
312    else if (add & bit)                      \
313    {                                        \
314       map_radio(exports, name, "ynx", 'y'); \
315    }                                        \
316    else                                     \
317    {                                        \
318       map_radio(exports, name, "ynx", 'x'); \
319    }
320
321 #define DEFINE_ACTION_STRING(name, bit, index)  \
322    DEFINE_ACTION_BOOL(name, bit);               \
323    mapped_param = 0;
324
325 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
326    if (add & bit)                                        \
327    {                                                     \
328       checked = !strcmp(action->string[index], value);   \
329    }                                                     \
330    else                                                  \
331    {                                                     \
332       checked = is_default;                              \
333    }                                                     \
334    mapped_param |= checked;                              \
335    map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1);
336
337 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val)    \
338    map(exports, name "-param-custom", 1,                      \
339       ((!mapped_param) ? "checked" : ""), 1);                     \
340    map(exports, name "-param", 1,                             \
341       (((add & bit) && !mapped_param) ?                           \
342       action->string[index] : default_val), 1);
343
344 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val)  \
345    map(exports, name "-param", 1,                             \
346       ((add & bit) ? action->string[index] : default_val), 1);
347
348 #define DEFINE_ACTION_MULTI(name, index)        \
349    if (action->multi_add[index]->first)         \
350    {                                            \
351       map_radio(exports, name, "ynx", 'y');     \
352    }                                            \
353    else if (action->multi_remove_all[index])    \
354    {                                            \
355       map_radio(exports, name, "ynx", 'n');     \
356    }                                            \
357    else if (action->multi_remove[index]->first) \
358    {                                            \
359       map_radio(exports, name, "ynx", 'y');     \
360    }                                            \
361    else                                         \
362    {                                            \
363       map_radio(exports, name, "ynx", 'x');     \
364    }
365
366 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
367
368 #include "actionlist.h"
369
370 #undef DEFINE_ACTION_MULTI
371 #undef DEFINE_ACTION_STRING
372 #undef DEFINE_ACTION_BOOL
373 #undef DEFINE_ACTION_ALIAS
374 #undef DEFINE_CGI_PARAM_CUSTOM
375 #undef DEFINE_CGI_PARAM_RADIO
376 #undef DEFINE_CGI_PARAM_NO_RADIO
377
378    return 0;
379 }
380
381
382 /*********************************************************************
383  *
384  * Function    :  cgi_edit_actions
385  *
386  * Description :  CGI function that edits the Actions list.
387  *
388  * Parameters  :
389  *           1 :  csp = Current client state (buffers, headers, etc...)
390  *           2 :  rsp = http_response data structure for output
391  *           3 :  parameters = map of cgi parameters
392  *
393  * CGI Parameters : None
394  *
395  * Returns     :  0
396  *
397  *********************************************************************/
398 int cgi_edit_actions(struct client_state *csp, struct http_response *rsp,
399                             struct map *parameters)
400 {
401    struct file_list *fl;
402    struct url_actions *actions;
403    struct map * exports = default_exports(csp, NULL);
404    int sectionid;
405    int i;
406    const char * s;
407    char c;
408
409    if (((fl = csp->actions_list) == NULL) || ((actions = fl->f) == NULL))
410    {
411       /* FIXME: Oops, No file to edit */
412       free_map(exports);
413       return cgi_default(csp, rsp, parameters);
414    }
415
416    s = lookup(parameters, "section");
417    if (!*s)
418    {
419       /* FIXME: Oops, No file to edit */
420       free_map(exports);
421       return cgi_default(csp, rsp, parameters);
422    }
423
424    sectionid = 0;
425    while ((c = *s++) != '\0')
426    {
427       /* Note:
428        * (((unsigned)(-1)) >> 1) == MAXINT.
429        * (MAXINT - 9) / 10 is the largest number that 
430        * can be safely multiplied by 10 then have 9 added.
431        */
432       if (c < '0' || c > '9' || sectionid > ((((unsigned)(-1)) >> 1) - 9) / 10)
433       {
434          /* FIXME: Oops, bad paramaters */
435          free_map(exports);
436          return cgi_default(csp, rsp, parameters);
437       }
438
439       sectionid *= 10;
440       sectionid += c - '0';
441    }
442
443    for (i = 0; actions != NULL && i < sectionid; i++)
444    {
445       actions = actions->next;
446    }
447
448    if (actions == NULL || i != sectionid || sectionid < 1)
449    {
450       free_map(exports);
451       return cgi_default(csp, rsp, parameters);
452    }
453
454    /* FIXME: need to fill in exports here */
455    actions_to_radio(exports, actions->action);
456
457    rsp->body = template_load(csp, "edit-actions");
458    template_fill(&rsp->body, exports);
459    free_map(exports);
460
461    return(0);
462 }
463
464
465 /*********************************************************************
466  *
467  * Function    :  cgi_edit_actions_submit
468  *
469  * Description :  CGI function that actually edits the Actions list.
470  *
471  * Parameters  :
472  *           1 :  csp = Current client state (buffers, headers, etc...)
473  *           2 :  rsp = http_response data structure for output
474  *           3 :  parameters = map of cgi parameters
475  *
476  * CGI Parameters : None
477  *
478  * Returns     :  0
479  *
480  *********************************************************************/
481 int cgi_edit_actions_submit(struct client_state *csp, struct http_response *rsp,
482                             struct map *parameters)
483 {
484    struct map * exports = default_exports(csp, NULL);
485
486    map(exports, "cgi-parameters", 1, dump_map(parameters), 0);
487
488    rsp->body = template_load(csp, "edit-actions-submit");
489    template_fill(&rsp->body, exports);
490    free_map(exports);
491
492    return(0);
493 }
494
495
496 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
497
498
499 /*
500   Local Variables:
501   tab-width: 3
502   end:
503 */