home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume31 / tin / part06 < prev    next >
Encoding:
Text File  |  1992-07-07  |  54.4 KB  |  2,430 lines

  1. Newsgroups: comp.sources.misc
  2. From: iain%anl433.uucp@Germany.EU.net (Iain Lea)
  3. Subject:  v31i006:  tin - threaded full screen newsreader v1.1 PL4, Part06/15
  4. Message-ID: <1992Jul7.181526.7020@sparky.imd.sterling.com>
  5. X-Md4-Signature: 21d1983ae44eb1de8360abe76a621e1e
  6. Date: Tue, 7 Jul 1992 18:15:26 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: iain%anl433.uucp@Germany.EU.net (Iain Lea)
  10. Posting-number: Volume 31, Issue 6
  11. Archive-name: tin/part06
  12. Environment: BSD, SCO, ISC, SUNOS, SYSVR3, SYSVR4, ULTRIX, XENIX
  13. Supersedes: tin: Volume 30, Issue 1-14
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then feed it
  17. # into a shell via "sh file" or similar.  To overwrite existing files,
  18. # type "sh file -c".
  19. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  20. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  21. # Contents:  art.c post.c
  22. # Wrapped by kent@sparky on Mon Jun 29 23:35:11 1992
  23. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  24. echo If this archive is complete, you will see the following message:
  25. echo '          "shar: End of archive 6 (of 15)."'
  26. if test -f 'art.c' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'art.c'\"
  28. else
  29.   echo shar: Extracting \"'art.c'\" \(22975 characters\)
  30.   sed "s/^X//" >'art.c' <<'END_OF_FILE'
  31. X/*
  32. X *  Project   : tin - a threaded Netnews reader
  33. X *  Module    : art.c
  34. X *  Author    : I.Lea & R.Skrenta
  35. X *  Created   : 01-04-91
  36. X *  Updated   : 12-05-92
  37. X *  Notes     :
  38. X *  Copyright : (c) Copyright 1991-92 by Iain Lea & Rich Skrenta
  39. X *              You may  freely  copy or  redistribute  this software,
  40. X *              so  long as there is no profit made from its use, sale
  41. X *              trade or  reproduction.  You may not change this copy-
  42. X *              right notice, and it must be included in any copy made
  43. X */
  44. X
  45. X#include    "tin.h"
  46. X
  47. Xchar index_file[PATH_LEN];
  48. Xchar *glob_art_group;
  49. Xstatic long last_read_article;
  50. X
  51. X
  52. X/*
  53. X *  Construct the pointers to the basenotes of each thread
  54. X *  arts[] contains every article in the group.  inthread is
  55. X *  set on each article that is after the first article in the
  56. X *  thread.  Articles which have been expired have their thread
  57. X *  set to -2 (ART_EXPIRED).
  58. X */
  59. X
  60. Xvoid find_base (only_unread)
  61. X    int only_unread;
  62. X{
  63. X    register int i;
  64. X    register int j;
  65. X
  66. X    top_base = 0;
  67. X
  68. X    debug_print_arts ();
  69. X
  70. X    if (only_unread) {
  71. X        for (i = 0; i < top; i++) {
  72. X            if (IGNORE_ART(i) || arts[i].inthread != FALSE) {
  73. X                continue;
  74. X            }    
  75. X            if (top_base >= max_art) {
  76. X                expand_art ();
  77. X            }
  78. X            if (arts[i].unread == ART_UNREAD) {
  79. X                base[top_base++] = i;
  80. X            } else {
  81. X                for (j = i ; j >= 0 ; j = arts[j].thread) {
  82. X                    if (arts[j].unread) {
  83. X                        base[top_base++] = i;
  84. X                        break;
  85. X                    }
  86. X                }
  87. X            }
  88. X        }
  89. X    } else {
  90. X        for (i = 0; i < top; i++) {
  91. X            if (IGNORE_ART(i) || arts[i].inthread != FALSE) {
  92. X                continue;
  93. X            }    
  94. X            if (top_base >= max_art) {
  95. X                expand_art ();
  96. X            }
  97. X            base[top_base++] = i;
  98. X        }
  99. X    }
  100. X}
  101. X
  102. X/* 
  103. X *  Count the number of non-expired articles in arts[]
  104. X */
  105. X
  106. Xint num_of_arts ()
  107. X{
  108. X    int sum = 0;
  109. X    register int i;
  110. X
  111. X    for (i = 0; i < top; i++) {
  112. X        if (arts[i].thread != ART_EXPIRED) {
  113. X            sum++;
  114. X        }
  115. X    }
  116. X
  117. X    return sum;
  118. X}
  119. X
  120. X/*
  121. X *  Do we have an entry for article art?
  122. X */
  123. X
  124. Xint valid_artnum (art)
  125. X    long art;
  126. X{
  127. X    register int i;
  128. X
  129. X    for (i = 0; i < top; i++)
  130. X        if (arts[i].artnum == art)
  131. X            return i;
  132. X
  133. X    return -1;
  134. X}
  135. X
  136. X/*
  137. X *  Return TRUE if arts[] contains any expired articles
  138. X *  (articles we have an entry for which don't have a corresponding
  139. X *   article file in the spool directory)
  140. X */
  141. X
  142. Xint purge_needed ()
  143. X{
  144. X    register int i;
  145. X
  146. X    for (i = 0; i < top; i++)
  147. X        if (arts[i].thread == ART_EXPIRED)
  148. X            return TRUE;
  149. X
  150. X    return FALSE;
  151. X}
  152. X
  153. X/*
  154. X *  Main group indexing routine.  Group should be the name of the
  155. X *  newsgroup, i.e. "comp.unix.amiga".  group_path should be the
  156. X *  same but with the .'s turned into /'s: "comp/unix/amiga"
  157. X *
  158. X *  Will read any existing index, create or incrementally update
  159. X *  the index by looking at the articles in the spool directory,
  160. X *  and attempt to write a new index if necessary.
  161. X */
  162. X
  163. Xvoid index_group (group, group_path)
  164. X    char *group;
  165. X    char *group_path;
  166. X{
  167. X    int killed = FALSE;
  168. X    int modified = FALSE;
  169. X    glob_art_group = group;
  170. X
  171. X    set_alarm_clock_off ();
  172. X
  173. X    set_signals_art ();
  174. X    
  175. X    if (! update) {
  176. X        sprintf (msg, txt_group, group);
  177. X        wait_message (msg);
  178. X    }
  179. X    hash_reclaim ();
  180. X    free_art_array ();
  181. X
  182. X    /*
  183. X     *  load articles from index file if it exists
  184. X     */
  185. X    read_index_file (group);
  186. X
  187. X    /*
  188. X     *  add any articles to arts[] that are new or were killed
  189. X     */
  190. X    modified = read_group (group, group_path);
  191. X
  192. X    if (modified || purge_needed ()) {
  193. X        write_index_file (group);
  194. X    }
  195. X    read_newsrc_line (group);
  196. X    killed = kill_any_articles (group); /* do after read_newsrc_line() */
  197. X    make_threads (FALSE);
  198. X    find_base (show_only_unread);
  199. X    
  200. X    if ((modified || killed) && ! update) {
  201. X        clear_message ();
  202. X    }
  203. X    
  204. X    set_alarm_clock_on ();
  205. X}
  206. X
  207. X/*
  208. X *  Index a group.  Assumes any existing index has already been
  209. X *  loaded.
  210. X */
  211. X
  212. Xint read_group (group, group_path)
  213. X    char *group;
  214. X    char *group_path;
  215. X{
  216. X    FILE *fp;
  217. X    int count = 0;
  218. X    int modified = FALSE;
  219. X    int respnum;
  220. X    long art;
  221. X    register int i;
  222. X
  223. X    setup_base (group, group_path);    /* load article numbers into base[] */
  224. X
  225. X    for (i = 0; i < top_base; i++) {    /* for each article # */
  226. X        art = base[i];
  227. X
  228. X/*
  229. X *  Do we already have this article in our index?  Change thread from
  230. X *  (ART_EXPIRED) to (ART_NORMAL) if so and skip the header eating.
  231. X */
  232. X
  233. X        if ((respnum = valid_artnum (art)) >= 0 || art <= last_read_article) {
  234. X            if (respnum >= 0) {
  235. X                arts[respnum].thread = ART_NORMAL;
  236. X                arts[respnum].unread = ART_UNREAD;
  237. X            }    
  238. X            continue;
  239. X        }
  240. X
  241. X        if (! modified) {
  242. X            modified = TRUE;   /* we've modified the index */
  243. X                               /* it will need to be re-written */
  244. X        }
  245. X
  246. X        if ((fp = open_header_fp (group_path, art)) == (FILE *) 0) {
  247. X            continue;
  248. X        }
  249. X        
  250. X        /*
  251. X         *  Add article to arts[]
  252. X         */
  253. X        if (top >= max_art)
  254. X            expand_art();
  255. X
  256. X        arts[top].artnum = art;
  257. X        arts[top].thread = ART_NORMAL;
  258. X
  259. X        set_article (&arts[top]);
  260. X
  261. X        if (! parse_headers (fp, &arts[top])) {
  262. X            debug_nntp ("read_group", "FAILED parse_header()");
  263. X            continue;
  264. X        }
  265. X
  266. X        fclose (fp);
  267. X        last_read_article = arts[top].artnum;    /* used if arts are killed */
  268. X        top++;
  269. X
  270. X        if (++count % MODULO_COUNT_NUM == 0 && ! update) {
  271. X#ifndef SLOW_SCREEN_UPDATE
  272. X            sprintf (msg, txt_indexing_num, group, count);
  273. X#else
  274. X            sprintf (msg, txt_indexing, group);
  275. X#endif
  276. X            wait_message (msg);
  277. X        }
  278. X    }
  279. X
  280. X    return modified;
  281. X}
  282. X
  283. X
  284. X/*
  285. X *  Go through the articles in arts[] and use .thread to snake threads
  286. X *  through them.  Use the subject line to construct threads.  The
  287. X *  first article in a thread should have .inthread set to FALSE, the
  288. X *  rest TRUE.  Only do unexprired articles we haven't visited yet
  289. X *  (arts[].thread == -1 ART_NORMAL).
  290. X */
  291. X
  292. Xvoid make_threads (rethread)
  293. X    int rethread;
  294. X{
  295. X    extern int cur_groupnum;
  296. X    register int i;
  297. X    register int j;
  298. X
  299. X    if (!cmd_line) {
  300. X        if (thread_arts) {
  301. X            wait_message (txt_threading_arts);
  302. X        } else {
  303. X            wait_message (txt_unthreading_arts);
  304. X        }
  305. X    }
  306. X
  307. X    /*
  308. X     *  .thread & .inthread need to be reset if re-threading arts[]
  309. X     */
  310. X    if (rethread && active[my_group[cur_groupnum]].attribute.thread) {
  311. X        for (i=0 ; i < top ; i++) {
  312. X            arts[i].thread = ART_NORMAL;
  313. X            arts[i].inthread = FALSE;
  314. X        }
  315. X    }
  316. X
  317. X    switch (sort_art_type) {
  318. X        case SORT_BY_NOTHING:        /* don't sort at all */
  319. X            qsort ((char *) arts, top, sizeof (struct article_t), artnum_comp);
  320. X            break;
  321. X        case SORT_BY_SUBJ_DESCEND:
  322. X        case SORT_BY_SUBJ_ASCEND:
  323. X            qsort ((char *) arts, top, sizeof (struct article_t), subj_comp);
  324. X            break;
  325. X        case SORT_BY_FROM_DESCEND:
  326. X        case SORT_BY_FROM_ASCEND:
  327. X            qsort ((char *) arts, top, sizeof (struct article_t), from_comp);
  328. X            break;
  329. X        case SORT_BY_DATE_DESCEND:
  330. X        case SORT_BY_DATE_ASCEND:
  331. X            qsort ((char *) arts, top, sizeof (struct article_t), date_comp);
  332. X            break;
  333. X        default:
  334. X            break;
  335. X    }
  336. X
  337. X    if (thread_arts == 0 || active[my_group[cur_groupnum]].attribute.thread == 0) {
  338. X        return;
  339. X    }
  340. X
  341. X    for (i = 0; i < top; i++) {
  342. X        if (arts[i].thread != ART_NORMAL || IGNORE_ART(i)) {
  343. X            continue;
  344. X        }    
  345. X        for (j = i+1; j < top; j++) {
  346. X            if (! IGNORE_ART(j) && 
  347. X               ((arts[i].subject == arts[j].subject) ||
  348. X               ((arts[i].part || arts[i].patch) &&
  349. X               arts[i].archive == arts[j].archive))) {
  350. X                arts[i].thread = j;
  351. X                arts[j].inthread = TRUE;
  352. X                break;
  353. X            }
  354. X        }
  355. X    }
  356. X}
  357. X
  358. X
  359. Xint parse_headers (fp, h)
  360. X    FILE *fp;
  361. X    struct article_t *h;
  362. X{
  363. X    char buf[HEADER_LEN];
  364. X    char buf2[HEADER_LEN];
  365. X    char art_from_addr[LEN];
  366. X    char art_full_name[LEN];
  367. X    char *ptr, *ptrline, *s;
  368. X    int flag, n = 0;
  369. X    int len = 0, lineno = 0;
  370. X    int got_subject = FALSE;
  371. X    int got_from = FALSE;
  372. X    int got_date = FALSE;
  373. X    int got_archive = FALSE;
  374. X    extern int errno;
  375. X
  376. X    while ((n = fread (buf, 1, sizeof (buf)-1, fp)) == 0) {
  377. X        if (feof (fp)) {
  378. X            break;
  379. X        }
  380. X  
  381. X        if (ferror (fp) && errno != EINTR) {
  382. X            break;
  383. X        }
  384. X        clearerr (fp);
  385. X    }
  386. X
  387. X    if (n == 0) {
  388. X        return FALSE;
  389. X    }
  390. X
  391. X    buf[n-1] = '\0';
  392. X      
  393. X    ptr = buf;
  394. X
  395. X    while (1) {
  396. X        for (ptrline = ptr; *ptr && *ptr != '\n'; ptr++) {
  397. X            if (((*ptr) & 0xFF) < ' ') {
  398. X                *ptr = ' ';
  399. X            }
  400. X        }
  401. X        flag = *ptr;
  402. X        *ptr++ = '\0';
  403. X        lineno++;
  404. X
  405. X        if (! got_from && match_header (ptrline, "From", buf2, HEADER_LEN)) {
  406. X            parse_from (buf2, art_from_addr, art_full_name); 
  407. X            h->from = hash_str (art_from_addr);
  408. X            h->name = hash_str (art_full_name);
  409. X            got_from = TRUE;
  410. X        } else if (! got_subject && match_header (ptrline, "Subject", buf2, HEADER_LEN)) {
  411. X            s = eat_re (buf2);
  412. X            h->subject = hash_str (eat_re (s));
  413. X            got_subject = TRUE;
  414. X        } else if (! got_date && match_header (ptrline, "Date", buf2, HEADER_LEN)) {
  415. X            parse_date (buf2, h->date);
  416. X            got_date = TRUE;
  417. X        } else if (match_header (ptrline, "Archive-name", buf2, HEADER_LEN) ||
  418. X                    match_header (ptrline, "Archive-Name", buf2, HEADER_LEN)) {
  419. X            if ((s = (char *) strchr (buf2, '/')) != NULL) {
  420. X                if (strncmp (s+1, "part", 4) == 0 ||
  421. X                    strncmp (s+1, "Part", 4) == 0) {
  422. X                    h->part = str_dup (s+5);
  423. X                    len = (int) strlen (h->part);
  424. X                    if (h->part[len-1] == '\n') {
  425. X                        h->part[len-1] = '\0';
  426. X                    }
  427. X                } else {
  428. X                    if (strncmp (s+1,"patch",5) == 0 ||
  429. X                        strncmp (s+1,"Patch",5) == 0) {
  430. X                        h->patch = str_dup (s+6);
  431. X                        len = (int) strlen (h->patch);
  432. X                        if (h->patch[len-1] == '\n') {
  433. X                            h->patch[len-1] = '\0';
  434. X                        }
  435. X                    }
  436. X                }
  437. X                if (h->part || h->patch) {
  438. X                    s = buf2;
  439. X                    while (*s && *s != '/')
  440. X                        s++;
  441. X                    *s = '\0';    
  442. X                    s = buf2;
  443. X                    h->archive = hash_str (s);
  444. X                    got_archive = TRUE;
  445. X                }
  446. X            }
  447. X        }
  448. X
  449. X        if (! flag || lineno > 25 || got_archive) {
  450. X            if (got_subject && got_from && got_date) {
  451. X                debug_print_header (h);
  452. X                return TRUE;
  453. X            } else {
  454. X                return FALSE;
  455. X            }    
  456. X        }
  457. X    }
  458. X    /* NOTREACHED */
  459. X}
  460. X
  461. X/* 
  462. X *  Write out  an index file.  Write the group name first so if
  463. X *  local indexing is done so we can disambiguate between group
  464. X *  name hash collisions by looking at the index file.
  465. X */
  466. X
  467. Xvoid write_index_file (group)
  468. X    char *group;
  469. X{
  470. X    char nam[LEN];
  471. X    FILE *fp;
  472. X    int *iptr;
  473. X    int realnum;
  474. X    register int i;
  475. X
  476. X    set_tin_uid_gid();
  477. X
  478. X        sprintf (nam, "%s.%d", index_file, process_id);
  479. X    if ((fp = fopen (nam, "w")) == NULL) {
  480. X        perror_message (txt_cannot_open, nam);
  481. X        set_real_uid_gid ();
  482. X        return;
  483. X    }
  484. X
  485. X    /*
  486. X     *  dump group header info.
  487. X     */
  488. X    if (sort_art_type != SORT_BY_NOTHING) {
  489. X        qsort ((char *) arts, top, sizeof (struct article_t), artnum_comp);
  490. X    }
  491. X    fprintf (fp, "%s\n", group);
  492. X    fprintf (fp, "%d\n", num_of_arts ());
  493. X    if (top <= 0) {
  494. X        fprintf (fp, "0\n");
  495. X    } else {
  496. X        if (last_read_article > arts[top-1].artnum) {
  497. X            fprintf (fp, "%ld\n", last_read_article);
  498. X        } else {
  499. X            fprintf (fp, "%ld\n", arts[top-1].artnum);
  500. X        }
  501. X    }
  502. X
  503. X    /*
  504. X     *  dump articles
  505. X     */
  506. X    realnum = 0; 
  507. X    for (i = 0; i < top; i++) {
  508. X        if (arts[i].thread == ART_EXPIRED) { 
  509. X            continue;
  510. X        }
  511. X#ifdef DEBUG            
  512. X        debug_print_header (&arts[i]);
  513. X#endif
  514. X        fprintf(fp, "%ld\n", arts[i].artnum);
  515. X
  516. X        iptr = (int *) arts[i].subject;
  517. X        iptr--;
  518. X
  519. X        if (! arts[i].subject) {
  520. X            fprintf(fp, " \n");
  521. X        } else if (*iptr < 0 || *iptr > top) {
  522. X            fprintf(fp, " %s\n", arts[i].subject);
  523. X            *iptr = realnum;
  524. X        } else if (*iptr == i) {
  525. X            fprintf(fp, " %s\n", arts[i].subject);
  526. X        } else {
  527. X            fprintf(fp, "%%%d\n", *iptr);
  528. X        }
  529. X    
  530. X        iptr = (int *) arts[i].from;
  531. X        iptr--;
  532. X
  533. X        if (! arts[i].from) {
  534. X            fprintf (fp, " \n");
  535. X        } else if (*iptr < 0 || *iptr > top) {
  536. X            fprintf (fp, " %s\n", arts[i].from);
  537. X            *iptr = realnum;
  538. X        } else if (*iptr == i) {
  539. X            fprintf(fp, " %s\n", arts[i].from);
  540. X        } else {
  541. X            fprintf(fp, "%%%d\n", *iptr);
  542. X        }
  543. X
  544. X        iptr = (int *) arts[i].name;
  545. X        iptr--;
  546. X
  547. X        if (! arts[i].name) {
  548. X            fprintf (fp, " \n");
  549. X        } else if (*iptr < 0 || *iptr > top) {
  550. X            fprintf (fp, " %s\n", arts[i].name);
  551. X            *iptr = realnum;
  552. X        } else if (*iptr == i) {
  553. X            fprintf(fp, " %s\n", arts[i].name);
  554. X        } else {
  555. X            fprintf(fp, "%%%d\n", *iptr);
  556. X        }
  557. X
  558. X        fprintf (fp, "%s\n", arts[i].date);
  559. X            
  560. X        iptr = (int *) arts[i].archive;
  561. X        iptr--;
  562. X
  563. X        if (! arts[i].archive) {
  564. X            fprintf (fp, "\n");
  565. X        } else if (*iptr < 0 || *iptr > top) {
  566. X            fprintf (fp, " %s\n", arts[i].archive);
  567. X            *iptr = realnum;
  568. X        } else if (arts[i].part || arts[i].patch) {
  569. X            if (*iptr == i) {
  570. X                fprintf(fp, " %s\n", arts[i].archive);
  571. X            } else {
  572. X                fprintf (fp, "%%%d\n", *iptr);
  573. X            }
  574. X        } else {
  575. X            fprintf (fp, "\n");
  576. X        }
  577. X            
  578. X        if (! arts[i].part) {
  579. X            fprintf (fp, " \n");
  580. X        } else {
  581. X            fprintf (fp, "%s\n", arts[i].part);
  582. X        }
  583. X
  584. X        if (! arts[i].patch) {
  585. X            fprintf (fp, " \n");
  586. X        } else {
  587. X            fprintf (fp, "%s\n", arts[i].patch);
  588. X        }
  589. X
  590. X        realnum++;
  591. X    }
  592. X    fclose (fp);
  593. X    rename_file (nam, index_file);
  594. X    chmod (index_file, 0644);
  595. X    set_real_uid_gid();
  596. X    if (debug == 2) {
  597. X        sprintf (msg, "cp %s INDEX", index_file);
  598. X        system (msg);
  599. X    }
  600. X}
  601. X
  602. X/*
  603. X *  Read in an index file.
  604. X *
  605. X *  index file header 
  606. X *    1.  newsgroup name (ie. alt.sources)
  607. X *    2.  number of articles (ie. 26)
  608. X *    3.  number of last read article (ie. 210)
  609. X *    4.  Is this a complete/killed index file (ie. COMPLETE/KILLED)
  610. X *
  611. X *  index file record
  612. X *    1.  article number    (ie. 183)               [mandatory]
  613. X *    2.  Subject: line     (ie. Which newsreader?) [mandatory]
  614. X *    3.  From: line (addr) (ie. iain@norisc)       [mandatory]
  615. X *    4.  From: line (name) (ie. Iain Lea)          [mandatory]
  616. X *    5.  Date: of posting  (ie. 911231125959)      [mandatory]
  617. X *    6.  Archive: name     (ie. compiler)          [optional]
  618. X *    7.  Part number of Archive: name  (ie. 01)    [optional]
  619. X *    8.  Patch number of Archive: name (ie. 01)    [optional]
  620. X */
  621. X
  622. Xint read_index_file (group_name)
  623. X    char *group_name;
  624. X{
  625. X    int error = 0;
  626. X    int i, n;
  627. X    char buf[LEN], *p;
  628. X    FILE *fp = NULL;
  629. X
  630. X    top = 0;
  631. X    last_read_article = 0L;
  632. X
  633. X    if ((fp = open_index_fp (group_name)) == NULL) {
  634. X        return FALSE;
  635. X    }
  636. X
  637. X    /*
  638. X     *  load header - discard group name, num. of arts in index file after any arts were killed
  639. X     */
  640. X    if (fgets(buf, sizeof buf, fp) == NULL ||
  641. X        fgets(buf, sizeof buf, fp) == NULL) {
  642. X        error = 0;            
  643. X        goto corrupt_index;    
  644. X    }
  645. X    i = atoi (buf);
  646. X
  647. X    /*
  648. X     * num. of last_read_article including any that were killed
  649. X     */
  650. X    if (fgets(buf, sizeof buf, fp) == NULL) {
  651. X        error = 1;                
  652. X        goto corrupt_index;    
  653. X    }                            
  654. X    last_read_article = (long) atol (buf);
  655. X    
  656. X    /*
  657. X     *  load articles
  658. X     */
  659. X    for (; top < i ; top++) {
  660. X        if (top >= max_art) {
  661. X            expand_art ();
  662. X        }
  663. X
  664. X        arts[top].thread = ART_EXPIRED;
  665. X        set_article (&arts[top]);
  666. X
  667. X        /*
  668. X         * Article no.
  669. X         */
  670. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  671. X            error = 2;
  672. X            goto corrupt_index;
  673. X        }
  674. X        arts[top].artnum = (long) atol (buf);
  675. X
  676. X        /*
  677. X         * Subject:
  678. X         */
  679. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  680. X            error = 3;
  681. X            goto corrupt_index;
  682. X        }
  683. X
  684. X        if (buf[0] == '%') {
  685. X            n = atoi (&buf[1]);
  686. X            if (n >= top || n < 0) {
  687. X                error = 4;
  688. X                goto corrupt_index;
  689. X            }
  690. X            arts[top].subject = arts[n].subject;
  691. X        } else if (buf[0] == ' ') {
  692. X            for (p = &buf[1];  *p && *p != '\n'; p++)
  693. X                continue;    
  694. X            *p = '\0';
  695. X            arts[top].subject = hash_str (&buf[1]);
  696. X        } else {
  697. X            error = 5;
  698. X            goto corrupt_index;
  699. X        }
  700. X            
  701. X        /*
  702. X         * From: (addr part)
  703. X         */
  704. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  705. X            error = 6;
  706. X            goto corrupt_index;
  707. X        }
  708. X
  709. X        if (buf[0] == '%') {
  710. X            n = atoi (&buf[1]);
  711. X            if (n >= top || n < 0) {
  712. X                error = 7;
  713. X                goto corrupt_index;
  714. X            }
  715. X            arts[top].from = arts[n].from;
  716. X        } else if (buf[0] == ' ') {
  717. X            for (p = &buf[1];  *p && *p != '\n'; p++)
  718. X                continue;
  719. X            *p = '\0';
  720. X            arts[top].from = hash_str (&buf[1]);
  721. X        } else {
  722. X            error = 8;
  723. X            goto corrupt_index;
  724. X        }
  725. X
  726. X        /*
  727. X         * From: (full name)
  728. X         */
  729. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  730. X            error = 9;
  731. X            goto corrupt_index;
  732. X        }
  733. X
  734. X        if (buf[0] == '%') {
  735. X            n = atoi (&buf[1]);
  736. X            if (n > top || n < 0) {
  737. X                error = 10;
  738. X                goto corrupt_index;
  739. X            }
  740. X            if (n == top) {        /* no full name so .name = .from */
  741. X                arts[top].name = arts[top].from;
  742. X            } else {
  743. X                arts[top].name = arts[n].name;
  744. X            }
  745. X        } else if (buf[0] == ' ') {
  746. X            for (p = &buf[1];  *p && *p != '\n'; p++)
  747. X                continue;
  748. X            *p = '\0';
  749. X            arts[top].name = hash_str (&buf[1]);
  750. X        } else {
  751. X            error = 11;
  752. X            goto corrupt_index;
  753. X        }
  754. X
  755. X        /*
  756. X         * Date:
  757. X         */
  758. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  759. X            error = 12;
  760. X            goto corrupt_index;
  761. X        }
  762. X
  763. X        buf[strlen (buf)-1] = '\0';
  764. X        my_strncpy (arts[top].date, buf, 12);
  765. X
  766. X        /*
  767. X         * Archive-name:
  768. X         */
  769. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  770. X            error = 13;
  771. X            goto corrupt_index;
  772. X        }
  773. X
  774. X        if (buf[0] == '\n') {
  775. X            arts[top].archive = (char *) 0;
  776. X        } else if (buf[0] == '%') {
  777. X            n = atoi (&buf[1]);
  778. X            if (n > top || n < 0) {
  779. X                error = 14;
  780. X                goto corrupt_index;
  781. X            }
  782. X            arts[top].archive = arts[n].archive;
  783. X        } else if (buf[0] == ' ') {
  784. X            for (p = &buf[1]; *p && *p != '\n' ; p++)
  785. X                continue;
  786. X            *p = '\0';
  787. X            arts[top].archive = hash_str (&buf[1]);
  788. X        } else {
  789. X            error = 15;
  790. X            goto corrupt_index;
  791. X        }
  792. X
  793. X        /*
  794. X         * part no.
  795. X         */
  796. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  797. X            error = 16;
  798. X            goto corrupt_index;
  799. X        }
  800. X
  801. X        if (buf[0] != ' ') { 
  802. X            buf[strlen (buf)-1] = '\0';
  803. X            arts[top].part = str_dup (buf);
  804. X        }
  805. X
  806. X        /*
  807. X         * patch no.
  808. X         */
  809. X        if (fgets(buf, sizeof buf, fp) == NULL) {
  810. X            error = 17;
  811. X            goto corrupt_index;
  812. X        }
  813. X
  814. X        if (buf[0] != ' ') { 
  815. X            buf[strlen (buf)-1] = '\0';
  816. X            arts[top].patch = str_dup (buf);
  817. X        }
  818. X
  819. X        debug_print_header (&arts[top]);
  820. X    }
  821. X
  822. X    fclose(fp);
  823. X    return TRUE;
  824. X
  825. Xcorrupt_index:
  826. X    if (! update) {
  827. X        sprintf (msg, txt_corrupt_index, index_file, error, top); 
  828. X        error_message (msg, "");
  829. X    }
  830. X
  831. X    if (debug == 2) {
  832. X        sprintf (msg, "cp %s INDEX.BAD", index_file);
  833. X        system (msg);
  834. X    }
  835. X
  836. X    last_read_article = 0L;
  837. X    if (fp) {
  838. X        fclose(fp);
  839. X    }    
  840. X    set_tin_uid_gid();
  841. X    unlink (index_file);
  842. X    set_real_uid_gid();
  843. X    top = 0;
  844. X    return FALSE;
  845. X}
  846. X
  847. X
  848. X/*
  849. X *  Look in the local $HOME/RCDIR/INDEXDIR (or wherever) directory for the
  850. X *  index file for the given group.  Hashing the group name gets
  851. X *  a number.  See if that #.1 file exists; if so, read first line.
  852. X *  Group we want?  If no, try #.2.  Repeat until no such file or
  853. X *  we find an existing file that matches our group.
  854. X */
  855. X
  856. Xvoid find_index_file (group)
  857. X    char *group;
  858. X{
  859. X    char *p;
  860. X    FILE *fp;
  861. X    int i = 1;
  862. X    static char buf[LEN];
  863. X    char dir[PATH_LEN];
  864. X    unsigned long h;
  865. X
  866. X    h = hash_groupname (group);
  867. X
  868. X    if (read_news_via_nntp && xindex_supported) {
  869. X        sprintf (index_file, "/tmp/xindex.%d", process_id);
  870. X        return;
  871. X    }
  872. X    
  873. X    if (local_index) {
  874. X        my_strncpy (dir, indexdir, sizeof (dir));
  875. X    } else {
  876. X        sprintf (dir, "%s/%s", spooldir, INDEXDIR);
  877. X    }
  878. X    
  879. X    while (TRUE) {
  880. X        sprintf (index_file, "%s/%lu.%d", dir, h, i);
  881. X        
  882. X        if ((fp = fopen (index_file, "r")) == (FILE *) 0) {
  883. X            return;
  884. X        }
  885. X
  886. X        if (fgets (buf, sizeof (buf), fp) == (char *) 0) {
  887. X            fclose (fp);
  888. X            return;
  889. X        }
  890. X        fclose (fp);
  891. X
  892. X        for (p = buf; *p && *p != '\n'; p++) {
  893. X            continue;
  894. X        }    
  895. X        *p = '\0';
  896. X
  897. X        if (strcmp (buf, group) == 0) {
  898. X            return;
  899. X        }    
  900. X        i++;
  901. X    }    
  902. X}
  903. X
  904. X/*
  905. X *  Run the index file updater only for the groups we've loaded.
  906. X */
  907. X
  908. Xvoid do_update ()
  909. X{
  910. X    int i, j;
  911. X    char group_path[PATH_LEN];
  912. X    char *p;
  913. X    long beg_epoch, end_epoch;
  914. X    
  915. X    if (verbose) {
  916. X        time (&beg_epoch);
  917. X    }
  918. X
  919. X    for (i = 0; i < group_top; i++) {
  920. X        my_strncpy (group_path, active[my_group[i]].name, sizeof (group_path));
  921. X        for (p = group_path ; *p ; p++) {
  922. X            if (*p == '.') {
  923. X                *p = '/';
  924. X            }
  925. X        }
  926. X        if (verbose) {
  927. X            printf ("%s %s\n", (catchup ? "Catchup" : "Updating"),
  928. X                    active[my_group[i]].name);
  929. X            fflush (stdout);
  930. X        }
  931. X        index_group (active[my_group[i]].name, group_path);
  932. X        if (catchup) {
  933. X            for (j = 0; j < top; j++) {
  934. X                arts[j].unread = ART_READ;
  935. X            }
  936. X            update_newsrc (active[my_group[i]].name, my_group[i], FALSE);
  937. X        }
  938. X    }
  939. X
  940. X    if (verbose) {
  941. X        time (&end_epoch);
  942. X        sprintf (msg, "%s %d groups in %ld seconds\n", 
  943. X            (catchup ? "Caughtup" : "Updated"), group_top, end_epoch - beg_epoch);
  944. X        wait_message (msg);
  945. X    }
  946. X}
  947. X
  948. X/*
  949. X * convert date from ctime format to sortable format
  950. X * "24 Jul 91 12:59:59", "Mon, 24 Jul 91 12:59:59" and
  951. X * "Mon, 24 Jul 1991 12:59:59" are parsed and produce
  952. X * output of the form "910724125959"
  953. X */
  954. X
  955. Xchar *parse_date (date, str)
  956. X    char *date;
  957. X    char *str;
  958. X{
  959. X    char buf[4];
  960. X    int i = 0;
  961. X
  962. X    /* Check for extraneous day-of-week at start of date */
  963. X    while (isalpha(date[i]) || date[i] == ',' || date[i] == ' ') {
  964. X        i++;
  965. X    }
  966. X    
  967. X    if (date[i+1] == ' ') {    /* ie. "2 Aug..." instead of "12 Aug... */
  968. X        str[4] = '0';        /* day */
  969. X        str[5] = date[i++];
  970. X        i++;
  971. X    } else {
  972. X        str[4] = date[i++];        /* day */
  973. X        str[5] = date[i++];
  974. X        i++;
  975. X    }
  976. X    
  977. X    buf[0] = date[i++];        /* month in Jan,Feb,.. form */
  978. X    buf[1] = date[i++];
  979. X    buf[2] = date[i++];
  980. X    buf[3] = '\0';
  981. X
  982. X    i++;
  983. X    
  984. X    str[0] = date[i++];        /* year */
  985. X    str[1] = date[i++];
  986. X    if (isdigit(date[i])) {         /* 19xx format */
  987. X        str[0] = date[i++];
  988. X        str[1] = date[i++];
  989. X    }
  990. X    
  991. X    i++;
  992. X    
  993. X    if (strcmp (buf, "Jan") == 0) {        /* convert Jan to 01 etc */
  994. X        str[2] = '0';
  995. X        str[3] = '1';
  996. X    } else if (strcmp (buf, "Feb") == 0) {
  997. X        str[2] = '0';
  998. X        str[3] = '2';
  999. X    } else if (strcmp (buf, "Mar") == 0) {
  1000. X        str[2] = '0';
  1001. X        str[3] = '3';
  1002. X    } else if (strcmp (buf, "Apr") == 0) {
  1003. X        str[2] = '0';
  1004. X        str[3] = '4';
  1005. X    } else if (strcmp (buf, "May") == 0) {
  1006. X        str[2] = '0';
  1007. X        str[3] = '5';
  1008. X    } else if (strcmp (buf, "Jun") == 0) {
  1009. X        str[2] = '0';
  1010. X        str[3] = '6';
  1011. X    } else if (strcmp (buf, "Jul") == 0) {
  1012. X        str[2] = '0';
  1013. X        str[3] = '7';
  1014. X    } else if (strcmp (buf, "Aug") == 0) {
  1015. X        str[2] = '0';
  1016. X        str[3] = '8';
  1017. X    } else if (strcmp (buf, "Sep") == 0) {
  1018. X        str[2] = '0';
  1019. X        str[3] = '9';
  1020. X    } else if (strcmp (buf, "Oct") == 0) {
  1021. X        str[2] = '1';
  1022. X        str[3] = '0';
  1023. X    } else if (strcmp (buf, "Nov") == 0) {
  1024. X        str[2] = '1';
  1025. X        str[3] = '1';
  1026. X    } else if (strcmp (buf, "Dec") == 0) {
  1027. X        str[2] = '1';
  1028. X        str[3] = '2';
  1029. X    } else {
  1030. X        str[2] = '0';
  1031. X        str[3] = '0';
  1032. X    }
  1033. X    
  1034. X    str[6] = date[i++];        /* hour */
  1035. X    str[7] = date[i++];
  1036. X
  1037. X    i++;
  1038. X    
  1039. X    str[8] = date[i++];        /* minutes */
  1040. X    str[9] = date[i++];
  1041. X    
  1042. X    i++;
  1043. X    
  1044. X    str[10] = date[i++];    /* seconds */
  1045. X    str[11] = date[i++];
  1046. X
  1047. X    str[12] = '\0';        /* terminate string */
  1048. X
  1049. X    return (str);
  1050. X}
  1051. X
  1052. X
  1053. Xint artnum_comp (p1, p2)
  1054. X    char *p1;
  1055. X    char *p2;
  1056. X{
  1057. X    struct article_t *s1 = (struct article_t *) p1;
  1058. X    struct article_t *s2 = (struct article_t *) p2;
  1059. X
  1060. X    /* s1->artnum less than s2->artnum */
  1061. X    if (s1->artnum < s2->artnum) {
  1062. X        return -1;
  1063. X    }
  1064. X    /* s1->artnum greater than s2->artnum */
  1065. X    if (s1->artnum > s2->artnum) {
  1066. X        return 1;
  1067. X    }
  1068. X    return 0;
  1069. X}
  1070. X
  1071. X
  1072. Xint subj_comp (p1, p2)
  1073. X    char *p1;
  1074. X    char *p2;
  1075. X{
  1076. X    struct article_t *s1 = (struct article_t *) p1;
  1077. X    struct article_t *s2 = (struct article_t *) p2;
  1078. X
  1079. X    /* return result of strcmp (reversed for descending) */
  1080. X    return (sort_art_type == SORT_BY_SUBJ_ASCEND 
  1081. X            ? my_stricmp (s1->subject, s2->subject) 
  1082. X            : my_stricmp (s2->subject, s1->subject));
  1083. X}
  1084. X
  1085. X
  1086. Xint from_comp (p1, p2)
  1087. X    char *p1;
  1088. X    char *p2;
  1089. X{
  1090. X    struct article_t *s1 = (struct article_t *) p1;
  1091. X    struct article_t *s2 = (struct article_t *) p2;
  1092. X
  1093. X    /* return result of strcmp (reversed for descending) */
  1094. X    return (sort_art_type == SORT_BY_FROM_ASCEND 
  1095. X            ? my_stricmp (s1->from, s2->from) 
  1096. X            : my_stricmp (s2->from, s1->from));
  1097. X}
  1098. X
  1099. X
  1100. Xint date_comp (p1, p2)
  1101. X    char *p1;
  1102. X    char *p2;
  1103. X{
  1104. X    struct article_t *s1 = (struct article_t *) p1;
  1105. X    struct article_t *s2 = (struct article_t *) p2;
  1106. X    /* return result of strcmp (reversed for descending) */
  1107. X    return (sort_art_type == SORT_BY_DATE_ASCEND 
  1108. X            ? strcmp (s1->date, s2->date) 
  1109. X            : strcmp (s2->date, s1->date));
  1110. X}
  1111. X
  1112. X
  1113. Xvoid set_article (art)
  1114. X    struct article_t *art;
  1115. X{    
  1116. X    art->subject = (char *) 0;
  1117. X    art->from = (char *) 0;
  1118. X    art->name = (char *) 0;
  1119. X    art->date[0] = '\0';
  1120. X    art->archive = (char *) 0;
  1121. X    art->part = (char *) 0;
  1122. X    art->patch = (char *) 0;
  1123. X    art->xref = NULL;
  1124. X    art->unread = ART_UNREAD;
  1125. X    art->inthread = FALSE;
  1126. X    art->killed = FALSE;
  1127. X    art->tagged = FALSE;
  1128. X    art->hot = FALSE;
  1129. X    art->zombie = FALSE;
  1130. X}
  1131. END_OF_FILE
  1132.   if test 22975 -ne `wc -c <'art.c'`; then
  1133.     echo shar: \"'art.c'\" unpacked with wrong size!
  1134.   fi
  1135.   # end of 'art.c'
  1136. fi
  1137. if test -f 'post.c' -a "${1}" != "-c" ; then 
  1138.   echo shar: Will not clobber existing file \"'post.c'\"
  1139. else
  1140.   echo shar: Extracting \"'post.c'\" \(28095 characters\)
  1141.   sed "s/^X//" >'post.c' <<'END_OF_FILE'
  1142. X/*
  1143. X *  Project   : tin - a threaded Netnews reader
  1144. X *  Module    : post.c
  1145. X *  Author    : I.Lea
  1146. X *  Created   : 01-04-91
  1147. X *  Updated   : 16-06-92
  1148. X *  Notes     : mail/post/replyto/followup/crosspost & cancel articles
  1149. X *  Copyright : (c) Copyright 1991-92 by Iain Lea
  1150. X *              You may  freely  copy or  redistribute  this software,
  1151. X *              so  long as there is no profit made from its use, sale
  1152. X *              trade or  reproduction.  You may not change this copy-
  1153. X *              right notice, and it must be included in any copy made
  1154. X */
  1155. X
  1156. X#include    "tin.h"
  1157. X
  1158. Xextern char note_h_distrib[LEN];        /* Distribution: */
  1159. Xextern char note_h_followup[LEN];        /* Followup-To: */
  1160. Xextern char note_h_messageid[LEN];        /* Message-ID:    */
  1161. Xextern char note_h_newsgroups[LEN];        /* Newsgroups:    */
  1162. Xextern char note_h_subj[LEN];            /* Subject:    */
  1163. Xextern char note_h_date[LEN];            /* Date:    */
  1164. Xextern FILE *note_fp;                /* the body of the current article */
  1165. Xextern int cur_groupnum;
  1166. Xextern long note_mark[MAX_PAGES];        /* ftells on beginnings of pages */
  1167. X
  1168. Xchar default_post_subject[LEN];    
  1169. X
  1170. Xint unlink_article = TRUE;
  1171. Xstruct posted_t *posted;
  1172. X
  1173. X
  1174. Xint user_posted_messages ()
  1175. X{
  1176. X    char buf[LEN];
  1177. X    FILE *fp;
  1178. X    int i, j, k;
  1179. X    int no_of_lines = 0;
  1180. X
  1181. X    if ((fp = fopen (postfile, "r")) == NULL) {
  1182. X        clear_message ();
  1183. X        return FALSE;
  1184. X    } else {
  1185. X        while (fgets (buf, sizeof (buf), fp) != NULL) {
  1186. X            no_of_lines++;
  1187. X        }
  1188. X        if (! no_of_lines) {
  1189. X            fclose (fp);
  1190. X            info_message (txt_no_arts_posted);
  1191. X            return FALSE;
  1192. X        }
  1193. X        rewind (fp);
  1194. X        posted = (struct posted_t *) my_malloc ((unsigned) (no_of_lines+1) * sizeof (struct posted_t));
  1195. X        for (i=0 ; fgets (buf, sizeof (buf), fp) != NULL ; i++) {
  1196. X            for (j=0 ; buf[j] != '|' && buf[j] != '\n' ; j++) {
  1197. X                posted[i].date[j] = buf[j];        /* posted date */
  1198. X            }
  1199. X            if (buf[j] == '\n') {    
  1200. X                error_message ("Corrupted file %s", postfile);
  1201. X                sleep (1);
  1202. X                fclose (fp);
  1203. X                clear_message ();
  1204. X                return FALSE;
  1205. X            }
  1206. X            posted[i].date[j++] = '\0';
  1207. X            posted[i].action = buf[j];
  1208. X            j += 2;
  1209. X            for (k=j,j=0 ; buf[k] != '|' && buf[k] != ',' ; k++, j++) {
  1210. X                posted[i].group[j] = buf[k];
  1211. X            }
  1212. X            if (buf[k] == ',') {
  1213. X                while (buf[k] != '|' && buf[k] != '\n') {
  1214. X                    k++;
  1215. X                }
  1216. X                posted[i].group[j++] = ',';
  1217. X                posted[i].group[j++] = '.';
  1218. X                posted[i].group[j++] = '.';
  1219. X                posted[i].group[j++] = '.';
  1220. X            }
  1221. X            posted[i].group[j++] = '\0';
  1222. X            k++;
  1223. X            for (j=k,k=0 ; buf[j] != '\n' ; j++, k++) {
  1224. X                posted[i].subj[k] = buf[j];
  1225. X            }
  1226. X            posted[i].subj[k++] = '\0';
  1227. X        }
  1228. X        fclose (fp);
  1229. X
  1230. X        show_info_page (POST_INFO, (char **) 0, txt_post_history_menu);
  1231. X        if (posted != (struct posted_t *) 0) {
  1232. X            free ((char *) posted);
  1233. X            posted = (struct posted_t *) 0;
  1234. X        }
  1235. X        return TRUE;
  1236. X    }
  1237. X}
  1238. X
  1239. X
  1240. Xvoid update_art_posted_file (group, action, subj)
  1241. X    char *group;
  1242. X    int action;
  1243. X    char *subj;
  1244. X{
  1245. X    char buf[LEN];
  1246. X    char tmp_post[LEN];
  1247. X    FILE *fp, *tmp_fp;
  1248. X    long epoch;
  1249. X    struct tm *tm;
  1250. X
  1251. X    sprintf (tmp_post, "%s.%d", postfile, process_id);
  1252. X
  1253. X    if ((tmp_fp = fopen (tmp_post, "w")) != NULL) {
  1254. X        time (&epoch);
  1255. X        tm = localtime (&epoch);
  1256. X        fprintf (tmp_fp, "%02d-%02d-%02d|%c|%s|%s\n",
  1257. X            tm->tm_mday, tm->tm_mon+1, tm->tm_year,
  1258. X            action, group, subj);
  1259. X        fclose (tmp_fp);
  1260. X    }
  1261. X
  1262. X    if ((tmp_fp = fopen (tmp_post, "a+")) != NULL) {
  1263. X        if ((fp = fopen (postfile, "r")) != NULL) {
  1264. X            while (fgets (buf, sizeof buf, fp) != NULL) {
  1265. X                fprintf (tmp_fp, "%s", buf);
  1266. X            }    
  1267. X            fclose (fp);
  1268. X            rename_file (tmp_post, postfile);
  1269. X        }
  1270. X        fclose (tmp_fp);
  1271. X    }
  1272. X}
  1273. X
  1274. X/*
  1275. X * Check the article file so that it is not missing the blank line
  1276. X * between the header information and the text.
  1277. X */
  1278. X
  1279. Xint post_header_ok (article)
  1280. X    char* article;
  1281. X{
  1282. X    FILE *fp;
  1283. X    char line[LEN];
  1284. X    int cnt= 0;
  1285. X    int len, ind;
  1286. X    char prev_ch;
  1287. X    int header;
  1288. X
  1289. X    if ((fp = fopen (article, "r")) == NULL) {
  1290. X        perror_message (txt_cannot_open, article);
  1291. X        return FALSE;
  1292. X    }
  1293. X
  1294. X    while (fgets (line, sizeof (line), fp) != NULL) {
  1295. X        cnt++;
  1296. X        len= strlen (line);
  1297. X        if (len > 0)
  1298. X            if (line[len - 1] == '\n') 
  1299. X                line[--len]= 0;
  1300. X                
  1301. X        if ((len == 0) && (cnt >= 2)) {
  1302. X            fclose(fp);
  1303. X            return TRUE;
  1304. X        }
  1305. X        prev_ch= ' ';
  1306. X        header= FALSE;
  1307. X        for (ind= 0; ind < len; ind++) /* Skip white space */
  1308. X            if ((line[ind] != ' ') && (line[ind] != '\t'))
  1309. X                break;
  1310. X        for (; ind < len; ind++) {   
  1311. X            /* Header as long as the first token ends with ':' */
  1312. X            if (((ind == len - 1) &&
  1313. X                 (line[ind] == ':')) ||
  1314. X                (((line[ind] == ' ') ||
  1315. X                  (line[ind] == '\t')) &&
  1316. X                 (prev_ch == ':'))) {
  1317. X                header= TRUE;
  1318. X                break;
  1319. X            }
  1320. X
  1321. X            if ((line[ind] == ' ') ||
  1322. X                (line[ind] == '\t'))
  1323. X                break;
  1324. X            prev_ch= line[ind];
  1325. X        }
  1326. X        if (! header) {
  1327. X            fclose (fp);
  1328. X            return FALSE;
  1329. X        }
  1330. X    }
  1331. X    fclose (fp);
  1332. X    return FALSE;
  1333. X}
  1334. X
  1335. X/*
  1336. X *  Post an original article (not a followup)
  1337. X */
  1338. X
  1339. Xint post_base (group, posted)
  1340. X    char *group;
  1341. X    int *posted;
  1342. X{
  1343. X    FILE *fp;
  1344. X    char ch;
  1345. X    char ch_default = 'p';
  1346. X    char subj[LEN];
  1347. X    char buf[LEN];
  1348. X    int redraw_screen = FALSE;
  1349. X
  1350. X    /*
  1351. X     * Don't allow if not active news feed 
  1352. X     */
  1353. X    if (! spooldir_is_active) {
  1354. X        info_message (txt_not_active_newsfeed);
  1355. X        return (redraw_screen);
  1356. X    }
  1357. X    
  1358. X    *posted = FALSE;
  1359. X    start_line_offset = 7;
  1360. X
  1361. X    if (active[my_group[cur_groupnum]].moderated == 'm') {
  1362. X        sprintf (msg, "Group %s is moderated. Continue? (y/n): ", group);
  1363. X        if (! prompt_yn (LINES, msg, 'y')) {
  1364. X            clear_message ();
  1365. X            return (redraw_screen);
  1366. X        }
  1367. X    }
  1368. X
  1369. X    sprintf (msg, txt_post_subject, default_post_subject);
  1370. X    
  1371. X    if (! prompt_string (msg, subj)) {
  1372. X        clear_message ();
  1373. X        return (redraw_screen);
  1374. X    }
  1375. X
  1376. X    if (strlen (subj)) {
  1377. X        my_strncpy (default_post_subject, subj,
  1378. X            sizeof (default_post_subject));
  1379. X    } else {
  1380. X        if (default_post_subject[0]) {
  1381. X            my_strncpy (subj, default_post_subject, sizeof (subj));
  1382. X        } else {
  1383. X            info_message (txt_no_subject);
  1384. X            return (redraw_screen);
  1385. X        }
  1386. X    }
  1387. X    
  1388. X    wait_message (txt_post_an_article);
  1389. X
  1390. X    if ((fp = fopen (article, "w")) == NULL) {
  1391. X        perror_message (txt_cannot_open, article);
  1392. X        return (redraw_screen);
  1393. X    }
  1394. X    chmod (article, 0600);
  1395. X
  1396. X    fprintf (fp, "Subject: %s\n", subj);
  1397. X    fprintf (fp, "Newsgroups: %s\n", group);
  1398. X    if (*my_org) {
  1399. X        fprintf (fp, "Organization: %s\n", my_org);
  1400. X        start_line_offset++;
  1401. X    }
  1402. X    if (*reply_to) {
  1403. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  1404. X        start_line_offset++;
  1405. X    }
  1406. X    fprintf (fp, "Distribution: %s\n", my_distribution);
  1407. X    fprintf (fp, "Summary: \n");
  1408. X    fprintf (fp, "Keywords: \n\n\n");
  1409. X
  1410. X    add_signature (fp, FALSE);
  1411. X    fclose (fp);
  1412. X
  1413. X    ch = 'e';
  1414. X    while (1) {
  1415. X        switch (ch) {
  1416. X        case 'e':
  1417. X            invoke_editor (article);
  1418. X            while (! post_header_ok(article)) {
  1419. X                do {
  1420. X                    sprintf (msg, "%s%c", txt_no_blank_line, 'e');
  1421. X                    wait_message (msg);
  1422. X                    MoveCursor (LINES, (int) strlen (txt_no_blank_line));
  1423. X                    if ((ch = (char) ReadCh ()) == CR)
  1424. X                        ch = 'e';
  1425. X                } while (ch != ESC && ch != 'q' && ch != 'e');
  1426. X                if (ch == 'e')
  1427. X                    invoke_editor (article);
  1428. X                else
  1429. X                    break;
  1430. X            }
  1431. X            redraw_screen = TRUE;
  1432. X            if (ch == 'e') {
  1433. X                break;
  1434. X            }
  1435. X        case 'q':
  1436. X        case ESC:
  1437. X            if (unlink_article)
  1438. X                unlink (article);
  1439. X            clear_message ();
  1440. X            return (redraw_screen);
  1441. X
  1442. X        case 'p':
  1443. X            wait_message (txt_posting);
  1444. X            if (submit_file (article)) {
  1445. X                info_message (txt_art_posted);
  1446. X                *posted = TRUE;
  1447. X                goto post_base_done;
  1448. X            } else {
  1449. X                rename_file (article, dead_article);
  1450. X                sprintf (buf, txt_art_rejected, dead_article);
  1451. X                info_message (buf);
  1452. X                sleep (3);
  1453. X                return (redraw_screen);
  1454. X            }
  1455. X        }
  1456. X
  1457. X        do {
  1458. X            sprintf (msg, "%s%c", txt_quit_edit_post, ch_default);
  1459. X            wait_message (msg);
  1460. X            MoveCursor (LINES, (int) strlen (txt_quit_edit_post));
  1461. X            if ((ch = (char) ReadCh ()) == CR)
  1462. X                ch = ch_default;
  1463. X        } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 'p');
  1464. X    }
  1465. X
  1466. Xpost_base_done:
  1467. X    find_mail_header (HEADER_SUBJECT, article, subj);
  1468. X    if (unlink_article)
  1469. X        unlink (article);
  1470. X    update_art_posted_file (group, 'w', subj);
  1471. X    /*
  1472. X     * Update default posting prompt
  1473. X     */
  1474. X    my_strncpy (default_post_subject, subj, sizeof (default_post_subject));
  1475. X
  1476. X    return (redraw_screen);
  1477. X}
  1478. X
  1479. X
  1480. Xint post_response (group, respnum, copy_text)
  1481. X    char *group;
  1482. X    int respnum;
  1483. X    int copy_text;
  1484. X{
  1485. X    FILE *fp;
  1486. X    char ch, *ptr;
  1487. X    char ch_default = 'p';
  1488. X    char buf[LEN];
  1489. X    int ret_code = POSTED_NONE;
  1490. X    int followup_to_poster = FALSE;
  1491. X    
  1492. X    /*
  1493. X     * Don't allow if not active news feed 
  1494. X     */
  1495. X    if (! spooldir_is_active) {
  1496. X        info_message (txt_not_active_newsfeed);
  1497. X        return (ret_code);
  1498. X    }
  1499. X
  1500. X    start_line_offset = 4;
  1501. X
  1502. X    wait_message (txt_post_a_followup);
  1503. X    
  1504. X    if (*note_h_followup && strcmp (note_h_followup, "poster") == 0) {
  1505. X        clear_message ();
  1506. X        if (! prompt_yn (LINES, txt_resp_to_poster, 'y')) {
  1507. X            return (ret_code);
  1508. X        }
  1509. X        *note_h_followup = '\0';
  1510. X        followup_to_poster = TRUE;
  1511. X
  1512. X        find_reply_to_addr (respnum, buf);
  1513. X        mail_to_someone (buf, TRUE, FALSE, &ret_code);
  1514. X        return (ret_code);
  1515. X    } else if (*note_h_followup && strcmp (note_h_followup, group) != 0) {
  1516. X        MoveCursor (LINES/2, 0);
  1517. X        CleartoEOS ();
  1518. X        center_line ((LINES/2)+2, TRUE, txt_resp_redirect);
  1519. X        MoveCursor ((LINES/2)+4, 0);
  1520. X
  1521. X        fputs ("    ", stdout);
  1522. X        ptr = note_h_followup;
  1523. X        while (*ptr) {
  1524. X            if (*ptr != ',') {
  1525. X                fputc (*ptr, stdout);
  1526. X            } else {
  1527. X                fputs ("\r\n    ", stdout);
  1528. X            }
  1529. X            ptr++;
  1530. X        }
  1531. X        fflush (stdout);
  1532. X
  1533. X        if (! prompt_yn (LINES, txt_continue, 'y')) {
  1534. X            return (ret_code);
  1535. X        }
  1536. X    }
  1537. X
  1538. X    if ((fp = fopen (article, "w")) == NULL) {
  1539. X        perror_message (txt_cannot_open, article);
  1540. X        return (ret_code);
  1541. X    }
  1542. X    chmod (article, 0600);
  1543. X
  1544. X    fprintf (fp, "Subject: Re: %s\n", eat_re (note_h_subj));
  1545. X
  1546. X    if (*note_h_followup && strcmp (note_h_followup, "poster") != 0) {
  1547. X        fprintf (fp, "Newsgroups: %s\n", note_h_followup);
  1548. X    } else {
  1549. X        fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups);
  1550. X    }
  1551. X
  1552. X    fprintf (fp, "References: %s\n", note_h_messageid);
  1553. X
  1554. X    if (*my_org) {
  1555. X        fprintf (fp, "Organization: %s\n", my_org);
  1556. X        start_line_offset++;
  1557. X    }
  1558. X    if (*reply_to) {
  1559. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  1560. X        start_line_offset++;
  1561. X    }
  1562. X    if (note_h_distrib != '\0') {
  1563. X        fprintf (fp, "Distribution: %s\n", note_h_distrib);
  1564. X        start_line_offset++;
  1565. X    }
  1566. X    fprintf (fp, "\n");
  1567. X
  1568. X    if (copy_text) {    /* if "copy_text" */
  1569. X        if (arts[respnum].from != (char *) 0) {     
  1570. X            if (arts[respnum].name == arts[respnum].from) {     
  1571. X                fprintf (fp, txt_writes, arts[respnum].from);
  1572. X            } else {
  1573. X                fprintf (fp, txt_writes_name, arts[respnum].from, arts[respnum].name);
  1574. X            }
  1575. X        }
  1576. X        fseek (note_fp, note_mark[0], 0);
  1577. X        copy_fp (note_fp, fp, DEFAULT_COMMENT);
  1578. X    }
  1579. X
  1580. X    add_signature (fp, FALSE);
  1581. X    fclose (fp);
  1582. X
  1583. X    ch = 'e';
  1584. X    while (1) {
  1585. X        switch (ch) {
  1586. X        case 'e':
  1587. X            invoke_editor (article);
  1588. X            while (! post_header_ok (article)) {
  1589. X                do {
  1590. X                    sprintf (msg, "%s%c", txt_no_blank_line, 'e');
  1591. X                    wait_message (msg);
  1592. X                    MoveCursor (LINES, (int) strlen (txt_no_blank_line));
  1593. X                    if ((ch = (char) ReadCh ()) == CR)
  1594. X                        ch = 'e';
  1595. X                } while (ch != ESC && ch != 'q' && ch != 'e');
  1596. X                if (ch == 'e')
  1597. X                    invoke_editor (article);
  1598. X                else
  1599. X                    break;
  1600. X            }
  1601. X            if (ch == 'e') {
  1602. X                break;
  1603. X            }
  1604. X            ret_code = POSTED_REDRAW;
  1605. X            break;
  1606. X
  1607. X        case 'q':
  1608. X        case ESC:
  1609. X            if (unlink_article)
  1610. X                unlink (article);
  1611. X            clear_message ();
  1612. X            return (ret_code);
  1613. X
  1614. X        case 'p':
  1615. X            wait_message (txt_posting);
  1616. X            if (submit_file (article)) {
  1617. X                ret_code = POSTED_OK;
  1618. X                info_message (txt_art_posted);
  1619. X                goto post_response_done;
  1620. X            } else {
  1621. X                rename_file (article, dead_article);
  1622. X                sprintf (buf, txt_art_rejected, dead_article);
  1623. X                info_message (buf);
  1624. X                sleep (3);
  1625. X                return (ret_code);
  1626. X            }
  1627. X        }
  1628. X
  1629. X        do {
  1630. X            sprintf (msg, "%s%c", txt_quit_edit_post, ch_default);
  1631. X            wait_message (msg);
  1632. X            MoveCursor(LINES, (int) strlen (txt_quit_edit_post));
  1633. X            if ((ch = (char) ReadCh()) == CR)
  1634. X                ch = ch_default;
  1635. X        } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 'p');
  1636. X    }
  1637. X
  1638. Xpost_response_done:
  1639. X    if (*note_h_followup && strcmp(note_h_followup, "poster") != 0) {
  1640. X        find_mail_header (HEADER_SUBJECT, article, buf);
  1641. X        update_art_posted_file (note_h_followup, 'f', buf);
  1642. X    } else {
  1643. X        find_mail_header (HEADER_SUBJECT, article, buf);
  1644. X        update_art_posted_file (note_h_newsgroups, 'f', buf);
  1645. X    }
  1646. X    /*
  1647. X     * Update default posting prompt
  1648. X     */
  1649. X    my_strncpy (default_post_subject, buf, sizeof (default_post_subject));
  1650. X
  1651. X    if (unlink_article) {
  1652. X        unlink (article);
  1653. X    }
  1654. X    
  1655. X    return (ret_code);
  1656. X}
  1657. X
  1658. X
  1659. Xint mail_to_someone (address, mail_to_poster, confirm_to_mail, mailed_ok)
  1660. X    char *address;
  1661. X    int mail_to_poster;
  1662. X    int confirm_to_mail;
  1663. X    int *mailed_ok;
  1664. X{
  1665. X    char nam[100];
  1666. X    char ch = 's';
  1667. X    char ch_default = 's';
  1668. X    char buf[LEN];
  1669. X    char mail_to[LEN];
  1670. X    FILE *fp;
  1671. X    int redraw_screen = FALSE;
  1672. X
  1673. X    start_line_offset = 4;
  1674. X    
  1675. X    strcpy (mail_to, address);
  1676. X    clear_message ();
  1677. X    
  1678. X    sprintf (nam, "%s/.letter", homedir);
  1679. X    if ((fp = fopen (nam, "w")) == NULL) {
  1680. X        perror_message (txt_cannot_open, nam);
  1681. X        return (redraw_screen);
  1682. X    }
  1683. X    chmod (nam, 0600);
  1684. X
  1685. X    fprintf (fp, "To: %s\n", mail_to);
  1686. X
  1687. X    if (mail_to_poster) {
  1688. X        fprintf (fp, "Subject: Re: %s\n", eat_re (note_h_subj));
  1689. X    } else {
  1690. X        fprintf (fp, "Subject: (fwd) %s\n", note_h_subj);
  1691. X    }
  1692. X    
  1693. X    if (*note_h_followup) {
  1694. X        fprintf (fp, "Newsgroups: %s\n\n", note_h_followup);
  1695. X    } else {
  1696. X        fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups);
  1697. X    }
  1698. X    if (*my_org) {
  1699. X        fprintf (fp, "Organization: %s\n", my_org);
  1700. X        start_line_offset++;
  1701. X    }
  1702. X    if (*reply_to) {
  1703. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  1704. X        start_line_offset++;
  1705. X    }
  1706. X    fputc ('\n', fp);
  1707. X    
  1708. X    if (mail_to_poster) {
  1709. X        ch = 'e';
  1710. X        fprintf (fp, "In article %s you wrote:\n", note_h_messageid);
  1711. X        fseek (note_fp, note_mark[0], 0);
  1712. X        copy_fp (note_fp, fp, DEFAULT_COMMENT);
  1713. X    } else {
  1714. X        fseek (note_fp, 0L, 0);
  1715. X        copy_fp (note_fp, fp, "");
  1716. X    }
  1717. X    
  1718. X    add_signature (fp, TRUE);
  1719. X    fclose (fp);
  1720. X    
  1721. X    while (1) {
  1722. X        if (confirm_to_mail) {
  1723. X            do {
  1724. X                sprintf (msg, "%s [%.*s]: %c", txt_quit_edit_send, 
  1725. X                    COLS-30, note_h_subj, ch_default);
  1726. X                wait_message (msg);
  1727. X                MoveCursor (LINES, (int) (strlen (msg)-1));
  1728. X                if ((ch = (char) ReadCh ()) == CR)
  1729. X                    ch = ch_default;
  1730. X            } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 's');
  1731. X        }
  1732. X        switch (ch) {
  1733. X            case 'e':
  1734. X                invoke_editor (nam);
  1735. X                redraw_screen = TRUE;
  1736. X                break;
  1737. X
  1738. X            case 'q':
  1739. X            case ESC:
  1740. X                unlink (nam);
  1741. X                clear_message ();
  1742. X                *mailed_ok = FALSE;
  1743. X                return (redraw_screen);
  1744. X
  1745. X            case 's':
  1746. X                /*
  1747. X                 *  Open letter and get the To: line in case they changed
  1748. X                 *  it with the editor
  1749. X                 */
  1750. X                find_mail_header (HEADER_TO, nam, mail_to);
  1751. X                sprintf (msg, txt_mailing_to, mail_to);
  1752. X                wait_message (msg);
  1753. X                sprintf (buf, "%s \"%s\" < %s", mailer, mail_to, nam);
  1754. X                if (invoke_cmd (buf)) {
  1755. X                    goto mail_to_someone_done;
  1756. X                } else {
  1757. X                    error_message (txt_command_failed_s, buf);
  1758. X                    *mailed_ok = FALSE;
  1759. X                    break;
  1760. X                }
  1761. X        }
  1762. X        if (mail_to_poster) {
  1763. X            do {
  1764. X                sprintf (msg, "%s [Re: %.*s]: %c", txt_quit_edit_send, 
  1765. X                    COLS-34, eat_re (note_h_subj), ch_default);
  1766. X                wait_message (msg);
  1767. X                MoveCursor (LINES, (int) (strlen (msg)-1));
  1768. X                if ((ch = (char) ReadCh ()) == CR)
  1769. X                    ch = ch_default;
  1770. X            } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 's');
  1771. X        }    
  1772. X    }
  1773. X
  1774. Xmail_to_someone_done:
  1775. X    unlink (nam);
  1776. X    *mailed_ok = TRUE;
  1777. X    return (redraw_screen);
  1778. X}
  1779. X
  1780. X
  1781. Xint mail_bug_report ()
  1782. X{
  1783. X    char nam[100];
  1784. X    char ch;
  1785. X    char ch_default = 's';
  1786. X    char buf[LEN];
  1787. X    char mail_to[LEN];
  1788. X    FILE *fp;
  1789. X    FILE *fp_uname;
  1790. X    int is_nntp = FALSE;
  1791. X    int is_nntp_only   = FALSE;
  1792. X    int is_longfiles   = FALSE;
  1793. X    int is_resync_active = 0;
  1794. X    int uname_ok = FALSE;
  1795. X
  1796. X    start_line_offset = 5;
  1797. X    
  1798. X    wait_message (txt_mail_bug_report);
  1799. X    
  1800. X    sprintf (nam, "%s/.bugreport", homedir);
  1801. X    if ((fp = fopen (nam, "w")) == NULL) {
  1802. X        perror_message (txt_cannot_open, nam);
  1803. X        return FALSE;
  1804. X    }
  1805. X    chmod(nam, 0600);
  1806. X
  1807. X    fprintf (fp, "To: %s%s\n", bug_addr, add_addr);
  1808. X    fprintf (fp, "Subject: BUG REPORT %s %s PL%d %s\n",    progname,
  1809. X        VERSION, PATCHLEVEL, (compiled_with_nntp ? "(NNTP)" : ""));
  1810. X    if (*my_org) {
  1811. X        fprintf (fp, "Organization: %s\n", my_org);
  1812. X        start_line_offset++;
  1813. X    }
  1814. X    if (*reply_to) {
  1815. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  1816. X        start_line_offset++;
  1817. X    }
  1818. X
  1819. X    if ((fp_uname = (FILE *) popen ("uname -a", "r")) != NULL) {
  1820. X        while (fgets (buf, sizeof (buf), fp_uname) != NULL) {
  1821. X            fprintf (fp, "\nBOX1: %s", buf);
  1822. X            start_line_offset += 2;
  1823. X            uname_ok = TRUE;
  1824. X        }
  1825. X        fclose (fp_uname);
  1826. X    }
  1827. X    if (! uname_ok) {
  1828. X        fprintf (fp, "\nPlease enter the following information:\n");
  1829. X        fprintf (fp, "BOX1: Machine+OS:\n");
  1830. X    }
  1831. X#ifndef NO_RESYNC_ACTIVE_FILE
  1832. X    is_resync_active = RESYNC_ACTIVE_SECS;
  1833. X#endif
  1834. X#ifdef USE_LONG_FILENAMES
  1835. X    is_longfiles = TRUE;
  1836. X#endif
  1837. X#ifdef NNTP_ABLE
  1838. X    is_nntp = TRUE;
  1839. X#endif
  1840. X#ifdef NNTP_ONLY
  1841. X    is_nntp_only = TRUE;
  1842. X#endif
  1843. X    fprintf (fp, "\nCFG1: active=%d  arts=%d  resync=%d  longfilenames=%d  setuid=%d\n\
  1844. XCFG2: nntp=%d  nntp_only=%d  nntp_xuser=%d  nntp_xindex=%d  nntp_xspooldir=%d\n",
  1845. X        DEFAULT_ACTIVE_NUM,DEFAULT_ARTICLE_NUM,is_resync_active,
  1846. X        is_longfiles, (tin_uid == real_uid ? 0 : 1), is_nntp, 
  1847. X        is_nntp_only,  xuser_supported, xindex_supported, 
  1848. X        xspooldir_supported);
  1849. X    start_line_offset += 2;
  1850. X    
  1851. X    fprintf (fp, "\nPlease enter bug report/gripe/comment:\n");
  1852. X
  1853. X    add_signature (fp, TRUE);
  1854. X    fclose (fp);
  1855. X    
  1856. X    ch = 'e';
  1857. X    while (1) {
  1858. X        switch (ch) {
  1859. X        case 'e':
  1860. X            invoke_editor (nam);
  1861. X            break;
  1862. X
  1863. X        case 'q':
  1864. X        case ESC:
  1865. X            unlink (nam);
  1866. X            clear_message ();
  1867. X            return TRUE;
  1868. X
  1869. X        case 's':
  1870. X            sprintf (msg, txt_mail_bug_report_confirm, bug_addr, add_addr);
  1871. X            if (prompt_yn (LINES, msg, 'y')) {
  1872. X                strcpy (mail_to, bug_addr);
  1873. X                find_mail_header (HEADER_TO, nam, mail_to);
  1874. X                sprintf (msg, txt_mailing_to, mail_to);
  1875. X                wait_message (msg);
  1876. X                sprintf (buf, "%s \"%s\" < %s", mailer, mail_to, nam);
  1877. X                if (invoke_cmd (buf)) {
  1878. X                    sprintf (msg, txt_mailed, 1);
  1879. X                    info_message (msg);
  1880. X                    goto mail_bug_report_done;
  1881. X                } else {
  1882. X                    error_message (txt_command_failed_s, buf);
  1883. X                    break;
  1884. X                }
  1885. X            } else {
  1886. X                goto mail_bug_report_done;
  1887. X            }
  1888. X        }
  1889. X
  1890. X        do {
  1891. X            sprintf (msg, "%s: %c", txt_quit_edit_send, ch_default);
  1892. X            wait_message (msg);
  1893. X            MoveCursor (LINES, (int) strlen (msg)-1);
  1894. X            if ((ch = (char) ReadCh ()) == CR)
  1895. X                ch = ch_default;
  1896. X        } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 's');
  1897. X    }
  1898. X
  1899. Xmail_bug_report_done:
  1900. X    unlink (nam);
  1901. X
  1902. X    return TRUE;
  1903. X}
  1904. X
  1905. X
  1906. Xint mail_to_author (group, respnum, copy_text)
  1907. X    char *group;
  1908. X    int respnum;
  1909. X    int copy_text;
  1910. X{
  1911. X    char buf[LEN];
  1912. X    char from_addr[LEN];
  1913. X    char nam[100];
  1914. X    char mail_to[LEN];
  1915. X    char ch, ch_default = 's';
  1916. X    FILE *fp;
  1917. X    int redraw_screen = FALSE;
  1918. X
  1919. X    start_line_offset = 4;
  1920. X    
  1921. X    wait_message (txt_reply_to_author);
  1922. X
  1923. X    sprintf (nam, "%s/.letter", homedir);
  1924. X    if ((fp = fopen (nam, "w")) == NULL) {
  1925. X        perror_message (txt_cannot_open, nam);
  1926. X        return (redraw_screen);
  1927. X    }
  1928. X    chmod (nam, 0600);
  1929. X
  1930. X    find_reply_to_addr (respnum, from_addr);
  1931. X
  1932. X    fprintf (fp, "To: %s\n", from_addr);
  1933. X    fprintf (fp, "Subject: Re: %s\n", eat_re(note_h_subj) );
  1934. X    fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups);
  1935. X    if (*my_org) {
  1936. X        fprintf (fp, "Organization: %s\n", my_org);
  1937. X        start_line_offset++;
  1938. X    }
  1939. X    if (*reply_to) {
  1940. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  1941. X        start_line_offset++;
  1942. X    }
  1943. X    fputc ('\n', fp);
  1944. X
  1945. X    if (copy_text) {        /* if "copy_text" */
  1946. X        fprintf (fp, txt_in_art_you_write, note_h_messageid);
  1947. X        fseek (note_fp, note_mark[0], 0);
  1948. X        copy_fp (note_fp, fp, DEFAULT_COMMENT);
  1949. X    }
  1950. X
  1951. X    add_signature (fp, TRUE);
  1952. X    fclose (fp);
  1953. X
  1954. X    ch = 'e';
  1955. X    while (1) {
  1956. X        switch (ch) {
  1957. X        case 'e':
  1958. X            invoke_editor (nam);
  1959. X            redraw_screen = TRUE;
  1960. X            break;
  1961. X
  1962. X        case 'q':
  1963. X        case ESC:
  1964. X            unlink (nam);
  1965. X            clear_message ();
  1966. X            return (redraw_screen);
  1967. X
  1968. X        case 's':
  1969. X            my_strncpy (mail_to, arts[respnum].from, sizeof (mail_to));
  1970. X            find_mail_header (HEADER_TO, nam, mail_to);
  1971. X            sprintf (msg, txt_mailing_to, mail_to);
  1972. X            wait_message (msg);
  1973. X            sprintf (buf, "%s \"%s\" < %s", mailer, mail_to, nam);
  1974. X            if (invoke_cmd (buf)) {
  1975. X                sprintf (msg, txt_mailed, 1);
  1976. X                info_message (msg);
  1977. X                goto mail_to_author_done;
  1978. X            } else {
  1979. X                error_message (txt_command_failed_s, buf);
  1980. X                break;
  1981. X            }
  1982. X        }
  1983. X
  1984. X        do {
  1985. X            sprintf (msg, "%s: %c", txt_quit_edit_send, ch_default);
  1986. X            wait_message (msg);
  1987. X            MoveCursor (LINES, (int) strlen (msg)-1);
  1988. X            if ((ch = (char) ReadCh ()) == CR)
  1989. X                ch = ch_default;
  1990. X        } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 's');
  1991. X    }
  1992. X
  1993. Xmail_to_author_done:
  1994. X    find_mail_header (HEADER_SUBJECT, nam, buf);
  1995. X    unlink (nam);
  1996. X    update_art_posted_file (group, 'r', buf);
  1997. X
  1998. X    return (redraw_screen);
  1999. X}
  2000. X
  2001. X/*
  2002. X *  Read a file grabbing the value of the specified mail header line
  2003. X */
  2004. X
  2005. Xvoid find_mail_header (header, file, value)
  2006. X    int header;
  2007. X    char *file;
  2008. X    char *value;
  2009. X{
  2010. X    FILE *fp;
  2011. X    char buf[LEN];
  2012. X    char buf2[LEN];
  2013. X    char new_value[LEN];
  2014. X    char *p;
  2015. X
  2016. X    *new_value = '\0';
  2017. X
  2018. X    if ((fp = fopen (file, "r")) == NULL) {
  2019. X        perror_message (txt_cannot_open, file);
  2020. X        return;
  2021. X    }
  2022. X
  2023. X    while (fgets (buf, sizeof (buf), fp) != NULL) {
  2024. X        for (p = buf; *p && *p != '\n'; p++)
  2025. X            continue;
  2026. X        *p = '\0';
  2027. X
  2028. X        if (*buf == '\0')
  2029. X            break;
  2030. X
  2031. X        switch (header) {
  2032. X            case HEADER_TO:
  2033. X                if (strncmp (buf, "To: ", 4) == 0 ||
  2034. X                    strncmp (buf, "Cc: ", 4) == 0) {
  2035. X                    my_strncpy (buf2, &buf[4], sizeof (buf2));
  2036. X                    yank_to_addr (buf2, new_value);
  2037. X                } 
  2038. X                break;
  2039. X
  2040. X            case HEADER_SUBJECT:
  2041. X                if (strncmp (buf, "Subject: ", 9) == 0) {
  2042. X                    my_strncpy (new_value, &buf[9], sizeof (new_value));
  2043. X                }
  2044. X                break;
  2045. X        }
  2046. X    }
  2047. X
  2048. X    fclose (fp);
  2049. X
  2050. X    if (new_value[0] == ' ') {
  2051. X        strcpy (value, &new_value[1]);
  2052. X    } else {
  2053. X        strcpy (value, new_value);
  2054. X    }
  2055. X}
  2056. X
  2057. X
  2058. Xint cancel_article (group, respnum)
  2059. X    char *group;
  2060. X    int respnum;
  2061. X{
  2062. X    char ch, ch_default = 'c';
  2063. X    char buf[LEN];
  2064. X    char cancel[PATH_LEN];
  2065. X    char from[PATH_LEN];
  2066. X    FILE *fp;
  2067. X    int redraw_screen = FALSE;
  2068. X
  2069. X    /*
  2070. X     * Don't allow if not active news feed 
  2071. X     */
  2072. X    if (! spooldir_is_active) {
  2073. X        info_message (txt_not_active_newsfeed);
  2074. X        return (redraw_screen);
  2075. X    }
  2076. X
  2077. X    start_line_offset = 4;
  2078. X
  2079. X    get_from_name (cancel);
  2080. X
  2081. X    if (arts[respnum].from != arts[respnum].name) {
  2082. X        sprintf (from, "%s (%s)", arts[respnum].from, arts[respnum].name);
  2083. X    } else {
  2084. X        my_strncpy (from, arts[respnum].from, sizeof (from));
  2085. X    }
  2086. X
  2087. X    if (debug == 2) {
  2088. X        sprintf (msg, "From=[%s]  Cancel=[%s]", from, cancel);        
  2089. X        error_message (msg, "");
  2090. X    }
  2091. X    
  2092. X    if (strcmp (from, cancel) != 0) {
  2093. X        info_message (txt_art_cannot_cancel);
  2094. X        return (redraw_screen);
  2095. X    }
  2096. X            
  2097. X    clear_message ();
  2098. X    
  2099. X    sprintf (cancel, "%s/.cancel", homedir);
  2100. X    if ((fp = fopen (cancel, "w")) == NULL) {
  2101. X        perror_message (txt_cannot_open, cancel);
  2102. X        return (redraw_screen);
  2103. X    }
  2104. X    chmod (cancel, 0600);
  2105. X
  2106. X    fprintf (fp, "Subject: cancel %s\n", note_h_messageid);
  2107. X    if (*note_h_followup) {
  2108. X        fprintf (fp, "Newsgroups: %s\n", note_h_followup);
  2109. X    } else {
  2110. X        fprintf (fp, "Newsgroups: %s\n", note_h_newsgroups);
  2111. X    }
  2112. X    fprintf (fp, "Control: cancel %s\n", note_h_messageid);
  2113. X    if (*my_org) { 
  2114. X        fprintf (fp, "Organization: %s\n", my_org);
  2115. X        start_line_offset++;    
  2116. X    }
  2117. X    if (*reply_to) {
  2118. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  2119. X        start_line_offset++;    
  2120. X    }
  2121. X    fputc ('\n', fp);
  2122. X
  2123. X    fprintf (fp, "Article cancelled from within tin\n");
  2124. X    
  2125. X    fclose (fp);
  2126. X    
  2127. X    while (1) {
  2128. X        do {
  2129. X            sprintf (msg, "%s [%.*s]: %c", txt_quit_edit_cancel,
  2130. X                COLS-30, note_h_subj, ch_default);
  2131. X            wait_message (msg);
  2132. X            MoveCursor (LINES, (int) strlen (msg)-1);
  2133. X            if ((ch = (char) ReadCh ()) == CR)
  2134. X                ch = ch_default;
  2135. X        } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 'c');
  2136. X
  2137. X        switch (ch) {
  2138. X        case 'e':
  2139. X            invoke_editor (cancel);
  2140. X            redraw_screen = TRUE;
  2141. X            break;
  2142. X
  2143. X        case 'q':
  2144. X        case ESC:
  2145. X            unlink (cancel);
  2146. X            clear_message ();
  2147. X            return (redraw_screen);
  2148. X
  2149. X        case 'c':
  2150. X            wait_message (txt_cancelling);
  2151. X            if (submit_file (cancel)) {
  2152. X                info_message (txt_art_cancelled);
  2153. X                goto cancel_article_done;
  2154. X            } else {
  2155. X                error_message (txt_command_failed_s, cancel);
  2156. X                break;
  2157. X            }
  2158. X        }
  2159. X    }
  2160. X
  2161. Xcancel_article_done:
  2162. X    find_mail_header (HEADER_SUBJECT, cancel, buf);
  2163. X    unlink (cancel);
  2164. X    update_art_posted_file (group, 'c', buf);
  2165. X
  2166. X    return (redraw_screen);
  2167. X}
  2168. X
  2169. X/*
  2170. X * Crosspost an already existing article to another group (ie. local group)
  2171. X */
  2172. Xint crosspost_article (group, respnum)
  2173. X    char *group;
  2174. X    int respnum;
  2175. X{
  2176. X    char buf[LEN];
  2177. X    char ch;
  2178. X    char ch_default = 'p';
  2179. X    FILE *fp;
  2180. X    int ret_code = POSTED_NONE;
  2181. X    
  2182. X    start_line_offset = 4;
  2183. X
  2184. X    if ((fp = fopen (article, "w")) == NULL) {
  2185. X        perror_message (txt_cannot_open, article);
  2186. X        return (ret_code);
  2187. X    }
  2188. X    chmod (article, 0600);
  2189. X
  2190. X    fprintf (fp, "Subject: %s\n", eat_re (note_h_subj));
  2191. X    fprintf (fp, "Newsgroups: %s\n", group);
  2192. X
  2193. X    if (*my_org) {
  2194. X        fprintf (fp, "Organization: %s\n", my_org);
  2195. X        start_line_offset++;
  2196. X    }
  2197. X    if (*reply_to) {
  2198. X        fprintf (fp, "Reply-To: %s\n", reply_to);
  2199. X        start_line_offset++;
  2200. X    }
  2201. X    if (note_h_distrib != '\0') {
  2202. X        fprintf (fp, "Distribution: %s\n", note_h_distrib);
  2203. X        start_line_offset++;
  2204. X    }
  2205. X
  2206. X    fprintf (fp, "\n[ Article crossposted from %s ]", note_h_newsgroups);
  2207. X    get_author (FALSE, respnum, buf);
  2208. X    fprintf (fp, "\n[ Author was %s ]", buf);
  2209. X    fprintf (fp, "\n[ Posted on %s ]\n\n", note_h_date);
  2210. X  
  2211. X    fseek (note_fp, note_mark[0], 0);
  2212. X    copy_fp (note_fp, fp, "");
  2213. X
  2214. X    add_signature (fp, FALSE);
  2215. X    fclose (fp);
  2216. X
  2217. X    while (1) {
  2218. X        do {
  2219. X            sprintf (msg, txt_quit_edit_xpost, 
  2220. X                COLS-(strlen (txt_quit_edit_xpost)-1),
  2221. X                note_h_subj, ch_default);
  2222. X            wait_message (msg);
  2223. X            MoveCursor (LINES, (int) strlen (msg)-1);
  2224. X            if ((ch = (char) ReadCh ()) == CR)
  2225. X                ch = ch_default;
  2226. X        } while (ch != ESC && ch != 'q' && ch != 'e' && ch != 'p');
  2227. X        switch (ch) {
  2228. X        case 'e':
  2229. X            invoke_editor (article);
  2230. X            ret_code = POSTED_REDRAW;
  2231. X            break;
  2232. X
  2233. X        case 'q':
  2234. X        case ESC:
  2235. X            if (unlink_article)
  2236. X                unlink (article);
  2237. X            clear_message ();
  2238. X            return (ret_code);
  2239. X
  2240. X        case 'p':
  2241. X            wait_message (txt_crosspost_an_article);
  2242. X            if (submit_file (article)) {
  2243. X                ret_code = POSTED_OK;
  2244. X                info_message (txt_art_posted);
  2245. X                goto crosspost_done;
  2246. X            } else {
  2247. X                rename_file (article, dead_article);
  2248. X                sprintf (buf, txt_art_rejected, dead_article);
  2249. X                info_message (buf);
  2250. X                sleep (3);
  2251. X                return (ret_code);
  2252. X            }
  2253. X        }
  2254. X    }
  2255. X
  2256. Xcrosspost_done:
  2257. X    find_mail_header (HEADER_SUBJECT, article, buf);
  2258. X    update_art_posted_file (group, 'x', buf);
  2259. X
  2260. X    if (unlink_article) {
  2261. X        unlink (article);
  2262. X    }
  2263. X    
  2264. X    return (ret_code);
  2265. X}
  2266. X
  2267. X
  2268. Xint submit_file (name)
  2269. X    char *name;
  2270. X{
  2271. X    char buf[LEN];
  2272. X    char *cp = buf;
  2273. X    int ret_code = FALSE;
  2274. X
  2275. X    insert_x_headers (name);
  2276. X
  2277. X    if (read_news_via_nntp) {
  2278. X#ifdef DEBUG
  2279. X        if (debug == 2) {
  2280. X            wait_message ("Using BUILTIN inews");
  2281. X            sleep (3);    
  2282. X        }
  2283. X#endif /* DEBUG */            
  2284. X        ret_code = submit_inews (name);
  2285. X    } else {
  2286. X#ifdef DEBUG
  2287. X        if (debug == 2) {
  2288. X            wait_message ("Using EXTERNAL inews");
  2289. X            sleep (3);    
  2290. X        }    
  2291. X#endif /* DEBUG */            
  2292. X#ifdef INEWSDIR
  2293. X        strcpy (buf, INEWSDIR);
  2294. X        strcat (buf, "/");
  2295. X        cp = &buf[strlen(buf)];
  2296. X#endif /* INEWSDIR */
  2297. X        sprintf (cp, "inews -h < %s %s", name, redirect_output);
  2298. X    
  2299. X        ret_code = invoke_cmd (buf);
  2300. X    } 
  2301. X
  2302. X    return (ret_code);
  2303. X}
  2304. X
  2305. X
  2306. Xvoid add_signature (fp, flag)
  2307. X    FILE *fp;
  2308. X    int flag;
  2309. X{
  2310. X    FILE *sigfp;
  2311. X
  2312. X    if (read_news_via_nntp) {
  2313. X#ifdef NNTP_INEWS
  2314. X        flag = TRUE;
  2315. X#endif
  2316. X    }
  2317. X
  2318. X    /*
  2319. X     * Use ~/.signature or ~/.Sig or custom .Sig files
  2320. X     */
  2321. X    if ((sigfp = fopen (default_signature, "r")) != NULL) {
  2322. X        if (flag) {
  2323. X            fprintf (fp, "\n--\n");
  2324. X            copy_fp (sigfp, fp, "");
  2325. X        }
  2326. X        fclose (sigfp);
  2327. X        return;
  2328. X    }
  2329. X
  2330. X    if ((sigfp = fopen (active[my_group[cur_groupnum]].attribute.sigfile, "r")) != NULL) {
  2331. X        fprintf (fp, "\n--\n");
  2332. X        copy_fp (sigfp, fp, "");
  2333. X        fclose (sigfp);
  2334. X    }
  2335. X}
  2336. X
  2337. X
  2338. Xvoid insert_x_headers (infile)
  2339. X    char *infile;
  2340. X{
  2341. X    char line[LEN];
  2342. X    char outfile[PATH_LEN];
  2343. X    FILE *fp_in, *fp_out;
  2344. X    int gotit = FALSE;
  2345. X    
  2346. X    if ((fp_in = fopen (infile, "r")) != NULL) {
  2347. X        sprintf (outfile, "%s.%d", infile, process_id);
  2348. X        if ((fp_out = fopen (outfile, "w")) != NULL) {
  2349. X            while (fgets (line, sizeof (line), fp_in) != NULL) {
  2350. X                if (! gotit && line[0] == '\n') {
  2351. X                    fprintf (fp_out, "X-Newsreader: Tin %s PL%d\n\n",
  2352. X                        VERSION, PATCHLEVEL);
  2353. X                    gotit = TRUE;
  2354. X                } else {
  2355. X                    fputs (line, fp_out); 
  2356. X                }    
  2357. X            }
  2358. X            fclose (fp_out);
  2359. X            fclose (fp_in);
  2360. X            rename_file (outfile, infile);
  2361. X        }
  2362. X    }
  2363. X}
  2364. X
  2365. X
  2366. Xvoid find_reply_to_addr (respnum, from_addr)
  2367. X    int respnum;
  2368. X    char *from_addr;
  2369. X{
  2370. X    char buf[LEN];
  2371. X    int found = FALSE;
  2372. X    int len = 0;
  2373. X    long orig_offset;
  2374. X
  2375. X    orig_offset = ftell (note_fp);
  2376. X    fseek (note_fp, 0L, 0);
  2377. X    
  2378. X    while (fgets (buf, sizeof (buf), note_fp) != NULL && 
  2379. X        found == FALSE && buf[0] != '\n') {
  2380. X        if (strncmp (buf, "Reply-To: ", 10) == 0) {
  2381. X            strcpy (from_addr, &buf[10]);
  2382. X            len = strlen (from_addr);
  2383. X            from_addr[len-1] = '\0';
  2384. X            sprintf (buf, "%s%s", from_addr, add_addr);
  2385. X            strcpy (from_addr, buf);
  2386. X            found = TRUE;
  2387. X        }    
  2388. X    }
  2389. X
  2390. X    if (! found) {
  2391. X        if (arts[respnum].name != arts[respnum].from) { 
  2392. X            sprintf (buf, "%s%s (%s)",
  2393. X                 arts[respnum].from, add_addr,
  2394. X                 arts[respnum].name);
  2395. X            strcpy (from_addr, buf);
  2396. X        } else { 
  2397. X            sprintf (from_addr, "%s%s",
  2398. X                 arts[respnum].from, add_addr);
  2399. X        }    
  2400. X    }
  2401. X    
  2402. X    fseek (note_fp, orig_offset, 0);
  2403. X}
  2404. X        
  2405. END_OF_FILE
  2406.   if test 28095 -ne `wc -c <'post.c'`; then
  2407.     echo shar: \"'post.c'\" unpacked with wrong size!
  2408.   fi
  2409.   # end of 'post.c'
  2410. fi
  2411. echo shar: End of archive 6 \(of 15\).
  2412. cp /dev/null ark6isdone
  2413. MISSING=""
  2414. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do
  2415.     if test ! -f ark${I}isdone ; then
  2416.     MISSING="${MISSING} ${I}"
  2417.     fi
  2418. done
  2419. if test "${MISSING}" = "" ; then
  2420.     echo You have unpacked all 15 archives.
  2421.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2422. else
  2423.     echo You still must unpack the following archives:
  2424.     echo "        " ${MISSING}
  2425. fi
  2426. exit 0
  2427. exit 0 # Just in case...
  2428.