1 /*
2 ** Copyright 2007-2009 Double Precision, Inc. See COPYING for
3 ** distribution information.
4 **
5 ** $Id: webmlmd.C,v 1.19 2009/11/08 18:14:47 mrsam Exp $
6 */
7
8 #include "config.h"
9 #include "cgi/cgi.h"
10 #include "rfc822/rfc822.h"
11 #include "rfc822/rfc2047.h"
12 #include "numlib/numlib.h"
13 #include "unicode/unicode.h"
14 #include "datadir.h"
15
16 #include <stdio.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #include <string.h>
23 #include <signal.h>
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #if HAVE_LOCALE_H
30 #if HAVE_SETLOCALE
31 #include <locale.h>
32 #endif
33 #endif
34
35 #include <list>
36 #include <vector>
37 #include <string>
38 #include <algorithm>
39 #include <iostream>
40 #include <fstream>
41 #include <sstream>
42 #include <map>
43
44 #include "cmlm.h"
45 #include "cmlmcmdmisc.h"
46 #include "webmlmd.H"
47 #include "webmlmdcmlm.H"
48 #include "webmlmddirs.H"
49 #include "webmlmdhandlers.H"
50
51 #define TEMPLATEDIR DATADIR "/couriermlm"
52
53 extern "C" {
54
fake_exit(int rc)55 void fake_exit(int rc)
56 {
57 exit(rc);
58 }
59
rfc2045_enomem(const char * errmsg)60 void rfc2045_enomem(const char *errmsg)
61 {
62 cginocache();
63 std::cout << "Content-Type: text/plain" << std::endl
64 << std::endl
65 << errmsg
66 << std::endl << std::flush;
67 fake_exit(1);
68 }
69
error(const char * errmsg)70 void error(const char *errmsg)
71 {
72 cginocache();
73 printf("Content-Type: text/html; charset=utf-8\n\n"
74 "<html><head><title>%s</title></head><body><h1>%s</h1></body></html>\n",
75 errmsg, errmsg);
76 fake_exit(1);
77 }
78 }
79
80 webmlmd::dirs mlm_dirs;
81
toutf8str(std::wstring w)82 std::string toutf8str(std::wstring w)
83 {
84 std::vector<unicode_char> u;
85
86 u.reserve(w.size()+1);
87
88 u.insert(u.end(), w.begin(), w.end());
89
90 u.push_back(0);
91
92 char *p=unicode_toutf8(&u[0]);
93
94 std::string s;
95
96 if (p)
97 {
98 s=p;
99 free(p);
100 }
101 return s;
102 }
103
104 // First component of PATH_INFO is the mailing list name
105
list_name()106 std::string webmlmd::list_name()
107 {
108 const char *pi=getenv("PATH_INFO");
109
110 if (!pi)
111 return "";
112
113 if (*pi == '/')
114 ++pi;
115
116 std::string list_name(pi);
117
118 std::string::iterator b=list_name.begin(),
119 p=std::find(b, list_name.end(), '/');
120
121 return std::string(b, p);
122 }
123
124 // The remaining component in PATH_INFO is the list command
125
list_cmd()126 static std::string list_cmd()
127 {
128 const char *pi=cgiextrapath();
129
130 if (*pi == '/')
131 ++pi;
132
133 std::string list_name(pi);
134
135 std::string::iterator b=list_name.begin(), e=list_name.end(),
136 p=std::find(b, e, '/');
137
138 if (p != e)
139 ++p;
140
141 return std::string(p, e);
142 }
143
144 static void main2(void *);
145
init_dirs(void * dummy)146 static void init_dirs(void *dummy)
147 {
148 const char *dirs=getenv("LISTS");
149
150 if (!mlm_dirs.initialize(dirs ? dirs:""))
151 {
152 exit(1);
153 }
154 }
155
main(int argc,char ** argv)156 int main(int argc, char **argv)
157 {
158 const char *cmd=NULL;
159 const char *port_s=getenv("PORT");
160 const char *prefork_s=getenv("PREFORK");
161
162 if (argc > 1)
163 cmd=argv[1];
164
165 #if HAVE_SETLOCALE
166 setlocale(LC_ALL, "C");
167 #endif
168
169 /*
170 ** For convenience's sake, we need to start webmlmd by a helper script.
171 ** Rather than confusing folks with webmlmd.rc, automatically invoke
172 ** the helper script, when needed.
173 */
174
175 if (cmd && strcmp(cmd, "check"))
176 {
177 std::vector<char> shscript_v;
178
179 {
180 std::string shscript=argv[0];
181
182 shscript += ".rc";
183
184 shscript_v.reserve(shscript.size()+1);
185
186 shscript_v.insert(shscript_v.end(),
187 shscript.begin(),
188 shscript.end());
189 shscript_v.push_back(0);
190 }
191
192 std::vector<char *> new_argv;
193
194 new_argv.push_back(&shscript_v[0]);
195
196 new_argv.insert(new_argv.end(), argv, argv+argc);
197 new_argv.push_back(0);
198
199 execvp(new_argv[0], &new_argv[0]);
200 perror(new_argv[0]);
201 exit(1);
202 }
203
204 if (!port_s || !*port_s)
205 {
206 std::cerr << "PORT not set in the configuration file"
207 << std::endl;
208 exit(1);
209 }
210
211 if (argc > 1 && strcmp(argv[1], "check") == 0)
212 {
213 if (access(port_s, 0) == 0)
214 {
215 if (access(port_s, W_OK) < 0)
216 {
217 perror(port_s);
218 exit(1);
219 }
220 }
221 else
222 {
223 int fd=socket(PF_UNIX, SOCK_STREAM, 0);
224
225 if (fd >= 0)
226 {
227 struct sockaddr_un skun;
228
229 memset(&skun, 0, sizeof(skun));
230
231 if (strlen(port_s) >= sizeof(skun.sun_path))
232 {
233 fprintf(stderr, "%s: pathname too long\n",
234 port_s);
235 exit(1);
236 }
237
238 skun.sun_family=AF_UNIX;
239
240 strcpy(skun.sun_path, port_s);
241
242 if (bind(fd, (const struct sockaddr *)&skun,
243 sizeof(skun)) ||
244 listen(fd, 5) < 0)
245 {
246 close(fd);
247 fd= -1;
248 }
249 }
250
251 if (fd < 0)
252 {
253 perror(port_s);
254 exit(1);
255 }
256 close(fd);
257 }
258 }
259
260 if (cmd)
261 {
262 init_dirs(NULL);
263 exit(0); /* The "check" command */
264 }
265
266 cgi_daemon(atoi(prefork_s), port_s, init_dirs, main2, NULL);
267
268 return (0);
269 }
270
getoption(std::string listdir,std::string option)271 std::string getoption(std::string listdir,
272 std::string option)
273 {
274 std::string n=listdir + "/options";
275
276 std::ifstream i(n.c_str());
277
278 std::string s;
279
280 if (!i.is_open())
281 return s;
282
283 while (std::getline(i, s).good())
284 {
285 std::string::iterator b=s.begin(), e=s.end(),
286 p=std::find(b, e, '=');
287
288 if (std::string(b, p) == option)
289 {
290 if (p != e)
291 ++p;
292
293 return std::string(p, e);
294 }
295 }
296 return "";
297 }
298
getoption(std::string option)299 std::string getoption(std::string option)
300 {
301 return getoption(".", option);
302 }
303
getwoption(std::string dir,std::string option)304 std::wstring getwoption(std::string dir, std::string option)
305 {
306 std::string s=getoption(dir, option);
307
308 unicode_char *u=unicode_fromutf8(s.c_str());
309
310 if (!u)
311 return std::wstring();
312
313 size_t ul=0;
314
315 for (ul=0; u[ul]; ++ul)
316 ;
317
318 std::wstring w(u, u+ul);
319 free(u);
320
321 return w;
322 }
323
getwoption(std::string option)324 std::wstring getwoption(std::string option)
325 {
326 return getwoption(".", option);
327 }
328
getlistname(std::string dir,std::string dirbasename)329 std::string getlistname(std::string dir, std::string dirbasename)
330 {
331 std::wstring listname=getwoption(dir, "LISTNAME");
332
333 if (listname.size() == 0)
334 {
335 listname.insert(listname.end(),
336 dirbasename.begin(),
337 dirbasename.end());
338 }
339
340 return toutf8str(webmlmd::html_escape(listname));
341 }
342
getlistname(std::string dirbasename)343 std::string getlistname(std::string dirbasename)
344 {
345 return getlistname(".", dirbasename);
346 }
347
348 HANDLER("LISTS", emit_lists)
349 {
350 std::vector<std::string>::iterator b=mlm_dirs.begin(),
351 e=mlm_dirs.end();
352
353 std::cout << "<ul>";
354
355 while (b != e)
356 {
357 std::string n=webmlmd::basename(*b);
358
359 std::cout << "<li><a href=\"" << cgirelscriptptr() << "/"
360 << webmlmd::html_escape(n) << "\">"
361 << getlistname(*b, n)
362 << "</a></li>";
363 ++b;
364 }
365 std::cout << "</ul>";
366 }
367
368 HANDLER("LISTNAME", emit_list_name)
369 {
370 std::cout << getlistname(list_name());
371 }
372
373 HANDLER("LISTDESCR", emit_list_descr)
374 {
375 std::cout << getoption("LISTDESCR");
376 }
377
378 HANDLER("LISTURL", emit_list_url)
379 {
380 std::cout << cgirelscriptptr() << "/" << list_name();
381 }
382
383 HANDLER("REPEATURL", emit_repeat_url)
384 {
385 std::cout << cgirelscriptptr() << cgiextrapath();
386 }
387
388 static void emit_select(const char *name,
389 const char *values,
390 const std::list<std::string> &options,
391 std::string default_option,
392 size_t list_size=0,
393 bool allow_multiple=false)
394 {
395 std::string options_str;
396
397 std::list<std::string>::const_iterator b=options.begin();
398
399 while (b != options.end())
400 {
401 options_str += *b++;
402 options_str += "\n";
403 }
404
405 char *buf=cgi_select(name, values, options_str.c_str(),
406 getoption("POST").c_str(), 0, 0);
407
408 if (buf)
409 {
410 std::cout << buf;
411 free(buf);
412 }
413 }
414
415
emit_checkbox(const char * name,const char * value,const char * opts)416 static void emit_checkbox(const char *name, const char *value,
417 const char *opts)
418 {
419 char *buf=cgi_checkbox(name, value, opts);
420
421 if (buf)
422 {
423 std::cout << buf;
424 free(buf);
425 }
426 }
427
emit_textarea(const char * name,std::string value,const char * wrap,int rows,int cols,const char * opts)428 static void emit_textarea(const char *name, std::string value,
429 const char *wrap,
430 int rows, int cols, const char *opts)
431 {
432 std::vector<unicode_char> u;
433
434 u.reserve(value.size()+1);
435
436 u.insert(u.end(), value.begin(), value.end());
437 u.push_back(0);
438
439 char *buf=cgi_textarea(name, rows, cols, &u[0], wrap, opts);
440
441 u.clear();
442
443 if (buf)
444 {
445 std::cout << buf;
446 free(buf);
447 }
448 }
449
emit_input(const char * name,std::wstring value,int size,int maxlength,const char * opts)450 static void emit_input(const char *name, std::wstring value,
451 int size, int maxlength,
452 const char *opts)
453 {
454 std::vector<unicode_char> unicode_buf;
455
456 unicode_buf.reserve(value.size()+1);
457
458 unicode_buf.insert(unicode_buf.end(), value.begin(), value.end());
459 unicode_buf.push_back(0);
460
461 char *buf=cgi_input(name, &unicode_buf[0], size, maxlength, "");
462 unicode_buf.clear();
463
464 if (buf)
465 {
466 std::cout << buf;
467 free(buf);
468 }
469 }
470
471 HANDLER("ADMINUPDATE", do_admin_update)
472 {
473 if (!*cgi("update"))
474 return;
475
476 std::list<std::string> new_cmds;
477
478 static const char * const booleans[]={
479 "!simpleconfirm",
480 "casesensitive",
481 "nobozos",
482 "nodsn",
483 };
484
485 size_t i;
486
487 for (i=0; i<sizeof(booleans)/sizeof(booleans[0]); i++)
488 {
489 const char *p=booleans[i];
490
491 const char *yes, *no;
492
493 if (*p =='!')
494 {
495 ++p;
496
497 yes="0";
498 no="1";
499 }
500 else
501 {
502 yes="1";
503 no="0";
504 }
505
506 std::string setting=p;
507
508 std::transform(setting.begin(), setting.end(),
509 setting.begin(), std::ptr_fun(toupper));
510
511 std::string varname("opt");
512
513 varname += p;
514
515 new_cmds.push_back(setting + "=" +
516 (*cgi(varname.c_str()) ? yes:no));
517 }
518
519 new_cmds.push_back(*cgi("optsubscribe") ?
520 "SUBSCRIBE=mod":"subscribe=all");
521
522 static const char * const plains[]={
523 "post",
524 "postarchive",
525 "reportaddr",
526 "digest",
527 "listname",
528 "keyword"
529 };
530
531 for (i=0; i<sizeof(plains)/sizeof(plains[0]); i++)
532 {
533
534 std::string setting=plains[i];
535
536 std::transform(setting.begin(), setting.end(),
537 setting.begin(), std::ptr_fun(toupper));
538
539 std::string varname("opt");
540
541 varname += plains[i];
542
543 new_cmds.push_back(setting + "=" + cgi(varname.c_str()));
544 }
545
546 static const struct {
547 const char *name;
548 unsigned minvalue;
549 unsigned maxvalue;
550 } numerical[]={
551 {"startprobe", 3, 30},
552 {"maxbounces", 10, 50},
553 {"purgebounce", 5, 30},
554 {"maxmodnotices", 5, 99},
555 {"remoderate", 12, 48},
556 {"maxfetchsize", 100, 9999},
557 {"purgearchive", 2, 9999},
558 {"purgecmd", 6, 720},
559 };
560
561 for (i=0; i<sizeof(numerical)/sizeof(numerical[0]); i++)
562 {
563
564 std::string setting=numerical[i].name;
565
566 std::transform(setting.begin(), setting.end(),
567 setting.begin(), std::ptr_fun(toupper));
568
569 std::string varname("opt");
570
571 varname += numerical[i].name;
572
573 std::ostringstream o;
574
575 o << setting << "=";
576
577 int nn=atoi(cgi(varname.c_str()));
578
579 if (nn > 0)
580 {
581 if ((unsigned)nn < numerical[i].minvalue)
582 nn=(int)numerical[i].minvalue;
583 if ((unsigned)nn > numerical[i].maxvalue)
584 nn=(int)numerical[i].maxvalue;
585
586 o << nn;
587 }
588
589 new_cmds.push_back(o.str());
590 }
591
592 char *p=rfc2047_encode_str(cgi("optname"), "utf-8",
593 rfc2047_qp_allow_word);
594
595 new_cmds.push_back(std::string("NAME=") + (p ? p:""));
596
597 if (p)
598 free(p);
599
600 std::vector<std::string> args_array;
601
602 args_array.reserve(new_cmds.size());
603 args_array.insert(args_array.end(), new_cmds.begin(),
604 new_cmds.end());
605
606 cmdset(args_array, false);
607
608 {
609 std::string key=cgi("optheaderadd");
610
611 key += "\n";
612
613 std::istringstream i(key);
614
615 std::ofstream ofs(HEADERADD ".new");
616
617 std::string s;
618
619 while (std::getline(i, s).good())
620 if (s.size() > 0)
621 {
622 ofs << s << "\n";
623 }
624
625 ofs.close();
626 rename(HEADERADD ".new", HEADERADD);
627 }
628
629 {
630 std::string key=cgi("optheaderdel");
631 key += "\n";
632
633 std::istringstream i(key);
634
635 std::ofstream ofs(HEADERDEL ".new");
636
637 std::string s;
638
639 while (std::getline(i, s).good())
640 if (s.size() > 0)
641 {
642 ofs << s << "\n";
643 }
644
645 ofs.close();
646 rename(HEADERDEL ".new", HEADERDEL);
647 }
648
649 }
650
651 HANDLER("OPTPOST", emit_optpost_setting)
652 {
653 if (args.size() != 3)
654 return;
655
656 emit_select("optpost",
657 "all\n"
658 "subscribers\n"
659 "mod\n", args, getoption("POST"));
660 }
661
662 HANDLER("OPTSUBSCRIBE", emit_optsubscribe)
663 {
664 emit_checkbox("optsubscribe", "X",
665 getoption("SUBSCRIBE") == "mod" ? "*":"");
666 }
667
668 HANDLER("OPTSIMPLECONFIRM", emit_simpleconfirm)
669 {
670 emit_checkbox("optsimpleconfirm", "X",
671 getoption("SIMPLECONFIRM") != "1" ? "*":"");
672 }
673
674 HANDLER("OPTPURGECMD", emit_purgecmd)
675 {
676 emit_input("optpurgecmd", getwoption("PURGECMD"), 8, 8, "");
677 }
678
679 HANDLER("OPTKEYWORD", emit_keyword)
680 {
681 emit_input("optkeyword", getwoption("KEYWORD"), 32, 255, "");
682 }
683
684 HANDLER("OPTNAME", emit_name)
685 {
686 std::string n=getoption("NAME");
687
688 char *p=rfc822_display_hdrvalue_tobuf("subject",
689 n.c_str(),
690 unicode_UTF8.chset,
691 NULL,
692 NULL);
693 if (p)
694 {
695 unicode_char *u=unicode_fromutf8(p);
696
697 if (u)
698 {
699 size_t ul=0;
700
701 for (ul=0; u[ul]; ++ul)
702 ;
703
704 emit_input("optname", std::wstring(u, u+ul), 32, 255,
705 "");
706 free(u);
707 }
708 free(p);
709 }
710 }
711
712 HANDLER("OPTLISTNAME", emit_listname)
713 {
714 emit_input("optlistname", getwoption("LISTNAME"), 32, 255, "");
715 }
716
717 HANDLER("OPTPOSTARCHIVE", emit_postarchive)
718 {
719 if (args.size() != 2)
720 return;
721
722 emit_select("optpostarchive",
723 "all\n"
724 "subscribers\n", args, getoption("POSTARCHIVE"));
725 }
726
727 HANDLER("OPTCASESENSITIVE", emit_casesensitive)
728 {
729 emit_checkbox("optcasesensitive", "X",
730 getoption("CASESENSITIVE") == "1" ? "*":"");
731 }
732
733 HANDLER("OPTREPORTADDR", emit_reportaddr)
734 {
735 emit_input("optreportaddr", getwoption("REPORTADDR"), 32, 255, "");
736 }
737
738 HANDLER("OPTNOBOZOS", emit_nobozos)
739 {
740 emit_checkbox("optnobozos", "X",
741 getoption("NOBOZOS") != "0" ? "*":"");
742 }
743
744 HANDLER("OPTDIGEST", emit_digest)
745 {
746 emit_input("optdigest", getwoption("DIGEST"), 32, 255, "");
747 }
748
749 HANDLER("OPTMAXFETCHSIZE", emit_maxfetchsize)
750 {
751 emit_input("optmaxfetchsize", getwoption("MAXFETCHSIZE"), 8, 8, "");
752 }
753
754 HANDLER("OPTNODSN", emit_nodsn)
755 {
756 emit_checkbox("optnodsn", "X",
757 getoption("NODSN") == "1" ? "*":"");
758 }
759
760 HANDLER("OPTSTARTPROBE", emit_startprobe)
761 {
762 emit_input("optstartprobe", getwoption("STARTPROBE"), 8, 8, "");
763 }
764
765 HANDLER("OPTMAXBOUNCES", emit_maxbounces)
766 {
767 emit_input("optmaxbounces", getwoption("MAXBOUNCES"), 8, 8, "");
768 }
769
770 HANDLER("OPTPURGEBOUNCE", emit_purgebounce)
771 {
772 emit_input("optpurgebounce", getwoption("PURGEBOUNCE"), 8, 8, "");
773 }
774
775 HANDLER("OPTMAXMODNOTICES", emit_maxmodnotices)
776 {
777 emit_input("optmaxmodnotices", getwoption("MAXMODNOTICES"), 8, 8, "");
778 }
779
780 HANDLER("OPTREMODERATE", emit_remoderate)
781 {
782 emit_input("optremoderate", getwoption("REMODERATE"), 8, 8, "");
783 }
784
785 HANDLER("OPTPURGEARCHIVE", emit_purgearchive)
786 {
787 emit_input("optpurgearchive", getwoption("PURGEARCHIVE"), 8, 8, "");
788 }
789
790 HANDLER("OPTHEADERADD", emit_headeradd)
791 {
792 std::ostringstream o;
793
794 std::ifstream ifs(HEADERADD);
795
796 if (ifs.is_open())
797 {
798 std::string line;
799
800 while (std::getline(ifs, line).good())
801 if (line.size())
802 o << line << std::endl;
803 }
804
805 emit_textarea("optheaderadd", o.str(), "off", 4, 40, "");
806 }
807
808 HANDLER("OPTHEADERDEL", emit_headerdel)
809 {
810 std::ostringstream o;
811
812 std::ifstream ifs(HEADERDEL);
813
814 if (ifs.is_open())
815 {
816 std::string line;
817
818 while (std::getline(ifs, line).good())
819 if (line.size())
820 o << line << std::endl;
821 }
822
823 emit_textarea("optheaderdel", o.str(), "off", 4, 40, "");
824 }
825
826
827
showform(const char * filename,const std::map<std::string,std::string> & parms)828 static void showform(const char *filename,
829 const std::map<std::string, std::string> &parms)
830 {
831 webmlmd::handler_list handlers;
832
833 std::ifstream i(filename);
834
835 if (!i.is_open())
836 {
837 std::cout << filename << ": cannot open"
838 << std::endl;
839 return;
840 }
841
842 std::string line;
843
844 while (std::getline(i, line).good())
845 {
846 std::string::iterator b=line.begin(), e=line.end();
847
848 while (b != e)
849 {
850 static const char mac_open[]="[@";
851 static const char mac_close[]="@]";
852
853 std::string::iterator p=
854 std::search(b, e, mac_open, mac_open+2);
855
856 std::cout << std::string(b, p);
857
858 b=p;
859
860 if (b == e)
861 break;
862
863 b += 2;
864
865 p=std::search(b, e, mac_close, mac_close+2);
866
867 std::list<std::string> args;
868
869 std::replace(b, p, '_', ' ');
870 while (b != p)
871 {
872 std::string::iterator word_start=b;
873
874 b=std::find(b, p, ':');
875
876 args.push_back(std::string(word_start, b));
877
878 if (b != p)
879 ++b;
880 }
881
882 if (b != e)
883 b += 2;
884
885 std::string handler_name;
886
887 if (!args.empty())
888 {
889 handler_name=args.front();
890 args.pop_front();
891 }
892
893 webmlmd::handler_list::iterator
894 h=handlers.find(handler_name);
895
896 if (h != handlers.end())
897 (*h->second)(args);
898
899 std::map<std::string, std::string>::const_iterator
900 vb=parms.find(handler_name);
901
902 if (vb != parms.end())
903 {
904 std::string txt=vb->second;
905
906 if (vb->first.substr(0, 1) != ".")
907 txt= webmlmd::html_escape(txt);
908 std::cout << txt;
909 }
910 }
911 std::cout << std::endl;
912 }
913 }
914
showhtmlform(const char * formname,std::map<std::string,std::string> & parms)915 void webmlmd::showhtmlform(const char *formname,
916 std::map<std::string, std::string> &parms)
917 {
918 cginocache();
919 std::cout << "Content-Type: text/html; charset=utf-8"
920 << std::endl
921 << std::endl;
922
923 showform(formname, parms);
924 }
925
showhtmlform(const char * formname)926 void webmlmd::showhtmlform(const char *formname)
927 {
928 std::map<std::string, std::string> dummy;
929
930 webmlmd::showhtmlform(formname, dummy);
931 }
932
showerrorform(const char * formname,std::string errmsg)933 static void showerrorform(const char *formname,
934 std::string errmsg)
935 {
936 std::map<std::string, std::string> parms;
937
938 parms["ERRMSG"]=errmsg;
939
940 webmlmd::showhtmlform(formname, parms);
941 }
942
sendsubunsub(std::string ext)943 static void sendsubunsub(std::string ext)
944 {
945 std::string address(cgi("address"));
946
947 std::string::iterator b=address.begin(),
948 e=address.end(),
949 p=std::find(b, e, '@');
950
951 if (p == e || std::find(p+1, e, '@') != e ||
952 std::find(p, e, '/') != e)
953 {
954 showerrorform("webmlmerror.tmpl.html",
955 "Invalid E-mail address: " + address);
956 return;
957 }
958
959 webmlmd::cmlm ctlmsg;
960
961 if (ctlmsg.start(ext, address, "ctlmsg"))
962 {
963 FILE *stdinptr=ctlmsg.stdinptr();
964
965 ctlmsg.mk_received_header();
966 fprintf(stdinptr, "From: %s\n", address.c_str());
967 fprintf(stdinptr, "Subject: %s\n", ext.c_str());
968 fprintf(stdinptr, "\nSubscription request received.\n");
969
970 if (ctlmsg.wait())
971 {
972 webmlmd::showhtmlform("webmlmrequestreceived.tmpl.html");
973 return;
974 }
975 }
976
977 showerrorform("webmlmerror.tmpl.html",
978 "An error occured processing this request.\n"
979 "Please try again later.\n");
980 }
981
982 /*
983 ** /admin request, password is good
984 */
985
adminrequest(std::string admin_path)986 static void adminrequest(std::string admin_path)
987 {
988 if (admin_path == "gosublist")
989 {
990 std::map<std::string, std::string> parms;
991
992 parms["REFRESHURL"]=std::string(cgirelscriptptr())
993 + "/" + webmlmd::list_name() + "/admin/sublist";
994 webmlmd::showhtmlform("webmlmpleasewait.tmpl.html", parms);
995 return;
996 }
997
998 if (admin_path == "sublist")
999 {
1000 webmlmd::showhtmlform("webmlmsublist.tmpl.html");
1001 return;
1002 }
1003
1004 if (admin_path == "subinfo")
1005 {
1006 webmlmd::showhtmlform("webmlmsubinfo.tmpl.html");
1007 return;
1008 }
1009
1010 if (admin_path == "mod")
1011 {
1012 std::string filename;
1013
1014 std::map<std::string, std::string> parms;
1015
1016 if (*cgi("domoderate"))
1017 {
1018 filename=cgi("msgname");
1019
1020 if (filename.find('/') != filename.npos)
1021 filename="";
1022 }
1023
1024 if (filename != "" && strcmp(cgi("modaction"), "accept") == 0)
1025 {
1026 parms[".ERRMSG"]=webmlmd::do_mod_accept(filename);
1027
1028 if (parms[".ERRMSG"] != "")
1029 filename="";
1030 }
1031 else if (filename != "" && strcmp(cgi("modaction"), "reject")
1032 == 0)
1033 {
1034 parms[".ERRMSG"]=
1035 webmlmd::do_mod_reject(filename,
1036 *cgi("returntosender")
1037 != 0,
1038 cgi("rejectmsg"));
1039
1040 if (parms[".ERRMSG"] != "")
1041 filename="";
1042 }
1043
1044 if (filename != "")
1045 {
1046 size_t dummy;
1047
1048 filename=webmlmd::get_next_mod_filename(filename,
1049 dummy);
1050
1051 if (filename == "")
1052 {
1053 std::string url=std::string(cgirelscriptptr())
1054 + "/" + webmlmd::list_name()
1055 + "/admin";
1056
1057 cgiredirect(url.c_str());
1058
1059 fflush(stdout);
1060 std::cout << "\nRedirect to " << url << "\n";
1061 return;
1062 }
1063
1064 cgi_put("msgname", filename.c_str());
1065
1066 }
1067
1068 webmlmd::showhtmlform("webmlmlistadminmod.tmpl.html", parms);
1069 return;
1070 }
1071
1072 webmlmd::showhtmlform("webmlmlistadmin.tmpl.html");
1073 }
1074
1075 /*
1076 ** Some list request
1077 */
1078
listrequest2(std::string list_name,std::string path_info)1079 static void listrequest2(std::string list_name, std::string path_info)
1080 {
1081 if (path_info == "sendsub")
1082 {
1083 sendsubunsub("subscribe");
1084 return;
1085 }
1086
1087 if (path_info == "sendunsub")
1088 {
1089 sendsubunsub("unsubscribe");
1090 return;
1091 }
1092
1093 if (path_info == "doconfirm")
1094 {
1095 std::string method(cgi("method"));
1096 std::string token(cgi("token"));
1097
1098 std::string errmsg="Confirmation failed. No further information is available.";
1099
1100 if (std::find_if(method.begin(), method.end(),
1101 std::not1(std::ptr_fun(::isalpha))) ==
1102 method.end() &&
1103 std::find_if(token.begin(), token.end(),
1104 std::not1(std::ptr_fun(::isalpha))) ==
1105 token.end())
1106 {
1107 webmlmd::cmlm confirm;
1108
1109 if (confirm.start(method + "-" + token, "",
1110 "ctlmsg"))
1111 {
1112 FILE *stdinptr=confirm.stdinptr();
1113
1114 confirm.mk_received_header();
1115 fprintf(stdinptr,
1116 "Subject: yes -- confirmed by WebMLM\n"
1117 "\n"
1118 "Confirmed\n");
1119
1120 if (confirm.wait())
1121 {
1122 webmlmd::showhtmlform("webmlmprocessed.html");
1123 return;
1124 }
1125
1126 char buf[1024];
1127 bool isfirst=true;
1128
1129 FILE *stdoutptr=confirm.stdoutptr();
1130
1131 while (fgets(buf, sizeof(buf), stdoutptr))
1132 {
1133 if (isfirst)
1134 {
1135 isfirst=false;
1136 errmsg="";
1137 }
1138 errmsg += buf;
1139 }
1140 }
1141 }
1142
1143 std::map<std::string, std::string> args;
1144
1145 args["ERROR"]=webmlmd::html_escape(errmsg);
1146 webmlmd::showhtmlform("webmlmnotprocessed.html", args);
1147 return;
1148 }
1149
1150
1151 std::string::iterator b=path_info.begin(), e=path_info.end();
1152
1153 std::string::iterator p=std::find(b, e, '/');
1154
1155 std::string w(b, p);
1156
1157 if (p != e) ++p;
1158
1159 if (w == "subconfirm" || w == "unsubconfirm")
1160 {
1161 std::map<std::string, std::string> args;
1162
1163 args["METHOD"]=w;
1164 args["TOKEN"]=std::string(p, e);
1165 webmlmd::showhtmlform("webmlmconfirm.html", args);
1166 return;
1167 }
1168
1169 if (w == "style.css")
1170 {
1171 std::map<std::string, std::string> args;
1172
1173 std::cout << "Content-Type: text/css"
1174 << std::endl << std::endl;
1175 showform("style.css.tmpl", args);
1176 return;
1177 }
1178
1179 if (w == "admin")
1180 {
1181 char *password_cookie=cgi_get_cookie("password");
1182 const char *password;
1183
1184 if (password_cookie)
1185 {
1186 if (getoption("LISTPW") == password_cookie)
1187 {
1188 free(password_cookie);
1189 adminrequest(std::string(p, e));
1190 return;
1191 }
1192
1193 free(password_cookie);
1194 }
1195
1196 password=cgi("password");
1197
1198 if (*password && getoption("LISTPW") == password)
1199 {
1200 struct cgi_set_cookie_info cookie_info;
1201
1202 cgi_set_cookie_info_init(&cookie_info);
1203
1204 cgi_set_cookie_session(&cookie_info, "password",
1205 password);
1206
1207 std::string cookie_path;
1208
1209 cookie_path=cgirelscriptptr();
1210
1211 cookie_path += "/";
1212 cookie_path += list_name;
1213
1214 cgi_set_cookie_url(&cookie_info, cookie_path.c_str());
1215
1216 cgi_set_cookies(&cookie_info, 1);
1217 cgi_set_cookie_info_free(&cookie_info);
1218 adminrequest(std::string(p, e));
1219 return;
1220 }
1221 webmlmd::showhtmlform("webmlmlistadminpw.tmpl.html");
1222 return;
1223 }
1224
1225 // Clear the password cookie when returning to the list screen
1226
1227 {
1228 struct cgi_set_cookie_info cookie_info;
1229
1230 cgi_set_cookie_info_init(&cookie_info);
1231
1232 cgi_set_cookie_expired(&cookie_info, "password");
1233
1234 std::string cookie_path;
1235
1236 cookie_path=cgirelscriptptr();
1237
1238 cookie_path += "/";
1239 cookie_path += list_name;
1240
1241 cgi_set_cookie_url(&cookie_info, cookie_path.c_str());
1242
1243 cgi_set_cookies(&cookie_info, 1);
1244 cgi_set_cookie_info_free(&cookie_info);
1245 }
1246
1247 webmlmd::showhtmlform("webmlmlistindex.tmpl.html");
1248 }
1249
listrequest(std::string list_name,std::string path_info)1250 static void listrequest(std::string list_name,
1251 std::string path_info)
1252 {
1253 std::vector<std::string>::iterator lb=mlm_dirs.begin(),
1254 le=mlm_dirs.end();
1255
1256 while (lb != le)
1257 {
1258 std::string d=*lb++;
1259
1260 if (webmlmd::basename(d) == list_name)
1261 {
1262 if (chdir(d.c_str()) < 0)
1263 {
1264 cginocache();
1265 std::cout << "Content-Type: text/plain"
1266 << std::endl
1267 << std::endl;
1268 perror(d.c_str());
1269 return;
1270 }
1271 listrequest2(list_name, path_info);
1272 return;
1273 }
1274 }
1275
1276 cginocache();
1277 std::cout << "Content-Type: text/plain"
1278 << std::endl
1279 << std::endl
1280 << "List " << list_name << " not found."
1281 << std::endl;
1282 return;
1283 }
1284
main2(void * dummy)1285 static void main2(void *dummy)
1286 {
1287 cgi_setup();
1288
1289 std::string n=webmlmd::list_name();
1290
1291 if (n.size() == 0)
1292 {
1293 webmlmd::showhtmlform(TEMPLATEDIR "/webmlmidx.html");
1294 }
1295 else
1296 {
1297 listrequest(n, list_cmd());
1298 }
1299
1300 std::cout.flush();
1301 }
1302