home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / answer.c < prev    next >
C/C++ Source or Header  |  1995-04-29  |  26KB  |  1,138 lines

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