This is spool.c in view mode; [Download] [Up]
/* Local spool support for slrn added by Olly Betts <olly@mantis.co.uk> */ #include "config.h" #include "features.h" #define DEBUG_SPOOL 0 #define DEBUG_SPOOL_FILENAME "SLRNDEBUG" #include <stdio.h> #ifdef HAVE_STDLIB_H # include <stdlib.h> #endif #include <string.h> #include <errno.h> #include <ctype.h> #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <slang.h> #include "jdmacros.h" #include <time.h> #include "misc.h" #include "slrn.h" #ifndef SLRN_SPOOL_ROOT # define SLRN_SPOOL_ROOT "/var/spool/news" /* a common place for the newsspool */ #endif #ifndef SLRN_SPOOL_NOV_ROOT # define SLRN_SPOOL_NOV_ROOT SLRN_SPOOL_ROOT #endif #ifndef SLRN_SPOOL_NOV_FILE # define SLRN_SPOOL_NOV_FILE ".overview" #endif #include <sys/stat.h> #include <assert.h> #if HAVE_DIRENT_H # include <dirent.h> # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # define NEED_D_NAMLEN # if HAVE_SYS_NDIR_H # include <sys/ndir.h> # endif # if HAVE_SYS_DIR_H # include <sys/dir.h> # endif # if HAVE_NDIR_H # include <ndir.h> # endif #endif #include <limits.h> #ifndef NNTP_MAX_XOVER_SIZE # define NNTP_MAX_XOVER_SIZE 1024 #endif static int spool_put_server_cmd (char *, char *, unsigned int ); static int spool_select_group (char *, int *, int *); static void spool_close_server (void); static int spool_has_cmd (char *); static int spool_initialize_server (void); static char *spool_read_line (char *, unsigned int ); static int spool_xpat_cmd (char *, int, int, char *); static int spool_xgtitle_cmd (char *); static int spool_select_article (int, char *); static int spool_open_xover (int, int); static char *spool_read_xover (char *, unsigned int); static void spool_close_xover (void); static char *spool_head_from_msgid (char *, char *, unsigned int ); static int spool_xhdr_command (char *, int, char *, unsigned int); static int spool_list_newsgroups (void); static int spool_list_active (void); static void spool_open_suspend_xover (void); static void spool_close_suspend_xover (void); static char *spool_get_extra_xover_header (char *); static char *spool_read_create_xover_line (int, char *); static Slrn_Server_Obj_Type Spool_Server_Obj; /* some state that the NNTP server would take care of if we were using one */ static FILE *Spool_fh_local=NULL; static char *Spool_Group=NULL; static FILE *Spool_fh_nov=NULL; /* we use the overview file lots, so keep it open */ static int Spool_cur_artnum=0; static int Spool_max_artnum=0; static int Spool_fhead=0; /* if non-0 we're emulating "HEAD" so stop on blank line */ static int Spool_fFakingActive=0; /* if non-0 we're doing funky stuff with MH folders */ static int spool_fake_active( char *); static char *spool_fakeactive_read_line(char *, int); static int Spool_fakeactive_newsgroups=0; #if DEBUG_SPOOL # include <stdarg.h> static void spool_debug (char *fmt, ...) { FILE *fp = NULL; va_list ap; if (fp == NULL) { fp = fopen (DEBUG_SPOOL_FILENAME, "w"); if (fp == NULL) return; } fprintf (fp, "%lu: ", (unsigned long) clock ()); if (fmt == NULL) { fputs ("(NULL)", fp); } else { va_start(ap, fmt); vfprintf(fp, fmt, ap); va_end (ap); } putc ('\n', fp); fflush (fp); } #endif /* close any current file (unless it's the overview file) and NULL the FILE* */ static int spool_fclose_local (void) { int res = 0; Spool_fhead=0; Spool_fFakingActive=0; if (Spool_fh_local != NULL) { if (Spool_fh_local != Spool_fh_nov) res = fclose(Spool_fh_local); Spool_fh_local=NULL; } return res; } static char *spool_dircat (char *root, char *name, int map_dots) { char *spool_group, *p, ch; unsigned int len; len = strlen (root); spool_group = SLMALLOC(strlen(name) + len + 2); if (spool_group == NULL) { slrn_exit_error ("Out of memory."); } strcpy (spool_group, root); p = spool_group + len; if (len && (*(p - 1) != '/')) *p++ = '/'; strcpy (p, name); if (map_dots) while ((ch = *p) != 0) { if (ch == '.') *p = '/'; p++; } return spool_group; } static int spool_find_artnum_from_msgid (char *msgid) { char buf [8 * NNTP_MAX_XOVER_SIZE]; char *p; #if DEBUG_SPOOL spool_debug ("spool_find_artnum_from_msgid('%s')", msgid); #endif spool_open_xover( 1, INT_MAX ); while (NULL != spool_read_xover (buf, sizeof(buf))) { char *q; /* 5th field is message id. */ if (NULL == (p = slrn_strchr(buf, '\t'))) continue; if (NULL == (p = slrn_strchr(p + 1, '\t'))) continue; if (NULL == (p = slrn_strchr(p + 1, '\t'))) continue; if (NULL == (p = slrn_strchr(p + 1, '\t'))) continue; p++; /* skip tab */ q = slrn_strchr(p,'\t'); if (q != NULL) *q='\0'; if (0 == strcmp(msgid, p)) { spool_close_xover(); #if DEBUG_SPOOL spool_debug ("spool_find_artnum_from_msgid() returns %d",atoi(buf)); #endif return atoi(buf); } } spool_close_xover(); #if DEBUG_SPOOL spool_debug ("spool_find_artnum_from_msgid() found no match"); #endif return -1; } /* This is a separate function so localspool backend can just close the file */ static void spool_discard_output (char *line, unsigned int len) { if (0 == Spool_fFakingActive) spool_fclose_local (); /* not critical to shut down part way through, as slrn doesn't try to */ else while (NULL != spool_read_line (line, len)) { ; } } static int spool_nntp_head (char *line, char *buf, unsigned int len) { char *p; spool_fclose_local(); p = slrn_skip_whitespace (line + 5); /* !HACK! ought to check for "head <msg-id>" (with or without < >) */ if (*p == '<') { Spool_cur_artnum = spool_find_artnum_from_msgid (p); /* !HACK! could check for -1 here */ } else if (*p) Spool_cur_artnum = atoi(p); /* else no number given, so use current article number */ sprintf (buf, "%d", Spool_cur_artnum); /* ugly temporary use of buffer !HACK! */ p = buf; Spool_fh_local = fopen (p,"r"); if (NULL == Spool_fh_local) { return ERR_NOARTIG; /* No such article in this group */ } Spool_fhead = 1; /* set flag to stop after headers */ sprintf (buf,"221 %d", Spool_cur_artnum); /* !HACK! INN also gives message-id in this message */ return OK_HEAD; /* Head follows */ } static int spool_nntp_next (char *line, char *buf, unsigned int len) { int i; spool_fclose_local(); i = Spool_cur_artnum; do { char num[80]; i++; sprintf (num,"%d",i); if (1 == slrn_file_exists (num)) { Spool_cur_artnum = i; sprintf(buf,"223 %d",Spool_cur_artnum); #if DEBUG_SPOOL spool_debug ("NEXT found article %d",Spool_cur_artnum); #endif return OK_NOTEXT; /* No text sent -- stat, next, last */ } } while (i <= Spool_max_artnum); /* !HACK! better to find value from overview file or active file in case the group grows while we're in it? */ #if DEBUG_SPOOL spool_debug ("No NEXT -- %d > %d", Spool_cur_artnum, Spool_max_artnum); #endif return ERR_NONEXT; /* No next article in this group */ } static int spool_nntp_newgroups (char *line, char *buf, unsigned int len) { /* expect something like "NEWGROUPS 960328 170939 GMT" GMT is optional */ char *p; int Y,M,D,h,m,s; int c; int fGMT; int n; time_t threshold; struct tm t; long fpos; char buf1[512]; int ch; int found; int first; c = sscanf(line+9,"%02d%02d%02d %02d%02d%02d%n",&Y,&M,&D,&h,&m,&s,&n); assert(c==6); /* group.c should have sanity checked the line */ p = slrn_skip_whitespace(line + 9 + n); fGMT = (0 == strncmp (p, "GMT", 3)); /* this !HACK! is good until 2051 -- round at 50 cos the RFC says to */ Y += ( Y>50 ? 1900 : 2000 ); /* for full version, use this instead: * if (Y<=50) Y+=100; * yr = this_year(); * Y += ((yr-51)/100*100); */ #if DEBUG_SPOOL spool_debug ("%d read, %04d-%02d-%02d %d:%02d:%02d %s", c,Y,M,D,h,m,s,fGMT?"GMT":"(local)"); #endif t.tm_year = Y - 1900; /* tm_year is years since 1900 */ t.tm_mon = M - 1; /* tm_mon has January as 0 */ t.tm_mday = D; t.tm_hour = h; t.tm_min = m; t.tm_sec = s; t.tm_isdst = 0; /* say it's not DST (? !HACK!) */ threshold = mktime( &t ); #if DEBUG_SPOOL spool_debug ("threshold (local) = %d",threshold); #endif if (fGMT) { threshold=mktime(gmtime(&threshold)); /* !HACK! is this correct? */ #if DEBUG_SPOOL spool_debug ("threshold (GMT) = %d",threshold); #endif } spool_fclose_local(); Spool_fh_local = fopen (Slrn_ActiveTimes_File, "r"); if (Spool_fh_local == NULL) { /* !HACK! at this point, it would be nice to be able to check for * recently created directories with readdir() and to return those * which would be useful for reading MH folders, etc. This would * be more than a little slow for a true newsspool though. * * Hmm, looked into this and it seems that you can't use the mtime * or ctime to decide when a directory was created. Hmmm. */ #if DEBUG_SPOOL spool_debug ("Couldn't open active.times"); #endif return ERR_FAULT; /* Program fault, command not performed */ } /* chunk size to step back through active.times by * when checking for new groups */ /* 128 should be enough to find the last line in the probably most * common case of no new groups */ #define SLRN_SPOOL_ACTIVETIMES_STEP 128 /* find start of a line */ fseek (Spool_fh_local, 0, SEEK_END ); /* !HACK! check return value from this and all the rest... */ fpos = ftell(Spool_fh_local); #if DEBUG_SPOOL spool_debug ("ftell=%ld errno=%d (%s)",fpos, errno, strerror(errno)); errno = 0; #endif found=0; first=1; while (!found) { int i, len1; len1 = SLRN_SPOOL_ACTIVETIMES_STEP; if (fpos < (long)len1) len1=fpos; /* don't run of the start of the file */ fpos -= len1; fseek (Spool_fh_local, fpos, SEEK_SET ); if (fpos == 0) break; #if DEBUG_SPOOL spool_debug ("ftell=%ld errno=%d (%s)", ftell (Spool_fh_local), errno, strerror(errno)); #endif if (first) { /* on the first pass, we want to ignore the last byte \n at eof */ --len1; first=0; } for (i = 0; i < len1; i++) { ch = getc(Spool_fh_local); assert(ch!=EOF); /* shouldn't happen */ if (ch != '\n') continue; while ((i < len1) && (NULL != fgets (buf1, sizeof(buf1), Spool_fh_local))) { i -= strlen(buf1); p = buf1; while (*p && (0 == isspace (*p))) p++; if (atol(p) < threshold)/* or <= ? !HACK! */ { found = 1; break; } } break; } } fpos=ftell(Spool_fh_local); #if DEBUG_SPOOL spool_debug ("ftell=%ld errno=%d (%s)",fpos, errno, strerror(errno)); #endif while (NULL != fgets( buf1, sizeof(buf1), Spool_fh_local )) { p = buf1; while (*p && (0 == isspace (*p))) p++; if (atol(p) >= threshold) /* or just > ? !HACK! */ { fseek( Spool_fh_local, fpos, SEEK_SET ); break; } fpos = ftell(Spool_fh_local); } return OK_NEWGROUPS; /* New newsgroups follow */ } typedef struct { char *name; unsigned int len; int (*f) (char *, char *, unsigned int); } Spool_NNTP_Map_Type; static Spool_NNTP_Map_Type Spool_NNTP_Maps [] = { {"NEXT", 4, spool_nntp_next}, {"HEAD", 4, spool_nntp_head}, {"NEWGROUPS", 9, spool_nntp_newgroups}, {NULL, 0, NULL} }; static int spool_put_server_cmd (char *line, char *buf, unsigned int len) { Spool_NNTP_Map_Type *nntpmap; #if DEBUG_SPOOL spool_debug ("spool_put_server_cmd('%s')", line); #endif nntpmap = Spool_NNTP_Maps; while (nntpmap->name != NULL) { if (!slrn_case_strncmp ((unsigned char *)nntpmap->name, (unsigned char *) line, nntpmap->len)) return (*nntpmap->f)(line, buf, len); nntpmap++; } #if DEBUG_SPOOL spool_debug ("Hmmm, didn't know about that command"); #endif return ERR_COMMAND; } static int spool_is_name_all_digits (char *p) { char *pmax; pmax = p + strlen (p); while (p < pmax) { if (!isdigit (*p)) return 0; p++; } return 1; } static int spool_read_minmax_from_dp (DIR *dp, int *min, int *max) { struct dirent *ep; char *p; long l; long hi = 0; long lo = LONG_MAX; /* Scan through all the files, checking the ones with numbers for names */ while ((ep = readdir(dp)) != NULL) { p = ep->d_name; #ifdef NEED_D_NAMLEN p[ep->d_namlen] = 0; #endif if (!isdigit(*p)) continue; if (0 == spool_is_name_all_digits (p)) continue; if (0 == (l = atol (p))) continue; if (l < lo) lo = l; if (l > hi) hi = l; } if ((lo == LONG_MAX) && (hi == 0)) return -1; *min=lo; *max=hi; return 0; } /* Get the lowest and highest article numbers by the simple method * or looking at the files in the directory. * Returns 1 on success, 0 on failure */ static int spool_read_minmax_from_dir( int *min, int *max ) { /* This is adapted from some code in INN's ng.c */ DIR *dp; /* I suspect this is very unlikely to fail */ if ((dp = opendir(".")) == NULL) return 0; if (-1 == spool_read_minmax_from_dp (dp, min, max)) { *min = 1; *max = 0; } (void) closedir(dp); return 1; } #if SPOOL_ACTIVE_FOR_ART_RANGE /* Get the lowest and highest article numbers from the active file * Returns 1 on success, 0 on failure * (failure => active file didn't open, or the group wasn't in it) */ static int spool_read_minmax_from_active( char *name, int *min, int *max ) { char buf[512]; unsigned int len; spool_fclose_local(); Spool_fh_local = fopen(Slrn_Active_File,"r"); if (Spool_fh_local == NULL) return 0; len = strlen(name); buf[len] = 0; /* init this for test below */ while (NULL != fgets (buf, sizeof(buf), Spool_fh_local)) { /* quick, crude test first to see if it could possibly be a match */ if ((buf[len] == ' ') && (0 == memcmp (buf, name, len))) { spool_fclose_local (); if (2 != sscanf (buf + len + 1, "%d%d", max, min)) return 0; Spool_max_artnum = *max; # if DEBUG_SPOOL spool_debug ("from active:%s %d %d", name,*min,*max); # endif return 1; } } spool_fclose_local(); return 0; } #endif /* Get the lowest and highest article numbers from the overview file * Returns 1 on success, 0 on failure */ static int spool_read_minmax_from_overview( char *name, int *min, int *max ) { /* chunk size to step back through .overview files by * when trying to find start of last line */ #define SPOOL_NOV_STEP 1024 /* If there's no .overview file, get min/max info from the active file */ /* ditto if .overview file is empty */ int ch; long fpos; int found; int first; /* !HACK! this assumes the overview file is rewound */ Spool_Server_Obj.sv_has_xover = ((Spool_fh_nov != NULL) && (1 == fscanf (Spool_fh_nov,"%d", min))); if (0 == Spool_Server_Obj.sv_has_xover) return 0; /* find start of last line */ fseek (Spool_fh_nov, 0, SEEK_END); /* !HACK! check return value from this and all the rest... */ fpos = ftell (Spool_fh_nov); #if DEBUG_SPOOL spool_debug("ftell=%ld errno=%d (%s)", fpos, errno, strerror(errno)); errno = 0; #endif found=0; first=1; while (!found && (fpos > 0)) { int i, len; len = SPOOL_NOV_STEP; /* don't run of the start of the file */ if (fpos < (long)len) len = fpos; fpos -= len; fseek(Spool_fh_nov, fpos, SEEK_SET); #if DEBUG_SPOOL spool_debug("ftell=%ld errno=%d (%s)", ftell (Spool_fh_nov), errno, strerror(errno)); errno = 0; #endif if (first) { /* on the first pass, we want to ignore the last byte \n at eof */ --len; first = 0; } for(i = 0; i < len; i++ ) { ch = getc(Spool_fh_nov); assert(ch!=EOF); /* shouldn't happen */ if (ch =='\n') found = i + 1; /* and keep going in case there's another */ } } fseek (Spool_fh_nov, fpos + found, SEEK_SET); #if DEBUG_SPOOL spool_debug("ftell=%ld errno=%d (%s)", ftell (Spool_fh_nov), errno, strerror(errno)); errno = 0; #endif fscanf (Spool_fh_nov, "%d", max); #if DEBUG_SPOOL spool_debug ("%s %d %d",name,*min,*max); #endif return 1; } int spool_select_group (char *name, int *min, int *max) { char *p, *q; /* close any open files */ spool_fclose_local(); if (Spool_fh_nov) fclose(Spool_fh_nov); if (Spool_Group != NULL) SLFREE(Spool_Group); Spool_Group = spool_dircat (Slrn_Spool_Root, name, 1); #if DEBUG_SPOOL spool_debug ("spool_select_group(%s) spool_group dir = %s", name, Spool_Group); #endif /* change directory to the spool directory to make opening articles easier */ if (chdir (Spool_Group)) return -1; p = spool_dircat (Slrn_Nov_Root, name, 1); q = spool_dircat (p, Slrn_Nov_File, 0); Spool_fh_nov = fopen(q,"rb"); SLFREE(q); SLFREE(p); if (!spool_read_minmax_from_overview( name, min, max ) #if SPOOL_ACTIVE_FOR_ART_RANGE && !spool_read_minmax_from_active( name, min, max ) #endif && !spool_read_minmax_from_dir( min, max )) return -1; Spool_max_artnum = *max; #if DEBUG_SPOOL spool_debug ("Group: %s %d - %d", name, *min, *max); #endif return 0; } static int Spool_Server_Inited = 0; static void spool_close_server (void) { #if DEBUG_SPOOL spool_debug ("spool_close_server()"); #endif if (Spool_Group != NULL) { SLFREE(Spool_Group); Spool_Group = NULL; } spool_fclose_local(); if (NULL != Spool_fh_nov) { fclose (Spool_fh_nov); Spool_fh_nov = NULL; } Spool_Server_Inited = 0; } static int spool_has_cmd (char *cmd) { return 0; /* deny everything */ } int spool_initialize_server (void) { int tt_inited = (Slrn_TT_Initialized & 1); if (Spool_Server_Inited) spool_close_server (); #if DEBUG_SPOOL spool_debug ("spool_initialize_server(%s)", host); #endif if (2 != slrn_file_exists (Slrn_Spool_Root)) { if (tt_inited == 0) slrn_tty_message (2, "Local spool directory '%s' doesn't exist", Slrn_Spool_Root); else { slrn_message ("Local spool directory '%s' doesn't exist", Slrn_Spool_Root); slrn_smg_refresh (); } return -1; } /* I think it's better to think that the *server* has XOVER, but * some (or all) groups may not. * So set this to 1 here, and then to 0 or 1 in spool_select_group if we * find an overview file */ Spool_Server_Obj.sv_has_xover = 1; Spool_Server_Inited = 1; return 0; } static char *spool_read_line (char *line, unsigned int len) { if (Spool_fFakingActive) return spool_fakeactive_read_line (line, len); if (!Spool_fh_local || fgets( line, len, Spool_fh_local)==NULL || (Spool_fhead && line[0]=='\n')) { spool_fclose_local(); return NULL; } len=strlen(line); if (len && line[len-1]=='\n') line[len-1]='\0'; /* client_get_server() just stops reading when it takes notice of len */ #if 0 else { /* skip rest of line which wouldn't fit */ while (((ch = getc(Spool_fh_local)) != '\n') && (ch != EOF)) ; /* do nothing */ } #endif return line; } int spool_xpat_cmd (char *hdr, int rmin, int rmax, char *pat) { return -1; } int spool_xgtitle_cmd (char *pattern) { return -1; } int spool_select_article (int n, char *msgid) { char num[20]; /* printf("spool_select_article(%d,%s)\n",n,msgid); */ if (n == -1) { n = spool_find_artnum_from_msgid(msgid); if (n == -1) return -1; } Spool_cur_artnum = n; sprintf (num,"%d",n); spool_fclose_local(); Spool_fh_local = fopen (num,"rb"); /* fopen the article in question */ if (Spool_fh_local == NULL) return -1; return 0; } static int Spool_XOver_Done; static int Spool_XOver_Min; static int Spool_XOver_Max; static int Spool_XOver_Next; static int Spool_Suspend_Xover_For_Kill = 0; int spool_open_xover (int min, int max) { int i, ch; long fp; #if DEBUG_SPOOL spool_debug ("spool_open_xover(%d,%d)", min, max); #endif spool_fclose_local(); if (Spool_Server_Obj.sv_has_xover && !Spool_Suspend_Xover_For_Kill) { Spool_fh_local = Spool_fh_nov; if (Spool_fh_local == NULL) return -1; /* find first record in range in overview file */ /* first look at the current position and see where we are */ /* this is worth trying as slrn will often read a series of ranges */ fp = ftell( Spool_fh_local ); if ((1 != fscanf(Spool_fh_local,"%d", &i)) || (i > min)) { /* looks like we're after the start of the range */ /* therefore we'll have to rescan the file from the start */ rewind (Spool_fh_local); i = -1; /* this might be improved by doing some binary-chop style searching */ } else { while (((ch = getc(Spool_fh_local)) != '\n') && (ch != EOF)) ; /* do nothing */ } #if DEBUG_SPOOL spool_debug ("Starting with i=%d",i); #endif while (i < min) { fp = ftell( Spool_fh_local ); if (1 != fscanf(Spool_fh_local,"%d", &i)) return -1; while (((ch = getc (Spool_fh_local)) != '\n') && (ch != EOF)) ; /* do nothing */ } fseek (Spool_fh_local, fp, SEEK_SET); /* reset to start of line */ } Spool_XOver_Next = Spool_XOver_Min = min; Spool_XOver_Max = max; Spool_XOver_Done = 0; return 0; } char *spool_read_xover (char *the_buf, unsigned int len) { char buf [NNTP_MAX_XOVER_SIZE]; int id; int already_tried = 0; #if DEBUG_SPOOL spool_debug ("spool_read_xover"); #endif if (Spool_Server_Obj.sv_has_xover && !Spool_Suspend_Xover_For_Kill) { char *p; p = spool_read_line (the_buf, len); /*#ifdef LOCAL*/ /* check if we've reached the end of the requested range */ if ((p != NULL) && (atoi(p) > Spool_XOver_Max)) p = NULL; #if DEBUG_SPOOL spool_debug (p); #endif /*#endif*/ return p; } if (Spool_XOver_Next > Spool_XOver_Max) return NULL; while (Spool_XOver_Done == 0) { sprintf (buf, "HEAD %d", Spool_XOver_Next); if (OK_HEAD != spool_put_server_cmd (buf, buf, sizeof(buf))) { already_tried = 0; /* This is ugly */ /* If a head fails even though server says it has article, jump * back and go on. */ server_is_messed_up_head_failed: do { switch (spool_put_server_cmd ("NEXT", buf, sizeof (buf))) { case OK_NOTEXT: /* OK, got next article */ break; case ERR_NONEXT: Spool_XOver_Done = 1; return NULL; default: slrn_exit_error ("Server failed NEXT request."); } id = atoi (buf + 4); /* Try going back 5 articles */ if ((already_tried == 0) && (id < Spool_XOver_Next - 5)) { sprintf (buf, "HEAD %d", Spool_XOver_Next - 5); if (OK_HEAD == spool_put_server_cmd (buf, buf, sizeof(buf))) { /* read all header */ spool_discard_output(buf, sizeof(buf)); } already_tried = 1; } } while (id < Spool_XOver_Next); if (OK_HEAD != spool_put_server_cmd ("HEAD", buf, sizeof(buf))) { sprintf (buf, "HEAD %d", id); if (OK_HEAD != spool_put_server_cmd (buf, buf, sizeof(buf))) { already_tried++; if (already_tried > 10) { slrn_exit_error ("Server Error: Head failed."); } goto server_is_messed_up_head_failed; } } } /* extract article number */ id = atoi(buf + 4); if (id >= Spool_XOver_Min) break; /* we just did a head. Read it and do next until range is ok */ spool_discard_output(buf, sizeof (buf)); while (id < Spool_XOver_Min) { if (OK_NOTEXT == spool_put_server_cmd ("NEXT", buf, sizeof (buf))) { id = atoi(buf + 4); continue; } Spool_XOver_Done = 1; break; } } if (Spool_XOver_Done) return NULL; if (id > Spool_XOver_Max) { /* clear out header text */ Spool_XOver_Done = 1; spool_discard_output(buf, sizeof (buf)); return NULL; } spool_read_create_xover_line (id, the_buf); if (id > Spool_XOver_Max) { Spool_XOver_Done = 1; return NULL; } if (OK_NOTEXT == spool_put_server_cmd ("NEXT", buf, sizeof (buf))) { Spool_XOver_Next = atoi(buf + 4); if (Spool_XOver_Next > Spool_XOver_Max) { Spool_XOver_Done = 1; } } else Spool_XOver_Done = 1; return the_buf; } void spool_close_xover () { #if DEBUG_SPOOL spool_debug ("spool_close_xover"); #endif Spool_XOver_Done = 1; } char *spool_head_from_msgid (char *msgid, char *buf, unsigned int len) { int id; if ((msgid == NULL) || (*msgid == 0)) return NULL; id=spool_find_artnum_from_msgid(msgid); sprintf(buf,"%d",id); /* !HACK! brief buf abuse */ Spool_fh_local=fopen(buf,"r"); if (!Spool_fh_local) return NULL; Spool_fhead=1; /* set flag to stop after headers */ return spool_read_create_xover_line (id, buf); } /* just hacked from the nntp_ version -- could be better coded */ int spool_xhdr_command (char *hdr, int num, char *buf, unsigned int buflen) { char cmd[256], tmpbuf[1024]; int found; unsigned int colon; sprintf (cmd, "HEAD %d", num); if (OK_HEAD != spool_put_server_cmd (cmd, buf, buflen)) return -1; found = 0; colon = strlen (hdr); while (NULL != spool_read_line (tmpbuf, sizeof (tmpbuf))) { char *b; if (found || slrn_case_strncmp ((unsigned char *) tmpbuf, (unsigned char *) hdr, colon) || (tmpbuf[colon] != ':')) continue; found = 1; b = tmpbuf + colon; if (*b == ' ') b++; strncpy (buf, b, buflen - 1); buf[buflen - 1] = 0; } return 0; } int spool_list_newsgroups (void) { spool_fclose_local(); Spool_fh_local=fopen(Slrn_Newsgroups_File,"r"); if (!Spool_fh_local) { /* Use readdir() to return a list of newsgroups read from the * "newsspool" so we can read MH folders, etc. This would be more * than a little slow for a true newsspool. */ spool_fake_active(Slrn_Spool_Root); Spool_fFakingActive=1; Spool_fakeactive_newsgroups=1; return 0; /* slrn_exit_error("Couldn't open newsgroups file '%s'", NEWSGROUPS); */ } return 0; } int spool_list_active (void) { spool_fclose_local(); Spool_fh_local=fopen (Slrn_Active_File,"r"); if (!Spool_fh_local) { spool_fake_active(Slrn_Spool_Root); Spool_fFakingActive=1; Spool_fakeactive_newsgroups=0; return 0; /* Use readdir() to return a list of newsgroups and article ranges read * from the "newsspool" so we can read MH folders, etc. This would * be more than a little slow for a true newsspool. */ /* slrn_exit_error("Couldn't open active file '%s'", ACTIVE);*/ } return 0; } typedef struct _Spool_DirTree_Type { struct _Spool_DirTree_Type *parent; DIR *dp; int len; long lo, hi; } Spool_DirTree_Type; static Spool_DirTree_Type *Spool_Head; static char Spool_Buf[256]; static char Spool_nBuf[256]; static int Spool_Is_LeafDir; static void spool_fake_active_in (char *dir) { char *p; DIR *dp; Spool_DirTree_Type *tmp; p = Spool_Buf + strlen(Spool_Buf); if (dir != NULL) { *p = '/'; strcpy (p + 1, dir); } if ((2 != slrn_file_exists (Spool_Buf)) || (NULL == (dp = opendir (Spool_Buf)))) { *p = 0; return; } Spool_Is_LeafDir = 1; tmp = (Spool_DirTree_Type *) SLMALLOC (sizeof(Spool_DirTree_Type)); if (tmp == NULL) { slrn_exit_error ("Out of memory."); } tmp->dp = dp; tmp->parent = Spool_Head; tmp->hi = 0; tmp->lo = LONG_MAX; if (dir == NULL) tmp->len = 1; else { tmp->len = strlen (dir); p = Spool_nBuf + strlen(Spool_nBuf); if (p != Spool_nBuf) *p++ = '.'; strcpy (p, dir); } Spool_Head = tmp; } static void spool_fake_active_out (void) { Spool_DirTree_Type *tmp; int i; (void)closedir (Spool_Head->dp); Spool_Is_LeafDir = 0; Spool_Buf [strlen(Spool_Buf) - Spool_Head->len - 1] = '\0'; i = strlen(Spool_nBuf) - Spool_Head->len - 1; if (i < 0) i = 0; Spool_nBuf[i]='\0'; tmp = Spool_Head; Spool_Head = Spool_Head->parent; SLFREE(tmp); } static int spool_fake_active (char *path) { strcpy(Spool_Buf, path); *Spool_nBuf='\0'; Spool_Head=NULL; spool_fake_active_in (NULL); return 0; } static char *spool_fakeactive_read_line(char *line, int len) { struct dirent *ep; char *p; long l; emptydir: if (!Spool_Head) { /* we've reached the end of the road */ Spool_fFakingActive = 0; return NULL; } /* Scan through all the files, checking the ones with numbers for names */ while ((ep = readdir(Spool_Head->dp)) != NULL) { p = ep->d_name; #ifdef NEED_D_NAMLEN p[ep->d_namlen] = 0; #endif if ((0 == spool_is_name_all_digits (p)) || ((l = atol (p)) == 0)) { if (!(p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0')))) { spool_fake_active_in(p); } continue; } if (l < Spool_Head->lo) Spool_Head->lo = l; if (l > Spool_Head->hi) Spool_Head->hi = l; } if (Spool_Head->lo == LONG_MAX && Spool_Head->hi==0) { /* assume all leaf directories are valid groups */ /* non-leaf directories aren't groups unless they have articles in */ if (!Spool_Is_LeafDir) { spool_fake_active_out(); goto emptydir; /* skip empty "groups" */ } Spool_Head->lo = 1; } if (Spool_fakeactive_newsgroups) { /* newsgroups: alt.foo A group about foo */ sprintf(line,"%s ?\n",Spool_nBuf); /* !HACK! check len */ } else { /* active: alt.guitar 0000055382 0000055345 y */ sprintf(line,"%s %ld %ld y\n",Spool_nBuf,Spool_Head->hi,Spool_Head->lo); /* !HACK! check len */ } spool_fake_active_out(); return line; } /* exactly the same as the nntp.c versions, apart from nntp_ -> spool_ */ /* RFC850: The required headers are * Relay-Version, Posting-Version, From, Date, Newsgroups, * Subject, Message-ID, Path. */ #define MAX_XOVER_SIZE 1024 typedef struct { char name[25]; unsigned int colon; char buf[MAX_XOVER_SIZE + 16]; } Spool_Header_Xover_Type; /* These MUST be arranged in XOver format: * id|subj|from|date|msgid|refs|bytes|line|misc stuff * * !!!DO NOT REARRANGE OR ADD ANYTHING WITHOUT UPDATING THE ARRAY REFERENCES!!! */ #define SPOOL_NUM_XOVER_HEADERS 7 #if SLRN_HAS_NNTPREAD extern Spool_Header_Xover_Type Spool_Xover_Headers[]; /* defined in nntp.c */ #else Spool_Header_Xover_Type Spool_Xover_Headers[SPOOL_NUM_XOVER_HEADERS] = { {"Subject:", 8, ""}, {"From:", 5, ""}, {"Date:", 5, ""}, {"Message-ID:", 11, ""}, {"References:", 11, ""}, /* {"Bytes:", 6, ""}, */ {"Lines:", 6, ""}, {"Xref:", 5, ""} }; #endif /* The rest of these are for scoring/killing */ #define MAX_EXTRA_XOVER_HEADERS 20 Spool_Header_Xover_Type *Spool_Extra_Xover_Headers[MAX_EXTRA_XOVER_HEADERS]; static unsigned int Spool_Next_Extra_Xover_Header; void spool_open_suspend_xover (void) { Spool_Suspend_Xover_For_Kill = 1; Spool_Next_Extra_Xover_Header = 0; } void spool_close_suspend_xover (void) { unsigned int i; if (Spool_Suspend_Xover_For_Kill == 0) return; Spool_Suspend_Xover_For_Kill = 0; for (i = 0; i < MAX_EXTRA_XOVER_HEADERS; i++) { if (Spool_Extra_Xover_Headers[i] != NULL) SLFREE (Spool_Extra_Xover_Headers[i]); Spool_Extra_Xover_Headers[i] = NULL; } Spool_Next_Extra_Xover_Header = 0; } char *spool_get_extra_xover_header (char *hdr) { unsigned int i; unsigned int colon = strlen (hdr) + 1; for (i = 0; i < Spool_Next_Extra_Xover_Header; i++) { Spool_Header_Xover_Type *xov = Spool_Extra_Xover_Headers[i]; if ((colon == xov->colon) && (!slrn_case_strcmp ((unsigned char *)xov->name, (unsigned char *)hdr))) return xov->buf; } return NULL; } static char *spool_read_create_xover_line (int id, char *the_buf) { char *hbuf, *b; char buf[8 * MAX_XOVER_SIZE]; unsigned int colon = 0; char *p, ch; unsigned int group_len; int i, headers_not_found; Spool_Header_Xover_Type *xov = NULL; for (i = 0; i < SPOOL_NUM_XOVER_HEADERS; i++) { *Spool_Xover_Headers[i].buf = 0; } Spool_Next_Extra_Xover_Header = 0; headers_not_found = SPOOL_NUM_XOVER_HEADERS; hbuf = NULL; while (NULL != spool_read_line (buf, sizeof(buf))) { unsigned int maxlen; ch = *buf; if ((ch == ' ') || (ch == '\t')) { if (hbuf == NULL) continue; if (hbuf - xov->buf >= MAX_XOVER_SIZE - 2) { hbuf = NULL; continue; } *hbuf++ = ' '; /* one blank to separate */ *hbuf = 0; colon = 0; /* Drop to add continuation */ } else { if (headers_not_found) i = 0; else i = SPOOL_NUM_XOVER_HEADERS; while (i < SPOOL_NUM_XOVER_HEADERS) { if ((ch == *Spool_Xover_Headers[i].name) && !slrn_case_strncmp ((unsigned char *)Spool_Xover_Headers[i].name, (unsigned char *)buf, Spool_Xover_Headers[i].colon)) { headers_not_found--; xov = Spool_Xover_Headers + i; colon = xov->colon; hbuf = xov->buf; break; } i++; } if (i == SPOOL_NUM_XOVER_HEADERS) { if ((!Spool_Suspend_Xover_For_Kill) || (Spool_Next_Extra_Xover_Header == MAX_EXTRA_XOVER_HEADERS)) { hbuf = NULL; continue; } xov = Spool_Extra_Xover_Headers[Spool_Next_Extra_Xover_Header]; if (xov == NULL) { xov = (Spool_Header_Xover_Type *) SLMALLOC (sizeof(Spool_Header_Xover_Type)); if (xov == NULL) { hbuf = NULL; continue; } Spool_Extra_Xover_Headers[Spool_Next_Extra_Xover_Header] = xov; } b = buf; while (*b && (*b != ':')) b++; if (*b != ':') { hbuf = NULL; continue; } *b++ = 0; colon = (b - buf); strncpy (xov->name, buf, sizeof (xov->name) - 1); xov->name[sizeof(xov->name) - 1] = 0; xov->colon = colon; Spool_Next_Extra_Xover_Header += 1; hbuf = xov->buf; } } /* trim trailing whitespace */ (void) slrn_trim_string (buf); /* trim leading whitespace */ b = slrn_skip_whitespace (buf + colon); maxlen = (MAX_XOVER_SIZE - (unsigned int)(hbuf - xov->buf)) - 1; strncpy (hbuf, b, maxlen); hbuf[maxlen] = 0; while (*hbuf) { if (*hbuf == '\t') *hbuf = ' '; hbuf++; } } /* we should try to extract the id from the Xref header if possible */ if (id == -1) { char *xref = Spool_Xover_Headers[6].buf; if (*xref != 0) { group_len = strlen (Slrn_Current_Group_Name); p = xref; while ((ch = *p) != 0) { if (ch == ' ') { p++; continue; } if (!strncmp (p, Slrn_Current_Group_Name, group_len)) { p += group_len; if (*p == ':') { p++; id = atoi (p); break; } } /* skip to next space */ while (((ch = *p) != 0) && (ch != ' ')) p++; } } #if 0 if ((id == -1) && PROBE_XCMD(Has_XPat, "XPAT")) { char xpatbuf[256]; char *msgid = Spool_Xover_Headers[3].buf; if (-1 != spool_xpat_cmd ("Message-Id", Slrn_Server_Min, Slrn_Server_Max, msgid)) { /* This should only loop once. */ while (spool_read_line (xpatbuf, sizeof (xpatbuf) - 1) != NULL) { id = atoi (xpatbuf); } } } #endif } /* put in standard XOVER format: * id|subj|from|date|msgid|refs|bytes|line|misc stuff */ sprintf (the_buf, "%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s\tXref: %s", id, Spool_Xover_Headers[0].buf, /* subject */ Spool_Xover_Headers[1].buf, /* from */ Spool_Xover_Headers[2].buf, /* date */ Spool_Xover_Headers[3].buf, /* msgid */ Spool_Xover_Headers[4].buf, /* references */ "", /* bytes */ Spool_Xover_Headers[5].buf, /* lines */ Spool_Xover_Headers[6].buf /* xref */ ); return the_buf; } char *Slrn_Inn_Root; char *Slrn_Spool_Root; char *Slrn_Nov_Root; char *Slrn_Nov_File; char *Slrn_Active_File; char *Slrn_ActiveTimes_File; char *Slrn_Newsgroups_File; static int spool_init_objects (void) { Spool_Server_Obj.sv_select_group = spool_select_group; Spool_Server_Obj.sv_read_line = spool_read_line; Spool_Server_Obj.sv_close = spool_close_server; Spool_Server_Obj.sv_initialize = spool_initialize_server; Spool_Server_Obj.sv_select_article = spool_select_article; Spool_Server_Obj.sv_head_from_msgid = spool_head_from_msgid; Spool_Server_Obj.sv_read_xover = spool_read_xover; Spool_Server_Obj.sv_open_xover = spool_open_xover; Spool_Server_Obj.sv_close_xover = spool_close_xover; Spool_Server_Obj.sv_put_server_cmd = spool_put_server_cmd; Spool_Server_Obj.sv_xpat_cmd = spool_xpat_cmd; Spool_Server_Obj.sv_xhdr_command = spool_xhdr_command; Spool_Server_Obj.sv_get_extra_xover_header = spool_get_extra_xover_header; Spool_Server_Obj.sv_close_suspend_xover = spool_close_suspend_xover; Spool_Server_Obj.sv_open_suspend_xover = spool_open_suspend_xover; Spool_Server_Obj.sv_xgtitle_cmd = spool_xgtitle_cmd; Spool_Server_Obj.sv_has_xover = 0; Spool_Server_Obj.sv_has_cmd = spool_has_cmd; Spool_Server_Obj.sv_list_newsgroups = spool_list_newsgroups; Spool_Server_Obj.sv_list_active = spool_list_active; Slrn_Inn_Root = slrn_make_startup_string (SLRN_SPOOL_INNROOT); Slrn_Spool_Root = slrn_make_startup_string (SLRN_SPOOL_ROOT); Slrn_Nov_Root = slrn_make_startup_string (SLRN_SPOOL_NOV_ROOT); Slrn_Nov_File = slrn_make_startup_string (SLRN_SPOOL_NOV_FILE); Slrn_Active_File = slrn_make_startup_string (SLRN_SPOOL_ACTIVE); Slrn_ActiveTimes_File = slrn_make_startup_string (SLRN_SPOOL_ACTIVETIMES); Slrn_Newsgroups_File = slrn_make_startup_string (SLRN_SPOOL_NEWSGROUPS); return 0; } /* This function is used below. It has a very specific purpose. */ static char *spool_root_dircat (char *file) { char *f; if (*file == '/') return file; f = spool_dircat (Slrn_Inn_Root, file, 0); SLFREE (file); return f; } static int spool_select_server_object (void) { Slrn_Server_Obj = &Spool_Server_Obj; Slrn_Active_File = spool_root_dircat (Slrn_Active_File); Slrn_ActiveTimes_File = spool_root_dircat (Slrn_ActiveTimes_File); Slrn_Newsgroups_File = spool_root_dircat (Slrn_Newsgroups_File); if (Spool_Server_Obj.sv_name != NULL) SLFREE (Spool_Server_Obj.sv_name); Spool_Server_Obj.sv_name = slrn_make_startup_string (Slrn_Spool_Root); return 0; } static void spool_usage (void) { fputs ("--spool options:\n\ ", stdout); exit (0); } static int spool_parse_args (char **argv, int argc) { int i; for (i = 0; i < argc; i++) { if (!strcmp (argv[i], "--help")) spool_usage (); else break; } return i; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.