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