1 #include <stdio.h>
2 #include <stdarg.h>
3 #include <time.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 
8 #ifdef IRIX_IS_CRAP
9 #include <poll.h>
10 #endif
11 
12 #ifndef WIN32
13 #include <sys/time.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <netdb.h>
18 #include <pwd.h>
19 
20 /* Fix for brain-dead IRIX system headers */
21 #undef EX_OK
22 
23 #include <sysexits.h>
24 #endif
25 
26 #include "alias.h"
27 #include "list.h"
28 #include "config.h"
29 #include "command.h"
30 #include "parse.h"
31 #include "init.h"
32 #include "forms.h"
33 #include "flag.h"
34 #include "file.h"
35 #include "regexp.h"
36 #include "smtp.h"
37 #include "lpm-mods.h"
38 #include "fileapi.h"
39 #include "variables.h"
40 #include "core.h"
41 #include "cookie.h"
42 #include "cmdarg.h"
43 #include "internal.h"
44 #include "modes.h"
45 #include "mysignal.h"
46 #include "hooks.h"
47 #include "user.h"
48 #include "tolist.h"
49 #include "compat.h"
50 #include "unmime.h"
51 #include "liscript.h"
52 #include "mystring.h"
53 #include "submodes.h"
54 #include "lcgi.h"
55 #include "funcparse.h"
56 #include "trust.h"
57 
58 #ifdef WIN32
59 extern void sock_init();
60 #endif
61 
62 #ifdef _AIX
63 extern int sys_nerr;
64 extern char *sys_errlist[];
65 #endif
66 
67 #ifdef DEC_UNIX_USLEEP
68 /* We need to give a function definition for usleep, or else
69  * DEC's compiler barfs.  This is the old (pre-ANSI) version */
70 extern void usleep __((unsigned int));
71 #endif /* DEC_UNIX_USLEEP */
72 
73 int messagecnt;
74 
75 extern void build_lpm_api();
76 
77 char pathname[BIG_BUF];
78 const char def_err[] = "Could not resolve error.";
79 
build_hostname(char * buffer,int len)80 void build_hostname(char *buffer, int len)
81 {
82   char *hostnameholder;
83   struct hostent *he;
84   int i;
85 
86   hostnameholder = (char *)malloc(len);
87   if (!hostnameholder) {
88     buffer_printf(buffer, len - 1, "unknown-hostname");
89 	return;
90   }
91 
92   if (gethostname(hostnameholder, len) == -1) {
93     /* gethostname() failed, just put something there. */
94     buffer_printf(buffer, len - 1, "unknown-hostname");
95 	free(hostnameholder);
96     return;
97   }
98 
99   buffer_printf(buffer, len - 1, "%s", hostnameholder);
100 
101   if (strchr(hostnameholder, '.')) {
102     /* Looks FQDN already; just return. */
103     return;
104   }
105   he = gethostbyname(hostnameholder);
106   free(hostnameholder);
107   if (!he) {
108     /* Couldn't look it up; do a best guess. */
109     return;
110   }
111 
112   buffer_printf(buffer, len - 1, "%s", he->h_name);
113   if (! strchr(buffer, '.')) {
114     for (i = 0; he->h_aliases[i]; i++) {
115       if (strchr(he->h_aliases[i], '.')) {
116         buffer_printf(buffer, len - 1, "%s", he->h_aliases[i]);
117         return;
118       }
119     }
120   }
121 }
122 
123 /* Sends a textfile (filename) to address in addy. */
send_textfile(const char * addy,const char * filename)124 int send_textfile(const char *addy, const char *filename)
125 {
126     FILE *myfile;
127     char inbuf[BIG_BUF];
128 
129     if (!exists_file(filename)) {
130         log_printf(3, "File '%s' doesn't exist\n", filename);
131         return 0;
132     }
133 
134     if ((myfile = open_file(filename,"r")) == NULL) {
135         log_printf(3, "Unable to open file %s\n", filename);
136         return 0;
137     }
138 
139     if(!task_heading(addy))
140         return 0;
141 
142     if (strcmp(get_string("realsender"),addy) && get_bool("adminmode") &&
143         get_bool("admin-actions-shown")) {
144         smtp_body_text(">> File sent due to actions of administrator ");
145         smtp_body_line(get_string("realsender"));
146         smtp_body_line("");
147     }
148 
149     while(read_file(inbuf, sizeof(inbuf), myfile)) {
150         smtp_body_text(inbuf);
151     }
152 
153     close_file(myfile);
154 
155     task_ending();
156     return 1;
157 }
158 
159 /* Sends a textfile (filename) to address in addy. */
160 /* Performs variable expansion */
send_textfile_expand(const char * addy,const char * filename)161 int send_textfile_expand(const char *addy, const char *filename)
162 {
163     FILE *myfile;
164     char inbuf[BIG_BUF];
165     char outfilename[BIG_BUF];
166 
167     if (!exists_file(filename)) {
168         log_printf(3, "File '%s' doesn't exist\n", filename);
169         return 0;
170     }
171 
172     buffer_printf(outfilename, sizeof(outfilename) - 1, "%s.expand", get_string("queuefile"));
173 
174     if (!liscript_parse_file(filename,outfilename)) {
175         log_printf(3, "Unable to Liscript expand file %s\n", filename);
176         return 0;
177     }
178 
179     if ((myfile = open_file(outfilename,"r")) == NULL) {
180         log_printf(3, "Unable to open decoded Liscript file %s\n",
181           outfilename);
182         unlink_file(outfilename);
183         return 0;
184     }
185 
186     if(!task_heading(addy))
187         return 0;
188 
189     if (strcmp(get_string("realsender"),addy) && get_bool("adminmode") &&
190         get_bool("admin-actions-shown")) {
191         smtp_body_text(">> File sent due to actions of administrator ");
192         smtp_body_line(get_string("realsender"));
193         smtp_body_line("");
194     }
195 
196     while(read_file(inbuf, sizeof(inbuf), myfile)) {
197         smtp_body_text(inbuf);
198     }
199 
200     close_file(myfile);
201     unlink_file(outfilename);
202 
203     task_ending();
204     return 1;
205 }
206 
207 /* Appends the Liscript file */
expand_append(const char * mainfile,const char * liscript)208 int expand_append(const char *mainfile, const char *liscript)
209 {
210     char outfilename[BIG_BUF];
211 
212     if (!exists_file(mainfile)) {
213         log_printf(3, "File '%s' doesn't exist\n", mainfile);
214         return 0;
215     }
216 
217     buffer_printf(outfilename, sizeof(outfilename) - 1, "%s.expand", get_string("queuefile"));
218 
219     log_printf(9,"Expanding %s to %s\n", liscript, outfilename);
220 
221     if (!liscript_parse_file(liscript,outfilename)) {
222         log_printf(3, "Unable to Liscript expand file %s\n", liscript);
223         return 0;
224     }
225 
226     if (!exists_file(outfilename)) {
227        log_printf(3, "Unable to read expanded Liscript file.");
228        return 0;
229     }
230 
231     append_file(mainfile,outfilename);
232     unlink_file(outfilename);
233 
234     return 1;
235 }
236 
237 /* Sends a textfile (filename) to address in addy.
238    Performs variable expansion.  Appends the queuefile if the
239    third variable is true. */
send_textfile_expand_append(const char * addy,const char * filename,int includequeue)240 int send_textfile_expand_append(const char *addy, const char *filename,
241     int includequeue)
242 {
243     FILE *myfile;
244     char inbuf[BIG_BUF];
245     char outfilename[BIG_BUF];
246 
247     if (!exists_file(filename)) {
248         log_printf(3, "File '%s' doesn't exist\n", filename);
249         return 0;
250     }
251 
252     buffer_printf(outfilename, sizeof(outfilename) - 1, "%s.expand", get_string("queuefile"));
253 
254     if (!liscript_parse_file(filename,outfilename)) {
255         log_printf(3, "Unable to Liscript expand file %s\n", filename);
256         return 0;
257     }
258 
259 
260     if ((myfile = open_file(outfilename,"r")) == NULL) {
261         log_printf(3, "Unable to open decoded Liscript file %s\n",
262           outfilename);
263         unlink_file(outfilename);
264         return 0;
265     }
266 
267     if(!task_heading(addy))
268         return 0;
269 
270     if (strcmp(get_string("realsender"),addy) && get_bool("adminmode") &&
271         get_bool("admin-actions-shown")) {
272         smtp_body_text(">> File sent due to actions of administrator ");
273         smtp_body_line(get_string("realsender"));
274         smtp_body_line("");
275     }
276 
277     while(read_file(inbuf, sizeof(inbuf), myfile)) {
278         smtp_body_text(inbuf);
279     }
280 
281     close_file(myfile);
282     unlink_file(outfilename);
283 
284     if (includequeue) {
285        if ((myfile = open_file(get_string("queuefile"),"r")) != NULL) {
286           smtp_body_line("");
287           smtp_body_line("--- Original mail message ---");
288           while(read_file(inbuf, sizeof(inbuf), myfile)) {
289              smtp_body_text(inbuf);
290           }
291           close_file(myfile);
292        }
293     }
294 
295     task_ending();
296     return 1;
297 }
298 
299 
300 /* Inserts a textfile into the results list */
301 /* Performs variable expansion */
insert_textfile_expand(const char * filename)302 int insert_textfile_expand(const char *filename)
303 {
304     FILE *myfile;
305     char inbuf[BIG_BUF];
306     char outfilename[BIG_BUF];
307 
308     if (!exists_file(filename)) {
309         log_printf(3, "File '%s' doesn't exist\n", filename);
310         return 0;
311     }
312 
313     buffer_printf(outfilename, sizeof(outfilename) - 1, "%s.expand", get_string("queuefile"));
314 
315     if (!liscript_parse_file(filename,outfilename)) {
316         log_printf(3, "Unable to Liscript expand file %s\n", filename);
317         return 0;
318     }
319 
320     if ((myfile = open_file(outfilename,"r")) == NULL) {
321         log_printf(3, "Unable to open decoded Liscript file %s\n",
322           outfilename);
323         unlink_file(outfilename);
324         return 0;
325     }
326 
327     while(read_file(inbuf, sizeof(inbuf), myfile)) {
328         result_printf("%s",inbuf);
329     }
330 
331     close_file(myfile);
332     unlink_file(outfilename);
333 
334     return 1;
335 }
336 
337 /* Spits to the result file.  Used by commands for output in user
338  * jobs.  Handles nice word-wrapping, too. */
spit_status(const char * statustext,...)339 void spit_status(const char *statustext, ...)
340 {
341     va_list vargs;
342     char buf[BIG_BUF];
343     int col = 0;
344     char *tmp;
345     FILE *perrfile;
346 
347     buffer_printf(buf, sizeof(buf) - 1, "%s.perr", get_string("queuefile"));
348     if ((perrfile = open_file(buf,"a")) != NULL) {
349         buffer_printf(buf, sizeof(buf) - 1, "%s", get_string("cur-parse-line"));
350         tmp = buf;
351         col = 0;
352         while(*tmp) {
353             if (col == 0) {
354                 write_file(perrfile, "\n>> ");
355             }
356             if (((*tmp == ' ') && (col > 65)) || (*tmp == '\n')) {
357                 col = 0;
358                 tmp++;
359             } else {
360                 write_file(perrfile, "%c", *tmp);
361                 tmp++; col++;
362             }
363         }
364         write_file(perrfile, "\n");
365         va_start(vargs, statustext);
366         vsprintf(buf, statustext, vargs);
367         va_end(vargs);
368         tmp = buf;
369         col = 0;
370         while(*tmp) {
371             if (((*tmp == ' ') && (col > 65)) || (*tmp == '\n')) {
372                 col = 1;
373                 write_file(perrfile,"\n");
374                 tmp++;
375             } else {
376                 write_file(perrfile, "%c", *tmp);
377                 tmp++; col++;
378             }
379         }
380         write_file(perrfile, "\n");
381         close_file(perrfile);
382     }
383 }
384 
385 /* Quotes last command, used by status/error outputs. */
quote_command()386 void quote_command()
387 {
388     char outbuf[BIG_BUF];
389 
390     buffer_printf(outbuf, sizeof(outbuf) - 1, ">> %s", get_string("cur-parse-line"));
391     smtp_body_line(outbuf);
392 }
393 
394 /* Handles a filesystem error on filename.  Call IMMEDIATELY after
395  * a filesys error to make sure errno is correct. */
filesys_error(const char * filename)396 void filesys_error(const char *filename)
397 {
398     char outbuf[BIG_BUF];
399     int lasterr;
400 
401     lasterr = errno;
402 
403     spit_status("Unable to process request due to filesystem error.");
404     if(!error_heading())
405         return;
406     buffer_printf(outbuf, sizeof(outbuf) - 1, "    List: %s", get_string("list"));
407     smtp_body_line(outbuf);
408     buffer_printf(outbuf, sizeof(outbuf) - 1, "    User: %s", get_string("fromaddress"));
409     smtp_body_line(outbuf);
410     buffer_printf(outbuf, sizeof(outbuf) - 1, "  Action: %s", get_string("cur-parse-line"));
411     smtp_body_line(outbuf);
412     buffer_printf(outbuf, sizeof(outbuf) - 1, "    File: %s", filename);
413     smtp_body_line(outbuf);
414     buffer_printf(outbuf, sizeof(outbuf) - 1, "   Error: %s", resolve_error(lasterr));
415     smtp_body_line(outbuf);
416     if (get_bool("error-include-queue")) {
417        FILE *infile;
418        char filebuf[BIG_BUF];
419 
420        smtp_body_line("-- queuefile in error --");
421        if ((infile = open_file(get_string("queuefile"),"r")) != NULL) {
422          while (read_file(filebuf, sizeof(filebuf), infile)) {
423             smtp_body_text(filebuf);
424          }
425          close_file(infile);
426        } else {
427          smtp_body_line("<< NO QUEUEFILE! >>");
428        }
429     }
430     error_ending();
431 }
432 
433 /* Handles an internal error. */
internal_error(const char * message)434 void internal_error(const char *message)
435 {
436     char outbuf[BIG_BUF];
437 
438     spit_status("Unable to process request due to internal error.");
439     if(!error_heading())
440         return;
441     buffer_printf(outbuf, sizeof(outbuf) - 1, "    User: %s", get_string("fromaddress"));
442     smtp_body_line(outbuf);
443     buffer_printf(outbuf, sizeof(outbuf) - 1, "   Error: %s", message);
444     smtp_body_line(outbuf);
445     if (get_bool("error-include-queue")) {
446        FILE *infile;
447        char filebuf[BIG_BUF];
448 
449        smtp_body_line("-- queuefile in error --");
450        if ((infile = open_file(get_string("queuefile"),"r")) != NULL) {
451          while (read_file(filebuf, sizeof(filebuf), infile)) {
452             smtp_body_text(filebuf);
453          }
454          close_file(infile);
455        } else {
456          smtp_body_line("<< NO QUEUEFILE! >>");
457        }
458     }
459     error_ending();
460 }
461 
462 /* Handles regex matching.  Used by blacklisting and other functions. */
match_reg(const char * pattern,const char * match)463 int match_reg(const char *pattern, const char *match)
464 {
465     int result;
466     regexp *treg;
467 
468     result = 0;
469 
470     if (!*pattern)
471         return 0;
472 
473     treg = regcomp((char *)pattern);
474     if (treg) {
475         result = regexec(treg, (char *)match);
476         free(treg);
477     }
478 
479     return result;
480 }
481 
482 /* Checks if an address is blacklisted either in current list
483  * (if there is one), or globally. */
blacklisted(const char * address)484 int blacklisted(const char *address)
485 {
486     FILE *blacklist;
487     char filebuf[BIG_BUF];
488 
489     /* This is where the checks against global and local banlists go */
490 
491     if (get_var("global-blacklist")) {
492        buffer_printf(filebuf, sizeof(filebuf) - 1, "%s/%s", get_string("listserver-conf"),
493             get_string("global-blacklist"));
494 
495        if ((blacklist = open_file(filebuf,"r")) != NULL) {
496            while(read_file(filebuf, sizeof(filebuf), blacklist)) {
497                if(filebuf[strlen(filebuf) - 1] == '\n')
498                    filebuf[strlen(filebuf) - 1] = '\0';
499                if (match_reg(filebuf,address)) {
500                    close_file(blacklist);
501                    return 1;
502                }
503            }
504            close_file(blacklist);
505        }
506     }
507 
508     if (get_var("list") && get_var("blacklist-mask")) {
509         listdir_file(filebuf, get_string("list"), get_string("blacklist-mask"));
510         if ((blacklist = open_file(filebuf,"r")) != NULL) {
511             while(read_file(filebuf, sizeof(filebuf), blacklist)) {
512                 if(filebuf[strlen(filebuf) - 1] == '\n')
513                     filebuf[strlen(filebuf) - 1] = '\0';
514                 if(match_reg(filebuf,address)) {
515                     close_file(blacklist);
516                     return 1;
517                 }
518             }
519             close_file(blacklist);
520         }
521     }
522     return 0;
523 }
524 
525 /* One of the most oft-called functions.  Handles output to the log.
526  * the 'level' is the debug level that must be met before the message
527  * will be printed. */
log_printf(int level,char * format,...)528 void log_printf(int level, char *format, ...)
529 {
530     static int inlogfunc = 0;  /* Prevents infinite loops due to reentry */
531     va_list vargs;
532     char mybuf[HUGE_BUF];
533     FILE *logfile = NULL;
534     const char *logfilename;
535 
536     if (inlogfunc) {
537 #ifdef DEBUG
538         fprintf(stderr, "Warning: attempted to re-enter log_printf\n");
539 #endif
540         return;
541     }
542     inlogfunc = 1;
543 
544     /* Sanity check! */
545     logfilename = get_var("logfile");
546     if (!logfilename) {
547     	inlogfunc = 0;
548     	return;
549     }
550 
551     /* Are we an absolute path? */
552     if (*logfilename == '/')
553         buffer_printf(mybuf, sizeof(mybuf) - 1, "%s", get_string("logfile"));
554     else {
555         const char *listdatadir;
556 
557         /* Sanity check! */
558         listdatadir = get_var("listserver-data");
559         if (!listdatadir) {
560             inlogfunc = 0;
561             return;
562         }
563 
564         buffer_printf(mybuf, sizeof(mybuf) - 1, "%s/%s", listdatadir, logfilename);
565     }
566 
567     if(level > get_number("debug")) {
568         inlogfunc = 0;
569         return;
570     }
571 
572     logfile = open_file(mybuf, "a");
573 
574     if (logfile) {
575         time_t now;
576         struct tm *tm_now;
577 
578         time(&now);
579 
580         tm_now = localtime(&now);
581 
582         strftime(mybuf, sizeof(mybuf) - 1,"[%m/%d/%Y-%H:%M:%S] ",tm_now);
583 
584         write_file(logfile, "%s", mybuf);
585 
586 #ifndef WIN32
587         write_file(logfile, "[%d] ", (int)getpid());
588 #endif
589 
590         va_start(vargs, format);
591         vsprintf(mybuf, format, vargs);
592         write_file(logfile, "%s", mybuf);
593 #ifdef DEBUG
594         fprintf(stderr, "%s", mybuf);
595 #endif
596         va_end(vargs);
597         close_file(logfile);
598     }
599     inlogfunc = 0;
600 }
601 
debug_printf(char * format,...)602 void debug_printf(char *format, ...)
603 {
604 #ifdef DEBUGCONSOLE
605     va_list vargs;
606     char mybuf[BIG_BUF];
607     time_t now;
608     struct tm *tm_now;
609 
610     time(&now);
611 
612     tm_now = localtime(&now);
613 
614     strftime(mybuf, sizeof(mybuf) - 1,"[%m/%d/%Y-%H:%M:%S] ",tm_now);
615 
616     fprintf(stderr, mybuf);
617 
618     va_start(vargs, format);
619     vsprintf(mybuf, format, vargs);
620     fprintf(stderr,"%s", mybuf);
621     va_end(vargs);
622 
623     fflush(stderr);
624 #endif
625 }
626 
result_append(const char * filename)627 void result_append(const char *filename)
628 {
629    char mybuf[BIG_BUF];
630 
631    buffer_printf(mybuf, sizeof(mybuf) - 1, "%s.perr", get_string("queuefile"));
632    append_file(mybuf,filename);
633 }
634 
635 /* Handles printf out to the results file for a user job.
636  * Used internally by spit_status, and in a few other places. */
result_printf(char * format,...)637 void result_printf(char *format, ...)
638 {
639     va_list vargs;
640     char mybuf[BIG_BUF];
641     FILE *perrfile;
642 
643     buffer_printf(mybuf, sizeof(mybuf) - 1, "%s.perr", get_string("queuefile"));
644     if ((perrfile = open_file(mybuf,"a")) != NULL) {
645         va_start(vargs, format);
646         vsprintf(mybuf, format, vargs);
647         write_file(perrfile, "%s", mybuf);
648         va_end(vargs);
649         close_file(perrfile);
650     }
651 }
652 
653 /* Generates the queue identifier for this session.  Guaranteed
654  * unique, as it's based off timestamp and pid. */
generate_queue()655 int generate_queue()
656 {
657     time_t now;
658     char tbuf[SMALL_BUF];
659 
660     time(&now);
661 #ifdef OBSDMOD
662     buffer_printf(tbuf, sizeof(tbuf) - 1, "%s/queue/%lX%d", get_string("listserver-data"), (long)now, (int)getpid());
663 #else
664 # if DEC_UNIX || _AIX
665     buffer_printf(tbuf, sizeof(tbuf) - 1, "%s/queue/%X%d", get_string("listserver-data"), now, (int)getpid());
666 # else /* ! DEC_UNIX */
667     buffer_printf(tbuf, sizeof(tbuf) - 1, "%s/queue/%lX%d", get_string("listserver-data"), now, (int)getpid());
668 # endif
669 #endif
670 
671     set_var("queuefile", &tbuf[0], VAR_GLOBAL);
672     return 1;
673 }
674 
675 /* Generates the queue identifier for this session.  Guaranteed
676  * unique, as it's based off timestamp and pid. */
init_queuefile()677 int init_queuefile()
678 {
679     FILE *tqueuefile;
680     int temp;
681 
682     if (!get_bool("fakequeue")) {
683        if ((tqueuefile = open_file(get_string("queuefile"),"w")) == NULL)
684           return 0;
685 
686        if (!get_var("listserver-infile")) {
687           while((temp = fgetc(stdin)) != EOF) {
688               putc_file((char)temp, tqueuefile);
689           }
690        } else {
691           FILE *infile;
692 
693           if (!exists_file(get_string("listserver-infile"))) {
694               log_printf(1,"Unable to open input file '%s'\n", get_string("listserver-infile"));
695               close_file(tqueuefile);
696               return 0;
697           }
698 
699           if ((infile = open_file(get_string("listserver-infile"),"r")) == NULL) {
700               log_printf(1,"Unable to open input file '%s'\n", get_string("listserver-infile"));
701               close_file(tqueuefile);
702               return 0;
703           }
704 
705           while((temp = fgetc(infile)) != EOF) {
706               putc_file((char)temp, tqueuefile);
707           }
708           close_file(infile);
709        }
710 
711        close_file(tqueuefile);
712     }
713 
714     return 1;
715 }
716 
717 /* Initializes listserver. */
init_listserver()718 void init_listserver()
719 {
720     time_t now;
721 
722     time(&now);
723 
724     messagecnt = 1;
725 
726     chdir(pathname);
727 
728     init_aliases();
729     init_vars();
730     new_cookies();
731     init_regvars();
732     set_var("path", pathname, VAR_GLOBAL);
733     set_var("listserver-root", pathname, VAR_GLOBAL);
734     set_var("global-pass", "yes", VAR_TEMP);
735     read_conf(GLOBAL_CFG_FILE, VAR_GLOBAL);
736     clean_var("global-pass", VAR_TEMP);
737     if(!get_var("listserver-modules")) {
738         char tmp[BIG_BUF];
739         buffer_printf(tmp, sizeof(tmp) - 1, "%s/modules", get_string("listserver-root"));
740         set_var("listserver-modules", tmp, VAR_GLOBAL);
741     }
742     if(!get_var("listserver-conf")) {
743         set_var("listserver-conf", get_string("listserver-root"), VAR_GLOBAL);
744     }
745     if(!get_var("listserver-data")) {
746         set_var("listserver-data", get_string("listserver-root"), VAR_GLOBAL);
747     }
748 
749 #ifndef WIN32
750     /* Check to make sure we're running as something OTHER than root.
751        It is bad to be root. */
752     if (!getuid()) {
753        uid_t euid;
754        gid_t egid;
755 
756        log_printf(1,"%s is running as root, attempting to demote.\n", SERVICE_NAME_MC);
757 
758        euid = geteuid(); egid = getegid();
759        if (euid) {
760           log_printf(1,"Effective UID is other than super-user, all we can demote to is that.\n");
761           if (setuid(euid) < 0) {
762              log_printf(0, "%s was unable to demote itself from super-user!\n",
763                 SERVICE_NAME_MC);
764           }
765           setgid(egid);
766        } else {
767           struct passwd *pwd;
768 
769           pwd = NULL;
770           setpwent();
771 
772           pwd = getpwent();
773 
774           while (pwd ? !strcasecmp(pwd->pw_name,get_string("lock-to-user")) :
775               0)
776              pwd = getpwent();
777 
778           if (pwd) {
779              log_printf(1,"Manually demoting to user '%s'\n",
780                 get_string("lock-to-user"));
781              if (setreuid(pwd->pw_uid, pwd->pw_uid) < 0) {
782                 log_printf(0,"Unable to demote to user from superuser!\n");
783              }
784              setregid(pwd->pw_gid, pwd->pw_gid);
785           } else {
786              log_printf(0,"%s is running as root, and cannot demote itself!\n", SERVICE_NAME_MC);
787              log_printf(0,"This is a VERY BAD situation.  Please correct it.\n");
788              log_printf(0,"Make sure there is a '%s' user who owns the %s installation.\n",
789                 get_string("lock-to-user"), SERVICE_NAME_MC);
790           }
791 
792           endpwent();
793        }
794     }
795 #endif
796 
797     log_printf(7, "** INIT ** %s v%s started: %s", SERVICE_NAME_MC,
798                VER_PRODUCTVERSION_STR, ctime(&now));
799     log_printf(7, "Path is: %s\n", pathname);
800 
801     log_printf(7, "Mailserver is: %s\n", get_string("mailserver"));
802 
803 #ifdef WIN32
804     sock_init();
805 #endif
806 
807 }
808 
809 /* Handles finishing listserver. */
finish_listserver()810 void finish_listserver()
811 {
812     time_t now;
813     const char *queue;
814     const char *errfile;
815     char buffer[BIG_BUF], buffer2[BIG_BUF];
816 
817     queue = get_string("queuefile");
818     errfile = get_string("smtp-errors-file");
819 
820     set_var("form-send-as",get_string("listserver-admin"),VAR_TEMP);
821     buffer_printf(buffer, sizeof(buffer) - 1, "%s.perr", queue);
822     if(exists_file(buffer)) {
823         set_var("task-expires","yes",VAR_TEMP);
824         if(!get_var("results-subject-override")) {
825            if(get_var("initial-cmd")) {
826                buffer_printf(buffer2, sizeof(buffer2) - 1, "%s command results: %s", SERVICE_NAME_MC,
827                  get_string("initial-cmd"));
828            } else {
829                buffer_printf(buffer2, sizeof(buffer2) - 1, "%s command results: No commands found",
830                        SERVICE_NAME_MC);
831            }
832         } else {
833            buffer_printf(buffer2, sizeof(buffer2) - 1, "%s: %s", SERVICE_NAME_MC,
834              get_string("results-subject-override"));
835         }
836         set_var("task-form-subject", buffer2, VAR_TEMP);
837         send_textfile(get_string("realsender"), buffer);
838         clean_var("task-form-subject", VAR_TEMP);
839 
840         if(get_var("copy-requests-to")) {
841            buffer_printf(buffer2, sizeof(buffer2) - 1, "%s: %s results",
842                 SERVICE_NAME_MC, get_string("realsender"));
843            set_var("task-form-subject", buffer2, VAR_TEMP);
844            send_textfile(get_string("copy-requests-to"), buffer);
845            clean_var("task-form-subject", VAR_TEMP);
846         }
847 
848         (void)unlink_file(buffer);
849     }
850 
851     set_var("form-send-as",get_string("listserver-admin"),VAR_TEMP);
852     buffer_printf(buffer, sizeof(buffer) - 1, "%s.errs", queue);
853     if(exists_file(buffer)) {
854         buffer_printf(buffer2, sizeof(buffer2) - 1, "%s error report.", SERVICE_NAME_MC);
855         set_var("task-form-subject", buffer2,VAR_TEMP);
856         send_textfile(get_string("listserver-admin"), buffer);
857         clean_var("task-form-subject", VAR_TEMP);
858         (void)unlink_file(buffer);
859     }
860 
861     if (!get_bool("preserve-queue")) (void)unlink_file(queue);
862     if(errfile)
863        (void)unlink_file(errfile);
864 
865     time(&now);
866     log_printf(9, "%s terminated on %s", SERVICE_NAME_MC, ctime(&now));
867 
868     log_printf(9,"Unloading modules...\n");
869     unload_all_modules();
870     nuke_funcs();
871     nuke_cgi_tempvars();
872     nuke_cgi_hooks();
873     nuke_cgi_modes();
874     nuke_submodes();
875     nuke_tolist();
876     nuke_modes();
877     nuke_cmdargs();
878     nuke_commands();
879     nuke_hooks();
880     nuke_flags();
881     nuke_files();
882     nuke_mime_handlers();
883     nuke_cookies();
884 #ifdef DYNMOD
885     nuke_modrefs();
886 #endif
887 #ifdef USE_HITCHING_LOCK
888     nuke_lockfiles();
889 #endif
890     nuke_vars();
891     nuke_aliases();
892 }
893 
main(int argc,char ** argv)894 int main (int argc, char** argv)
895 {
896     char *temp;
897     int errors = 0;
898     int exitearly = 0;
899     int count = 0;
900     char buf[BIG_BUF];
901 
902     buffer_printf(pathname, sizeof(pathname) - 1, "%s", argv[0]);
903     temp = strrchr(pathname, '/');
904 
905 #ifdef WIN32
906     if(!temp)
907          temp = strrchr(pathname, '\\');
908 #endif
909 
910     if (temp)
911         *(temp) = '\0';
912     else
913         buffer_printf(pathname, sizeof(pathname) - 1, ".");
914 
915     argv++;
916 
917     init_signals();
918     init_listserver();
919 
920     new_flags();
921     new_commands();
922     new_hooks();
923     new_files();
924     new_cmdargs();
925     new_modes();
926     new_tolist();
927     new_mime_handlers();
928     new_submodes();
929     new_cgi_hooks();
930     new_cgi_modes();
931     new_cgi_tempvars();
932     new_funcs();
933 
934     init_internal();
935     build_lpm_api();
936 #ifdef DYNMOD
937     log_printf(9,"Preparing to load dynamic modules...\n");
938     init_modrefs();
939 #endif
940     log_printf(9,"Loading modules...\n");
941     load_all_modules();
942     /*
943      * Reload the global config file to pick up any variables defined by
944      * the modules
945      */
946     read_conf(GLOBAL_CFG_FILE, VAR_GLOBAL);
947     log_printf(9,"Initializing modules...\n");
948     init_all_modules();
949 
950     if(!get_var("lists-root")) {
951         buffer_printf(buf, sizeof(buf) - 1, "%s/lists", get_string("listserver-data"));
952         set_var("lists-root", buf, VAR_GLOBAL);
953     } else {
954 		const char *listsroot = get_var_unexpanded("lists-root");
955 
956         /* redirect lists-root to be relative to listserver-data */
957         buffer_printf(buf, sizeof(buf) - 1, "%s/%s", get_string("listserver-data"), listsroot);
958         set_var("lists-root", buf, VAR_GLOBAL);
959     }
960 
961     init_restricted_vars();
962 
963     generate_queue();
964 
965 
966     while(*argv) {
967         struct listserver_cmdarg *tmp = find_cmdarg(argv[0]);
968         if(!tmp) {
969             buffer_printf(buf, sizeof(buf) - 1, "Unrecognized command line argument '%s'.", argv[0]);
970             internal_error(buf);
971             errors = 1;
972             exitearly = 1;
973             break;
974         } else {
975             int res = tmp->fn(++argv, ++count);
976             if(res == CMDARG_ERR) {
977                 exitearly = 1;
978                 errors = 1;
979                 break;
980             } else if(res == CMDARG_EXIT) {
981                 exitearly = 1;
982                 break;
983             }
984             argv += tmp->params;
985         }
986     }
987 
988     if(!exitearly) {
989         /*
990          * this has to go here so that cookies expire before they can be used
991          * if they are stale.   It has to come after the argument parsing so
992          * that virtual host files are picked up correctly.
993          */
994         log_printf(8, "Expiring old cookies.\n");
995         expire_all_cookies();
996         log_printf(8, "Done expiring old cookies.\n");
997         wipe_vars(VAR_LIST|VAR_TEMP);
998 
999         /*
1000          * Now, we need to initialize the list config file if we have a current
1001          * list
1002          */
1003         if(get_var("list")) list_read_conf();
1004 
1005         if (init_queuefile()) {
1006            if(parse_message() == PARSE_ERR)
1007                errors = 1;
1008         }
1009     }
1010 
1011     finish_listserver();
1012 
1013     if(!errors)
1014         return 0;
1015     else
1016         return EX_TEMPFAIL;
1017 }
1018 
1019 /* Bounce a message */
bounce_message(void)1020 void bounce_message(void)
1021 {
1022     char buf[BIG_BUF];
1023     char buffer[BIG_BUF];
1024     const char *sender;
1025     time_t now;
1026     FILE *errfile;
1027 
1028 
1029     if(!get_var("smtp-errors-file"))
1030         return;
1031     /*
1032      * The error file better have been closed before we get into here other
1033      * wise we have a locking conflict.
1034      */
1035     errfile = open_file(get_string("smtp-errors-file"), "r");
1036     if(!errfile)
1037         return;
1038 
1039     buffer_printf(buf, sizeof(buf) - 1, "MAILER-DAEMON@%s", get_string("mailserver"));
1040     if (smtp_start(0)) {
1041        if(!smtp_from(buf)) {
1042            return;
1043        }
1044 
1045        sender = get_var("send-as");
1046        if(!sender) {
1047            sender = get_var("list-owner");
1048            if(!sender) {
1049                sender = get_var("listserver-admin");
1050                if(!sender) {
1051                    /* we're screwed.. abort. */
1052                    close_file(errfile);
1053                    return;
1054                }
1055            }
1056        }
1057 
1058        if(!smtp_to(sender))
1059            return;
1060        if(!smtp_body_start())
1061            return;
1062 
1063        now = time(NULL);
1064        buffer_printf(buffer, sizeof(buffer) - 1, "Date: %s", ctime(&now));
1065        smtp_body_text(buffer);
1066        buffer_printf(buffer, sizeof(buffer) - 1, "From: %s", buf);
1067        smtp_body_line(buffer);
1068        buffer_printf(buffer, sizeof(buffer) - 1, "To: %s", sender);
1069        smtp_body_line(buffer);
1070        smtp_body_line("Subject: Errors while delivering message");
1071        buffer_printf(buffer, sizeof(buffer) - 1, "X-%s-Bounce: %s",
1072           SERVICE_NAME_MC, get_string("listserver-owner"));
1073        smtp_body_line(buffer);
1074 
1075        smtp_body_line("");
1076        while(read_file(buffer, sizeof(buffer), errfile)) {
1077           smtp_body_text(buffer);
1078        }
1079 
1080        smtp_body_end();
1081        smtp_end();
1082     }
1083     close_file(errfile);
1084     unlink_file(get_string("smtp-errors-file"));
1085 }
1086 
flagged_send_textfile(const char * fromaddy,const char * list,const char * flag,const char * filename,const char * subject)1087 int flagged_send_textfile(const char *fromaddy, const char *list,
1088                           const char *flag, const char *filename,
1089                           const char *subject)
1090 {
1091     FILE *queuefile, *userfile, *errfile;
1092     char buffer[BIG_BUF];
1093     char hostname[SMALL_BUF];
1094     char datebuffer[80];
1095     char datestr[80];
1096     int count, errors;
1097     time_t now;
1098     struct tm *tm_now;
1099     struct list_user user;
1100     const char *fromname;
1101     char *listdir;
1102 
1103     log_printf(15,"Entering flagged_send_textfile...\n");
1104 
1105     if (!exists_file(filename)) return 0;
1106 
1107     if ((queuefile = open_file(filename,"r")) == NULL)
1108         return 0;
1109 
1110     buffer_printf(buffer, sizeof(buffer) - 1, "%s.serr", get_string("queuefile"));
1111     set_var("smtp-errors-file", buffer, VAR_GLOBAL);
1112     errfile = open_file(buffer, "w");
1113     errors = 0;
1114 
1115     listdir = list_directory(list);
1116 
1117     if (listdir) {
1118        buffer_printf(buffer, sizeof(buffer) - 1, "%s/users", listdir);
1119        userfile = open_file(buffer, "r");
1120        free(listdir);
1121     } else userfile = NULL;
1122 
1123     if(!userfile) {
1124        if(errfile) close_file(errfile);
1125         log_printf(0, "Unable to open users file for list '%s'.", list);
1126         return 0;
1127     }
1128     if(!smtp_start(1))
1129         return 0;
1130     if(!smtp_from(fromaddy)) {
1131         smtp_end();
1132         if(errfile) {
1133             write_file(errfile, "%s\n", get_string("smtp-last-error"));
1134             close_file(errfile);
1135             close_file(userfile);
1136             close_file(queuefile);
1137             bounce_message();
1138         }
1139         return 0;
1140     }
1141     count = 0;
1142     while(user_read(userfile, &user)) {
1143         if(user_hasflag(&user, flag) &&
1144            (strcmp(flag,"VACATION") ? !user_hasflag(&user, "VACATION") : 1)) {
1145             count++;
1146             if(!smtp_to(user.address) && errfile) {
1147                 errors++;
1148                 write_file(errfile, "%s\n", get_string("smtp-last-error"));
1149             }
1150         }
1151     }
1152     /* If we get here, we didn't send to anyone, so abort early */
1153     if((count == errors) || !count){
1154         smtp_end();
1155         if(errfile) {
1156             close_file(errfile);
1157             if (count)
1158                 bounce_message();
1159             else {
1160                 buffer_printf(buffer, sizeof(buffer) - 1, "%s.serr", get_string("queuefile"));
1161                 unlink_file(buffer);
1162             }
1163         }
1164         close_file(userfile);
1165         close_file(queuefile);
1166         return 0;
1167     }
1168 
1169     time(&now);
1170     get_date(datestr, sizeof(datestr), now);
1171     buffer_printf(datebuffer, sizeof(datebuffer) - 1, "Date: %s", datestr);
1172 
1173     if(!smtp_body_start())
1174         return 0;
1175 	if(get_var("hostname")) {
1176 		buffer_printf(hostname, sizeof(hostname) - 1, "%s", get_string("hostname"));
1177 	} else {
1178 		memset(hostname, 0, sizeof(hostname));
1179 		build_hostname(hostname, sizeof(hostname));
1180 	}
1181     buffer_printf(buffer, sizeof(buffer) - 1, "Received: with %s (v%s); %s", SERVICE_NAME_MC,
1182             VER_PRODUCTVERSION_STR, datestr);
1183     smtp_body_line(buffer);
1184     smtp_body_line(datebuffer);
1185     fromname = get_var("listserver-full-name");
1186     buffer_printf(buffer, sizeof(buffer) - 1, "From: %s <%s>", fromname, get_string("listserver-address"));
1187     smtp_body_line(buffer);
1188     buffer_printf(buffer, sizeof(buffer) - 1, "To: Members flagged %s of list %s <%s>", flag, list,
1189        get_string("listserver-address"));
1190     smtp_body_line(buffer);
1191     buffer_printf(buffer, sizeof(buffer) - 1, "Subject: %s", subject);
1192     smtp_body_line(buffer);
1193     tm_now = localtime(&now);
1194     buffer_printf(buffer, sizeof(buffer) - 1, "%s-%s", SERVICE_NAME_LC, "%m%d%Y%H%M%S");
1195     strftime(datebuffer, sizeof(datebuffer) - 1, buffer, tm_now);
1196     buffer_printf(buffer, sizeof(buffer) - 1, "Message-ID: <%s.%d.%d@%s>", datebuffer, (int)getpid(),
1197 			messagecnt++, hostname);
1198     smtp_body_line(buffer);
1199     buffer_printf(buffer, sizeof(buffer) - 1, "X-%s-Version: %s v%s", SERVICE_NAME_MC, SERVICE_NAME_MC,
1200             VER_PRODUCTVERSION_STR);
1201     smtp_body_line(buffer);
1202     if (get_var("stocksend-extra-headers")) {
1203        smtp_body_line(get_string("stocksend-extra-headers"));
1204     }
1205     smtp_body_line("");
1206 
1207     while(read_file(buffer, sizeof(buffer), queuefile)) {
1208         smtp_body_text(buffer);
1209     }
1210 
1211     if (!get_bool("task-no-footer")) {
1212        smtp_body_line("");
1213        smtp_body_line("---");
1214        buffer_printf(buffer, sizeof(buffer) - 1, "%s v%s - job execution complete.", SERVICE_NAME_MC,
1215                VER_PRODUCTVERSION_STR);
1216        smtp_body_line(buffer);
1217     }
1218 
1219     smtp_body_end();
1220     smtp_end();
1221 
1222     /* Now handle any bounces from local recipients */
1223     if(errfile) {
1224         close_file(errfile);
1225         if(errors)
1226             bounce_message();
1227         else {
1228             buffer_printf(buffer, sizeof(buffer) - 1, "%s.serr", get_string("queuefile"));
1229             unlink_file(buffer);
1230         }
1231     }
1232     close_file(userfile);
1233     close_file(queuefile);
1234     return 1;
1235 }
1236 
resolve_error(int error)1237 const char *resolve_error(int error)
1238 {
1239    const char *tmp;
1240 
1241 #ifdef _AIX
1242    if (error > sys_nerr) tmp = &def_err[0]; else tmp = sys_errlist[error];
1243 #else
1244    tmp = strerror (error);
1245 #endif
1246 
1247    return tmp;
1248 }
1249 
nosuch(const char * listname)1250 void nosuch(const char *listname)
1251 {
1252     spit_status("No such list '%s'", listname);
1253 
1254     if (!get_bool("adminmode")) {
1255         result_printf("\nFor information on what mailing lists are available on this\n");
1256         result_printf("site, send e-mail to %s with 'lists' in the subject\n",get_string("listserver-address"));
1257         result_printf("or body.\n");
1258     }
1259 }
1260 
get_date(char * buffer,int len,time_t now)1261 void get_date(char *buffer, int len, time_t now)
1262 {
1263 #if defined(WIN32) || !defined(GNU_STRFTIME)
1264    char tstr[80];
1265 # ifdef WIN32
1266    static set_tz = 0;
1267    static TIME_ZONE_INFORMATION tzInfo;
1268    static long tzType;
1269    static long bias;
1270 # endif
1271 # ifdef NO_TM_GMTOFF
1272    static int set_tz = 0;
1273    static long bias;
1274    struct tm tmptime = *gmtime(&now);
1275 # endif
1276 #endif
1277    struct tm *tm_now = localtime(&now);
1278 
1279 #ifndef WIN32
1280 # ifdef GNU_STRFTIME
1281    strftime(buffer, len - 1,"%a, %d %b %Y %H:%M:%S %z (%Z)",tm_now);
1282 # else
1283 #  ifdef NO_TM_GMTOFF
1284    /*
1285     * This isn't the best answer, and I will need access to a solaris
1286     * box to get a better one
1287     */
1288    strftime(buffer, len - 1,"%a, %d %b %Y %H:%M:%S",tm_now);
1289    if(!set_tz) {
1290        set_tz = 1;
1291        bias = (tm_now->tm_hour - tmptime.tm_hour) * 60 +
1292               tm_now->tm_min - tmptime.tm_min;
1293 
1294        /* assume that offset isn't more than a day ... */
1295        if (tm_now->tm_year < tmptime.tm_year)
1296            bias -= 24 * 60;
1297        else if (tm_now->tm_year > tmptime.tm_year)
1298            bias += 24 * 60;
1299        else if (tm_now->tm_yday < tmptime.tm_yday)
1300            bias -= 24 * 60;
1301        else if (tm_now->tm_yday > tmptime.tm_yday)
1302            bias += 24 * 60;
1303    }
1304 #   ifdef HAVE_TZNAME
1305 #    ifdef MY_PRINTF_IS_BRAINDEAD
1306    buffer_printf(tstr, sizeof(tstr) - 1, " %+.03d%.02d (%s)", (int)(bias/60), (int)(bias%60),
1307            tzname[(tm_now->tm_isdst ? 1 : 0)]);
1308 #    else
1309    buffer_printf(tstr, sizeof(tstr) - 1, " %+.02d%.02d (%s)", (int)(bias/60), (int)(bias%60),
1310            tzname[(tm_now->tm_isdst ? 1 : 0)]);
1311 #    endif
1312 #   else
1313 #    ifdef HAVE_TIMEZONE
1314 #     ifdef MY_PRINTF_IS_BRAINDEAD
1315    buffer_printf(tstr, sizeof(tstr) - 1, " %+.03d%.02d (%s)", (int)(bias/60), (int)(bias%60),
1316            timezone(bias, tm_now->tm_isdst));
1317 #     else
1318    buffer_printf(tstr, sizeof(tstr) - 1, " %+.02d%.02d (%s)", (int)(bias/60), (int)(bias%60),
1319            timezone(bias, tm_now->tm_isdst));
1320 #     endif
1321 #    else
1322    buffer_printf(tstr, sizeof(tstr) - 1, " %+.02d%.02d", (int)(bias/60), (int)(bias%60));
1323 #    endif /* HAVE_TIMEZONE */
1324 #   endif /* HAVE_TZNAME */
1325    strncat(buffer, tstr, len - 1 - strlen(buffer));
1326 #  else
1327    strftime(buffer,len - 1,"%a, %d %b %Y %H:%M:%S",tm_now);
1328 #   ifdef MY_PRINTF_IS_BRAINDEAD
1329    buffer_printf(tstr, sizeof(tstr) - 1, " %+.03d%.02d (%s)", (int)((tm_now->tm_gmtoff)/3600),
1330            (int)(((tm_now->tm_gmtoff)/60)%60), tm_now->tm_zone );
1331 #   else
1332    buffer_printf(tstr, sizeof(tstr) - 1, " %+.02d%.02d (%s)", (int)((tm_now->tm_gmtoff)/3600),
1333            (int)(((tm_now->tm_gmtoff)/60)%60), tm_now->tm_zone );
1334 #   endif
1335    strncat(buffer, tstr, len - 1 - strlen(buffer));
1336 #  endif
1337 # endif
1338 #else
1339    if(!set_tz) {
1340       _tzset();
1341       tzType = GetTimeZoneInformation( &tzInfo );
1342       bias = tzInfo.Bias;
1343       switch (tzType) {
1344          case TIME_ZONE_ID_STANDARD:
1345             bias += tzInfo.StandardBias;
1346             break;
1347          case TIME_ZONE_ID_DAYLIGHT:
1348             bias += tzInfo.DaylightBias;
1349             break;
1350       }
1351       bias *= -1;
1352       set_tz = 1;
1353    }
1354    strftime(buffer,len - 1,"%a, %d %b %Y %H:%M:%S", tm_now);
1355 
1356    /*
1357     * Windows refuses to return the 3 character time zone, so we will
1358     * just not use it.
1359     */
1360    buffer_printf(tstr, sizeof(tstr) - 1, " %+.02d%.02d", (bias/60), (bias%60));
1361    strncat(buffer, tstr, len - 1 - strlen(buffer));
1362 #endif
1363 }
1364 
do_sleep(int millis)1365 void do_sleep(int millis) {
1366 #ifndef WIN32
1367 # ifdef NEED_USLEEP
1368     struct timeval tv;
1369     tv.tv_usec = millis * 1000;
1370     select(0, NULL, NULL, NULL, &tv);
1371 # else
1372     usleep(millis*1000);
1373 # endif
1374 #else
1375     Sleep(millis);
1376 #endif
1377 }
1378 
1379