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 #include <stdlib.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <ctype.h>
12 #include "config.h"
13 #include "global.h"
14 #include "answer.h"
15 #include "aux.h"
16 #include "db.h"
17 #include "fullname.h"
18 #include "group.h"
19 #include "hostname.h"
20 #include "init.h"
21 #include "kill.h"
22 #include "match.h"
23 #include "news.h"
24 #include "nn.h"
25 #include "nntp.h"
26 #include "options.h"
27 #include "printconf.h"
28 #include "regexp.h"
29 #include "reroute.h"
30 #include "nn_term.h"
31 #include "variable.h"
32 
33 /* answer.c */
34 
35 static int      subj_line(FILE * t, int re, char *subj, char *prefix);
36 static void     ng_line(FILE * t, int use_follow);
37 static void     ref_line(FILE * t);
38 static int      to_line(FILE * t, char *to);
39 static void     alt_to_line(FILE * t, char *to, int mask);
40 static void     end_header(FILE * t, register char *extra_headers);
41 static int      reply_to(FILE * t, char *address);
42 static char    *get_distr(char *orig, char *distr);
43 static char    *inet_name(void);
44 static int      check_sender(char *sender);
45 
46 extern char    *temp_file;
47 extern char    *news_lib_directory;
48 
49 #ifndef DEFAULT_DISTRIB
50 #define DEFAULT_DISTRIB "world"
51 #endif
52 
53 char           *default_distribution = DEFAULT_DISTRIB;
54 char           *distribution_follow = "always same default";
55 char           *distribution_post = "ask default";
56 char           *extra_mail_headers = NULL;
57 char           *extra_news_headers = NULL;
58 char           *mail_record = NULL;
59 char           *news_record = NULL;
60 char           *mail_script = NULL;
61 char           *news_script = NULL;
62 char           *inews_program = NULL;
63 int             inews_pipe_input = 1;
64 
65 char           *bug_address = BUG_REPORT_ADDRESS;
66 
67 int             include_art_id = 0;
68 int             include_full_header = 0;
69 int             orig_to_include_mask = 0x3;
70 int             include_mark_blanks = 0;
71 
72 
73 #ifdef APPEND_SIGNATURE
74 int             append_sig_mail = 1;	/* mail does not append it */
75 #else
76 int             append_sig_mail = 0;
77 #endif
78 
79 int             append_sig_post = 1;	/* we should do this */
80 
81 
82 #define INCL_MARK_SIZE	10
83 
84 char            included_mark[INCL_MARK_SIZE + 1] = ">";
85 
86 extern char     delayed_msg[];
87 extern int      auto_select_rw;
88 extern int      ignore_fancy_select;
89 extern char    *organization;
90 
91 extern key_type help_key;
92 
93 extern regexp  *pg_regexp;
94 extern int      pg_new_regexp;
95 
96 static int      ed_line;
97 
98 #ifdef NNTP
99 #include <time.h>
100 #include <pwd.h>
101 
102 extern struct tm *gmtime();
103 extern char    *asctime();
104 #endif
105 
106 static int
checkhold(article_header * ah,int command)107 checkhold(article_header * ah, int command)
108 {
109     int             ans;
110 
111     if (file_exist(relative(nn_directory, "hold.work"), (char *) NULL)) {
112 	prompt("\1An uncompleted reponse exists\1 - complete now? ");
113 	if ((ans = yes(1)) < 0)
114 	    return 0;
115 	if (ans) {
116 	    if (ah && ah->a_group)
117 		init_group(ah->a_group);
118 	    new_temp_file();
119 	    if (command == K_REPLY)
120 		aux_sh(ah, (char *) NULL, "COMPLETE.mail", (char *) NULL,
121 		       (char *) NULL, "Mail%s sent", 0, 0);
122 	    else
123 		aux_sh(ah, (char *) NULL, "COMPLETE.post", (char *) NULL,
124 		       (char *) NULL, "Article%s posted", 0, 0);
125 	    return 1;
126 	}
127 	prompt("Remove uncompleted reponse? ");
128 	if ((ans = yes(1)) < 0)
129 	    return 0;
130 	if (ans) {
131 	    unlink(relative(nn_directory, "hold.work"));
132 	    unlink(relative(nn_directory, "hold.param"));
133 	}
134     }
135     return 0;
136 }
137 
138 
139 
140 static int
subj_line(FILE * t,int re,char * subj,char * prefix)141 subj_line(FILE * t, int re, char *subj, char *prefix)
142 {
143     if (subj == NULL)
144 	return 0;
145 
146     fputs("Subject: ", t);
147 
148     if (re >= 0)
149 	fputs("Re: ", t);
150 
151     if (prefix) {
152 	fputs(prefix, t);
153 	fputc(' ', t);
154     }
155     fputs(subj, t);
156     fputc(NL, t);
157 
158     ed_line++;
159     return 1;
160 }
161 
162 
163 static void
ng_line(FILE * t,int use_follow)164 ng_line(FILE * t, int use_follow)
165 {
166     char           *ng;
167 
168     ng = use_follow && news.ng_follow ? news.ng_follow : news.ng_groups;
169     if (ng == NULL)
170 	return;
171 
172     fprintf(t, "Newsgroups: %s\n", ng);
173     ed_line++;
174 }
175 
176 static void
ref_line(FILE * t)177 ref_line(FILE * t)
178 {
179     if (news.ng_ref == NULL && news.ng_ident == NULL)
180 	return;
181 
182     fputs("References:", t);
183 
184     /*
185      * Trim References: to less than 1000 bytes. The first References: item
186      * must remain the base article id. Then delete as many as necessary,
187      * then output the tail.
188      */
189     if (news.ng_ref) {
190 	int             pos = 11;	/* output position, currently
191 					 * strlen("References:") */
192 	char           *p = news.ng_ref;
193 
194 	/*
195 	 * maximum position is 997 and maybe minus news.ng_ident, which if
196 	 * output is preceded by a space
197 	 */
198 	int             maxpos = (news.ng_ident ? 997 - (1 + strlen(news.ng_ident)) : 997);
199 	int             rlen;
200 
201 	/* First, output the base article's message id. */
202 	/* First output a space between "References:" and the message id. */
203 	putc(' ', t);
204 	pos++;
205 	/* Skip leading white space. */
206 	while (*p && isascii(*p) && isspace(*p))
207 	    p++;
208 	/* Output through next white space. */
209 	while (*p && !(isascii(*p) && isspace(*p))) {
210 	    putc(*p++, t);
211 	    pos++;
212 	}
213 
214 	/*
215 	 * Now, the rest of the previous article's references, but maybe
216 	 * leaving some of them out to avoid exceeding 997 bytes.
217 	 */
218 	rlen = strlen(p);	/* number of bytes remaining */
219 	if (pos + rlen <= maxpos) {
220 	    fputs(p, t);
221 	} else {
222 
223 	    /*
224 	     * Find message id at which to start.  Jump into the middle and
225 	     * then find a valid starting place.
226 	     */
227 	    p += rlen - (maxpos - pos);
228 	    /* find next '<' */
229 	    while (*p && *p != '<')
230 		p++;
231 	    if (*p)
232 		fputs(p - 1, t);
233 
234 	    /*
235 	     * go back to include one white space character.  This is
236 	     * guaranteed to stay within the array because maxpos-pos is
237 	     * strictly less than rlen, therefore rlen - (maxpos - pos) is
238 	     * positive, therefore p is greater than it was before this
239 	     * enclosing 'else'.
240 	     */
241 	}
242     }
243     if (news.ng_ident)
244 	fprintf(t, " %s", news.ng_ident);
245     putc(NL, t);
246     ed_line++;
247 }
248 
249 
250 static int
to_line(FILE * t,char * to)251 to_line(FILE * t, char *to)
252 {
253     if (to == NULL)
254 	return 0;
255 
256     fprintf(t, "To: %s\n", to);
257     ed_line++;
258     return 1;
259 }
260 
261 static void
alt_to_line(FILE * t,char * to,int mask)262 alt_to_line(FILE * t, char *to, int mask)
263 {
264     if (to == NULL)
265 	return;
266     if (mask && (orig_to_include_mask & mask) == 0)
267 	return;
268 
269     fprintf(t, "Orig-To: %s\n", to);
270     ed_line++;
271 }
272 
273 static void
end_header(FILE * t,register char * extra_headers)274 end_header(FILE * t, register char *extra_headers)
275 {
276     if (extra_headers != NULL && *extra_headers != NUL) {
277 	while (*extra_headers != NUL) {
278 	    if (*extra_headers == ';' || *extra_headers == NL) {
279 		if (*++extra_headers == NUL)
280 		    break;
281 		fputc(NL, t);
282 		ed_line++;
283 	    } else {
284 		if (*extra_headers == '\\' && *++extra_headers == NUL)
285 		    break;
286 		fputc(*extra_headers++, t);
287 	    }
288 	}
289 	fputc(NL, t);
290 	ed_line++;
291     }
292     fputc(NL, t);
293     ed_line++;
294 }
295 
296 
297 static int
reply_to(FILE * t,char * address)298 reply_to(FILE * t, char *address)
299 {
300     char            route[512];
301 
302     if (address == NULL)
303 	return 0;
304 
305     if (reroute(route, address)) {
306 	to_line(t, route);
307 	return 1;
308     }
309     return 0;
310 }
311 
312 /*
313  *	open_purpose_file(void)
314  *
315  *	Open "newsgroups" file once - just rewind() otherwise.
316  *	Caller must NOT close it!
317  */
318 
319 FILE           *
open_purpose_file(void)320 open_purpose_file(void)
321 {
322     static FILE    *f = NULL;
323     static int      is_open = 0;
324 
325     if (is_open) {
326 	if (f != NULL)
327 	    rewind(f);
328 	return f;
329     }
330     is_open = 1;
331 
332 #ifdef NNTP
333     if (use_nntp) {
334 	f = nntp_get_newsgroups();
335     } else
336 #endif
337 
338 	f = open_file(relative(news_lib_directory, "newsgroups"), OPEN_READ);
339     return f;
340 }
341 
342 /*
343  * display list of group purposes
344  */
345 
346 static int
display_group_list(int get_regexp)347 display_group_list(int get_regexp)
348 {
349     FILE           *f;
350     char            line[512];
351     char           *expr = NULL;
352 
353     if (get_regexp) {
354 	prompt("\1/\1");
355 	expr = get_s((char *) NULL, (char *) NULL, (char *) NULL, NULL_FCT);
356 	if (expr == NULL || *expr == NUL)
357 	    return 0;
358     }
359     if ((f = open_purpose_file()) == NULL)
360 	return 0;
361     if (who_am_i == I_AM_POST) {
362 	gotoxy(0, prompt_line + 1);
363 	clrpage();
364 	pg_init(prompt_line + 1, 1);
365     } else {
366 	home();
367 	clrdisp();
368 	pg_init(0, 1);
369     }
370     if (expr)
371 	pg_regexp = regcomp(expr);
372 
373     while (fgets(line, 512, f)) {
374 	if (pg_regexp && regexec_fold(pg_regexp, line) == 0)
375 	    continue;
376 	if (pg_next() < 0)
377 	    break;
378 	if (pg_new_regexp) {
379 	    pg_new_regexp = 0;	/* pg_next has cleared display */
380 	    rewind(f);
381 	    continue;
382 	}
383 	tprintf("%s", line);
384     }
385     return 1;
386 }
387 
388 static char    *
get_distr(char * orig,char * distr)389 get_distr(char *orig, char *distr)
390 {
391     flag_type       opts;
392     int             always, ask, same, dflt;
393     char           *str, *dd;
394 
395     if ((dd = default_distribution) == NULL)
396 	dd = DEFAULT_DISTRIB;
397 
398     if (distr == NULL)
399 	distr = "default";
400     var_options(&distr, "always\0ask\0same\0default\0", &opts);
401     always = (opts & FLAG(1));
402     ask = (opts & FLAG(2));
403     same = (opts & FLAG(3));
404     dflt = (opts & FLAG(4));
405 
406     if (*distr == NUL || dflt)
407 	distr = dd;
408 
409     if (same && orig != NULL) {
410 	distr = orig;
411 	if (always)
412 	    ask = 0;
413     }
414     if (!ask)
415 	return distr;
416 
417     prompt("Distribution: (default '%s') ", distr);
418     str = get_s(NONE, NONE, NONE, NULL_FCT);
419     if (str == NULL)
420 	return NULL;
421     if (*str != NUL)
422 	distr = str;
423     return distr;
424 }
425 
426 int
answer(article_header * ah,int command,int incl)427 answer(article_header * ah, int command, int incl)
428  /* incl:	<0: ask, 0: don't include, >0: include article */
429 {
430     register FILE  *t, *art;
431     char           *pgm = NULL, *first_action, *record_file;
432     int             edit_message, append_sig;
433     char           *str, *script;
434     news_header_buffer nhbuf, dhbuf;
435     flag_type       nn_st_flag = 0;
436 
437     if (checkhold(ah, command))
438 	return 1;
439     first_action = "edit";
440     edit_message = 1;
441     append_sig = 0;
442 
443     if (incl < 0) {
444 	prompt("Include original article? ");
445 	if ((incl = yes(0)) < 0)
446 	    return 0;
447     }
448     art = NULL;
449     if (ah && ah->a_group)
450 	init_group(ah->a_group);
451 
452     if (incl || (command != K_MAIL_OR_FORWARD && command != K_BUG_REPORT)) {
453 	int             open_modes;
454 
455 	open_modes = FILL_NEWS_HEADER | GET_ALL_FIELDS | SKIP_HEADER;
456 	if (ah->flag & A_DIGEST)
457 	    open_modes |= FILL_DIGEST_HEADER;
458 
459 	art = open_news_article(ah, open_modes, nhbuf, dhbuf);
460 	if (art == NULL) {
461 	    msg("Can't find original article");
462 	    return 0;
463 	}
464 	if (ah->flag & A_DIGEST) {
465 	    if (digest.dg_from) {
466 		if (news.ng_reply)
467 		    news.ng_from = news.ng_reply;
468 		news.ng_reply = digest.dg_from;
469 	    }
470 	    if (digest.dg_subj)
471 		news.ng_subj = digest.dg_subj;
472 	}
473     } else
474 	ah = NULL;
475 
476     /* build header */
477     new_temp_file();
478 
479     if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) {
480 	msg("Can't create %s", temp_file);
481 	return 0;
482     }
483     ed_line = 0;
484 
485 follow_to_poster:
486 
487     record_file = mail_record;
488     script = mail_script;
489 
490     if (command == K_REPLY) {
491 	pgm = "reply";
492 	append_sig = append_sig_mail;
493 
494 	nn_st_flag = A_ST_REPLY;
495 
496 	if (reply_to(t, news.ng_reply) ||
497 	    reply_to(t, news.ng_from) ||
498 	    reply_to(t, news.ng_path))
499 	    goto alt0;
500 	if (to_line(t, news.ng_reply))
501 	    goto alt1;
502 	if (to_line(t, news.ng_from))
503 	    goto alt2;
504 	if (to_line(t, news.ng_path))
505 	    goto alt3;
506 	goto err;
507 
508 alt0:
509 	alt_to_line(t, news.ng_reply, 0x1);
510 alt1:
511 	alt_to_line(t, news.ng_from, 0x2);
512 alt2:
513 	alt_to_line(t, news.ng_path, 0x4);
514 alt3:
515 
516 	if (news.ng_subj)
517 	    subj_line(t, ah->replies, ah->subject, (char *) NULL);
518 	else
519 	    subj_line(t, 0, current_group->group_name, "Your Article in");
520 
521 	ng_line(t, 0);
522 	ref_line(t);
523 
524 	end_header(t, extra_mail_headers);
525 
526 	if (incl) {
527 	    if (current_group->group_flag & G_FOLDER)
528 		fprintf(t, "You write:\n");
529 	    else
530 		fprintf(t, "In %s you write:\n", current_group->group_name);
531 	    ed_line++;
532 	}
533     }
534     if (command == K_FOLLOW_UP) {
535 	if (news.ng_follow && strcmp(news.ng_follow, "poster") == 0) {
536 	    command = K_REPLY;
537 	    msg("Followup by reply to poster");
538 	    goto follow_to_poster;
539 	}
540 	pgm = "follow";
541 	record_file = news_record;
542 	script = news_script;
543 	append_sig = append_sig_post;
544 
545 	nn_st_flag = A_ST_FOLLOW;
546 
547 	ng_line(t, 1);
548 
549 	if (news.ng_subj)
550 	    subj_line(t, ah->replies, ah->subject, (char *) NULL);
551 	else if (!subj_line(t, 0, news.ng_from, "Babble from"))
552 	    if (!subj_line(t, 0, news.ng_ident, "Article")) {
553 		prompt("Subject: ");
554 		str = get_s(NONE, NONE, NONE, NULL_FCT);
555 		if (str == NULL)
556 		    goto err;
557 		subj_line(t, -1, str, (char *) NULL);
558 	    }
559 	if (news.ng_keyw) {
560 	    fprintf(t, "Keywords: %s\n", news.ng_keyw);
561 	    ed_line++;
562 	}
563 	str = get_distr(news.ng_dist, distribution_follow);
564 	if (str == NULL)
565 	    goto close_t;
566 	if (strcmp(str, "world")) {
567 	    fprintf(t, "Distribution: %s\n", str);
568 	    ed_line++;
569 	}
570 	ref_line(t);
571 
572 	end_header(t, extra_news_headers);
573 
574 	if (incl) {
575 	    if (news.ng_from) {
576 		if (include_art_id && news.ng_ident)
577 		    fprintf(t, "In%s %s ",
578 			    ah->flag & A_DIGEST ? " digest" : "",
579 			    news.ng_ident);
580 		fprintf(t, "%s writes:\n", news.ng_from);
581 		ed_line++;
582 	    } else if (news.ng_ident) {
583 		fprintf(t, "In %s %s:\n",
584 			ah->flag & A_DIGEST ? "digest" : "article",
585 			news.ng_ident);
586 		ed_line++;
587 	    }
588 	}
589     }
590     if (command == K_MAIL_OR_FORWARD || command == K_BUG_REPORT) {
591 	pgm = incl ? "forward" : "mail";
592 	append_sig = append_sig_mail;
593 
594 m3_again:
595 	prompt("To: ");
596 	str = get_s(user_name(),
597 		    command == K_BUG_REPORT ? bug_address : NONE,
598 		    NONE, NULL_FCT);
599 	if (str == NULL)
600 	    goto close_t;
601 
602 	if (*str == NUL)
603 	    str = user_name();
604 	if (*str == '?')
605 	    goto m3_again;
606 
607 	if (strcmp(str, user_name()) == 0)
608 	    record_file = NULL;	/* we will get this anyway, there is so no
609 				 * need to save it */
610 
611 /*	if (reply_to(t, str)) {	    alt_to_line(t, str, 0);	} else */
612 	to_line(t, str);
613 
614 	do {
615 	    prompt("Subject: ");
616 	    str = get_s(incl ? ah->subject : NONE,
617 			command == K_BUG_REPORT ? "nn bug report" : NONE,
618 			NONE, NULL_FCT);
619 	    if (str == NULL)
620 		goto close_t;
621 	    if (*str == NUL && incl)
622 		str = ah->subject;
623 	} while (*str == NUL);
624 
625 	subj_line(t, -1, str, (char *) NULL);
626 
627 	end_header(t, extra_mail_headers);
628 
629 	if (incl) {
630 	    prompt("\1Edit\1 forwarded message? ");
631 	    if ((edit_message = yes(0)) < 0)
632 		goto close_t;
633 	    if (!edit_message) {
634 		first_action = "send";
635 		fseek(art, ah->hpos, 0);
636 	    } else if (include_full_header && command == K_MAIL_OR_FORWARD)
637 		fseek(art, ah->hpos, 0);
638 	}
639 	if (command == K_BUG_REPORT) {
640 	    fprintf(t, "\n=== configuration ===\n");
641 	    print_config(t);
642 	    fprintf(t, "=== variable settings ===\n");
643 	    print_variable_config(t, 0);
644 	    fprintf(t, "=== end ===\n");
645 	}
646     }
647     prompt("\1WAIT\1");
648 
649     if (incl) {
650 	register int    c, prevnl = 1;
651 
652 	fputc(NL, t);
653 	ed_line++;
654 
655 	while ((c = getc(art)) != EOF) {
656 	    if (c == NL) {
657 		putc(c, t);
658 		if (ftell(art) >= ah->lpos)
659 		    break;
660 		prevnl++;
661 		if (!include_mark_blanks)
662 		    continue;
663 	    }
664 	    if (prevnl) {
665 		if (command != K_MAIL_OR_FORWARD || ftell(art) < ah->fpos)
666 		    fputs(included_mark, t);
667 		prevnl = 0;
668 		if (c == NL)
669 		    continue;
670 	    }
671 	    putc(c, t);
672 	}
673     } else {
674 	putc(NL, t);
675 	ed_line++;
676     }
677 
678     fclose(t);
679     if (art)
680 	fclose(art);
681 
682     if (aux_sh(ah, script, pgm, first_action, record_file,
683 	       command == K_FOLLOW_UP ? "Article%s posted" : "Mail%s sent",
684 	       append_sig, ed_line) == 0)
685 	if (ah)
686 	    ah->flag |= nn_st_flag;
687 
688     return edit_message;
689 
690 err:
691     msg("Can't build header for %s",
692 	command != K_FOLLOW_UP ? "letter" : "article");
693 
694 close_t:
695     fclose(t);
696     unlink(temp_file);
697     if (art)
698 	fclose(art);
699 
700     return 0;
701 }
702 
703 
704 /*
705  *	inet_name: return "<user_name()>@<host_name()>"
706  */
707 
708 static char    *
inet_name(void)709 inet_name(void)
710 {
711     static char    *inetname = NULL;
712     char            hname[100], *un;
713 
714     if (inetname == NULL) {
715 	nn_gethostname(hname, 100);
716 	un = user_name();
717 	inetname = newstr(strlen(hname) + strlen(un) + 2);
718 	sprintf(inetname, "%s@%s", un, hname);
719     }
720     return inetname;
721 }
722 
723 /*
724  *  check_sender: If sender is "root", "news", the full name or the internet
725  *  name of the user, return 1 otherwise 0
726  */
727 
728 static int
check_sender(char * sender)729 check_sender(char *sender)
730 {
731     return strcmp(user_name(), "root") == 0
732     || strcmp(user_name(), "news") == 0
733     || strmatch(full_name(), sender)
734     || strmatch(inet_name(), sender);
735 }
736 
737 int
cancel(article_header * ah)738 cancel(article_header * ah)
739 {
740     news_header_buffer nhbuf;
741     FILE           *f;
742 
743     if (ah->a_group)
744 	init_group(ah->a_group);
745 
746     if (ah->flag & A_DIGEST) {
747 	tprintf("\rCancel entire digest ? ");
748 	clrline();
749 	if (yes(1) > 0)
750 	    ah->flag &= ~A_DIGEST;
751 	else {
752 	    msg("Can only cancel entire digests (yet?)");
753 	    return 2;
754 	}
755     }
756     f = open_news_article(ah, FILL_NEWS_HEADER | GET_ALL_FIELDS, nhbuf, (char *) NULL);
757     if (f == NULL) {
758 	msg("Article not found");
759 	return 2;
760     }
761     fclose(f);
762 
763     if (!check_sender(news.ng_from)) {
764 	msg("You can only cancel your own articles!");
765 	return 1;
766     }
767     prompt("Confirm cancel: '%s: %.30s'",
768 	   ah->sender ? ah->sender : "",
769 	   ah->subject ? ah->subject : "");
770     if (yes(1) <= 0)
771 	return 1;
772 
773     tprintf("\rCancelling article %s in group %s",
774 	    news.ng_ident, current_group->group_name);
775     clrline();
776 
777     ed_line = -1;
778 
779     new_temp_file();
780     if (aux_sh(ah, (char *) NULL, "cancel", news.ng_ident, current_group->group_name,
781 	       "Article%s cancelled", 0, 0))
782 	return -1;
783 
784     return 0;
785 }
786 
787 static char    *post_distribution = NULL;
788 static char    *post_subject = NULL;
789 static char    *post_summary = NULL;
790 static char    *post_keywords = NULL;
791 static char    *post_source_file = NULL;
792 static int      post_no_edit = 0;
793 static char    *post_to_groups = NULL;
794 
795 static
Option_Description(post_options)796 Option_Description(post_options)
797 {
798     'd', String_Option(post_distribution),
799     'f', String_Option(post_source_file),
800     'k', String_Option(post_keywords),
801     's', String_Option(post_subject),
802     'y', String_Option(post_summary),
803     'p', Bool_Option(post_no_edit),
804     '\0', 0,
805 };
806 
807 void
do_nnpost(int argc,char * argv[])808 do_nnpost(int argc, char *argv[])
809 {
810     int             ngroups, i;
811     char            newsgroups[FILENAME * 2];
812 
813     init_term(0);
814     visit_init_file(0, (char *) NULL);
815     current_group = NULL;
816 
817     ngroups =
818 	parse_options(argc, argv, (char *) NULL, post_options,
819 		      " newsgroup...");
820 
821     if (post_no_edit && post_source_file == NULL)
822 	nn_exitmsg(1, "Must specify a source file with -p\n");
823 
824     if (ngroups > 0) {
825 	strcpy(newsgroups, argv[1]);
826 	for (i = 2; i <= ngroups; i++) {
827 	    strcat(newsgroups, ",");
828 	    strcat(newsgroups, argv[i]);
829 	}
830 	post_to_groups = newsgroups;
831     }
832     if (ngroups > 0 && post_no_edit && post_subject && post_distribution) {
833 	if (!post_summary)
834 	    post_summary = "";
835 	if (!post_keywords)
836 	    post_keywords = "";
837 	post_menu();
838 	goto no_dialogue;
839     }
840     init_term(1);
841 
842     nn_raw();
843     clrdisp();
844     prompt_line = 0;
845     if (post_menu() == 2)
846 	clrdisp();
847     tputc(CR);
848     tputc(NL);
849     unset_raw();
850 
851 no_dialogue:
852     if (*delayed_msg)
853 	nn_exitmsg(0, "%s", delayed_msg);
854     else
855 	nn_exit(0);
856 }
857 
858 int
post_menu(void)859 post_menu(void)
860 {
861     register FILE  *t, *src = NULL;
862     register int    c;
863     int             must_redraw = 0;
864     char            brk_chars[4];
865     char           *str, *tail;
866     group_header    gh;
867     char            group_name[FILENAME], subject[FILENAME], distribution[FILENAME],
868                     keywords[FILENAME], summary[FILENAME];
869 
870     if (checkhold(NULL, K_POST))
871 	return 1;
872     if (post_source_file) {
873 	src = open_file(post_source_file, OPEN_READ);
874 	if (src == NULL)
875 	    nn_exitmsg(1, "File %s not found\n", post_source_file);
876     }
877     if (post_to_groups)
878 	strcpy(group_name, post_to_groups);
879     else {
880 	group_name[0] = NUL;
881 
882 again_group:
883 
884 	prompt(who_am_i == I_AM_POST ? "Newsgroups: " : "\1POST to group\1 ");
885 
886 	strcpy(brk_chars, " /?");
887 	brk_chars[0] = help_key;
888 	str = get_s(current_group ? current_group->group_name : NONE,
889 		    group_name, brk_chars, group_completion);
890 	if (str == NULL)
891 	    goto no_post;
892 	if (*str == '?' || (key_type) (*str) == help_key || *str == '/') {
893 	    if (display_group_list(*str == '/'))
894 		must_redraw = 2;
895 	    else
896 		msg("No group list is available");
897 	    goto again_group;
898 	}
899 	if (*str == NUL) {
900 	    if (current_group == NULL || (current_group->group_flag & G_FAKED))
901 		goto no_post;
902 	    str = current_group->group_name;
903 	}
904 	strcpy(group_name, str);
905 
906 	for (str = group_name; str; str = tail) {
907 	    tail = strchr(str, ',');
908 	    if (tail)
909 		*tail = NUL;
910 
911 	    if (lookup(str) == NULL) {
912 		msg("unknown group: %s", str);
913 		*str = NUL;
914 		goto again_group;
915 	    }
916 	    if (tail)
917 		*tail++ = ',';
918 	}
919 	if (who_am_i == I_AM_POST) {
920 	    prompt_line++;
921 	    if (must_redraw) {
922 		gotoxy(0, prompt_line);
923 		clrpage();
924 		must_redraw = 1;
925 	    }
926 	}
927     }
928 
929     if ((str = post_subject) == NULL) {
930 	prompt("Subject: ");
931 	str = get_s(NONE, NONE, NONE, NULL_FCT);
932 	if (str == NULL || *str == NUL)
933 	    goto no_post;
934 	if (who_am_i == I_AM_POST)
935 	    prompt_line++;
936     }
937     strcpy(subject, str);
938 
939     if ((str = post_keywords) == NULL) {
940 	prompt("Keywords: ");
941 	str = get_s(NONE, NONE, NONE, NULL_FCT);
942 	if (str == NULL)
943 	    goto no_post;
944 	if (who_am_i == I_AM_POST)
945 	    prompt_line++;
946     }
947     strcpy(keywords, str);
948 
949     if ((str = post_summary) == NULL) {
950 	prompt("Summary: ");
951 	str = get_s(NONE, NONE, NONE, NULL_FCT);
952 	if (str == NULL)
953 	    goto no_post;
954 	if (who_am_i == I_AM_POST)
955 	    prompt_line++;
956     }
957     strcpy(summary, str);
958 
959     if ((str = post_distribution) == NULL) {
960 	str = get_distr(NULL, distribution_post);
961 	if (str == NULL)
962 	    goto no_post;
963 	if (who_am_i == I_AM_POST)
964 	    prompt_line++;
965     }
966     strcpy(distribution, str);
967 
968     new_temp_file();
969     if ((t = open_file(temp_file, OPEN_CREATE)) == NULL) {
970 	msg("Can't create %s", temp_file);
971 	goto no_post;
972     }
973     if (!post_no_edit)
974 	prompt("\1WAIT\1");
975 
976     ed_line = 3;
977     fprintf(t, "Newsgroups: %s\n", group_name);
978     if (strcmp(distribution, "world")) {
979 	fprintf(t, "Distribution: %s\n", distribution);
980 	ed_line++;
981     }
982     fprintf(t, "Subject: %s\n", subject);
983     if (organization) {
984 	fprintf(t, "Organization: %s\n", organization);
985 	ed_line++;
986     }
987     if (*summary) {
988 	fprintf(t, "Summary: %s\n", summary);
989 	ed_line++;
990     }
991     if (*keywords) {
992 	fprintf(t, "Keywords: %s\n", keywords);
993 	ed_line++;
994     }
995     end_header(t, extra_news_headers);
996 
997     if (post_source_file) {
998 	while ((c = getc(src)) != EOF)
999 	    putc(c, t);
1000 	fclose(src);
1001     } else
1002 	fputc(NL, t);
1003 
1004     fclose(t);
1005 
1006     if (auto_select_rw && !ignore_fancy_select) {
1007 	tail = strchr(group_name, ',');
1008 	if (tail)
1009 	    *tail = NUL;
1010 	gh.group_name = group_name;
1011 	enter_kill_file(&gh, subject, 6, 60);
1012 	if (tail)
1013 	    *tail = ',';
1014     }
1015     aux_sh((article_header *) NULL, news_script, "post",
1016 	   post_no_edit ? "send" : "edit", news_record,
1017 	   "Article%s posted", append_sig_post, ed_line);
1018     must_redraw = 1;
1019 
1020 no_post:
1021     return must_redraw;
1022 }
1023