1 /*
2 ** Copyright 2000-2009 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #include "config.h"
7
8 #include "afx/afx.h"
9 #include "rfc822/rfc822.h"
10 #include "random128/random128.h"
11 #include "numlib/numlib.h"
12 #include "dbobj.h"
13
14 #if HAVE_LOCALE_H
15 #include <locale.h>
16 #endif
17
18 #include <iostream>
19 #include <sstream>
20 #include <iomanip>
21 #include <fstream>
22 #include <signal.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <ctype.h>
33 #include <time.h>
34 #include "mydirent.h"
35 #include <sysexits.h>
36
37 #include "cmlm.h"
38 #include "cmlmsublist.h"
39 #include "cmlmsubunsub.h"
40 #include "cmlmsubunsubmsg.h"
41 #include "cmlmcmdmisc.h"
42 #include "cmlmstartmail.h"
43 #include "cmlmarchive.h"
44 #include "cmlmmoderate.h"
45 #include "cmlmbounce.h"
46 #include "cmlmcleanup.h"
47 #include "cmlmfetch.h"
48
49 #include <string>
50 #include <vector>
51 #include <set>
52
53 static const char rcsid[]="$Id: cmlm.C,v 1.33 2009/11/08 18:14:47 mrsam Exp $";
54
55 // Keywords we try to trap for messages sent to the list address.
56
57
58 static const char *admin_keywords[]={
59 "subscribe",
60 "unsubscribe",
61 "help",
62 0
63 };
64
usage()65 static void usage()
66 {
67 std::cerr << "Usage: cmlm [command] [directory] [options]..." << std::endl;
68 exit(1);
69 }
70
71
72 static int cmdctlmsg(const std::vector<std::string> &);
73 static int cmdmsg(const std::vector<std::string> &);
74
75 static struct ncmdtab {
76 const char *cmdname;
77 int (*cmdfunc)(const std::vector<std::string> &);
78 } ncommands[]= {
79 {"create", cmdcreate},
80 {"update", cmdupdate},
81 {"ctlmsg", cmdctlmsg},
82 {"msg", cmdmsg},
83 {"set", cmdset},
84 {"sub", cmdsub},
85 {"unsub", cmdunsub},
86 {"lsub", cmdlsub},
87 {"export", cmdexport},
88 {"import", cmdimport},
89 {"laliases", cmdlaliases},
90 {"info", cmdinfo},
91 {"hourly", cmdhourly},
92 {"daily", cmddaily},
93 {"digest", cmddigest},
94 };
95
main(int argc,char ** argv)96 int main(int argc, char **argv)
97 {
98 if (argc < 3) usage();
99
100 setlocale(LC_ALL, "");
101
102 signal(SIGPIPE, SIG_IGN);
103 signal(SIGCHLD, SIG_DFL);
104
105 const char *cmd=argv[1];
106 const char *dir=argv[2];
107
108 if (strcmp(cmd, "create") == 0)
109 {
110 int i;
111
112 for (i=3; i<argc; i++)
113 if (strncmp(argv[i], "ADDRESS=", 8) == 0)
114 break;
115
116 if (i >= argc)
117 {
118 std::cerr << "Missing ADDRESS." << std::endl;
119 return (EX_SOFTWARE);
120 }
121
122 if (mkdir(dir, 0700))
123 {
124 perror(dir);
125 exit(1);
126 }
127 }
128
129 if (chdir(dir))
130 {
131 perror(dir);
132 exit(1);
133 }
134
135 unsigned i;
136
137 std::vector<std::string> argv_cpy;
138
139 argv_cpy.insert(argv_cpy.end(), argv+3, argv+argc);
140 for (i=0; i<sizeof(ncommands)/sizeof(ncommands[0]); i++)
141 if (strcmp(ncommands[i].cmdname, cmd) == 0)
142 exit ( (*ncommands[i].cmdfunc)(argv_cpy));
143
144
145 usage();
146 return (0);
147 }
148
149 ////////////////////////////////////////////////////////////////////////////
150 //
151 // Control message
152 //
153
154 static int help();
155
cmdctlmsg(const std::vector<std::string> &)156 static int cmdctlmsg(const std::vector<std::string> &)
157 {
158 const char *cmd=getenv("DEFAULT");
159
160 if (!cmd) cmd="";
161
162 if (strcasecmp(cmd, "help") == 0)
163 return (help());
164
165 if (strncasecmp(cmd, "subscribe", 9) == 0)
166 {
167 cmd += 9;
168 if (*cmd == 0 || (*cmd == '-' && *++cmd != 0))
169 return (dosub(cmd));
170 }
171 else if (strncasecmp(cmd, "alias-subscribe", 15) == 0)
172 {
173 cmd += 15;
174 if (*cmd == 0 || (*cmd == '-' && *++cmd != 0))
175 return (doalias(cmd));
176 }
177 else if (strncasecmp(cmd, "modsubconfirm-", 14) == 0)
178 {
179 cmd += 14;
180 if (*cmd)
181 return (domodsub(cmd));
182 }
183 else if (strncasecmp(cmd, "unsubscribe", 11) == 0)
184 {
185 cmd += 11;
186 if (*cmd == 0 || (*cmd == '-' && *++cmd != 0))
187 return (dounsub(cmd));
188 }
189 else if (strncasecmp(cmd, "subconfirm-", 11) == 0)
190 {
191 cmd += 11;
192 if (*cmd)
193 return (dosubunsubconfirm(cmd, "sub",
194 docmdsub,
195 "sub2.tmpl",
196 "sub3.tmpl", 0));
197 }
198 else if (strncasecmp(cmd, "aliasconfirm-", 13) == 0)
199 {
200 cmd += 13;
201 if (*cmd)
202 {
203 int rc=dosubunsubconfirm(cmd, "alias",
204 docmdalias,
205 "sub2.tmpl",
206 "sub3.tmpl", 1);
207 if (rc == 9)
208 rc=EX_SOFTWARE;
209 return (rc);
210 }
211 }
212 else if (strncasecmp(cmd, "bounce-", 7) == 0)
213 {
214 cmd += 7;
215 if (*cmd)
216 return (dobounce(cmd));
217 }
218 else if (strncasecmp(cmd, "bounce1-", 8) == 0)
219 {
220 if (*cmd)
221 return (dobounce1(cmd));
222 }
223 else if (strncasecmp(cmd, "bounce2-", 8) == 0)
224 {
225 if (*cmd)
226 return (dobounce2(cmd));
227 }
228 else if (strncasecmp(cmd, "unsubconfirm-", 13) == 0)
229 {
230 cmd += 13;
231 if (*cmd)
232 return (dosubunsubconfirm(cmd, "unsub",
233 docmdunsub,
234 "unsub2.tmpl",
235 "unsub3.tmpl", 0));
236 }
237 else if (strcasecmp(cmd, "moderate") == 0)
238 {
239 return (cmdmoderate());
240 }
241 else if (strncasecmp(cmd, "index", 5) == 0)
242 {
243 cmd += 5;
244 if (*cmd == 0 || *cmd++ == '-')
245 return (doindex(cmd));
246 }
247 else if (strncasecmp(cmd, "fetch-", 6) == 0)
248 {
249 cmd += 6;
250 if (*cmd)
251 return (dofetch(cmd));
252 }
253 std::cerr << "Invalid address.\n";
254 return (1);
255 }
256
257 //////////////////////////////////////////////////////////////////////////
258 //
259 // Send back a template, followed by copy of the original headers
260 //
261 //////////////////////////////////////////////////////////////////////////
262
ack_template(std::ostream & fs,const char * filename,std::string webcmd,std::string msg)263 void ack_template(std::ostream &fs, const char *filename,
264 std::string webcmd, std::string msg)
265 {
266 fs << "Mime-Version: 1.0" << std::endl
267 << "Content-Type: multipart/mixed; boundary=courier_mlm_bound"
268 << std::endl
269 << std::endl
270 << std::endl
271 << "--courier_mlm_bound"
272 << std::endl;
273
274 copy_template(fs, filename, webcmd);
275
276 fs << std::endl << "--courier_mlm_bound" << std::endl
277 << "Content-Type: text/rfc822-headers; charset=iso-8859-1"
278 << std::endl
279 << "Content-Transfer-Encoding: 8bit"
280 << std::endl
281 << std::endl;
282
283 std::istringstream i(msg);
284 std::string buf;
285
286 while (std::getline(i, buf).good())
287 {
288 if (buf == "")
289 break;
290
291 fs << buf << std::endl;
292 }
293
294 fs << std::endl << "--courier_mlm_bound--" << std::endl;
295 }
296
simple_template(std::ostream & fs,const char * filename,std::string msg)297 void simple_template(std::ostream &fs, const char *filename, std::string msg)
298 {
299 copy_template(fs, filename, "");
300
301 fs << MSGSEPARATOR << std::endl << std::endl;
302
303 std::istringstream i(msg);
304 std::string buf;
305
306 while (std::getline(i, buf).good())
307 {
308 if (buf == "")
309 break;
310
311 fs << buf << std::endl;
312 }
313 }
314
copy_template(std::ostream & fs,const char * filename,std::string webcmd)315 void copy_template(std::ostream &fs, const char *filename,
316 std::string webcmd)
317 {
318 std::ifstream ifs(filename);
319 std::string buf;
320
321 if (!ifs.is_open())
322 {
323 perror(filename);
324 exit(1);
325 }
326
327 while (std::getline(ifs, buf).good())
328 {
329 std::string::iterator b=buf.begin(), e=buf.end(), p=b;
330
331 while ((p=std::find(p, e, '[')) != e)
332 {
333 std::string::iterator q=p;
334 size_t offset=q-b;
335
336
337 p=std::find(p, e, ']');
338
339 if (p != e)
340 ++p;
341
342 std::string token(q, p);
343
344 std::string repl;
345
346 if (token == "[url]")
347 {
348 repl=cmdget_s("URL") + "/" + webcmd;
349 }
350 else
351 {
352 if (webcmd != "" && cmdget_s("URL") != "")
353 copy_template(fs,
354 token.substr(1,
355 token.size()
356 -2)
357 .c_str(),
358 webcmd);
359 }
360
361 buf=std::string(b, q)
362 + repl
363 + std::string(p, e);
364
365 b=buf.begin();
366 e=buf.end();
367 p=b + offset + repl.size();
368 }
369
370 fs << buf << std::endl;
371 }
372 }
373
374
375 //
376 // Determine if this is a good confirm.
377 //
checkconfirm(std::string msg)378 bool checkconfirm(std::string msg)
379 {
380 std::string subject=header_s(msg, "subject");
381
382 static const char yes[]="yes";
383
384 if (cmdget_s("SIMPLECONFIRM") == "1")
385 return (true);
386
387 if (std::search(subject.begin(), subject.end(),
388 yes, yes+sizeof(yes)-1) == subject.end())
389 return false;
390
391 return true;
392 }
393
394 /////////////////////////////////////////////////////////////////////////////
395 //
396 // Distribute a message.
397 //
398 /////////////////////////////////////////////////////////////////////////////
399
400 static const char *fn;
401 static const char *fn2;
402
sighandler(int n)403 static RETSIGTYPE sighandler(int n)
404 {
405 unlink(fn);
406 if (fn2) unlink(fn2);
407 _exit(1);
408 #if RETSIGTYPE != void
409 return (0);
410 #endif
411 }
412
trapsigs(const char * p)413 void trapsigs(const char *p)
414 {
415 fn=p;
416 fn2=0;
417 signal(SIGINT, sighandler);
418 signal(SIGHUP, sighandler);
419 signal(SIGTERM, sighandler);
420 signal(SIGPIPE, SIG_IGN);
421 }
422
trapsig2(const char * p)423 static void trapsig2(const char *p)
424 {
425 fn2=p;
426 }
427
clearsigs(int rc)428 void clearsigs(int rc)
429 {
430 if (rc)
431 {
432 unlink(fn);
433 if (fn2) unlink(fn2);
434 }
435
436 signal(SIGINT, SIG_DFL);
437 signal(SIGHUP, SIG_DFL);
438 signal(SIGTERM, SIG_DFL);
439 }
440
441 static int savemsg(std::istream &, std::ostream &);
442
cmdmsg(const std::vector<std::string> & argv)443 static int cmdmsg(const std::vector<std::string> &argv)
444 {
445 std::string postoptions= cmdget_s("POST");
446
447 if (postoptions == "mod")
448 {
449 int rc=0;
450
451 //
452 // Insert into the moderators queue
453 //
454
455 std::string modfilename=mktmpfilename();
456 std::string tmodname=TMP "/" + modfilename;
457 std::string mmodname=MODQUEUE "/" + modfilename;
458
459 trapsigs(tmodname.c_str());
460
461 std::fstream modfs(tmodname.c_str(),
462 std::ios::in | std::ios::out | std::ios::trunc);
463
464 if (!modfs.is_open())
465 {
466 perror(tmodname.c_str());
467 rc=EX_TEMPFAIL;
468 }
469
470 const char *token=random128_alpha();
471
472 if (rc == 0)
473 {
474 modfs << "X-Magic-Token: " << token << std::endl;
475 rc=savemsg(std::cin, modfs);
476 }
477
478 ExclusiveLock modqueue_lock(MODQUEUELOCKFILE);
479
480 if (rc || rename(tmodname.c_str(), mmodname.c_str()))
481 {
482 modfs.close();
483 clearsigs(rc);
484 return (rc);
485 }
486
487 clearsigs(0);
488 modfs.close();
489
490 int fd_msg=open(mmodname.c_str(), O_RDONLY);
491 if (fd_msg < 0)
492 {
493 perror(mmodname.c_str());
494 exit(EX_OSERR);
495 }
496
497 afxipipestream ifs(fd_msg);
498
499 rc=sendinitmod(ifs, modfilename.c_str(), "modtext.tmpl");
500 return (rc);
501 }
502 return (postmsg(std::cin, savemsg));
503 }
504
505 //----------------------------------------------------------------------------
506 //
507 // The postmsg() function posts a message to the mailing list. postmsg()
508 // is used to post messages to both unmoderated and moderated mailing lists.
509 // For a moderated mailing list, postmsg() is called to post a moderator
510 // approved message. postmsg()'s behavior is controlled by passing it two
511 // arguments:
512 //
513 // msgsource - the source of the message. For unmoderated mailing lists,
514 // this is cin/stdin, the message as its being delivered directly to
515 // couriermlm. For moderated mailing list, this is an opened file in
516 // modqueue, that the moderator just approved.
517 //
518 // savefunc - a function that copies a message from an input source (the
519 // msgsource argument), to an output stream, transforming the message for
520 // mailing list posting. For unmoderated mailing lists, this is savemsg(),
521 // which removed and adds headers based upon the headerdel/headeradd control
522 // files. For a moderated list this is a stub function that simply copies
523 // an input stream to an output stream (that's because savemsg() was already
524 // called previously when the message was inserted into the moderator queue.
525 //
526 // In both cases, savefunc *must* call flush on the output stream!
527 //
528 // postmsg() allocates a message sequence number in the archive directory,
529 // calls savefunc() to save the message in the archive directory, then
530 // repeatedly mails the message to everyone on the mailing list.
531 //
532 //----------------------------------------------------------------------------
533
postmsg(std::istream & msgsource,int (* savefunc)(std::istream &,std::ostream &))534 int postmsg(std::istream &msgsource, int (*savefunc)(std::istream &, std::ostream &))
535 {
536 char buf[NUMBUFSIZE+100], buf2[NUMBUFSIZE+100];
537 unsigned long nextseqno;
538 int rc;
539 std::fstream fs;
540
541 {
542 Archive archive;
543
544 if ( (rc=archive.get_seq_no(nextseqno)) != 0)
545 return (rc);
546
547 libmail_str_off_t(nextseqno, buf);
548
549 if ( (rc=archive.save_seq_no()) != 0)
550 return (rc);
551
552 std::string filename_str(archive.filename(nextseqno));
553
554 trapsigs(filename_str.c_str());
555
556 fs.open(filename_str.c_str(),
557 std::ios::in | std::ios::out | std::ios::trunc);
558 if (!fs.is_open())
559 {
560 perror(filename_str.c_str());
561 rc=EX_TEMPFAIL;
562 }
563 else rc=(*savefunc)(msgsource, fs);
564
565 if (rc == 0)
566 {
567 if (rename(NEXTSEQNO, SEQNO))
568 {
569 perror(SEQNO);
570 rc=EX_TEMPFAIL;
571 }
572 }
573
574 std::string digestdir;
575
576 if (rc == 0)
577 {
578 digestdir=cmdget_s("DIGEST");
579 if (digestdir.size() > 0)
580 {
581 digestdir += "/" MODQUEUE "/";
582 digestdir += buf;
583 trapsig2(digestdir.c_str());
584 if ((rc=link(filename_str.c_str(),
585 digestdir.c_str())) != 0)
586 perror(digestdir.c_str());
587 }
588 }
589
590 clearsigs(rc);
591 }
592 if (rc == 0)
593 {
594 strcpy(buf2, "bounce-");
595 strcat(buf2, buf);
596 post(fs, buf2);
597 }
598
599 fs.close();
600 return (rc);
601 }
602
get_post_address(const char * p,std::string & addr)603 static void get_post_address(const char *p, std::string &addr)
604 {
605 if (strncmp(p, "x-couriermlm-date:", 18) == 0)
606 {
607 p += 18;
608 while (*p && *p != '\n' && isspace((int)(unsigned char)*p))
609 ++p;
610 while (isdigit((int)(unsigned char)*p))
611 {
612 ++p;
613 }
614 while (*p && *p != '\n' && isspace((int)(unsigned char)*p))
615 ++p;
616
617 size_t i;
618 bool atfound=false;
619
620 for (i=0; p[i] && p[i] != '\n'; i++)
621 if (p[i] == '@')
622 atfound=true;
623
624 if (atfound)
625 addr=std::string(p, p+i);
626 }
627 }
628
post(std::istream & fs,const char * verp_ret)629 void post(std::istream &fs, const char *verp_ret)
630 {
631 SubscriberList sublist;
632 StartMail mail(fs, verp_ret);
633 std::string addr, lastaddr;
634
635 if ( sublist.Next(addr) )
636 {
637 get_post_address(sublist.sub_info.c_str(), addr);
638 mail.To(addr);
639 lastaddr=addr;
640
641 // Call Send() when we change domains, VERP works better
642 // that way.
643
644 while (sublist.Next(addr))
645 {
646 const char *a=strrchr(addr.c_str(), '@');
647 const char *b=strrchr(lastaddr.c_str(), '@');
648
649 if (!a || !b || strcmp(a, b))
650 mail.Send();
651
652 get_post_address(sublist.sub_info.c_str(), addr);
653 mail.To(addr);
654 lastaddr=addr;
655 }
656 }
657 mail.Send();
658 }
659
savemsg(std::istream & msgs,std::ostream & fs)660 static int savemsg(std::istream &msgs, std::ostream &fs)
661 {
662 const char *dt=getenv("DTLINE");
663 std::string buf;
664
665 int n;
666 std::string keyword= cmdget_s("KEYWORD"), keywords;
667 int adminrequest=0;
668
669 if (keyword.size() > 0)
670 keywords="["+keyword+"]";
671
672 if (dt && *dt)
673 fs << dt << std::endl;
674
675 {
676 std::ifstream ifs(HEADERADD);
677
678 if (ifs.is_open())
679 {
680 std::string line;
681
682 while (std::getline(ifs, line).good())
683 fs << line << std::endl;
684 }
685 }
686
687 std::set<std::string> delete_headers;
688
689 {
690 std::ifstream ifs(HEADERDEL);
691
692 std::string header_name;
693
694 if (ifs.is_open())
695 while (!std::getline(ifs, header_name).eof())
696 {
697 std::transform(header_name.begin(),
698 header_name.end(),
699 header_name.begin(),
700 std::ptr_fun(::tolower));
701
702 TrimLeft(header_name);
703 TrimRight(header_name);
704
705 delete_headers.insert(header_name);
706 }
707 }
708
709 std::string h, from;
710 bool dodelete=false;
711
712 while (std::getline(msgs, buf).good())
713 {
714 if (buf.size() == 0)
715 break;
716
717 size_t colon_pos=buf.find(':');
718
719 if (isspace((int)(unsigned char)*buf.c_str()))
720 colon_pos=buf.npos;
721
722 if (colon_pos == buf.npos)
723 {
724 if (h == "from:")
725 {
726 from += " ";
727 from += buf;
728 }
729
730 if (!dodelete)
731 fs << buf << std::endl;
732 continue;
733 }
734
735 h=buf.substr(0, ++colon_pos);
736 std::transform(h.begin(), h.end(), h.begin(),
737 std::ptr_fun(::tolower));
738
739 if (h == "subject:")
740 {
741 std::string firstword=buf.substr(colon_pos);
742 TrimLeft(firstword);
743
744 firstword=
745 std::string(firstword.begin(),
746 std::find_if(firstword
747 .begin(),
748 firstword
749 .end(),
750 std::not1
751 (std::ptr_fun
752 (::isalpha)))
753 );
754
755
756 std::transform(firstword.begin(),
757 firstword.end(),
758 firstword.begin(),
759 std::ptr_fun(::tolower));
760
761 size_t i;
762
763 for (i=0; admin_keywords[i]; i++)
764 {
765 if (firstword == admin_keywords[i])
766 {
767 adminrequest=1;
768 break;
769 }
770 }
771 }
772
773 if (h == "subject:" && keyword.size() > 0)
774 {
775
776 std::string keyword_br="[" + keyword + "]";
777
778 if (std::search(buf.begin(), buf.end(),
779 keyword_br.begin(),
780 keyword_br.end()) ==
781 buf.end())
782 {
783
784 buf=buf.substr(0, colon_pos) + " "
785 + keyword_br
786 + buf.substr(colon_pos);
787 }
788 }
789
790 dodelete=delete_headers.find(h) != delete_headers.end();
791
792 if (h == "from:")
793 from=buf.substr(colon_pos);
794
795 if (!dodelete)
796 fs << buf << std::endl;
797 }
798
799 fs << "\n";
800
801
802 struct rfc822t *t=rfc822t_alloc_new(from.c_str(), 0, 0);
803
804 if (!t)
805 {
806 perror("malloc");
807 return (EX_TEMPFAIL);
808 }
809
810 struct rfc822a *a=rfc822a_alloc(t);
811
812 if (!a)
813 {
814 rfc822t_free(t);
815 perror("malloc");
816 return (EX_TEMPFAIL);
817 }
818
819 char *nn=0;
820
821 if (a->naddrs > 0)
822 {
823 nn=rfc822_getaddr(a, 0);
824 if (!nn)
825 {
826 rfc822a_free(a);
827 rfc822t_free(t);
828 perror("malloc");
829 return (EX_TEMPFAIL);
830 }
831 }
832 rfc822a_free(a);
833 rfc822t_free(t);
834 if (nn)
835 {
836 from=nn;
837 free(nn);
838 }
839 else from="";
840
841
842 int first_line=1;
843
844 while (std::getline(msgs, buf).good())
845 {
846 fs << buf << std::endl;
847
848 // Check for "subscribe/unsubscribe on the first line" wankers.
849
850 if (!first_line) continue;
851
852 std::string firstword(buf.begin(),
853 std::find_if(buf.begin(),
854 buf.end(),
855 std::not1(std::ptr_fun
856 (::isalpha))));
857
858 std::transform(firstword.begin(), firstword.end(),
859 firstword.begin(), std::ptr_fun(::tolower));
860
861 for (n=0; admin_keywords[n]; n++)
862 {
863 if (firstword == admin_keywords[n])
864 adminrequest=1;
865 }
866 }
867
868 fs.flush();
869 fs.seekp(0);
870 if (fs.bad() || fs.fail())
871 {
872 perror( "write" );
873 return (EX_TEMPFAIL);
874 }
875
876 if ( cmdget_s("NOBOZOS") == "0")
877 adminrequest=0;
878
879 if (adminrequest)
880 {
881 std::ifstream ifs("adminrequest.tmpl");
882 int flag=0;
883
884 if (ifs.is_open())
885 {
886 while (std::getline(ifs, buf).good())
887 {
888 std::cout << buf << std::endl;
889 flag=1;
890 }
891 }
892
893 if (!flag) std::cout << "Administractive request blocked."
894 << std::endl;
895 std::cout << std::flush;
896 return (EX_NOUSER);
897 }
898
899
900 int rc=is_subscriber(from.c_str());
901
902 if (rc == EX_NOUSER)
903 {
904 std::string postoptions= cmdget_s("POST");
905
906 if (postoptions == "all")
907 rc=0;
908 }
909
910 if (rc)
911 {
912 if (rc == EX_NOUSER)
913 {
914 std::cout << "You are not subscribed to this mailing list."
915 << std::endl;
916 }
917 rc=EX_NOPERM;
918 return (rc);
919 }
920
921 return (0);
922 }
923
copy_report(const char * s,afxopipestream & o)924 void copy_report(const char *s, afxopipestream &o)
925 {
926 int i_fd=open(s, O_RDONLY);
927
928 if (i_fd < 0)
929 {
930 perror(s);
931 exit(1);
932 }
933
934 afxipipestream i(i_fd);
935
936 if (copyio_noseek(i, o) < 0)
937 {
938 perror(s);
939 exit(1);
940 }
941 }
942
copyio(afxipipestream & i,afxopipestream & o)943 int copyio(afxipipestream &i, afxopipestream &o)
944 {
945 i.seekg(0);
946 if (i.bad())
947 {
948 perror("seek");
949 return (EX_OSERR);
950 }
951
952 return (copyio_noseek(i, o));
953 }
954
copyio_noseek(afxipipestream & i,afxopipestream & o)955 int copyio_noseek(afxipipestream &i, afxopipestream &o)
956 {
957 return (copyio_noseek_cnt(i, o, 0));
958 }
959
copyio_noseek_cnt(afxipipestream & i,afxopipestream & o,unsigned long * maxbytes)960 int copyio_noseek_cnt(afxipipestream &i, afxopipestream &o,
961 unsigned long *maxbytes)
962 {
963 char buf[BUFSIZ];
964
965 i.read(buf, sizeof(buf));
966
967 int x;
968
969 while ((x=i.gcount()) > 0)
970 {
971 if (maxbytes)
972 {
973 if ((unsigned long)x > *maxbytes)
974 {
975 std::cerr << "Message too large." << std::endl;
976 return (EX_SOFTWARE);
977 }
978 *maxbytes -= x;
979 }
980
981 o.write(buf, x);
982 i.read(buf, sizeof(buf));
983 }
984 if (o.bad() || i.bad())
985 return (EX_OSERR);
986 return (0);
987 }
988
tryboundary(afxipipestream & io,const char * boundary)989 static int tryboundary(afxipipestream &io, const char *boundary)
990 {
991 int boundary_l=strlen(boundary);
992 std::string line;
993
994 io.clear();
995 io.seekg(0);
996 if (io.fail()) return (-1);
997
998 while (std::getline(io, line).good())
999 {
1000 if (strncmp(line.c_str(), boundary, boundary_l) == 0)
1001 return (1);
1002 }
1003 io.clear();
1004 return (0);
1005 }
1006
1007 // Create MIME multipart boundary delimiter.
1008
mkboundary_msg_s(afxipipestream & io)1009 std::string mkboundary_msg_s(afxipipestream &io)
1010 {
1011 std::string base=mkfilename();
1012 unsigned i=0;
1013 std::string boundary;
1014 int rc;
1015
1016 do
1017 {
1018 std::ostringstream o;
1019
1020 o << base << "." << std::setw(3) << std::setfill('0')
1021 << i++;
1022
1023 boundary=o.str();
1024
1025 } while ((rc=tryboundary(io, boundary.c_str())) > 0);
1026 if (rc) return ("");
1027 return (boundary);
1028 }
1029
help()1030 static int help()
1031 {
1032 std::string msg(readmsg());
1033 std::string addr=returnaddr(msg, "");
1034
1035 pid_t p;
1036
1037 if (addr.find('@') == addr.npos) return (0);
1038
1039 time_t curtime;
1040
1041 time(&curtime);
1042
1043 addrlower(addr);
1044
1045 std::string vaddr= TMP "/help." + toverp(addr);
1046
1047 struct stat sb;
1048
1049 if (stat(vaddr.c_str(), &sb) == 0 && sb.st_mtime + 30 * 60 > curtime)
1050 return (0);
1051
1052 close(open(vaddr.c_str(), O_RDWR|O_CREAT, 0755));
1053
1054 std::string owner=get_verp_return("owner");
1055
1056 int nodsn= (cmdget_s("NODSN") == "1");
1057 afxopipestream ack(sendmail_bcc(p, owner, nodsn));
1058
1059 ack << "From: " << myname() << " <" << owner << ">" << std::endl
1060 << "To: " << addr << std::endl
1061 << "Bcc: " << addr << std::endl;
1062
1063 simple_template(ack, "help.tmpl", msg);
1064 ack.close();
1065 return (wait4sendmail(p));
1066 }
1067