1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Response handling.
6  */
7 
8 /* aux.c */
9 
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <time.h>
14 #include <string.h>
15 #include "config.h"
16 #include "global.h"
17 #include "chset.h"
18 #include "execute.h"
19 #include "news.h"
20 #include "nn.h"
21 #include "nntp.h"
22 #include "reroute.h"
23 #include "nn_term.h"
24 
25 extern char    *temp_file;
26 
27 extern int      novice;
28 extern char     delayed_msg[];
29 extern char    *pager;
30 extern char    *inews_program;
31 extern int      inews_pipe_input;
32 extern int      use_mmdf_folders;
33 extern int      data_bits;
34 extern int      append_sig_post;
35 extern char    *home_directory;
36 
37 int             empty_answer_check = 1;	/* reject replies that are not edited */
38 int             query_signature = 1;	/* query user */
39 char           *editor_program = NULL;
40 int             use_ed_line = 1;
41 char           *sign_type = SIGN_TYPE;
42 char           *spell_checker = NULL;
43 char           *mail_alias_expander = NULL;
44 char           *mailer_program = REC_MAIL;
45 int             mailer_pipe_input = 1;
46 int             response_check_pause = 0;	/* time to wait for
47 						 * background cmds */
48 char           *response_dflt_answer = "send";
49 
50 #ifndef NNTP
51 /*
52  * invoke aux shell script with suitable arguments
53  *
54  * WARNING: record may be NULL, so it must be the last argument!!
55  */
56 
57 static void
aux_param_bool(FILE * f,char * name,int on)58 aux_param_bool(FILE * f, char *name, int on)
59 {
60     fprintf(f, "%s=%s\n", name, on ? "true" : "false");
61 }
62 
63 static void
aux_param_str(FILE * f,char * name,char * str)64 aux_param_str(FILE * f, char *name, char *str)
65 {
66     int             xport = (*name == '*');
67 
68     if (xport)
69 	name++;
70     fprintf(f, "%s='%s'\n", name, str != NULL ? str : "");
71     if (xport)
72 	fprintf(f, "export %s\n", name);
73 }
74 
75 static void
aux_param_int(FILE * f,char * name,int i)76 aux_param_int(FILE * f, char *name, int i)
77 {
78     fprintf(f, "%s=%d\n", name, i);
79 }
80 
81 int
aux_sh(article_header * ah,char * script,char * prog,char * action,char * record,char * sent_fmt,int append_sig,int ed_line)82 aux_sh(article_header * ah, char *script, char *prog, char *action, char *record, char *sent_fmt, int append_sig, int ed_line)
83 {
84     FILE           *param;
85     char           *args[10], *fn;
86     char            route[512], *poster;
87     register char **ap = args;
88 
89     if (strncmp(prog, "COMPLETE", 8) == 0)
90 	goto no_params;
91 
92     param = open_file(relative(nn_directory, ".param"), OPEN_CREATE);
93     if (param == NULL) {
94 	strcpy(delayed_msg, "cannot create .param file for aux script");
95 	return 1;
96     }
97     if (getenv("LOGNAME") == NULL)
98 	aux_param_str(param, "*LOGNAME", user_name());
99 
100     /*
101      * with the nn-directory variable, the aux script needs to know where to
102      * find the .param file, can't always look in $HOME/.nn
103      */
104     if (!getenv("NNDIR")) {
105 	char            ebuf[FILENAME + 8];
106 	sprintf(ebuf, "NNDIR=%s", nn_directory);
107 	putenv(ebuf);
108     }
109     if (strcmp(prog, "cancel") == 0) {
110 	aux_param_str(param, "ART_ID", action);	/* article id for cancel */
111 	aux_param_str(param, "GROUP", record);	/* group name for cancel */
112 	if (news.ng_dist)
113 	    aux_param_str(param, "DIST", news.ng_dist);	/* dist for cancel */
114 	else
115 	    aux_param_str(param, "DIST", "none");	/* dist for cancel */
116     } else {
117 	aux_param_str(param, "FIRST_ACTION", action);
118 	aux_param_str(param, "RECORD", record);
119 	aux_param_str(param, "WORK", temp_file);
120 	aux_param_int(param, "ED_LINE", ed_line);
121 
122 	aux_param_bool(param, "NOVICE", novice);
123 	aux_param_bool(param, "EMPTY_CHECK", empty_answer_check);
124 	aux_param_bool(param, "APPEND_SIG", append_sig);
125 	aux_param_bool(param, "QUERY_SIG", append_sig && query_signature);
126 
127 	if (editor_program != NULL)
128 	    aux_param_str(param, "EDITOR", editor_program);
129 	aux_param_str(param, "SPELL_CHECKER", spell_checker);
130 	aux_param_str(param, "ALIAS_EXPANDER", mail_alias_expander);
131 	aux_param_str(param, "PAGER", pager);
132 	aux_param_str(param, "MAILER", mailer_program);
133 	aux_param_bool(param, "MAILER_PIPE", mailer_pipe_input);
134 	aux_param_int(param, "WAIT_PERIOD", response_check_pause);
135 	aux_param_str(param, "DFLT_ANSW", response_dflt_answer);
136 	aux_param_str(param, "POST", inews_program);
137 	aux_param_bool(param, "POST_PIPE", inews_pipe_input);
138 	aux_param_str(param, "MMDF_SEP", use_mmdf_folders ? "\1\1\1\1" : "");
139 
140 	aux_param_str(param, "CHSET_NAME", curchset->cs_name);
141 	if (curchset->cs_width != 0)
142 	    aux_param_int(param, "CHSET_WIDTH", curchset->cs_width);
143 	else
144 	    aux_param_int(param, "CHSET_WIDTH", data_bits);
145 
146 	if (current_group != NULL) {
147 	    aux_param_str(param, "*G", current_group->group_name);
148 	    if (ah == NULL)
149 		fn = NULL;
150 	    else
151 		fn = group_path_name;
152 	    aux_param_str(param, "*A", fn != NULL ? fn : "");
153 	}
154 	poster = NULL;
155 	if (ah != NULL && strcmp(prog, "follow") == 0) {
156 	    poster = news.ng_reply != NULL ? news.ng_reply : news.ng_from;
157 	    if (poster && reroute(route, poster))
158 		poster = route;
159 	    else
160 		poster = NULL;
161 	}
162 	aux_param_str(param, "POSTER_ADR", poster);
163     }
164 
165     fclose(param);
166 
167 no_params:
168     stop_usage();
169 
170     /* OBS: relative() returns ptr to static data below */
171     *ap++ = "nnaux";
172     *ap++ = script != NULL ? script : relative(lib_directory, "aux");
173     *ap++ = prog;
174     *ap++ = temp_file;
175     *ap++ = NULL;
176 
177     if (execute(SHELL, args, 1)) {
178 	sprintf(delayed_msg, sent_fmt, " not");
179 	return 1;
180     }
181     sprintf(delayed_msg, sent_fmt, "");
182     return 0;
183 }
184 
185 #else				/* NNTP */
186 
187 #include <sys/stat.h>
188 #include <pwd.h>
189 
190 static char    *
tempsuffix(char * name,char * suffix)191 tempsuffix(char *name, char *suffix)
192 {
193     static char     concat_name[FILENAME];
194 
195     sprintf(concat_name, "%s%s", name, suffix);
196     return concat_name;
197 }
198 
199 static FILE    *
find_signature(void)200 find_signature(void)
201 {
202     struct passwd  *passwd;
203     char           *dotdir;
204     char            sigfile[FILENAME];
205     FILE           *fp;
206 
207     passwd = getpwuid(getuid());
208     if (passwd == NULL)
209 	return (NULL);
210     dotdir = passwd->pw_dir;
211 
212     if (dotdir[0] == '~') {
213 	(void) strcpy(sigfile, passwd->pw_dir);
214 	(void) strcat(sigfile, &dotdir[1]);
215     } else {
216 	(void) strcpy(sigfile, dotdir);
217     }
218     (void) strcat(sigfile, "/");
219     (void) strcat(sigfile, ".signature");
220 
221 #ifdef DEBUG
222     fprintf(stderr, "sigfile = '%s'\n", sigfile);
223     sleep(2);
224 #endif
225 
226     fp = fopen(sigfile, "r");
227 
228 #ifdef DEBUG
229     if (fp != NULL) {
230 	fprintf(stderr, "sigfile opened OK\n");
231 	sleep(2);
232     }
233 #endif
234 
235     return fp;
236 }
237 
238 #define MAX_SIGNATURE   4
239 
240 static void
append_signature(FILE * sigfile)241 append_signature(FILE * sigfile)
242 {
243     char            line[256];
244     char           *cp;
245     int             count = 0;
246     FILE           *temp;
247 
248     temp = open_file(temp_file, OPEN_APPEND);
249 
250 #ifdef DEBUG
251     if (temp)
252 	fprintf(stderr, "temp = %s\n", temp_file);
253     else
254 	fprintf(stderr, "temp = NULL\n");
255 #endif
256 
257     fprintf(temp, "-- \n");
258     while (fgets(line, sizeof(line), sigfile)) {
259 	count++;
260 	if (count > MAX_SIGNATURE) {
261 	    fprintf(stderr, "Warning: .signature files should be no longer than %d lines.\n", MAX_SIGNATURE);
262 	    fprintf(stderr, "(Only %d lines of your .signature were posted.)\n", MAX_SIGNATURE);
263 	    break;
264 	}
265 	if ((cp = strchr(line, '\n')))
266 	    *cp = '\0';
267 	fprintf(temp, "%s\n", line);
268     }
269     (void) fclose(temp);
270 
271 #ifdef DEBUG
272     printf(".signature appended (from %s)\n", sigfile);
273     sleep(10);
274 #endif
275 }
276 
277 
278 /*
279  * return codes:	0 post succeeded
280  *			1 post failed
281  *			22 aborted
282  *			   unable to stat temp file
283  *			   original and copy identical
284  *			   hold response
285  */
286 
287 int
aux_sh(article_header * ah,char * script,char * prog,char * action,char * record,char * sent_fmt,int append_sig,int ed_line)288 aux_sh(article_header * ah, char *script, char *prog, char *action, char *record, char *sent_fmt, int append_sig, int ed_line)
289 {
290     FILE           *sigfile, *temp, *tempf;
291     char            final[FILENAME], copy[FILENAME];
292     char            hdrs[FILENAME], bdy[FILENAME], sgn[FILENAME];
293     char            route[512], *poster = NULL;
294     int             goodsigntype = 0;
295     int             loop = 1, prmpt = 0;
296     char            cc[256], pr[80], pr1[80], fname[FILENAME], buf[80];
297     char            buf2[10];
298     char            lookfor[16], send[8], sent[8], sendpr[8], message[8];
299     int             x, act = response_dflt_answer[0];
300     struct stat     statb;
301 
302     if (strncmp(prog, "COMPLETE", 8) != 0) {
303 	poster = NULL;
304 	if (ah != NULL && strcmp(prog, "follow") == 0) {
305 	    poster = news.ng_reply != NULL ? news.ng_reply : news.ng_from;
306 	    if (poster && reroute(route, poster))
307 		poster = route;
308 	    else
309 		poster = NULL;
310 	}
311     }
312     stop_usage();
313 
314     cc[0] = CR;
315 
316     if (strncmp(prog, "COMPLETE", 8) == 0) {
317 	empty_answer_check = 0;
318 	action = "edit";
319 	if (strcmp(prog, "COMPLETE.post") == 0)
320 	    prog = "post";
321 	else
322 	    prog = "mail";
323 	copy_file(relative(nn_directory, "hold.work"), temp_file, 0);
324 	unlink(relative(nn_directory, "hold.work"));
325     }
326     temp = open_file(temp_file, OPEN_APPEND);
327 
328     if (strcmp(prog, "cancel") == 0) {
329 	fprintf(temp, "Newsgroups: %s\n", record);
330 	fprintf(temp, "Subject: cmsg cancel %s\n", action);
331 	fprintf(temp, "Control: cancel %s\n", action);
332 	if (news.ng_dist)
333 	    fprintf(temp, "Distribution: %s\n", news.ng_dist);
334 	fprintf(temp, "\n");
335 	fprintf(temp, "cancel %s in newsgroup %s\n", action, record);
336 	fprintf(temp, "\n");
337 	fprintf(temp, "This article was cancelled from within %s\n", version_id);
338 	fprintf(temp, "\n");
339 	fclose(temp);
340 	x = (nntp_post(temp_file));
341 	if (x != 0)
342 	    msg("Unable to cancel article");
343 	unlink(temp_file);
344 	return (x);
345     } else if (strcmp(prog, "post") == 0 || strcmp(prog, "follow") == 0) {
346 	strcpy(lookfor, "Newsgroups:");
347 	strcpy(send, "post ");
348 	strcpy(sent, "posted ");
349 	strcpy(sendpr, "p)ost ");
350 	strcpy(message, "article");
351     } else {
352 	strcpy(lookfor, "To:");
353 	strcpy(send, "send ");
354 	strcpy(sent, "sent ");
355 	strcpy(sendpr, "s)end ");
356 	strcpy(message, "letter");
357     }
358 
359     if (action != "send") {
360 	strcpy(copy, tempsuffix(temp_file, "C"));
361 	copy_file(temp_file, copy, 0);
362     }
363     strcpy(pr, "a)bort ");
364 
365     if (poster)
366 	strcat(pr, "c)c ");
367 
368     strcat(pr, "e)dit h)old ");
369 
370     if (spell_checker)
371 	strcat(pr, "i)spell ");
372 
373     strcat(pr, "m)ail ");
374 
375     if (strcmp(prog, "post") == 0 || strcmp(prog, "follow") == 0)
376 	strcat(pr, "p)ost ");
377 
378     strcat(pr, "r)eedit ");
379 
380     if (strcmp(prog, "post") != 0 && strcmp(prog, "follow") != 0)
381 	strcat(pr, "s)end ");
382 
383     strcat(pr, "v)iew w)rite S)ign ");
384 
385     if (response_dflt_answer) {
386 	if (strncmp(response_dflt_answer, "p", 1) || strncmp(response_dflt_answer, "s", 1)) {
387 	    strcpy(pr1, send);
388 	    strcat(pr1, message);
389 	} else {
390 	    strcpy(pr1, response_dflt_answer);
391 	}
392     } else {
393 	strcpy(pr1, "");
394     }
395 
396 
397     while (loop) {
398 	if (action != NULL) {
399 	    act = action[0];
400 	    action = NULL;
401 	} else if (prmpt) {
402 	    prompt_line = Lines - 1;
403 	    prompt("%s\r\nAction: (%s) ", pr, pr1);
404 	    act = get_c();
405 	    if (act == CR)
406 		act = response_dflt_answer[0];
407 	}
408 	prmpt = 1;
409 
410 	switch (act) {
411 	    case 'a':
412 		prompt_line = Lines - 1;
413 		prompt("Confirm abort: (y) ");
414 		switch (get_c()) {
415 		    case CR:
416 		    case 'y':
417 		    case 'Y':
418 			unlink(temp_file);
419 			unlink(copy);
420 			return (22);
421 		}
422 		break;
423 
424 	    case 'c':
425 		if (poster) {
426 		    prompt_line = Lines - 1;
427 		    prompt("CC: %s (y)", poster);
428 		    switch (get_c()) {
429 			case CR:
430 			case 'y':
431 			case 'Y':
432 			    msg("Sending copy to poster....");
433 			    strcpy(cc, poster);
434 			    break;
435 		    }
436 		} else
437 		    msg("Not doing follow-up");
438 		break;
439 
440 	    case 'e':
441 		if (!editor_program)
442 		    editor_program = getenv("EDITOR");
443 
444 		if (!editor_program)
445 		    editor_program = "vi";
446 
447 		strncpy(buf, editor_program, 50);
448 
449 		if (use_ed_line) {
450 		    sprintf(buf2, " +%d", ed_line);
451 		    strcat(buf, buf2);
452 		}
453 		strcat(buf, " ");
454 		strcat(buf, temp_file);
455 		unset_raw();
456 		system(buf);
457 		nn_raw();
458 
459 		if (stat(temp_file, &statb) < 0 || statb.st_size == 0) {
460 		    sprintf(delayed_msg, sent_fmt, " not");
461 		    unlink(temp_file);
462 		    unlink(copy);
463 		    return (22);
464 		}
465 		if (empty_answer_check)
466 		    if (cmp_file(temp_file, copy) != 1) {
467 			sprintf(delayed_msg, sent_fmt, " not");
468 			unlink(temp_file);
469 			unlink(copy);
470 			return (22);
471 		    }
472 		if (lookfor);	/* grep for lookfor */
473 
474 		break;
475 
476 	    case 'h':
477 		prompt_line = Lines - 1;
478 		prompt("Complete response later: (y) ");
479 		switch (get_c()) {
480 		    case CR:
481 		    case 'y':
482 		    case 'Y':
483 			copy_file(temp_file, relative(nn_directory, "hold.work"), 0);
484 			unlink(temp_file);
485 			unlink(copy);
486 			return (22);
487 		}
488 		break;
489 
490 	    case 'i':
491 		if (spell_checker) {
492 		    unset_raw();
493 		    clrdisp();
494 		    gotoxy(0, 1);
495 		    strncpy(buf, spell_checker, 50);
496 		    strcat(buf, " ");
497 		    strcat(buf, temp_file);
498 		    system(buf);
499 		    nn_raw();
500 		} else {
501 		    msg("spell-checker not defined");
502 		    user_delay(2);
503 		}
504 		break;
505 
506 	    case 'm':
507 		prompt_line = Lines - 1;
508 		prompt("To: ");
509 		unset_raw();
510 		if (get_line(&cc[0], sizeof(cc)) == 0)
511 		    cc[0] = CR;
512 		nn_raw();
513 		if (cc[0] != CR)
514 		    msg("Sending copy....");
515 		break;
516 
517 	    case 'n':
518 		act = 'a';
519 		prmpt = 0;
520 		break;
521 
522 	    case 'p':
523 	    case 's':
524 		loop = 0;
525 		break;
526 
527 	    case 'r':
528 		prompt_line = Lines - 1;
529 		prompt("Undo all changes? (n) ");
530 		switch (get_c()) {
531 		    case 'y':
532 		    case 'Y':
533 			copy_file(copy, temp_file, 0);
534 		}
535 		action = "edit";
536 		break;
537 
538 	    case 'v':
539 		unset_raw();
540 		clrdisp();
541 		gotoxy(0, 1);
542 		if (!pager)
543 		    pager = getenv("PAGER");
544 		if (!pager)
545 		    pager = "cat";
546 		strncpy(buf, pager, 50);
547 		strcat(buf, " ");
548 		strcat(buf, temp_file);
549 		system(buf);
550 		nn_raw();
551 		break;
552 
553 	    case 'w':
554 		prompt_line = Lines - 1;
555 		prompt("Append %s to file: ", message);
556 		strcpy(fname, get_s((char *) NULL, (char *) NULL, (char *) NULL, NULL_FCT));
557 		if (fname)
558 		    copy_file(temp_file, fname, 1);
559 		break;
560 
561 	    case 'y':
562 		act = response_dflt_answer[0];
563 		prmpt = 0;
564 		break;
565 
566 	    case 'S':
567 		unset_raw();
568 		strcpy(hdrs, tempsuffix(temp_file, "H"));
569 		strcpy(bdy, tempsuffix(temp_file, "B"));
570 		strcpy(sgn, tempsuffix(temp_file, "S"));
571 		sprintf(buf, "sed -e \'/^$/q\' < %s > %s", temp_file, hdrs);
572 		system(buf);
573 		sprintf(buf, "awk \'{if (S== 1) print $0; if ($0 == \"\") S=1}\' < %s > %s", temp_file, bdy);
574 		system(buf);
575 		if (strcmp(sign_type, "pgp") == 0) {
576 		    sprintf(buf, "pgp -stfaw < %s > %s", bdy, sgn);
577 		    goodsigntype = 1;
578 		} else if (strcmp(sign_type, "gpg") == 0) {
579 		    sprintf(buf, "gpg -sta < %s > %s", bdy, sgn);
580 		    goodsigntype = 1;
581 		}
582 		if (goodsigntype) {
583 		    system(buf);
584 		    sprintf(buf, "cat %s %s > %s", hdrs, sgn, temp_file);
585 		    system(buf);
586 		} else
587 		    msg("sign-type must be either pgp or gpg");
588 		sleep(5);
589 		unlink(hdrs);
590 		unlink(bdy);
591 		unlink(sgn);
592 		nn_raw();
593 		break;
594 	}
595 
596 	if (cc[0] != CR) {
597 	    strcpy(final, tempsuffix(temp_file, "F"));
598 	    tempf = open_file(final, OPEN_CREATE);
599 	    x = fprintf(tempf, "To: %s\n", cc);
600 	    fclose(tempf);
601 	    sprintf(buf, "sed -e \"s/^To:/X-To:/\" -e \"/^Orig-To:/d\" %s >> %s",
602 		    temp_file, final);
603 	    system(buf);
604 	    if (mailer_pipe_input) {
605 		sprintf(buf, "%s < %s", mailer_program, final);
606 		x = system(buf);
607 	    } else {
608 		strncpy(buf, mailer_program, 50);
609 		strcat(buf, " ");
610 		strcat(buf, final);
611 		x = system(buf);
612 	    }
613 	    if (!x)
614 		msg("Done");
615 	    else
616 		msg("%s failed", mailer_program);
617 	    cc[0] = CR;
618 	}
619     }
620 
621     sigfile = find_signature();
622     if (sigfile != NULL) {
623 	if (query_signature) {
624 	    prompt_line = Lines - 1;
625 	    prompt("Append .signature? (%c) ", append_sig ? 'Y' : 'N');
626 
627 	    switch (get_c()) {
628 		case 'y':
629 		case 'Y':
630 		    append_sig = 1;
631 		    break;
632 
633 		case 'n':
634 		case 'N':
635 		    append_sig = 0;
636 		    break;
637 
638 		default:
639 		    break;
640 	    }
641 	}
642 	if (append_sig)
643 	    append_signature(sigfile);
644 	(void) fclose(sigfile);
645     }
646     if (!strcmp("post", prog) || !strcmp("follow", prog))
647 	if (novice) {
648 	    msg("Be patient! Your new %s will not show up immediately.", message);
649 	    if (response_check_pause < 2)
650 		user_delay(2);
651 	    else
652 		user_delay(response_check_pause);
653 	}
654 /* trap signals 1 2 3 here */
655 
656     if (record) {
657 	FILE           *rec;
658 	char           *logname;
659 	time_t          t;
660 
661 	rec = open_file(record, OPEN_APPEND);
662 	if (rec != NULL) {
663 	    if (use_mmdf_folders)
664 		fprintf(rec, "\001\001\001\001\n");
665 	    logname = getenv("LOGNAME");
666 	    if (!logname)
667 		logname = user_name();
668 	    time(&t);
669 	    fprintf(rec, "From %s %s", logname, ctime(&t));
670 	    fprintf(rec, "From: %s\n", logname);
671 	    fclose(rec);
672 	    sprintf(buf, "cat %s >> %s", temp_file, record);
673 	    system(buf);
674 	}
675     }
676     if (!strcmp("reply", prog) || !strcmp("forward", prog) || !strcmp("mail", prog)) {
677 	if (mail_alias_expander) {
678 	    strncpy(buf, mail_alias_expander, 50);
679 	    strcat(buf, " ");
680 	    strcat(buf, temp_file);
681 	    system(buf);
682 	}
683 	if (mailer_pipe_input) {
684 	    sprintf(buf, "%s < %s", mailer_program, temp_file);
685 	    x = system(buf);
686 	} else {
687 	    strncpy(buf, mailer_program, 50);
688 	    strcat(buf, " ");
689 	    strcat(buf, temp_file);
690 	    x = system(buf);
691 	}
692 	if (x)
693 	    msg("%s failed", mailer_program);
694     } else if (!strcmp("post", prog) || !strcmp("follow", prog)) {
695 	if (nntp_post(temp_file)) {
696 	    copy_file(temp_file, relative(home_directory, "dead.letter"), 1);
697 	    msg("failed post saved in dead.letter");
698 	    user_delay(2);
699 	    return (1);
700 	}
701     } else {
702 	msg("Invalid operation: %s -- help", prog);
703 	user_delay(2);
704     }
705 
706     /* onfail copy to dead.letter and email user */
707 
708     unlink(temp_file);
709     unlink(copy);
710     unlink(final);
711     sprintf(delayed_msg, sent_fmt, "");
712     return (0);
713 }
714 
715 #endif
716