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