/* rt-ov.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "head.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "nntp.h" #include "util.h" #include "util2.h" #include "env.h" #include "ng.h" #include "term.h" #include "intrp.h" #include "final.h" #include "rthread.h" #include "rt-process.h" #include "rt-util.h" #include "parsedate.h" #include "INTERN.h" #include "rt-ov.h" #include "rt-ov.ih" bool ov_init() { bool has_overview_fmt; Uchar* fieldnum = datasrc->fieldnum; Uchar* fieldflags = datasrc->fieldflags; datasrc->flags &= ~DF_TRY_OVERVIEW; #ifdef SUPPORT_NNTP if (!datasrc->over_dir) { int ret; /* Check if the server is XOVER compliant */ if (nntp_command("XOVER") <= 0) return FALSE; if (nntp_check() < 0) return FALSE;/*$$*/ if (atoi(ser_line) == NNTP_BAD_COMMAND_VAL) return FALSE; /* Just in case... */ if (*ser_line == NNTP_CLASS_OK) nntp_finish_list(); if ((ret = nntp_list("overview.fmt",nullstr,0)) < -1) return FALSE; has_overview_fmt = ret > 0; } else #endif { has_overview_fmt = datasrc->over_fmt != NULL && (tmpfp = fopen(datasrc->over_fmt, "r")) != NULL; } if (has_overview_fmt) { int i; fieldnum[0] = OV_NUM; fieldflags[OV_NUM] = FF_HAS_FIELD; for (i = 1;;) { #ifdef SUPPORT_NNTP if (!datasrc->over_dir) { if (nntp_gets(buf, sizeof buf) < 0) break;/*$$*/ if (nntp_at_list_end(buf)) break; } #endif ElseIf (!fgets(buf, sizeof buf, tmpfp)) { fclose(tmpfp); break; } if (*buf == '#') continue; if (i < OV_MAX_FIELDS) { char *s = index(buf,':'); fieldnum[i] = ov_num(buf,s); fieldflags[fieldnum[i]] = FF_HAS_FIELD | ((s && strncaseEQ("full",s+1,4))? FF_HAS_HDR : 0); i++; } } if (!fieldflags[OV_SUBJ] || !fieldflags[OV_MSGID] || !fieldflags[OV_FROM] || !fieldflags[OV_DATE]) return FALSE; if (i < OV_MAX_FIELDS) { int j; for (j = OV_MAX_FIELDS; j--; ) { if (!fieldflags[j]) break; } while (i < OV_MAX_FIELDS) fieldnum[i++] = j; } } else { int i; for (i = 0; i < OV_MAX_FIELDS; i++) { fieldnum[i] = i; fieldflags[i] = FF_HAS_FIELD; } fieldflags[OV_XREF] = FF_CHECK4FIELD | FF_CHECK4HDR; } datasrc->flags |= DF_TRY_OVERVIEW; return TRUE; } int ov_num(hdr,end) char* hdr; char* end; { if (!end) end = hdr + strlen(hdr); switch (set_line_type(hdr,end)) { case SUBJ_LINE: return OV_SUBJ; case AUTHOR_LINE: /* This hack is for the Baen NNTP server */ case FROM_LINE: return OV_FROM; case DATE_LINE: return OV_DATE; case MSGID_LINE: return OV_MSGID; case REFS_LINE: return OV_REFS; case BYTES_LINE: return OV_BYTES; case LINES_LINE: return OV_LINES; case XREF_LINE: return OV_XREF; } return 0; } /* Process the data in the group's news-overview file. */ bool ov_data(first, last, cheating) ART_NUM first, last; bool_int cheating; { ART_NUM artnum, an; char* line; char* last_buf = buf; MEM_SIZE last_buflen = LBUFLEN; bool success = TRUE; ART_NUM real_first = first; #ifdef SUPPORT_NNTP ART_NUM real_last = last; int line_cnt; int ov_chunk_size = cheating? OV_CHUNK_SIZE : OV_CHUNK_SIZE * 8; #endif time_t started_request; bool remote = !datasrc->over_dir; #ifdef SUPPORT_NNTP beginning: #endif for (;;) { artnum = article_first(first); if (artnum > first || !(article_ptr(artnum)->flags & AF_CACHED)) break; spin_todo--; first++; } if (first > last) goto exit; #ifdef SUPPORT_NNTP if (remote) { if (last - first > ov_chunk_size + ov_chunk_size/2 - 1) { last = first + ov_chunk_size - 1; line_cnt = 0; } } #endif started_request = time((time_t*)NULL); for (;;) { artnum = article_last(last); if (artnum < last || !(article_ptr(artnum)->flags & AF_CACHED)) break; spin_todo--; last--; } #ifdef SUPPORT_NNTP if (remote) { sprintf(ser_line, "XOVER %ld-%ld", (long)first, (long)last); if (nntp_command(ser_line) <= 0 || nntp_check() <= 0) { success = FALSE; goto exit; } # ifdef VERBOSE IF(verbose && !first_subject && !datasrc->ov_opened) printf("\nGetting overview file."), fflush(stdout); # endif } #endif ElseIf (datasrc->ov_opened < started_request - 60*60) { ov_close(); if ((datasrc->ov_in = fopen(ov_name(ngname), "r")) == NULL) return FALSE; #ifdef VERBOSE IF(verbose && !first_subject) printf("\nReading overview file."), fflush(stdout); #endif } if (!datasrc->ov_opened) { if (cheating) setspin(SPIN_BACKGROUND); else { #ifdef SUPPORT_NNTP int lots2do = ((datasrc->flags & DF_REMOTE)? netspeed : 20) * 100; #else int lots2do = 20 * 100; #endif if (spin_estimate > spin_todo) spin_estimate = spin_todo; setspin(spin_estimate > lots2do? SPIN_BARGRAPH : SPIN_FOREGROUND); } datasrc->ov_opened = started_request; } artnum = first-1; for (;;) { #ifdef SUPPORT_NNTP if (remote) { line = nntp_get_a_line(last_buf,last_buflen,last_buf!=buf); if (nntp_at_list_end(line)) break; line_cnt++; } #endif ElseIf (!(line = get_a_line(last_buf,last_buflen,last_buf!=buf,datasrc->ov_in))) break; last_buf = line; last_buflen = buflen_last_line_got; an = atol(line); if (an < first) continue; if (an > last) { artnum = last; #ifdef SUPPORT_NNTP if (remote) continue; #endif break; } spin_todo -= an - artnum - 1; ov_parse(line, artnum = an, remote); if (int_count) { int_count = 0; success = FALSE; #ifdef SUPPORT_NNTP if (!remote) #endif break; } if (!remote && cheating) { if (input_pending()) { success = FALSE; break; } if (curr_artp != sentinel_artp) { pushchar('\f' | 0200); success = FALSE; break; } } } #ifdef SUPPORT_NNTP if (remote && line_cnt == 0 && last < real_last) { an = nntp_find_real_art(last); if (an > 0) { last = an - 1; spin_todo -= last - artnum; artnum = last; } } if (remote) { int cachemask = (ThreadedGroup? AF_THREADED : AF_CACHED); ARTICLE* ap; for (ap = article_ptr(article_first(real_first)); ap && article_num(ap) <= artnum; ap = article_nextp(ap)) { if (!(ap->flags & cachemask)) onemissing(ap); } spin_todo -= last - artnum; } #endif if (artnum > last_cached && artnum >= first) last_cached = artnum; exit: if (int_count || !success) { int_count = 0; success = FALSE; } #ifdef SUPPORT_NNTP else if (remote) { if (cheating && curr_artp != sentinel_artp) { pushchar('\f' | 0200); success = FALSE; } else if (last < real_last) { if (!cheating || !input_pending()) { long elapsed_time = time((time_t*)NULL) - started_request; long expected_time = cheating? 2 : 10; int max_chunk_size = cheating? 500 : 2000; ov_chunk_size += (expected_time - elapsed_time) * OV_CHUNK_SIZE; if (ov_chunk_size <= OV_CHUNK_SIZE / 2) ov_chunk_size = OV_CHUNK_SIZE / 2 + 1; else if (ov_chunk_size > max_chunk_size) ov_chunk_size = max_chunk_size; first = last+1; last = real_last; goto beginning; } success = FALSE; } } #endif if (!cheating && datasrc->ov_in) fseek(datasrc->ov_in, 0L, 0); /* rewind it for the cheating phase */ if (success && real_first <= first_cached) { first_cached = real_first; cached_all_in_range = TRUE; } setspin(SPIN_POP); if (last_buf != buf) free(last_buf); return success; } static void ov_parse(line, artnum, remote) register char* line; ART_NUM artnum; bool_int remote; { register ARTICLE* article; register int i; int fn; Uchar* fieldnum = datasrc->fieldnum; Uchar* fieldflags = datasrc->fieldflags; char* fields[OV_MAX_FIELDS]; char* cp; char* tab; article = article_ptr(artnum); if (article->flags & AF_THREADED) { spin_todo--; return; } if (len_last_line_got > 0 && line[len_last_line_got-1] == '\n') { #ifdef SUPPORT_NNTP if (len_last_line_got > 1 && line[len_last_line_got-2] == '\r') line[len_last_line_got-2] = '\0'; else #endif line[len_last_line_got-1] = '\0'; } cp = line; bzero((char*)fields, sizeof fields); for (i = 0; cp && i < OV_MAX_FIELDS; cp = tab) { if ((tab = index(cp, '\t')) != NULL) *tab++ = '\0'; fn = fieldnum[i]; if (!(fieldflags[fn] & (FF_HAS_FIELD | FF_CHECK4FIELD))) break; if (fieldflags[fn] & (FF_HAS_HDR | FF_CHECK4HDR)) { char* s = index(cp, ':'); if (fieldflags[fn] & FF_CHECK4HDR) { if (s) fieldflags[fn] |= FF_HAS_HDR; fieldflags[fn] &= ~FF_CHECK4HDR; } if (fieldflags[fn] & FF_HAS_HDR) { if (!s) break; if (s - cp != htype[hdrnum[fn]].length || strncaseNE(cp,htype[hdrnum[fn]].name,htype[hdrnum[fn]].length)) continue; cp = s; while (*++cp == ' ') ; } } fields[fn] = cp; i++; } if (!fields[OV_SUBJ] || !fields[OV_MSGID] || !fields[OV_FROM] || !fields[OV_DATE]) return; /* skip this line if it's too short */ if (!article->subj) set_subj_line(article, fields[OV_SUBJ], strlen(fields[OV_SUBJ])); if (!article->msgid) set_cached_line(article, MSGID_LINE, savestr(fields[OV_MSGID])); if (!article->from) set_cached_line(article, FROM_LINE, savestr(fields[OV_FROM])); if (!article->date) article->date = parsedate(fields[OV_DATE]); #ifdef USE_FILTER if (!article->refs && fields[OV_REFS]) set_cached_line(article, REFS_LINE, *fields[OV_REFS]? savestr(fields[OV_REFS]) : nullstr); #endif if (!article->bytes && fields[OV_BYTES]) set_cached_line(article, BYTES_LINE, fields[OV_BYTES]); if (!article->lines && fields[OV_LINES]) set_cached_line(article, LINES_LINE, fields[OV_LINES]); if (fieldflags[OV_XREF] & (FF_HAS_FIELD | FF_CHECK4FIELD)) { if (!article->xrefs && fields[OV_XREF]) { /* Exclude an xref for just this group */ cp = index(fields[OV_XREF], ':'); if (cp && index(cp+1, ':')) article->xrefs = savestr(fields[OV_XREF]); } if (fieldflags[OV_XREF] & FF_HAS_FIELD) { if (!article->xrefs) article->xrefs = nullstr; } else if (fields[OV_XREF]) { ART_NUM an; ARTICLE* ap; for (an=article_first(absfirst); anxrefs) ap->xrefs = nullstr; } fieldflags[OV_XREF] |= FF_HAS_FIELD; } } if (remote) article->flags |= AF_EXISTS; if (ThreadedGroup) { if (valid_article(article)) thread_article(article, fields[OV_REFS]); } else if (!(article->flags & AF_CACHED)) cache_article(article); if (article->flags & AF_UNREAD) check_poster(article); spin(100); } /* Change a newsgroup name into the name of the overview data file. We ** subsitute any '.'s in the group name into '/'s, prepend the path, and ** append the '/.overview' or '.ov') on to the end. */ static char* ov_name(group) char* group; { register char* cp; strcpy(buf, datasrc->over_dir); cp = buf + strlen(buf); *cp++ = '/'; strcpy(cp, group); while ((cp = index(cp, '.'))) *cp = '/'; strcat(buf, OV_FILE_NAME); return buf; } void ov_close() { if (datasrc->ov_opened) { if (datasrc->ov_in) { (void) fclose(datasrc->ov_in); datasrc->ov_in = NULL; } datasrc->ov_opened = 0; } } char* ov_fieldname(num) int num; { return htype[hdrnum[num]].name; } char* ov_field(ap, num) ARTICLE* ap; int num; { char* s; int fn; fn = datasrc->fieldnum[num]; if (!(datasrc->fieldflags[fn] & (FF_HAS_FIELD | FF_CHECK4FIELD))) return NULL; if (fn == OV_NUM) { sprintf(cmd_buf, "%ld", (long)ap->num); return cmd_buf; } if (fn == OV_DATE) { sprintf(cmd_buf, "%ld", (long)ap->date); return cmd_buf; } s = get_cached_line(ap, hdrnum[fn], TRUE); return s? s : nullstr; }