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