1 #include <stdlib.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <stdio.h>
5 
6 #include "alias.h"
7 #include "variables.h"
8 #include "core.h"
9 #include "mystring.h"
10 #include "moderate.h"
11 #include "liscript.h"
12 #include "fileapi.h"
13 #include "trust.h"
14 
15 static const char *_sortorder = NULL;
16 struct list_vars *listdata = NULL;
17 struct var_data **sortedvars = NULL;
18 
19 static int count_all_vars();
20 static int var_cmp(const void *e1, const void *e2);
21 
22 /* Hash a variable name to a hash bucket */
var_hash(const char * varname)23 int var_hash(const char *varname)
24 {
25     int i, len, val;
26 
27     len = strlen(varname);
28     val = 0;
29     for(i = 0; i < len; i++) {
30         val += tolower(varname[i]);
31     }
32     return val % HASHSIZE;
33 }
34 
35 /* get the current variable value at a specific level */
get_cur_varval_level(struct var_data * var,int level)36 const char *get_cur_varval_level(struct var_data *var, int level)
37 {
38     if(level == VAR_TEMP) {
39         if(var->temp && (var->flags & VAR_TEMP)) return var->temp;
40     } else if (level == VAR_LIST) {
41         if(var->list && (var->flags & VAR_LIST)) return var->list;
42     } else if (level == VAR_SITE) {
43         if(var->site && (var->flags & VAR_SITE)) return var->site;
44     } else if (level == VAR_GLOBAL) {
45         if(var->global && (var->flags & VAR_GLOBAL)) return var->global;
46     } else {
47         return NULL;
48     }
49     return NULL;
50 }
51 
52 /* get the current variable value at a specific level or above */
get_cur_varval_level_default(struct var_data * var,int level)53 const char *get_cur_varval_level_default(struct var_data *var, int level)
54 {
55     switch(level) {
56         case VAR_TEMP:
57             if(var->temp && (var->flags & VAR_TEMP)) return var->temp;
58         case VAR_LIST:
59             if(var->list && (var->flags & VAR_LIST)) return var->list;
60         case VAR_SITE:
61             if(var->site && (var->flags & VAR_SITE)) return var->site;
62         case VAR_GLOBAL:
63             if(var->global && (var->flags & VAR_GLOBAL)) return var->global;
64         default:
65             return var->defval;
66     }
67 }
68 
69 /* Get the current value of a variable from it's variable record */
get_cur_varval(struct var_data * var)70 const char *get_cur_varval(struct var_data *var)
71 {
72     if(var->temp && (var->flags & VAR_TEMP)) return var->temp;
73     if(var->list && (var->flags & VAR_LIST)) return var->list;
74     if(var->site && (var->flags & VAR_SITE)) return var->site;
75     if(var->global && (var->flags & VAR_GLOBAL)) return var->global;
76     return var->defval;
77 }
78 
79 /* Lookup the variable and return it's record if it exists, else null */
find_var_rec(const char * varname)80 struct var_data *find_var_rec(const char *varname)
81 {
82     const char *alias = lookup_alias(varname);
83     int hash;
84     struct var_data *temp;
85 
86     if(!alias)
87         alias = varname;
88 
89     hash = var_hash(alias);
90     temp = listdata->bucket[hash];
91 
92     while(temp) {
93         if(strcasecmp(temp->name, alias) == 0)
94             return temp;
95         temp = temp->next;
96     }
97     return NULL;
98 }
99 
100 /* clean up values on a variable record */
clean_var(const char * varname,int flags)101 void clean_var(const char *varname, int flags)
102 {
103     struct var_data *temp = find_var_rec(varname);
104     const char *varval = NULL;
105 
106     if(!temp) {
107         log_printf(9, "clean_var: variable '%s' doesn't exist.\n", varname);
108         return;
109     }
110     if(temp->flags & VAR_LOCKED) {
111         log_printf(1, "clean_var: Attempting to clean locked variable '%s'\n",
112                    varname);
113         return;
114     }
115     temp->flags &= ~VAR_NOEXPAND;
116 
117     if((flags & VAR_TEMP) && temp->temp) {
118         log_printf(9, "clean_var: cleaning var '%s' (TEMP)\n", temp->name);
119         if(temp->type != VAR_DATA)
120             free(temp->temp);
121         temp->temp = NULL;
122     }
123     if((flags & VAR_LIST) && temp->list) {
124         log_printf(9, "clean_var: cleaning var '%s' (LIST)\n", temp->name);
125         if(temp->type != VAR_DATA)
126             free(temp->list);
127         temp->list = NULL;
128     }
129     if((flags & VAR_SITE) && temp->site) {
130         log_printf(9, "clean_var: cleaning var '%s' (SITE)\n", temp->name);
131         if(temp->type != VAR_DATA)
132             free(temp->site);
133         temp->site = NULL;
134     }
135     if((flags & VAR_GLOBAL) && temp->global) {
136         log_printf(9, "clean_var: cleaning var '%s' (GLOBAL)\n", temp->name);
137         if(temp->type != VAR_DATA)
138             free(temp->global);
139         temp->global = NULL;
140     }
141 
142     if(temp->expanded){
143         free(temp->expanded);
144         temp->expanded = NULL;
145     }
146     varval = get_cur_varval(temp);
147     if(!varval || strstr(varval, "<$") == NULL)
148         temp->flags  |= VAR_NOEXPAND;
149 }
150 
151 /* Wipe all non-protected variables */
wipe_vars(int flags)152 void wipe_vars(int flags)
153 {
154     struct var_data *temp;
155     int i;
156     const char *varval = NULL;
157 
158     for(i = 0; i < HASHSIZE; i++) {
159         temp = listdata->bucket[i];
160 
161         while(temp) {
162             if(temp->flags & VAR_LOCKED) {
163                 log_printf(9, "wipe_vars: skipping locked variable '%s'\n",
164                            temp->name);
165                 temp = temp->next;
166                 continue;
167             }
168             temp->flags &= ~VAR_NOEXPAND;
169 
170             if((flags & VAR_TEMP) && temp->temp) {
171                 if(temp->type != VAR_DATA)
172                     free(temp->temp);
173                 temp->temp = NULL;
174             }
175             if((flags & VAR_LIST) && temp->list) {
176                 if(temp->type != VAR_DATA)
177                     free(temp->list);
178                 temp->list = NULL;
179             }
180             if((flags & VAR_SITE) && temp->site) {
181                 if(temp->type != VAR_DATA)
182                     free(temp->site);
183                 temp->site = NULL;
184             }
185             if((flags & VAR_GLOBAL) && temp->global) {
186                 if(temp->type != VAR_DATA)
187                     free(temp->global);
188                 temp->global = NULL;
189             }
190            if(temp->expanded) {
191                free(temp->expanded);
192                temp->expanded = NULL;
193            }
194            varval = get_cur_varval(temp);
195            if(!varval || strstr(varval, "<$") == NULL)
196                temp->flags  |= VAR_NOEXPAND;
197            temp = temp->next;
198         }
199     }
200 }
201 
202 /* Destroy the variable hash table */
nuke_vars(void)203 void nuke_vars(void)
204 {
205     struct var_data *temp, *temp2;
206     int i;
207 
208     for(i = 0; i < HASHSIZE; i++) {
209         temp = listdata->bucket[i];
210 
211         while(temp) {
212             if(temp->flags & VAR_LOCKED) {
213                 log_printf(9, "nuke_vars: variable '%s' was never unlocked.\n",
214                            temp->name);
215             }
216 
217             temp2 = temp->next;
218             if(temp->name) free(temp->name);
219             if(temp->description) free(temp->description);
220             if(temp->section) free(temp->section);
221             if(temp->example) free(temp->example);
222             if(temp->defval) free(temp->defval);
223             if(temp->global && temp->type != VAR_DATA) free(temp->global);
224             if(temp->site && temp->type != VAR_DATA) free(temp->site);
225             if(temp->list && temp->type != VAR_DATA) free(temp->list);
226             if(temp->temp && temp->type != VAR_DATA) free(temp->temp);
227             if(temp->expanded) free(temp->expanded);
228             if(temp->choices) free(temp->choices);
229             free(temp);
230             temp = temp2;
231         }
232         listdata->bucket[i] = NULL;
233     }
234 }
235 
236 /* register a variables information */
register_var(const char * varname,const char * defval,const char * section,const char * desc,const char * example,enum var_type type,int flags)237 void register_var(const char *varname, const char *defval,
238                   const char *section, const char *desc,
239                   const char *example, enum var_type type, int flags)
240 {
241     struct var_data *temp = find_var_rec(varname);
242     int hash;
243 
244     if(temp) {
245         log_printf(0, "Attempting to reregister variable '%s'\n", varname);
246         return;
247     }
248     if(flags & VAR_LOCKED) {
249         flags &= ~VAR_LOCKED;
250     }
251     if(flags & VAR_NOEXPAND) {
252         flags &= ~VAR_NOEXPAND;
253     }
254 
255     hash = var_hash(varname);
256     temp = (struct var_data *)malloc(sizeof(struct var_data));
257     temp->name = strdup(varname);
258     temp->description = NULL;
259     temp->section = NULL;
260     temp->example = NULL;
261     temp->defval= NULL;
262     temp->global = NULL;
263     temp->site = NULL;
264     temp->list = NULL;
265     temp->temp = NULL;
266     temp->expanded = NULL;
267     temp->choices = NULL;
268     temp->type = type;
269     if(desc)
270         temp->description = strdup(desc);
271     if(section)
272         temp->section = strdup(section);
273     if(example)
274         temp->example = strdup(example);
275     if(defval) {
276         /* lets do some validation of the variables */
277         switch (temp->type) {
278             case VAR_DATA:
279                 temp->defval = (char *)defval;
280                 break;
281             case VAR_CHOICE: {
282                 char *deftmp, *delim, *dv;
283                 deftmp = strdup(defval);
284                 delim = strchr(deftmp, ':');
285                 dv = strchr(deftmp, '|');
286                 if(delim == NULL || dv == NULL) {
287                    log_printf(0, "Choice variable '%s' had no list of choices.\n",
288                               varname);
289                    if(temp->name) free(temp->name);
290                    if(temp->description) free(temp->description);
291                    if(temp->section) free(temp->section);
292                    if(temp->example) free(temp->example);
293                    free(deftmp);
294                    free(temp);
295                    return;
296                 } else {
297                    if((*(delim+1) != '|') || (delim[strlen(delim)-1] != '|')) {
298                        log_printf(0, "Choice variable '%s' had invalid list of choices.\n",
299                                   varname);
300                        if(temp->name) free(temp->name);
301                        if(temp->description) free(temp->description);
302                        if(temp->section) free(temp->section);
303                        if(temp->example) free(temp->example);
304                        free(temp);
305                        free(deftmp);
306                        return;
307                    }
308                    temp->choices = strdup(delim+1);
309                    if(delim != defval) {
310                        *delim = '\0';
311                        temp->defval = strdup(defval);
312                    }
313                    free(deftmp);
314                 }
315                 break;
316             }
317             case VAR_BOOL:
318                 if(atoi(defval) || !strcasecmp(defval, "yes") ||
319                    !strcasecmp(defval, "on") || !strcasecmp(defval, "y") ||
320                    !strcasecmp(defval, "true")) {
321                     temp->defval = strdup("1");
322                 } else {
323                     temp->defval = strdup("0");
324                 }
325                 break;
326             case VAR_TIME:
327                 if(atoi(defval)) {
328                     temp->defval = strdup(defval);
329                 } else {
330                     char buf[BIG_BUF];
331                     time_t now = time(NULL);
332                     buffer_printf(buf, sizeof(buf) - 1, "%d", (int)now);
333                     temp->defval = strdup(buf);
334                 }
335                 break;
336             case VAR_INT:
337                 if(atoi(defval)) {
338                     temp->defval = strdup(defval);
339                 } else {
340                     temp->defval = strdup("0");
341                 }
342                 break;
343             case VAR_DURATION:
344             case VAR_STRING:
345                 temp->defval = strdup(defval);
346                 break;
347             default:
348                 /* We should never get here!! */
349                 log_printf(0, "Attempt to register var of unknown type '%s'\n",
350                            varname);
351                 return;
352         }
353     }
354     temp->flags = flags;
355     if(!temp->defval || strstr(temp->defval, "<$") == NULL)
356         temp->flags |= VAR_NOEXPAND;
357     temp->next = listdata->bucket[hash];
358     listdata->bucket[hash] = temp;
359 }
360 
361 /* Set a variables value */
set_var(const char * varname,const char * varval,int level)362 void set_var(const char *varname, const char *varval, int level)
363 {
364     struct var_data *listtemp;
365     char *temp = NULL;
366 
367     listtemp = find_var_rec(varname);
368     if(!listtemp) {
369         if(!get_bool("global-pass")) {
370             log_printf(0, "set_var: Setting an unknown variable '%s'\n",
371                        varname);
372         }
373         return;
374     }
375     if(listtemp->flags & VAR_LOCKED) {
376         if(listtemp->type == VAR_DATA) {
377             log_printf(1, "Attempt to set locked variable '%s' to '%x'\n",
378                        varname, varval);
379         } else {
380             log_printf(1, "Attempt to set locked variable '%s' to '%s'\n",
381                        varname, varval);
382         }
383     }
384     if((listtemp->flags & VAR_RESTRICTED) && (level & VAR_LIST) &&
385        !(level & VAR_RESTRICTED)) {
386         const char *listname;
387 
388         listname = get_var("list");
389 
390         if (!listname)
391            return;
392 
393         if (!is_trusted(listname))
394            return;
395     }
396 
397     if(level & VAR_LOCKED) {
398         level &= ~VAR_LOCKED;
399     }
400     if(level & VAR_INTERNAL) {
401         level &= ~VAR_INTERNAL;
402     }
403     if(level & VAR_NOEXPAND) {
404         level &= ~VAR_NOEXPAND;
405     }
406     if(level & VAR_RESTRICTED) {
407         level &= ~VAR_RESTRICTED;
408     }
409 
410     if((listtemp->flags & level) == 0) {
411         log_printf(0, "set_var: Variable '%s' illegal at level %d\n", varname,
412                    level);
413         return;
414     }
415 
416     listtemp->flags &= ~VAR_NOEXPAND;
417 
418     /* lets do some validation of the variables */
419     switch (listtemp->type) {
420         case VAR_DATA:
421             temp = (char *)varval;
422             break;
423         case VAR_CHOICE: {
424             char buf[BIG_BUF];
425             buffer_printf(buf, sizeof(buf) - 1, "|%s|", varval);
426             if(strstr(listtemp->choices, buf) == NULL) {
427                 log_printf(0, "Attempt to set invalid value for choice variable '%s'\n", varname);
428                 return;
429             }
430             temp = strdup(varval);
431             break;
432         }
433         case VAR_BOOL:
434             if(varval) {
435                 if(atoi(varval) || !strcasecmp(varval, "yes") ||
436                    !strcasecmp(varval, "on") || !strcasecmp(varval, "y") ||
437                    !strcasecmp(varval, "true")) {
438                     temp = strdup("1");
439                 } else {
440                     temp = strdup("0");
441                 }
442             }
443             break;
444         case VAR_TIME:
445             if(varval && atoi(varval)) {
446                 temp = strdup(varval);
447             } else {
448                 char buf[BIG_BUF];
449                 time_t now = time(NULL);
450                 buffer_printf(buf, sizeof(buf) - 1, "%d", (int)now);
451                 temp = strdup(buf);
452             }
453             break;
454         case VAR_INT:
455             if(varval && atoi(varval)) {
456                 temp = strdup(varval);
457             } else {
458                 temp = strdup("0");
459             }
460             break;
461         case VAR_DURATION:
462         case VAR_STRING:
463             if(varval)
464                 temp = strdup(varval);
465             break;
466         default:
467             /* We should never get here!! */
468             log_printf(0, "Attempt to set a variable of unknown type '%s'\n",
469                        varname);
470             return;
471     }
472 
473     switch (level) {
474         case VAR_GLOBAL:
475             if(listtemp->global && listtemp->type != VAR_DATA)
476                 free(listtemp->global);
477             listtemp->global = temp;
478             break;
479         case VAR_SITE:
480             if(listtemp->site && listtemp->type != VAR_DATA)
481                 free(listtemp->site);
482             listtemp->site = temp;
483             break;
484         case VAR_LIST:
485             if(listtemp->list && listtemp->type != VAR_DATA)
486                 free(listtemp->list);
487             listtemp->list = temp;
488             break;
489         case VAR_TEMP:
490             if(listtemp->temp && listtemp->type != VAR_DATA)
491                 free(listtemp->temp);
492             listtemp->temp = temp;
493             break;
494         default:
495             log_printf(1, "Attempt to set var '%s' at unknown level %d\n",
496                        varname, level);
497             return;
498     }
499     if(listtemp->type == VAR_DATA) {
500       log_printf(9, "Setvar: '%s'='%x' at level %d\n", varname, varval, level);
501     } else {
502       log_printf(9, "Setvar: '%s'='%s' at level %d\n", varname, varval, level);
503     }
504     if(listtemp->expanded) {
505         free(listtemp->expanded);
506         listtemp->expanded = NULL;
507     }
508     temp = (char *)get_cur_varval(listtemp);
509     if(!temp || strstr(temp, "<$") == NULL)
510         listtemp->flags |= VAR_NOEXPAND;
511 }
512 
513 /* Return the variable as a data_type */
get_data(const char * varname)514 const void *get_data(const char *varname)
515 {
516     struct var_data *tmp = find_var_rec(varname);
517 
518     if(!tmp) {
519         log_printf(9, "get_data: Query for non-variable '%s'\n", varname);
520         return NULL;
521     }
522     if(tmp->type != VAR_DATA) {
523         log_printf(9, "get_data: Variable '%s' is not type DATA\n", varname);
524         return NULL;
525     }
526     return (void *)get_cur_varval(tmp);
527 }
528 
529 /* Return a copy of the variable unexpanded */
530 
get_var_unexpanded(const char * varname)531 const char *get_var_unexpanded(const char *varname)
532 {
533     struct var_data *tmp = find_var_rec(varname);
534     const char *c;
535 
536     if(!tmp) {
537         log_printf(9, "get_var: Query for non-variable '%s'\n", varname);
538         return NULL;
539     }
540     c = get_cur_varval(tmp);
541 	log_printf(19, "Getting %s unexpaned as %s\n", varname, c);
542 	return c;
543 }
544 
545 /* Return the variable data as a raw pointer */
get_var(const char * varname)546 const char *get_var(const char *varname)
547 {
548     struct var_data *tmp = find_var_rec(varname);
549     const char *c;
550 
551     if(!tmp) {
552         log_printf(9, "get_var: Query for non-variable '%s'\n", varname);
553         return NULL;
554     }
555     c = get_cur_varval(tmp);
556     if(c && (tmp->type == VAR_STRING || tmp->type == VAR_CHOICE)) {
557         if((tmp->flags & VAR_NOEXPAND) == 0) {
558             char tbuf[BIG_BUF];
559             liscript_parse_line(c, tbuf, sizeof(tbuf) - 1);
560             if(tmp->expanded) free(tmp->expanded);
561             log_printf(19,"Expanded '%s' -> '%s'\n", c, tbuf);
562             tmp->expanded = strdup(tbuf);
563             c = tmp->expanded;
564         }
565     }
566     return c;
567 }
568 
569 /* Convert the raw data to a boolean value and return it */
get_bool(const char * varname)570 int get_bool(const char *varname)
571 {
572     struct var_data *tmp = find_var_rec(varname);
573     const char *c;
574 
575     if(!tmp) {
576         log_printf(9, "get_bool: Query for non-variable '%s'\n", varname);
577         return 0;
578     }
579     if(tmp->type != VAR_BOOL) {
580         log_printf(9, "get_bool: Variable '%s' is not of type BOOL\n", varname);
581     }
582 
583     c = get_cur_varval(tmp);
584 
585     if(!c)
586        return 0;
587 
588     return atoi(c);
589 }
590 
591 /* Convert the raw data to an integer and return it */
get_number(const char * varname)592 int get_number(const char *varname)
593 {
594     struct var_data *tmp = find_var_rec(varname);
595     const char *c;
596 
597     if(!tmp) {
598         log_printf(9, "get_number: Query for non-variable '%s'\n", varname);
599         return 0;
600     }
601     if(tmp->type != VAR_INT && tmp->type != VAR_TIME) {
602         log_printf(9,"get_number: Variable '%s' is not available as type INT\n",varname);
603         return 0;
604     }
605 
606     c = get_cur_varval(tmp);
607     if(tmp->type == VAR_TIME) {
608        if(!c)
609           return (int)time(NULL);
610        return atoi(c);
611     } else {
612        if(!c)
613           return 0;
614        return atoi(c);
615     }
616 }
617 
618 /* Return the raw value as a string, NULL is the empty string */
get_string(const char * varname)619 const char *get_string(const char *varname)
620 {
621     struct var_data *tmp = find_var_rec(varname);
622     const char *c;
623     /* used if we are asked for the string value of a null VAR_TIME var */
624     static char datebuf[BIG_BUF];
625 
626     if(!tmp) {
627         log_printf(9, "get_string: Query for non-variable '%s'\n", varname);
628         return "";
629     }
630     if((tmp->type != VAR_STRING) && (tmp->type != VAR_TIME) &&
631        (tmp->type != VAR_DURATION) && (tmp->type != VAR_CHOICE)) {
632         log_printf(9, "get_string: Variable '%s' is not available as type STRING\n",
633                    varname);
634         return "";
635     }
636 
637     c = get_cur_varval(tmp);
638     if(tmp->type == VAR_TIME) {
639         if(!c)  {
640             time_t now = time(NULL);
641             get_date(datebuf, sizeof(datebuf), now);
642             return &datebuf[0];
643         } else {
644             get_date(datebuf, sizeof(datebuf), atoi(c));
645             return &datebuf[0];
646         }
647     } else {
648         if(!c)
649             return "";
650         if(tmp->flags & VAR_NOEXPAND) {
651             return c;
652         } else {
653             char tbuf[BIG_BUF];
654             liscript_parse_line(c, tbuf, sizeof(tbuf) - 1);
655             if(tmp->expanded) free(tmp->expanded);
656             log_printf(19,"Expanded '%s' -> '%s'\n", c, tbuf);
657             tmp->expanded = strdup(tbuf);
658             return tmp->expanded;
659         }
660     }
661     return "";
662 }
663 
664 /* Parse the string for a date format and return it as a number of seconds */
get_seconds(const char * varname)665 int get_seconds(const char *varname)
666 {
667     struct var_data *tmp = find_var_rec(varname);
668     const char *c;
669     int res = 0;
670     int total = 0;
671 
672     if(!tmp) {
673         log_printf(9, "get_seconds: Query for non-variable '%s'\n", varname);
674         return 0;
675     }
676     if(tmp->type != VAR_DURATION) {
677         log_printf(9, "get_seconds: Variable '%s' is not of type DURATION\n",
678                    varname);
679         return 0;
680     }
681 
682     c = get_cur_varval(tmp);
683 
684     if(!c)
685         return 0;
686     while(*c) {
687         while (*c && isspace((int)(*c))) c++;
688         while (*c && isdigit((int)(*c))) { res = res * 10 + (*c - '0'); c++; }
689         while (*c && isspace((int)(*c))) c++;
690         switch(*c) {
691             case 'd': total += (24 * 60 * 60) * res; c++; break;
692             case 'h': total += (60 * 60) * res; c++; break;
693             case 'm': total += (60) * res; c++; break;
694             default : total += res; c++; break;
695         }
696     }
697     return total;
698 }
699 
700 /* Initialize the hash table */
init_vars(void)701 void init_vars(void)
702 {
703     int i;
704     listdata = (struct list_vars *)malloc(sizeof(struct list_vars));
705     for(i = 0; i < HASHSIZE; i++)
706         listdata->bucket[i] = NULL;
707 }
708 
709 static int varwalkcount, varwalkmax;
710 
start_varlist(void)711 struct var_data *start_varlist(void)
712 {
713     int count, num, i;
714 
715     count = count_all_vars();
716 
717     sortedvars = (struct var_data **)malloc(sizeof(struct var_data *)*count);
718     if(!sortedvars) {
719         log_printf(0, "Unable to allocate memory for sorting.\n");
720         return NULL;
721     }
722 
723     num = 0;
724 
725     for (i = 0; i < HASHSIZE; i++) {
726         struct var_data *tmp = listdata->bucket[i];
727         while(tmp) {
728             if(!(tmp->flags & VAR_INTERNAL)) {
729                 sortedvars[num++] = tmp;
730             }
731             tmp = tmp->next;
732         }
733     }
734     _sortorder = NULL;
735     qsort(sortedvars, count, sizeof(struct var_data *), var_cmp);
736 
737     varwalkcount = 0;
738     varwalkmax = count;
739 
740     return(sortedvars[0]);
741 }
742 
next_varlist()743 struct var_data *next_varlist ()
744 {
745     if (!sortedvars) return NULL;
746 
747     varwalkcount++;
748     if (varwalkcount >= varwalkmax)
749        return NULL;
750 
751     return sortedvars[varwalkcount];
752 }
753 
finish_varlist(void)754 void finish_varlist(void)
755 {
756     if (!sortedvars) return;
757     varwalkcount = 0; varwalkmax = 0;
758     free(sortedvars);
759     sortedvars = NULL;
760 }
761 
lock_var(const char * varname)762 void lock_var(const char *varname)
763 {
764     struct var_data *temp = find_var_rec(varname);
765     if(temp)
766         temp->flags |= VAR_LOCKED;
767 }
768 
restrict_var(const char * varname)769 void restrict_var(const char *varname)
770 {
771     struct var_data *temp = find_var_rec(varname);
772     log_printf(8,"restrict_var: %s\n", varname);
773     if(temp)
774         temp->flags |= VAR_RESTRICTED;
775 }
776 
unlock_var(const char * varname)777 void unlock_var(const char *varname)
778 {
779     struct var_data *temp = find_var_rec(varname);
780     if(temp)
781         temp->flags &= ~VAR_LOCKED;
782 }
783 
count_vars(int level)784 static int count_vars(int level)
785 {
786     int i;
787     int count = 0;
788     int trusted;
789 
790     if (level == VAR_LIST) {
791        if (get_var("list"))
792           trusted = is_trusted(get_var("list"));
793        else trusted = 0;
794     } else trusted = 1;
795 
796     for (i = 0; i < HASHSIZE; i++) {
797         struct var_data *tmp = listdata->bucket[i];
798         while(tmp) {
799             if((tmp->flags & level) && !(tmp->flags & VAR_INTERNAL)
800                && (!(tmp->flags & VAR_RESTRICTED) || trusted))
801                 count++;
802             tmp = tmp->next;
803         }
804     }
805     return count;
806 }
807 
count_all_vars()808 static int count_all_vars()
809 {
810     int i;
811     int count = 0;
812     for (i = 0; i < HASHSIZE; i++) {
813         struct var_data *tmp = listdata->bucket[i];
814         while(tmp) {
815             if(!(tmp->flags & VAR_INTERNAL))
816                 count++;
817             tmp = tmp->next;
818         }
819     }
820     return count;
821 }
822 
823 
var_cmp(const void * e1,const void * e2)824 static int var_cmp(const void *e1, const void *e2)
825 {
826     struct var_data *v1, *v2;
827     int cmpval;
828 
829     v1 = *(struct var_data **)e1;
830     v2 = *(struct var_data **)e2;
831 
832     /* Sanity check! */
833     if (v1->section && !v2->section) return -1;
834     if (v2->section && !v1->section) return 1;
835     if (!v1->section && !v2->section) return 0;
836 
837     if(_sortorder) {
838         char *s1, *s2;
839         char buf1[BIG_BUF];
840         char buf2[BIG_BUF];
841         buffer_printf(buf1, sizeof(buf1) - 1, ":%s:", v1->section);
842         buffer_printf(buf2, sizeof(buf2) - 1, ":%s:", v2->section);
843         s1 = strstr(_sortorder, buf1);
844         s2 = strstr(_sortorder, buf2);
845         if(s1 && !s2) return -1;
846         else if(s2 && !s1) return 1;
847         else if(s1 && s2) {
848             if(s1 < s2) return -1;
849             else if(s2 < s1) return 1;
850             else return 0;
851         }
852     }
853 
854     cmpval = strcasecmp(v1->section, v2->section);
855 
856     if (!cmpval) {
857         cmpval = strcasecmp(v1->name,v2->name);
858     }
859 
860     return cmpval;
861 }
862 
write_configfile(const char * filename,int level,const char * sortorder)863 void write_configfile(const char *filename, int level, const char *sortorder)
864 {
865     /* Eventually, sortorder will provide a way to sort sections.  NYI */
866     FILE *ofile = open_exclusive(filename, "w");
867     int i, trusted;
868     int count = 0, num = 0;
869     struct var_data **arr = NULL;
870     char *lastsection;
871 
872     if(!ofile) {
873         log_printf(0, "Unable to open config file '%s' for writing.\n",
874                    filename);
875         return;
876     }
877 
878     if (level & VAR_LIST) {
879        if (get_var("list"))
880            trusted = is_trusted(get_var("list"));
881        else trusted = 0;
882     } else trusted = 1;
883 
884     count = count_vars(level);
885     if(count == 0) {
886         log_printf(0, "No variables to write at level %d.\n", level);
887         close_file(ofile);
888         return;
889     }
890 
891     arr = (struct var_data **)malloc(sizeof(struct var_data *)*count);
892     if(!arr) {
893         log_printf(0, "Unable to allocate memory for sorting.\n");
894         close_file(ofile);
895         return;
896     }
897 
898     for (i = 0; i < HASHSIZE; i++) {
899         struct var_data *tmp = listdata->bucket[i];
900         while(tmp) {
901             if((tmp->flags & level) && !(tmp->flags & VAR_INTERNAL) &&
902                ((!(tmp->flags & VAR_RESTRICTED)) || trusted)) {
903                 arr[num++] = tmp;
904             }
905             tmp = tmp->next;
906         }
907     }
908     _sortorder = sortorder;
909     qsort(arr, count, sizeof(struct var_data *), var_cmp);
910     _sortorder = NULL;
911 
912     lastsection = NULL;
913 
914     for(i = 0; i < count; i++) {
915         char *desc = arr[i]->description;
916         const char *val;
917         int col = 0;
918 
919         if (lastsection) {
920            if(!arr[i]->section) {
921               free(lastsection);
922               lastsection = NULL;
923               write_file(ofile,"\n\n# Miscellaneous Settings\n\n");
924            } else if(strcasecmp(arr[i]->section,lastsection) != 0) {
925               unsigned int counter;
926 
927               free(lastsection);
928               lastsection = upperstr(arr[i]->section);
929 
930               for (counter = 0; counter < (strlen(lastsection) + 4);
931                    counter++) {
932                   write_file(ofile,"#");
933               }
934 
935               write_file(ofile,"\n# %s #\n", lastsection);
936 
937               for (counter = 0; counter < (strlen(lastsection) + 4);
938                    counter++) {
939                   write_file(ofile,"#");
940               }
941               write_file(ofile,"\n\n");
942            }
943         } else {
944            if (arr[i]->section) {
945               unsigned int counter;
946 
947               lastsection = upperstr(arr[i]->section);
948               for (counter = 0; counter < (strlen(lastsection) + 4);
949                    counter++) {
950                   write_file(ofile,"#");
951               }
952 
953               write_file(ofile,"\n# %s #\n", lastsection);
954 
955               for (counter = 0; counter < (strlen(lastsection) + 4);
956                    counter++) {
957                   write_file(ofile,"#");
958               }
959               write_file(ofile,"\n\n");
960            }
961         }
962 
963         write_file(ofile, "# %s\n", arr[i]->name);
964 
965         while(desc ? *desc : 0) {
966             if(col == 0) {
967                 write_file(ofile, "# ");
968                 col = 2;
969             }
970             if((*desc == ' ' && col > 65)) {
971                 col = 0;
972                 write_file(ofile, "\n");
973             } else {
974                 write_file(ofile, "%c", *desc);
975                 col++;
976             }
977             desc++;
978         }
979         if (arr[i]->example)
980            write_file(ofile, "\n# Example: %s\n", arr[i]->example);
981         write_file(ofile, "#\n");
982         val = get_cur_varval_level(arr[i], level);
983         if(val) {
984             if(arr[i]->type == VAR_CHOICE) {
985                  char *z = strchr(val, ':');
986                  if(z) *z = '\0';
987             }
988             write_file(ofile, "%s = %s\n\n", arr[i]->name,
989                  (arr[i]->type == VAR_BOOL) ?
990                     (val ? (*val == '1' ? "true" : "false" ) : "false") :
991                     (val ? val : ""));
992         } else {
993             val = get_cur_varval_level_default(arr[i], level);
994             if(arr[i]->type == VAR_CHOICE) {
995                  char *z = strchr(val, ':');
996                  if(z) *z = '\0';
997             }
998             write_file(ofile, "# %s = %s\n\n", arr[i]->name,
999                  (arr[i]->type == VAR_BOOL) ?
1000                     (val ? (*val == '1' ? "true" : "false" ) : "false") :
1001                     (val ? val : ""));
1002         }
1003     }
1004     close_file(ofile);
1005 }
1006 
write_configfile_section(const char * filename,int level,const char * section)1007 void write_configfile_section(const char *filename, int level,
1008                               const char *section)
1009 {
1010     FILE *ofile = open_exclusive(filename, "w");
1011     int i;
1012 
1013     if(!ofile) {
1014         log_printf(0, "Unable to open config file '%s' for writing.\n",
1015                    filename);
1016         return;
1017     }
1018 
1019     for (i = 0; i < HASHSIZE; i++) {
1020         struct var_data *tmp = listdata->bucket[i];
1021         while(tmp) {
1022             if((tmp->flags & level) && !(tmp->flags & VAR_INTERNAL)) {
1023                 if(strcasecmp(tmp->section, section) == 0) {
1024                     char *desc = tmp->description;
1025                     const char *val;
1026                     int col = 0;
1027                     write_file(ofile, "# %s\n", tmp->name);
1028                     while(desc ? *desc : 0) {
1029                         if(col == 0) {
1030                             write_file(ofile, "# ");
1031                             col = 2;
1032                         }
1033                         if((*desc == ' ' && col > 65)) {
1034                             col = 0;
1035                             write_file(ofile, "\n");
1036                         } else {
1037                             write_file(ofile, "%c", *desc);
1038                             col++;
1039                         }
1040                         desc++;
1041                     }
1042                     if (tmp->example)
1043                        write_file(ofile, "\n# Example: %s", tmp->example);
1044                     write_file(ofile, "#\n");
1045                     val = get_cur_varval_level(tmp, level);
1046                     if(val)
1047                         write_file(ofile, "%s = %s\n\n", tmp->name,
1048                              (tmp->type == VAR_BOOL) ?
1049                              (val ? (*val == '1' ? "true" : "false" ) : "false") :
1050                              (val ? val : ""));
1051                     else {
1052                         val = get_cur_varval_level_default(tmp, level);
1053                         if (val)
1054                            write_file(ofile, "# %s = %s\n\n", tmp->name,
1055                                 (tmp->type == VAR_BOOL) ?
1056                                 (val ? (*val == '1' ? "true" : "false" ) : "false") :
1057                                 (val ? val : ""));
1058                         else
1059                            write_file(ofile, "# %s = \n\n",
1060                                 tmp->name);
1061                     }
1062                 }
1063             }
1064             tmp = tmp->next;
1065         }
1066     }
1067     close_file(ofile);
1068 }
1069 
1070 
init_regvars(void)1071 void init_regvars(void)
1072 {
1073     char buf[BIG_BUF];
1074 
1075     /* I'm going to register cookies here too since I don't have any
1076        better place */
1077     register_cookie('M', "modpost-expiration-time", NULL, expire_modpost);
1078 
1079     /* and register some variables */
1080     register_var("path", ".", NULL, NULL, NULL, VAR_STRING,
1081                  VAR_GLOBAL|VAR_INTERNAL);
1082     register_var("queuefile", NULL, NULL, NULL, NULL, VAR_STRING,
1083                  VAR_GLOBAL|VAR_INTERNAL);
1084     register_var("listserver-root", NULL, "Location",
1085                  "The path to the root of the Listserver installation",
1086                  "listserver-root = /usr/local/listserver", VAR_STRING,
1087                  VAR_GLOBAL);
1088     register_alias("listar-root", "listserver-root");
1089     register_alias("sllist-root", "listserver-root");
1090     register_alias("ecartis-root", "listserver-root");
1091     register_var("config-file", "config", "Basic Configuration",
1092                  "The name of the list-specific configuration file.",
1093                  "config-file = config", VAR_STRING, VAR_GLOBAL|VAR_SITE);
1094     register_var("listserver-modules", NULL, "Location",
1095                  "The path to the directory containing the LPM modules.",
1096                  "listserver-modules = /usr/local/lists/modules", VAR_STRING,
1097                  VAR_GLOBAL);
1098     register_alias("listar-modules", "listserver-modules");
1099     register_alias("sllist-modules", "listserver-modules");
1100     register_alias("ecartis-modules", "listserver-modules");
1101     register_var("listserver-conf", NULL, "Location",
1102                  "The path to the listserver configuration files.",
1103                  "listserver-conf = /usr/local/mylists/configs",
1104                  VAR_STRING, VAR_GLOBAL);
1105     register_alias("listar-conf", "listserver-conf");
1106     register_alias("sllist-conf", "listserver-conf");
1107     register_alias("ecartis-conf", "listserver-conf");
1108     register_var("listserver-data", NULL, "Location",
1109                  "The path to the listserver data root.",
1110                  "listserver-data = /usr/local/mylists/data",
1111                  VAR_STRING, VAR_GLOBAL|VAR_SITE);
1112     register_alias("listar-data", "listserver-data");
1113     register_alias("sllist-data", "listserver-data");
1114     register_alias("ecartis-data", "listserver-data");
1115     register_var("mailserver", "localhost", "SMTP",
1116                  "The name of the outging SMTP server to use.",
1117                  "mailserver = mail.host1.dom", VAR_STRING,
1118                  VAR_GLOBAL|VAR_SITE);
1119     register_var("smtp-errors-file", NULL, NULL, NULL, NULL, VAR_STRING,
1120                  VAR_INTERNAL|VAR_GLOBAL);
1121     register_var("form-send-as", NULL, NULL, NULL, NULL, VAR_STRING,
1122                  VAR_TEMP|VAR_INTERNAL);
1123     register_var("listserver-admin", "root@localhost", "Addresses",
1124                  "The email address of the human in charge of the listserver.",
1125                  "listserver-admin = user1@host2.dom", VAR_STRING,
1126                  VAR_GLOBAL|VAR_SITE);
1127     register_alias("listar-admin", "listserver-admin");
1128     register_alias("sllist-admin", "listserver-admin");
1129     register_alias("ecartis-admin", "listserver-admin");
1130     register_alias("listar-owner", "listserver-admin");
1131     register_alias("sllist-owner", "listserver-admin");
1132     register_alias("ecartis-owner", "listserver-admin");
1133     register_alias("listserver-owner", "listserver-admin");
1134     register_var("task-expires", "no", NULL, NULL, NULL, VAR_BOOL,
1135                  VAR_INTERNAL|VAR_TEMP);
1136     register_var("results-subject-override", NULL, NULL, NULL, NULL,
1137                  VAR_STRING, VAR_INTERNAL|VAR_TEMP);
1138     register_var("initial-cmd", NULL, NULL, NULL, NULL,
1139                  VAR_STRING, VAR_INTERNAL|VAR_GLOBAL);
1140     register_var("task-form-subject", NULL, NULL, NULL, NULL, VAR_STRING,
1141                  VAR_INTERNAL|VAR_TEMP);
1142     register_var("global-pass", "no", NULL, NULL, NULL, VAR_BOOL,
1143                  VAR_INTERNAL|VAR_TEMP);
1144     register_var("preserve-queue", "no", "Debugging",
1145                  "Controls whether to remove queue file after processing.",
1146                  "preserve-queue = yes", VAR_BOOL, VAR_ALL);
1147     register_var("lists-root", "<$listserver-data>/lists", "Location",
1148                  "Location of the directory containing all the list info.",
1149                  "lists-root = lists", VAR_STRING, VAR_GLOBAL|VAR_SITE);
1150     register_var("mode", "nolist", NULL, NULL, NULL, VAR_STRING,
1151                  VAR_TEMP|VAR_GLOBAL|VAR_INTERNAL);
1152     register_var("send-as", NULL, "SMTP",
1153                  "Controls what the SMTP return path is set to.",
1154                  "send-as = list2-bounce@test2.dom",  VAR_STRING, VAR_ALL);
1155     register_var("list-owner", NULL, "Basic Configuration",
1156                  "Defines an email address to reach the list owner(s).",
1157                  "list-owner = list2-admins@hostname.dom", VAR_STRING, VAR_ALL);
1158     register_var("list", NULL, NULL, NULL, NULL, VAR_STRING,
1159                  VAR_INTERNAL|VAR_GLOBAL|VAR_TEMP);
1160     register_var("smtp-last-error", NULL, NULL, NULL, NULL, VAR_STRING,
1161                  VAR_INTERNAL|VAR_TEMP);
1162     register_var("listserver-full-name", SERVICE_NAME_MC, "Addresses",
1163                  "The friendly name used to identify the listserver.",
1164                  "listserver-full-name = List Server",
1165                  VAR_STRING, VAR_GLOBAL|VAR_SITE);
1166     register_alias("listar-full-name", "listserver-full-name");
1167     register_alias("sllist-full-name", "listserver-full-name");
1168     register_alias("ecartis-full-name", "listserver-full-name");
1169     register_var("listserver-address", SERVICE_ADDRESS, "Addresses",
1170                  "The email address for the listserver control account.",
1171                  "listserver-address = listserv@myhost.dom",
1172                  VAR_STRING, VAR_GLOBAL|VAR_SITE);
1173     register_alias("listar-address", "listserver-address");
1174     register_alias("sllist-address", "listserver-address");
1175     register_alias("ecartis-address", "listserver-address");
1176     register_var("fakequeue", NULL, NULL, NULL, NULL, VAR_BOOL,
1177                  VAR_INTERNAL|VAR_GLOBAL);
1178     register_var("listserver-infile", NULL, NULL, NULL, NULL, VAR_STRING,
1179                  VAR_INTERNAL|VAR_GLOBAL);
1180     register_var("cookie-for", NULL, NULL, NULL, NULL, VAR_STRING,
1181                  VAR_INTERNAL|VAR_TEMP);
1182     register_var("realsender", NULL, NULL, NULL, NULL, VAR_STRING,
1183                  VAR_INTERNAL|VAR_GLOBAL);
1184     register_var("cookie-expiration-time", "1 d", "Timeouts",
1185                  "How long until a generated cookie expires.",
1186                  "cookie-expiration-time = 3 d 6 h", VAR_DURATION, VAR_ALL);
1187     register_var("modpost-expiration-time", NULL, "Timeouts",
1188                  "How long until a moderated post cookie expires.",
1189                  "modpost-expiration-time = 2 h", VAR_DURATION, VAR_ALL);
1190     register_var("reply-expires-time", "1 d", "Timeouts",
1191                  "How long until an automatic reply expires from the mailbox",
1192                  "reply-expires-time = 3 h", VAR_DURATION, VAR_NOLIST);
1193     register_var("form-cc-address", NULL, "SMTP",
1194                  "Who should be cc'd on any tasks/forms that the server sends.",
1195                  "form-cc-address = user2@host1.dom", VAR_STRING, VAR_ALL);
1196     register_var("form-reply-to", NULL, NULL, NULL, NULL, VAR_STRING,
1197                  VAR_INTERNAL|VAR_TEMP);
1198     register_var("task-no-footer", "no", "Basic Configuration",
1199                  "Should the messages produced by the server have a footer with version information",
1200                  "task-no-footer = yes", VAR_BOOL, VAR_ALL);
1201     register_var("site-config-file", NULL, NULL, NULL, NULL, VAR_STRING,
1202                  VAR_GLOBAL|VAR_INTERNAL);
1203     register_var("deny-822-from", "no", "Address Handling",
1204                  "Should the RFC822 From: header be trusted for sender.",
1205                  "deny-822-from = no", VAR_BOOL, VAR_ALL);
1206     register_var("deny-822-bounce", "no", "Address Handling",
1207                  "Should the RFC822 Resent-From: header be trusted for sender.",
1208                  "deny-822-bounce = yes", VAR_BOOL, VAR_ALL);
1209     register_var("just-unquoted", "no", NULL, NULL, NULL, VAR_BOOL,
1210                  VAR_INTERNAL|VAR_GLOBAL);
1211     register_var("just-unmimed", "no", NULL, NULL, NULL, VAR_BOOL,
1212                  VAR_INTERNAL|VAR_GLOBAL);
1213     register_var("unmime-moderate-mode", "no", NULL, NULL, NULL, VAR_BOOL,
1214                  VAR_ALL|VAR_INTERNAL);
1215     register_var("resent-from", NULL, NULL, NULL, NULL, VAR_STRING,
1216                  VAR_INTERNAL|VAR_GLOBAL);
1217     register_var("jobeoj-wrapper", "no", NULL, NULL, NULL, VAR_BOOL,
1218                  VAR_INTERNAL|VAR_GLOBAL);
1219     register_var("ignore-subject-commands", "no", "Basic Configuration",
1220                  "Should the server ignore commands in the subject line.",
1221                  "ignore-subject-commands = false", VAR_BOOL,
1222                  VAR_GLOBAL| VAR_SITE);
1223     register_var("fromaddress", NULL, NULL, NULL, NULL, VAR_STRING,
1224                  VAR_INTERNAL|VAR_GLOBAL);
1225     register_var("socket-timeout", "30 s", "Socket IO",
1226                  "How long should the server wait on reading a socket.",
1227                  "socket-timeout = 5 m", VAR_DURATION, VAR_GLOBAL|VAR_SITE);
1228     register_var("debug", "0", "Debugging", "How much logging should be done.",
1229                  "debug = 10", VAR_INT, VAR_ALL);
1230     register_var("cur-parse-line", NULL, NULL, NULL, NULL, VAR_STRING,
1231                  VAR_INTERNAL|VAR_GLOBAL);
1232     register_var("approved-address", "<$list>-repost@<$hostname>", "Addresses",
1233                  "Address to which approved/rejected/modified moderated posts should be sent.",
1234                  "approved-address = mylist-repost@myhost.dom", VAR_STRING,
1235                  VAR_ALL);
1236     register_var("moderator", NULL, "Moderation",
1237                  "Address for the list moderator(s).",
1238                  "moderator = foolist-moderators@hostname.dom", VAR_STRING,
1239                  VAR_ALL);
1240     register_var("moderate-notify-nonsub", "no", "Moderation",
1241                  "Should posts from non-subscribers be acked if they are moderated.",
1242                  "moderate-notify-nonsub = true", VAR_BOOL, VAR_ALL);
1243     register_var("moderate-force-notify", "no", NULL, NULL, NULL,
1244                  VAR_BOOL, VAR_TEMP|VAR_INTERNAL);
1245     register_var("moderated-approved-by", NULL, NULL, NULL, NULL,
1246                  VAR_STRING, VAR_GLOBAL|VAR_INTERNAL);
1247     register_var("max-rcpt-tries", "5", "SMTP",
1248                  "How many times to attempt reading a RCPT TO: response.",
1249                  "max-recpt-tries = 3", VAR_INT, VAR_GLOBAL|VAR_SITE);
1250     register_var("sendmail-sleep", "no", "SMTP",
1251                  "Should we attempt to sleep a short time between message recipients.",
1252                  "sendmail-sleep = on", VAR_BOOL, VAR_GLOBAL|VAR_SITE);
1253     register_var("sendmail-sleep-length", "3 s", "SMTP",
1254                  "Override if you need the 'Sendmail Sleeper' option at a different duration than the 3 second default.",
1255                  "sendmail-sleep-length = 1 s", VAR_DURATION,
1256                  VAR_GLOBAL|VAR_SITE);
1257 
1258     register_var("per-user-queuefile", NULL, NULL, NULL, NULL, VAR_STRING,
1259                  VAR_INTERNAL|VAR_TEMP);
1260     register_var("per-user-datafile", NULL, NULL, NULL, NULL, VAR_STRING,
1261                  VAR_INTERNAL|VAR_TEMP);
1262     register_var("per-user-list", NULL, NULL, NULL, NULL, VAR_STRING,
1263                  VAR_INTERNAL|VAR_TEMP);
1264     register_var("per-user-address", NULL, NULL, NULL, NULL, VAR_STRING,
1265                  VAR_INTERNAL|VAR_TEMP);
1266     register_var("per-user-data", NULL, NULL, NULL, NULL, VAR_DATA,
1267                  VAR_INTERNAL|VAR_TEMP);
1268     register_var("megalist", "no", "ToList",
1269                  "Should we process this list on-disk instead of in memory? This disables the receipient list sorting and list-merging functionality of Ecartis, in order to prevent large memory footprint operations.  It is useful for lists where the receipient list is too large to effectively do memory-based operations on.",
1270                  "megalist = true", VAR_BOOL, VAR_ALL);
1271     register_var("smtp-queue-chunk", NULL, "SMTP",
1272                  "Maximum recipients per message submitted to the mail server.  Larger lists will be split into chunks of this size.",
1273                  "smtp-queue-chunk = 25", VAR_INT, VAR_ALL);
1274     register_var("per-user-modifications", "no", "ToList",
1275                  "Do we do per-user processing for list members.",
1276                  "per-user-modifications = false", VAR_BOOL, VAR_ALL);
1277     register_var("tolist-send-pause", "0", "ToList",
1278                  "How long (in milliseconds) do we sleep between SMTP chunks.",
1279                  "tolist-send-pause = 30", VAR_INT, VAR_ALL);
1280     register_var("unmimed-file", "no", NULL, NULL, NULL, VAR_BOOL,
1281                  VAR_INTERNAL|VAR_GLOBAL);
1282     register_var("unquoted-file", "no", NULL, NULL, NULL, VAR_BOOL,
1283                  VAR_INTERNAL|VAR_GLOBAL);
1284     register_var("unmime-first-level", "no", NULL, NULL, NULL, VAR_BOOL,
1285                  VAR_INTERNAL|VAR_TEMP);
1286     register_var("unmime-quiet", "no", "MIME",
1287                  "Should the listserver report when it strips MIME attachments.",
1288                  "unmime-quiet = no", VAR_BOOL, VAR_ALL);
1289     register_var("validate-users", "no", "Debugging",
1290                  "Perform a minimal validation of user@host.dom on all users in the list's user file and log errors.",
1291                  "validate-users = true", VAR_BOOL, VAR_ALL);
1292     register_var("no-loose-domain-match", "no", "ToList",
1293                  "Should the server treat users of a subdomain as users of the domain for validation purposes.",
1294                  "no-loose-domain-match = on", VAR_BOOL, VAR_ALL);
1295     register_var("default-flags", "|ECHOPOST|", "Basic Configuration",
1296                  "Default flags given to a user when they are subscribed.",
1297                  "default-flags = |NOPOST|DIGEST|", VAR_STRING, VAR_ALL);
1298     register_var("global-blacklist", "banned", "Files",
1299                  "Global file containing regular expressions for users who are not allowed to subscribe to lists hosted on this server.",
1300                  "global-blacklist = banned", VAR_STRING, VAR_GLOBAL|VAR_SITE);
1301     register_var("logfile", NULL, "Debugging",
1302                  "Filename where debugging log information will be stored.",
1303                  "logfile = ./server.log", VAR_STRING, VAR_GLOBAL|VAR_SITE);
1304     register_var("full-bounce", "no", "SMTP",
1305                  "Should bounces contain the full message or only the headers.",
1306                  "full-bounce = false", VAR_BOOL, VAR_ALL);
1307     register_var("error-include-queue", "yes", "Error Handling",
1308                  "Should error reports contain the queue associated with that run",
1309                  "error-include-queue = yes", VAR_BOOL, VAR_ALL);
1310     register_var("address-failure", "no", NULL, NULL, NULL,
1311                  VAR_BOOL, VAR_TEMP|VAR_INTERNAL);
1312     register_var("moderate-quiet","no",NULL,NULL,NULL,
1313                  VAR_BOOL, VAR_TEMP|VAR_INTERNAL);
1314     register_var("moderate-include-queue", "no", "Moderation",
1315                  "Should moderated messages contain the full message that triggered moderation?",
1316                  "moderate-include-queue = yes", VAR_BOOL, VAR_ALL);
1317     register_var("moderate-verbose-subject", "yes", "Moderation",
1318                  "Should moderated messages have a more informative subject?",
1319                  "moderate-verbose-subject = yes", VAR_BOOL, VAR_ALL);
1320     buffer_printf(buf, sizeof(buf) - 1, "%s.hlp", SERVICE_NAME_LC);
1321     register_var("no-command-file", buf, "Files",
1322                  "This is a global file to send if a message to the main listserver or request address has no commands.",
1323                  "no-command-file = helpfile",VAR_STRING, VAR_GLOBAL|VAR_SITE);
1324     register_var("submodes-file", "submodes", "Files",
1325                  "File containing list specific customized subscription modes.",
1326                  "submodes-file = submodes", VAR_STRING, VAR_ALL);
1327     register_var("submodes-mode", NULL, NULL, NULL, NULL, VAR_STRING,
1328                  VAR_TEMP|VAR_INTERNAL);
1329     register_var("rabid-mime","no","MIME",
1330                  "Should ABSOLUTELY no attachments, EVEN text/plain, be allowed",
1331                  "rabid-mime = no",VAR_BOOL,VAR_ALL);
1332     register_var("smtp-socket", "25", "SMTP",
1333                  "Which socket should the SMTP server be contacted on.",
1334                  "smtp-socket = 26", VAR_INT, VAR_SITE|VAR_GLOBAL);
1335     register_var("cgi-template-dir", "<$listserver-data>/templates",
1336                  "CGI", "Directory for CGI gateway templates.",
1337                  "cgi-template-dir = <$listserver-data>/templates",
1338                  VAR_STRING, VAR_ALL);
1339     register_var("version-file", "<$lists-root>/SITEDATA/version", NULL, NULL,
1340                  NULL, VAR_STRING, VAR_GLOBAL|VAR_INTERNAL);
1341     register_var("verbose-moderate-fail", "yes", "Moderation",
1342                  "When a moderator approves a message but it is rejected, should the message in question be included in the rejection note?",
1343                  "verbose-moderate-fail = yes", VAR_BOOL, VAR_GLOBAL|VAR_SITE|VAR_LIST);
1344 
1345     register_var("assume-lists-valid", "no", "Misc",
1346                  "Should we assume that all list directories are valid or should we perform checks",
1347                  "assume-lists-valid = yes", VAR_BOOL, VAR_GLOBAL|VAR_SITE);
1348     register_var("expire-all-cookies","yes","Cookies",
1349                  "Should we expire cookies for all lists on initial run? Should only be set to 'no' on installations with a huge (multi-thousand) number of lists.",
1350                  "expire-all-cookies = yes", VAR_BOOL, VAR_GLOBAL|VAR_SITE);
1351     register_var("hooktype", NULL, NULL, NULL, NULL, VAR_STRING, VAR_TEMP|VAR_INTERNAL);
1352     register_var("cheatsheet-file", NULL, NULL, NULL, NULL, VAR_STRING, VAR_TEMP|VAR_INTERNAL);
1353     register_var("stocksend-extra-headers", NULL, NULL, NULL, NULL, VAR_STRING, VAR_TEMP|VAR_INTERNAL);
1354     register_var("form-show-listname", "no", "Misc",
1355                  "Should we use the list name (or RFC2369 name) instead of the listserver full name for forms on a per-list basis?  (Like admin wrappers and such.)",
1356                  "form-show-listname = yes", VAR_BOOL, VAR_GLOBAL|VAR_SITE|VAR_LIST);
1357     register_var("lock-to-user", SERVICE_NAME_LC, "Basic Configuration",
1358                  "What is the name of the user whose UID/GID we should lock to if we're run as root?",
1359                  "lock-to-user = ecartis", VAR_STRING, VAR_GLOBAL|VAR_INTERNAL);
1360     register_var("liscript-allow-explicit-list", "false", NULL, NULL, NULL,
1361                  VAR_BOOL, VAR_TEMP|VAR_INTERNAL);
1362     register_var("pwdfile", "<$lists-root>/SITEDATA/site-passwords",
1363                  "Files", "Path to the file containing sitewide passwords (used by web interface and others).",
1364                  "pwdfile = SITEDATA/passwd", VAR_STRING, VAR_GLOBAL|VAR_INTERNAL);
1365     register_var("hostname", NULL, "Basic Configuration",
1366                  "Hostname for URLs/addresses/headers",
1367                  "hostname = lists.mydomain.com", VAR_STRING,
1368                  VAR_GLOBAL|VAR_SITE);
1369 
1370     register_var("smtp-blind-blast", "false", "SMTP",
1371                  "Should Ecartis perform all SMTP operations without regard for result codes (e.g. trust that it was delivered)?  NOT RECOMMENDED.",
1372                  "smtp-blind-blast = yes", VAR_BOOL,
1373                  VAR_GLOBAL|VAR_SITE);
1374     register_var("smtp-retry-forever", "false", "SMTP",
1375                  "Should Ecartis continue to wait for SMTP responses for an infinite amount of time, e.g. never give up?  This can negatively impact delivery times.",
1376                  "smtp-retry-forever = yes", VAR_BOOL,
1377                  VAR_GLOBAL|VAR_SITE);
1378     register_var("headers-charset", NULL, "Global",
1379                  "Charset used in headers", "headers-charset = ISO-8859-1",
1380                  VAR_STRING, VAR_GLOBAL|VAR_SITE|VAR_LIST|VAR_INTERNAL);
1381     register_var("headers-charset-frombody", NULL, "Global",
1382                  "Charset used in body, possibly header", "headers-charset-frombody = ISO-8859-1",
1383                  VAR_STRING, VAR_GLOBAL|VAR_SITE|VAR_LIST|VAR_INTERNAL|VAR_TEMP);
1384 
1385     register_var("copy-requests-to", NULL, "Misc",
1386                  "If set, all user request results will be sent to this address.  Useful for debugging.",
1387                  "copy-requests-to = <$list>-admins@<$hostname>",
1388                  VAR_STRING, VAR_GLOBAL|VAR_SITE|VAR_LIST);
1389 }
1390 
check_duration(const char * d)1391 int check_duration(const char *d)
1392 {
1393     if(!d)
1394         return 0;
1395     while(*d) {
1396         while (*d && isspace((int)(*d))) d++;
1397         while (*d && isdigit((int)(*d))) d++;
1398         while (*d && isspace((int)(*d))) d++;
1399         switch(*d) {
1400             case 'd':
1401             case 'h':
1402             case 'm':
1403             case 's':
1404                 d++; break;
1405             default:
1406                 if(isspace((int)*d)) break;
1407                 else return 0;
1408         }
1409     }
1410     return 1;
1411 }
1412 
write_cheatsheet(const char * filename,const char * sortorder)1413 void write_cheatsheet(const char *filename, const char *sortorder)
1414 {
1415     /* Eventually, sortorder will provide a way to sort sections.  NYI */
1416     FILE *ofile = open_exclusive(filename, "w");
1417     int i;
1418     int count = 0, num = 0;
1419     struct var_data **arr = NULL;
1420     char *lastsection;
1421 
1422     log_printf(1, "Writing variable cheatsheet file '%s'\n", filename);
1423 
1424     if(!ofile) {
1425         log_printf(0, "Unable to open cheatsheet file '%s' for writing.\n",
1426                    filename);
1427         return;
1428     }
1429 
1430     count = count_all_vars();
1431 
1432     arr = (struct var_data **)malloc(sizeof(struct var_data *)*count);
1433     if(!arr) {
1434         log_printf(0, "Unable to allocate memory for sorting.\n");
1435         close_file(ofile);
1436         return;
1437     }
1438 
1439     for (i = 0; i < HASHSIZE; i++) {
1440         struct var_data *tmp = listdata->bucket[i];
1441         while(tmp) {
1442             if(!(tmp->flags & VAR_INTERNAL)) {
1443                 arr[num++] = tmp;
1444             }
1445             tmp = tmp->next;
1446         }
1447     }
1448     _sortorder = sortorder;
1449     qsort(arr, count, sizeof(struct var_data *), var_cmp);
1450     _sortorder = NULL;
1451 
1452     lastsection = NULL;
1453 
1454     write_file(ofile,"<title>%s Variable Reference</title>\n\n",
1455             SERVICE_NAME_MC);
1456 
1457     write_file(ofile,"<P><font size=+3>%s %s Variable Reference</font></P>\n\n",
1458         SERVICE_NAME_MC, VER_PRODUCTVERSION_STR);
1459 
1460     write_file(ofile,"<P>\n<i><b>Note</b>: The 'valid' field describes the %s config files\n", SERVICE_NAME_MC);
1461     write_file(ofile,"where that variable is valid.  'G' means the global config file, 'V' means\n");
1462     write_file(ofile,"a virtual host configuration file, and 'L' means individual list files.\n</P>\n");
1463 
1464     write_file(ofile,"<table border=1 width=100%%>\n");
1465 
1466     for(i = 0; i < count; i++) {
1467         char *desc = arr[i]->description;
1468 
1469         write_file(ofile,"\t<tr>\n");
1470 
1471         if (lastsection) {
1472            if(!arr[i]->section) {
1473               free(lastsection);
1474               lastsection = NULL;
1475               write_file(ofile,"\t\t<td colspan=4><font size=+2>Miscellaneous Settings</font></td>\n\t</tr>\n\t<tr>\n");
1476               write_file(ofile,"\t<tr>\n\t<td><b>Variable</b></td><td><b>Type</b></td><td><b>Valid</b></td><td><b>Description</b></td>\n\t</tr>\n<tr>");
1477            } else if(strcasecmp(arr[i]->section,lastsection) != 0) {
1478               free(lastsection);
1479               lastsection = upperstr(arr[i]->section);
1480 
1481               write_file(ofile,"\t\t<td colspan=4><font size=+2>%s</font></td>\n\t</tr>\n\t<tr>\n", lastsection);
1482               write_file(ofile,"\t<tr>\n\t<td><b>Variable</b></td><td><b>Type</b></td><td><b>Valid</b></td><td><b>Description</b></td>\n\t</tr>\n<tr>");
1483            }
1484         } else {
1485            if (arr[i]->section) {
1486               lastsection = upperstr(arr[i]->section);
1487 
1488               write_file(ofile,"\t\t<td colspan=4><font size=+2>%s</font></td>\n\t</tr>\n", lastsection);
1489               write_file(ofile,"\t<tr>\n\t<td><b>Variable</b></td><td><b>Type</b></td><td><b>Valid</b></td><td><b>Description</b></td>\n\t</tr>\n<tr>");
1490            }
1491         }
1492 
1493         write_file(ofile,"\t\t<td valign=top><b>%s</b></td>\n", arr[i]->name);
1494 
1495         write_file(ofile,"\t\t<td valign=top>");
1496         switch (arr[i]->type) {
1497             case VAR_STRING:
1498                 write_file(ofile,"string");
1499                 break;
1500             case VAR_BOOL:
1501                 write_file(ofile,"boolean");
1502                 break;
1503             case VAR_INT:
1504                 write_file(ofile,"integer");
1505                 break;
1506             case VAR_DURATION:
1507                 write_file(ofile,"duration");
1508                 break;
1509             case VAR_DATA:
1510                 write_file(ofile,"data");
1511                 break;
1512             case VAR_TIME:
1513                 write_file(ofile,"time");
1514                 break;
1515             case VAR_CHOICE:
1516                 write_file(ofile,"choice");
1517                 break;
1518             default:
1519                 write_file(ofile,"<i>unknown</i>");
1520         }
1521         write_file(ofile,"</td>\n");
1522 
1523         write_file(ofile,"\t\t<td valign=top>\n\t\t\t");
1524 
1525         if (arr[i]->flags & VAR_GLOBAL) {
1526             write_file(ofile,"G");
1527         }
1528         if (arr[i]->flags & VAR_SITE) {
1529             write_file(ofile,"V");
1530         }
1531         if (arr[i]->flags & VAR_LIST) {
1532             write_file(ofile,"L");
1533         }
1534 
1535         write_file(ofile,"\n\t\t</td>\n\t\t<td>\n\t\t\t");
1536         if (desc) {
1537             write_file(ofile,"<P>");
1538             while(*desc) {
1539                 if (*desc == '<')
1540                     write_file(ofile,"&lt;");
1541                 else if (*desc == '>')
1542                     write_file(ofile,"&gt;");
1543                 else
1544                     write_file(ofile,"%c", *desc);
1545 
1546                 desc++;
1547             }
1548             write_file(ofile,"</P>\n\t\t\t");
1549         } else
1550             write_file(ofile,"<P><i>No description</i></P>\n\t\t\t");
1551 
1552         if (arr[i]->example)
1553            write_file(ofile,"Example:<BR>&nbsp;&nbsp;<code>%s</code><BR><BR>\n",
1554                 arr[i]->example);
1555 
1556         if (arr[i]->defval) {
1557             if (arr[i]->type == VAR_BOOL) {
1558                 write_file(ofile,"Default value is <code>%s</code><BR>\n",
1559                    atoi(arr[i]->defval) ? "true" : "false");
1560             } else if (arr[i]->type == VAR_CHOICE) {
1561                 char tempstr[SMALL_BUF];
1562                 char *tempptr;
1563 
1564                 buffer_printf(tempstr, sizeof(tempstr) - 1, "%s", arr[i]->defval);
1565                 tempptr = strchr(tempstr,':');
1566                 if (tempptr) *tempptr = 0;
1567 
1568                 write_file(ofile,"Default value is <code>%s</code><BR>\n",
1569                    tempstr);
1570             } else {
1571                 write_file(ofile,"Default value is <code>%s</code><BR>\n",
1572                    arr[i]->defval);
1573             }
1574         }
1575 
1576         write_file(ofile,"\t\t</td>\n\t</tr>\n");
1577     }
1578     write_file(ofile,"</table>\n");
1579     close_file(ofile);
1580 }
1581