home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / collect.c < prev    next >
C/C++ Source or Header  |  1996-08-14  |  12KB  |  506 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Collect and save article information in database.
  5.  */
  6.  
  7. #include "config.h"
  8. #include "db.h"
  9. #include "news.h"
  10.  
  11. /* collect.c */
  12.  
  13. static void do_auto_archive __APROTO((group_header *gh, register FILE *f, article_number num));
  14. static void build_hdr __APROTO((int type));
  15. static collect_article __APROTO((register group_header *gh, article_number art_num));
  16. static long collect_group __APROTO((register group_header *gh));
  17.  
  18.  
  19.  
  20. #define COUNT_RE_REFERENCES    /* no of >>> depends on Reference: line */
  21.  
  22. export int ignore_bad_articles = 1;    /* no Newsgroups: line */
  23. export int remove_bad_articles = 0;
  24. export time_t max_article_age = 0;
  25.  
  26. import int trace, debug_mode;
  27.  
  28. extern time_stamp pack_date();
  29.  
  30. #ifdef NNTP
  31. import nntp_failed;
  32. #endif
  33.  
  34. static long bad_count;
  35.  
  36. static FILE *ix, *data;
  37.  
  38. static void
  39. do_auto_archive(gh, f, num)
  40. group_header *gh;
  41. register FILE *f;
  42. article_number num;
  43. {
  44.     char line[200];
  45.     article_number last;
  46.     register FILE *arc;
  47.     register int c;
  48.     off_t start;
  49.     static char *arc_header = "Archived-Last: ";
  50.     /* Header format: Archived-Last: 88888888 group.name */
  51.     /* Fixed constants length == 15 and offset == 24 are used below */
  52.  
  53.     arc = open_file(gh->archive_file, OPEN_READ);
  54.     last = 0;
  55.     start = 0;
  56.     if (arc != NULL) {
  57.     while (fgets(line, 200, arc) != NULL) {
  58.         if (strncmp(line, arc_header, 15)) {
  59.         log_entry('E', "%s not archive for %s\n",
  60.               gh->archive_file, gh->group_name);
  61.         gh->master_flag &= ~M_AUTO_ARCHIVE;
  62.         fclose(arc);
  63.         return;
  64.         }
  65.         if (strncmp(line + 24, gh->group_name, gh->group_name_length)) {
  66.         start = ftell(arc);
  67.         continue;
  68.         }
  69.         last = atol(line + 15);
  70.         break;
  71.     }
  72.     fclose(arc);
  73.     }
  74.  
  75.     if (last >= num) return;
  76.  
  77.     arc = open_file(gh->archive_file, last > 0 ? OPEN_UPDATE : OPEN_CREATE);
  78.     if (arc == NULL) {
  79.     log_entry('E', "Cannot create archive file: %s\n", gh->archive_file);
  80.     gh->master_flag &= ~M_AUTO_ARCHIVE;
  81.     return;
  82.     }
  83.  
  84.     fseek(arc, start, 0);
  85.     fprintf(arc, "%s%8ld %s\n", arc_header, (long)num, gh->group_name);
  86.     fseek(arc, (off_t)0, 2);
  87.  
  88.     fseek(f, (off_t)0, 0);
  89.     while ((c = getc(f)) != EOF) putc(c, arc);
  90.     putc(NL, arc);
  91.     fclose(arc);
  92. }
  93.  
  94. static void
  95. build_hdr(type)
  96. int type;
  97. {
  98.     register char *name, *subj;
  99.     int re;
  100.  
  101.     db_data.dh_type = type;
  102.  
  103.     if (type == DH_SUB_DIGEST) {
  104.  
  105.     name = digest.dg_from;
  106.     subj = digest.dg_subj;
  107.  
  108.     db_hdr.dh_lines = digest.dg_lines;
  109.  
  110.     db_hdr.dh_hpos = digest.dg_hpos;
  111.     db_hdr.dh_fpos = (int16)(digest.dg_fpos - db_hdr.dh_hpos);
  112.     db_hdr.dh_lpos = digest.dg_lpos;
  113.  
  114.     db_hdr.dh_date = pack_date(digest.dg_date ? digest.dg_date : news.ng_date);
  115.     } else {
  116.  
  117.     if (!news.ng_from) news.ng_from = news.ng_reply;
  118.  
  119.     name = news.ng_from;
  120.     subj = news.ng_subj;
  121.  
  122.     db_hdr.dh_lines = news.ng_lines;
  123.  
  124.     db_hdr.dh_hpos = 0;
  125.     db_hdr.dh_fpos = (int16)(news.ng_fpos);
  126.     db_hdr.dh_lpos = news.ng_lpos;
  127.  
  128.     db_hdr.dh_date = pack_date(news.ng_date);
  129.     }
  130.  
  131.     if (name) {
  132.     db_hdr.dh_sender_length = pack_name(db_data.dh_sender, name, NAME_LENGTH);
  133.     } else
  134.         db_hdr.dh_sender_length = 0;
  135.  
  136.     if (type == DH_DIGEST_HEADER) {
  137.     db_hdr.dh_subject_length = 1;
  138.     db_data.dh_subject[0] = '@';
  139.     } else
  140.     db_hdr.dh_subject_length = 0;
  141.  
  142.     db_hdr.dh_subject_length +=
  143.     pack_subject(db_data.dh_subject + db_hdr.dh_subject_length, subj, &re,
  144.              DBUF_SIZE);
  145.  
  146. #ifdef COUNT_RE_REFERENCES
  147.     if (re) re = 0x80;
  148.     if (news.ng_ref) {
  149.     for (name = news.ng_ref; *name; name++) {
  150.         if ((re & 0x7f) == 0x7f) break;
  151.         if (*name == '<') re++;
  152.     }
  153.     }
  154. #endif
  155.     db_hdr.dh_replies = re;
  156.  
  157.     if (db_write_art(data) < 0) write_error();
  158. }
  159.  
  160.  
  161. static int
  162. collect_article(gh, art_num)
  163. register group_header *gh;
  164. article_number art_num;
  165. {
  166.     FILE *art_file;
  167.     news_header_buffer nhbuf, dgbuf;
  168.     article_header art_hdr;
  169.     int mode, count;
  170.     cross_post_number *cp_ptr;
  171.     long age;
  172.  
  173.     count = 0;
  174.  
  175.     db_hdr.dh_number = art_num;
  176.  
  177.     /* get article header */
  178.  
  179.     art_hdr.a_number = art_num;
  180.     art_hdr.hpos = (off_t)0;
  181.     art_hdr.lpos = (off_t)0;
  182.     art_hdr.flag = 0;
  183.  
  184.     mode = FILL_NEWS_HEADER | FILL_OFFSETS | SKIP_HEADER;
  185.     if ((gh->master_flag & (M_CONTROL | M_NEVER_DIGEST | M_ALWAYS_DIGEST)) == 0)
  186.     mode |= DIGEST_CHECK;
  187. #ifdef NNTP
  188.     if ((gh->master_flag & M_ALWAYS_DIGEST) == 0)
  189.     mode |= LAZY_BODY;
  190. #endif
  191.     if ((art_file = open_news_article(&art_hdr, mode, nhbuf, (char *)NULL)) == NULL) {
  192.  
  193. #ifdef NNTP
  194.  
  195.     if (nntp_failed) {
  196.         /*
  197.          * connection to nntp_server is broken
  198.          * stop collection of articles immediately
  199.          */
  200.         return -1;
  201.     }
  202. #endif
  203.     /*
  204.      * it is not really necessary to save anything in the data file
  205.      * we simply use the index file to get the *first* available article
  206.      */
  207.     return 0;
  208.     }
  209.  
  210.     if (art_file == (FILE *)1) {    /* empty file */
  211.     if (!ignore_bad_articles) return 0;
  212.     news.ng_groups = NULL;
  213.     art_file = NULL;
  214.     } else
  215.     if ( max_article_age &&    /* == 0 if use_nntp */
  216.         (gh->master_flag & M_INCLUDE_OLD) == 0 &&
  217.         (age = m_time(art_file)) < max_article_age) {
  218.  
  219.         if (remove_bad_articles) unlink(group_path_name);
  220.  
  221.         log_entry('O', "%sold article (%ld days): %s/%ld",
  222.               remove_bad_articles ? "removed " : "",
  223.               (cur_time() - age) / (24 * 60 * 60),
  224.               current_group->group_name, (long)art_num);
  225.         bad_count++;
  226.         fclose(art_file);
  227.         return 0;
  228.     }
  229.  
  230.     if (ignore_bad_articles && news.ng_groups == NULL) {
  231.     char *rem = "";
  232.  
  233.     if (!use_nntp && remove_bad_articles) {
  234.         unlink(group_path_name);
  235.         rem = "removed ";
  236.     }
  237.  
  238.     log_entry('B', "%sbad article: %s/%ld", rem,
  239.           current_group->group_name, (long)art_num);
  240.     if (art_file != NULL) fclose(art_file);
  241.     bad_count++;
  242.     return 0;
  243.     }
  244.  
  245.     /* map cross-postings into a list of group numbers */
  246.  
  247.     db_hdr.dh_cross_postings = 0;
  248.  
  249.     if (gh->master_flag & M_CONTROL) {
  250.     /* we cannot trust the Newsgroups: line in the control group */
  251.     /* so we simply ignore it (i.e. use "Newsgroups: control") */
  252.     goto dont_digest;
  253.     }
  254.  
  255.     if (news.ng_groups) {
  256.     char *curg, *nextg;
  257.     group_header *gh1;
  258.  
  259.     for (nextg = news.ng_groups, cp_ptr = db_data.dh_cross; *nextg; ) {
  260.         curg = nextg;
  261.  
  262.         if ((nextg = strchr(curg, ',')))
  263.         *nextg++ = NUL;
  264.         else
  265.         nextg = "";
  266.  
  267.         if (strcmp(gh->group_name, curg) == 0)
  268.         gh1 = gh;
  269.         else
  270.         if ((gh1 = lookup(curg)) == NULL) continue;
  271.  
  272.         *cp_ptr++ = NETW_CROSS_EXT(gh1->group_num);
  273.         if (++db_hdr.dh_cross_postings == DBUF_SIZE) break;
  274.     }
  275.     }
  276.  
  277.     if (db_hdr.dh_cross_postings == 1)
  278.     db_hdr.dh_cross_postings = 0;    /* only current group */
  279.  
  280.     if (gh->master_flag & M_NEVER_DIGEST)
  281.     goto dont_digest;
  282.  
  283.     /* split digest */
  284.  
  285.     if ((gh->master_flag & M_ALWAYS_DIGEST) || (news.ng_flag & N_DIGEST)) {
  286.     int any = 0, cont = 1;
  287.  
  288.     skip_digest_body(art_file);
  289.  
  290.     while (cont && (cont = get_digest_article(art_file, dgbuf)) >= 0) {
  291.  
  292.         if (any == 0) {
  293.         build_hdr(DH_DIGEST_HEADER);    /* write DIGEST_HEADER */
  294.         count++;
  295.         db_hdr.dh_cross_postings = 0;    /* no cross post in sub */
  296.         any++;
  297.         }
  298.         build_hdr(DH_SUB_DIGEST);    /* write SUB_DIGEST */
  299.         count++;
  300.     }
  301.  
  302.     if (any) goto finish;
  303.     }
  304.  
  305.     /* not a digest */
  306.  
  307.  dont_digest:
  308.  
  309.     build_hdr(DH_NORMAL);    /* normal article */
  310.     count++;
  311.  
  312. finish:
  313.  
  314.     if (gh->master_flag & M_AUTO_ARCHIVE) {
  315. #ifdef NNTP
  316.       FILE *nntp_get_article(), *f;
  317.       f = nntp_get_article(art_num, 0);
  318.       do_auto_archive(gh, f, art_num);
  319.       fclose(f);
  320. #else
  321.       do_auto_archive(gh, art_file, art_num);
  322. #endif /* NNTP */
  323.     }
  324.     fclose(art_file);
  325.  
  326.     return count;
  327. }
  328.  
  329.  
  330. /*
  331.  *    Collect unread articles in current group
  332.  *
  333.  *    On entry, init_group has been called to setup the proper environment
  334.  */
  335.  
  336. static long collect_group(gh)
  337. register group_header *gh;
  338. {
  339.     long article_count, temp, obad;
  340.     article_number start_collect;
  341.  
  342.     if (gh->last_db_article == 0) {
  343.     gh->first_db_article = gh->first_a_article;
  344.     gh->last_db_article = gh->first_db_article - 1;
  345.     }
  346.  
  347.     if (gh->last_db_article >= gh->last_a_article) return 0;
  348.  
  349.     if (gh->index_write_offset) {
  350.     ix = open_data_file(gh, 'x', OPEN_UPDATE|MUST_EXIST);
  351.     fseek(ix, gh->index_write_offset, 0);
  352.     } else
  353.         ix = open_data_file(gh, 'x', OPEN_CREATE|MUST_EXIST);
  354.  
  355.     if (gh->data_write_offset) {
  356.     data = open_data_file(gh, 'd', OPEN_UPDATE|MUST_EXIST);
  357.     fseek(data, gh->data_write_offset, 0);
  358.     } else
  359.     data = open_data_file(gh, 'd', OPEN_CREATE|MUST_EXIST);
  360.  
  361.     article_count = 0;
  362.     start_collect = gh->last_db_article+1;
  363.  
  364.     if (debug_mode) {
  365.     printf("\t\t%s (%ld..%ld)\r",
  366.            gh->group_name, start_collect, gh->last_a_article);
  367.     fl;
  368.     }
  369.     bad_count = obad = 0;
  370.  
  371.     while (gh->last_db_article < gh->last_a_article) {
  372.     if (s_hangup) break;
  373.     gh->last_db_article++;
  374.     if (debug_mode) {
  375.         printf("\r%ld", gh->last_db_article);
  376.         if (obad != bad_count) printf("\t%ld", bad_count);
  377.         obad = bad_count;
  378.         fl;
  379.     }
  380.     gh->data_write_offset = ftell(data);
  381. #ifdef NNTP
  382.     gh->index_write_offset = ftell(ix);
  383. #endif
  384.     temp = collect_article(gh, gh->last_db_article);
  385. #ifdef NNTP
  386.     if (temp < 0) {
  387.         /* connection failed, current article is not collected */
  388.         gh->last_db_article--;
  389.         article_count = -1;
  390.         goto out;
  391.     }
  392. #endif
  393. #ifndef RENUMBER_DANGER
  394.     if (temp == 0 && gh->data_write_offset == (off_t)0) {
  395.         gh->first_db_article = gh->last_db_article + 1;
  396.         continue;
  397.     }
  398. #endif
  399.     if (!db_write_offset(ix, &(gh->data_write_offset)))
  400.         write_error();
  401.     article_count += temp;
  402.     }
  403.  
  404.     if (start_collect < gh->first_db_article)
  405.     start_collect = gh->first_db_article;
  406.  
  407.     if (trace && start_collect <= gh->last_db_article)
  408.     log_entry('T', "Col %s (%d to %d) %d",
  409.           gh->group_name,
  410.           start_collect, gh->last_db_article,
  411.           article_count);
  412.  
  413.     if (debug_mode)
  414.     printf("\nCol %s (%d to %d) %d",
  415.            gh->group_name,
  416.            start_collect, gh->last_db_article,
  417.            article_count);
  418.  
  419.     gh->data_write_offset = ftell(data);
  420.     gh->index_write_offset = ftell(ix);
  421.  
  422. #ifdef NNTP
  423.  out:
  424. #endif
  425.     fclose(data);
  426.     fclose(ix);
  427.  
  428.     if (debug_mode) putchar(NL);
  429.  
  430.     return article_count;
  431. }
  432.  
  433. int
  434. do_collect()
  435. {
  436.     register group_header *gh;
  437.     long col_article_count, temp;
  438.     int col_group_count;
  439.     time_t start_time;
  440.  
  441.     start_time = cur_time();
  442.     col_article_count = col_group_count = 0;
  443.     current_group = NULL; /* for init_group */
  444.     temp = 0;
  445.  
  446.     Loop_Groups_Header(gh) {
  447.     if (s_hangup) {
  448.         temp = -1;
  449.         break;
  450.     }
  451.  
  452.     if (gh->master_flag & M_IGNORE_GROUP) continue;
  453.  
  454.     if (gh->master_flag & M_MUST_CLEAN)
  455.         clean_group(gh);
  456.  
  457.     if (gh->last_db_article == gh->last_a_article) {
  458.         if (gh->master_flag & M_BLOCKED) goto unblock_group;
  459.         continue;
  460.     }
  461.  
  462.     if (!init_group(gh)) {
  463.         if ((gh->master_flag & M_NO_DIRECTORY) == 0) {
  464.         log_entry('R', "%s: no directory", gh->group_name);
  465.         gh->master_flag |= M_NO_DIRECTORY;
  466.         }
  467.         gh->last_db_article = gh->last_a_article;
  468.         gh->first_db_article = gh->last_a_article;    /* OBS: not first */
  469.         gh->master_flag &= ~(M_EXPIRE | M_BLOCKED);
  470.         db_write_group(gh);
  471.         continue;
  472.     }
  473.  
  474.     if (gh->master_flag & M_NO_DIRECTORY) {
  475.         /* The directory has been created now */
  476.         gh->master_flag &= ~M_NO_DIRECTORY;
  477.         clean_group(gh);
  478.     }
  479.  
  480.     temp = collect_group(gh);
  481. #ifdef NNTP
  482.     if (temp < 0) {
  483.         /* connection broken */
  484.         gh->master_flag &= ~M_EXPIRE;    /* remains blocked */
  485.         db_write_group(gh);
  486.         break;
  487.     }
  488. #endif
  489.     if (temp > 0) {
  490.         col_article_count += temp;
  491.         col_group_count++;
  492.     }
  493.  
  494.      unblock_group:
  495.     gh->master_flag &= ~(M_EXPIRE | M_BLOCKED);
  496.     db_write_group(gh);
  497.     }
  498.  
  499.     if (col_article_count > 0)
  500.     log_entry('C', "Collect: %ld art, %d gr, %ld s",
  501.           col_article_count, col_group_count,
  502.           cur_time() - start_time);
  503.  
  504.     return temp > 0;    /* return true IF we got articles */
  505. }
  506.