1 /* -*- mode: C; mode: fold; -*- */
2 /* Local spool support for slrn added by Olly Betts <olly@mantis.co.uk> */
3 /* Modified by Thomas Schultz:
4 * Copyright (c) 2001-2006 Thomas Schultz <tststs@gmx.de>
5 */
6 #include "config.h"
7 #include "slrnfeat.h"
8
9 /* define this if you want to trace command-execution in spool.c.
10 * Normally, you only get errors in the debug-file. If you set this,
11 * please keep an eye on your disk-space.
12 */
13 #define DEBUG_SPOOL_TRACE 1
14
15 #include <stdio.h>
16 #ifndef SEEK_SET
17 # define SEEK_SET 0
18 #endif
19
20 #ifdef HAVE_STDLIB_H
21 # include <stdlib.h>
22 #endif
23
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 #include <slang.h>
33 #include "jdmacros.h"
34
35 #include <time.h>
36
37 #include "misc.h"
38 #include "util.h"
39 #include "slrn.h"
40 #include "slrndir.h"
41 #include "snprintf.h"
42 #include "vfile.h"
43 #include "strutil.h"
44
45 #ifndef SLRN_SPOOL_ROOT
46 # define SLRN_SPOOL_ROOT "/var/spool/news" /* a common place for the newsspool */
47 #endif
48
49 #ifndef SLRN_SPOOL_NOV_ROOT
50 # define SLRN_SPOOL_NOV_ROOT SLRN_SPOOL_ROOT
51 #endif
52
53 #ifndef SLRN_SPOOL_NOV_FILE
54 # define SLRN_SPOOL_NOV_FILE ".overview"
55 #endif
56
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <assert.h>
60 #include <stdarg.h>
61
62 #include <limits.h>
63
64 extern int Slrn_Prefer_Head;
65 char *Slrn_Overviewfmt_File;
66 char *Slrn_Requests_File;
67
68 static int spool_put_server_cmd (char *, char *, unsigned int );
69 static int spool_select_group (char *, NNTP_Artnum_Type *, NNTP_Artnum_Type *);
70 static int spool_refresh_groups (Slrn_Group_Range_Type *, int);
71 static void spool_close_server (void);
72 static int spool_has_cmd (char *);
73 static int spool_initialize_server (void);
74 static int spool_read_line (char *, unsigned int );
75 static int spool_xpat_cmd (char *, NNTP_Artnum_Type, NNTP_Artnum_Type, char *);
76 static int spool_select_article (NNTP_Artnum_Type, char *);
77 static int spool_get_article_size (NNTP_Artnum_Type);
78 static int spool_one_xhdr_command (char *, NNTP_Artnum_Type, char *, unsigned int);
79 static int spool_xhdr_command (char *, NNTP_Artnum_Type, NNTP_Artnum_Type);
80 static int spool_list (char *);
81 static int spool_list_newsgroups (void);
82 static int spool_list_active (char *);
83 static int spool_send_authinfo (void);
84 static int spool_read_xover (char *, unsigned int);
85 static int spool_read_xpat (char *, unsigned int);
86 static int spool_read_xhdr (char *, unsigned int);
87 static int spool_article_num_exists (NNTP_Artnum_Type);
88 static unsigned int spool_get_bytes (int clear);
89
90 static Slrn_Server_Obj_Type Spool_Server_Obj;
91
92 /* some state that the NNTP server would take care of if we were using one */
93 static FILE *Spool_fh_local=NULL;
94 static int Spool_Ignore_Comments=0; /* ignore comments while reading */
95 static SLRegexp_Type *Spool_Output_Regexp=NULL;
96 static char *Spool_Group=NULL;
97 static char *Spool_Group_Name;
98
99 static FILE *Spool_fh_nov=NULL; /* we use the overview file lots, so keep it open */
100 static NNTP_Artnum_Type Spool_cur_artnum = 0;
101
102 /* These are set when the group is selected. */
103 static NNTP_Artnum_Type Spool_Max_Artnum = 0;
104 static NNTP_Artnum_Type Spool_Min_Artnum = 0;
105
106 static int Spool_Doing_XOver; /* if non-zero, reading from ,overview */
107 static int Spool_Doing_XPat; /* reading xpat */
108 static char *Spool_XHdr_Field; /* when reading xhdr */
109 static int Spool_fhead=0; /* if non-0 we're emulating "HEAD" so stop on blank line */
110 static int Spool_fFakingActive=0; /* if non-0 we're doing funky stuff with MH folders */
111
112 static int spool_fake_active( char *);
113 static int spool_fakeactive_read_line(char *, int);
114 static int Spool_fakeactive_newsgroups=0;
115
116 static unsigned int bytes_read=0;
117
118 /* Note: Please leave debugging output untranslated.
119 * It cannot be interpreted by the end user anyway. */
120 static void debug_output (char *file, int line, char *fmt, ...) ATTRIBUTE_PRINTF(3,4);
121
debug_output(char * file,int line,char * fmt,...)122 static void debug_output (char *file, int line, char *fmt, ...)
123 {
124 va_list ap;
125 if (Slrn_Debug_Fp != NULL)
126 {
127 if ((file != NULL) && (line != -1))
128 fprintf (Slrn_Debug_Fp, "%s:%d: ", file, line);
129 else
130 #ifdef DEBUG_SPOOL_TRACE
131 fprintf (Slrn_Debug_Fp, "%lu: ", (unsigned long) clock ());
132 #else
133 return;
134 #endif
135
136 if (fmt == NULL)
137 {
138 fputs ("(NULL)", Slrn_Debug_Fp);
139 }
140 else
141 {
142 va_start(ap, fmt);
143 vfprintf(Slrn_Debug_Fp, fmt, ap);
144 va_end (ap);
145 }
146
147 putc ('\n', Slrn_Debug_Fp);
148 }
149 }
150 #ifndef __FILE__
151 # define __FILE__ "spool.c"
152 #endif
153 #ifndef __LINE__
154 # define __LINE__ 0
155 #endif
156
157 /* close any current file (unless it's the overview file) and NULL the FILE* */
spool_fclose_local(void)158 static int spool_fclose_local (void)
159 {
160 int res = 0;
161
162 Spool_fhead=0;
163 Spool_fFakingActive=0;
164 #if SLANG_VERSION >= 20000
165 if (Spool_Output_Regexp != NULL)
166 SLregexp_free (Spool_Output_Regexp);
167 #endif
168 Spool_Output_Regexp = NULL;
169 Spool_Ignore_Comments=0;
170
171 if (Spool_fh_local != NULL)
172 {
173 if (Spool_fh_local != Spool_fh_nov)
174 res = fclose(Spool_fh_local);
175 Spool_fh_local=NULL;
176 }
177 return res;
178 }
179
spool_open_nov_file(void)180 static FILE *spool_open_nov_file (void)
181 {
182 char *p, *q;
183 FILE *fp;
184
185 /* The spool_dircat function will exit if it fails to malloc. */
186 p = slrn_spool_dircat (Slrn_Nov_Root, Spool_Group_Name, 1);
187 q = slrn_spool_dircat (p, Slrn_Nov_File, 0);
188
189 fp = fopen (q,"rb");
190 SLFREE(q);
191 SLFREE(p);
192
193 return fp;
194 }
195
overview_file_seek(long fp,NNTP_Artnum_Type cur,NNTP_Artnum_Type dest)196 static int overview_file_seek (long fp, NNTP_Artnum_Type cur, NNTP_Artnum_Type dest)
197 {
198 int ch;
199
200 while (cur < dest)
201 {
202 if (-1 == (fp = ftell(Spool_fh_local)))
203 {
204 debug_output (__FILE__, __LINE__, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
205 return -1;
206 }
207 if (1 != fscanf (Spool_fh_local, NNTP_FMT_ARTNUM, &cur))
208 {
209 spool_fclose_local ();
210 return -1;
211 }
212
213 while (((ch = getc (Spool_fh_local)) != '\n') && (ch != EOF))
214 ; /* do nothing */
215 }
216 if (-1 == fseek(Spool_fh_local, fp, SEEK_SET)) /* reset to start of line */
217 {
218 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
219 return ERR_FAULT;
220 }
221
222 return 0;
223 }
224
225 static NNTP_Artnum_Type Spool_XOver_Next;
226 static NNTP_Artnum_Type Spool_XOver_Max;
227 static NNTP_Artnum_Type Spool_XOver_Min;
228
spool_nntp_xover(NNTP_Artnum_Type min,NNTP_Artnum_Type max)229 static int spool_nntp_xover (NNTP_Artnum_Type min, NNTP_Artnum_Type max)
230 {
231 NNTP_Artnum_Type i;
232 long fp;
233
234 Spool_Doing_XOver = 0;
235
236 spool_fclose_local ();
237
238 if (max > Spool_Max_Artnum)
239 max = Spool_Max_Artnum;
240
241 if (min < Spool_Min_Artnum)
242 min = Spool_Min_Artnum;
243
244 if (Spool_Server_Obj.sv_has_xover)
245 {
246 Spool_fh_local = Spool_fh_nov;
247 if (Spool_fh_local == NULL)
248 return -1;
249
250 /* find first record in range in overview file */
251 /* first look at the current position and see where we are */
252 /* this is worth trying as slrn will often read a series of ranges */
253 if (-1 == (fp = ftell (Spool_fh_local)))
254 {
255 debug_output (__FILE__, __LINE__, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
256 return ERR_FAULT;
257 }
258
259 if ((1 != fscanf (Spool_fh_local, NNTP_FMT_ARTNUM, &i))
260 || (i > min))
261 {
262 /* looks like we're after the start of the range */
263 /* therefore we'll have to rescan the file from the start */
264 rewind (Spool_fh_local);
265 i = -1;
266 /* this might be improved by doing some binary-chop style searching */
267 }
268 else
269 {
270 int ch;
271
272 while (((ch = getc(Spool_fh_local)) != '\n')
273 && (ch != EOF))
274 ; /* do nothing */
275
276 if (ch == EOF)
277 {
278 rewind (Spool_fh_local);
279 i = -1;
280 }
281 }
282
283 if (-1 == overview_file_seek (fp, i, min))
284 return -1;
285 }
286
287 Spool_XOver_Next = Spool_XOver_Min = min;
288 Spool_XOver_Max = max;
289 Spool_Doing_XOver = 1;
290
291 return OK_XOVER;
292 }
293
spool_read_xover(char * the_buf,unsigned int len)294 static int spool_read_xover (char *the_buf, unsigned int len)
295 {
296 long pos;
297
298 if (Spool_Doing_XOver == 0)
299 return 0;
300
301 if (Spool_XOver_Next > Spool_XOver_Max)
302 {
303 Spool_Doing_XOver = 0;
304 return 0;
305 }
306
307 while (1)
308 {
309 unsigned int buflen;
310
311 if (-1 == (pos = ftell (Spool_fh_nov)))
312 {
313 debug_output (__FILE__, __LINE__, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
314 return -1;
315 }
316
317 if (NULL == fgets (the_buf, len, Spool_fh_nov))
318 {
319 Spool_Doing_XOver = 0;
320 return 0;
321 }
322
323 buflen = strlen (the_buf);
324 if (buflen && (the_buf[buflen - 1] == '\n'))
325 the_buf [buflen - 1] = 0;
326
327 /* check if we've reached the end of the requested range */
328 Spool_XOver_Next = NNTP_STR_TO_ARTNUM (the_buf);
329 if (Spool_XOver_Next > Spool_XOver_Max)
330 {
331 if (-1 == fseek(Spool_fh_nov, pos, SEEK_SET))
332 {
333 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
334 return -1;
335 }
336 Spool_Doing_XOver = 0;
337 return 0;
338 }
339
340 #if 0
341 /* We now do this check in xover.c and get the article size (in bytes)
342 * using the same call to stat(). */
343 if (Slrn_Spool_Check_Up_On_Nov == 0)
344 break;
345
346 /* check that the article file actually exists */
347 /* if not, this nov entry is defunct, so ignore it */
348 if (0 == spool_article_num_exists (Spool_XOver_Next))
349 {
350 break;
351 }
352 debug_output (NULL, -1, "Nov entry %ld skipped!", Spool_XOver_Next);
353 #else
354 break;
355 #endif
356 }
357
358 Spool_XOver_Next++;
359 return 1;
360 }
361
spool_find_artnum_from_msgid(char * msgid,NNTP_Artnum_Type * idp)362 static int spool_find_artnum_from_msgid (char *msgid, NNTP_Artnum_Type *idp)
363 {
364 char buf [4096];
365 char *p;
366
367 debug_output (NULL, -1, "spool_find_artnum_from_msgid('%s')", msgid);
368
369 if (Slrn_Server_Obj->sv_has_xover == 0)
370 {
371 NNTP_Artnum_Type n;
372 unsigned int len = strlen (msgid);
373
374 for (n = Spool_Min_Artnum; n <= Spool_Max_Artnum; n++)
375 {
376 if (-1 == spool_one_xhdr_command ("Message-ID", n, buf, sizeof (buf)))
377 continue;
378
379 p = slrn_skip_whitespace (buf);
380 if (0 == strncmp (p, msgid, len))
381 {
382 *idp = n;
383 return 0;
384 }
385 }
386
387 return -1;
388 }
389
390 if (OK_XOVER != spool_nntp_xover (1, NNTP_ARTNUM_TYPE_MAX))
391 return -1;
392
393 while (1 == spool_read_xover (buf, sizeof(buf)))
394 {
395 char *q;
396
397 /* 5th field is message id. */
398
399 if (NULL == (p = slrn_strbyte(buf, '\t'))) continue;
400 if (NULL == (p = slrn_strbyte(p + 1, '\t'))) continue;
401 if (NULL == (p = slrn_strbyte(p + 1, '\t'))) continue;
402 if (NULL == (p = slrn_strbyte(p + 1, '\t'))) continue;
403
404 p++; /* skip tab */
405 q = slrn_strbyte(p,'\t');
406 if (q != NULL) *q='\0';
407
408 if (0 == strcmp(msgid, p))
409 {
410 debug_output (NULL, -1, ("spool_find_artnum_from_msgid() returns " NNTP_FMT_ARTNUM)
411 ,NNTP_STR_TO_ARTNUM(buf));
412 Spool_Doing_XOver = 0;
413 *idp = NNTP_STR_TO_ARTNUM(buf);
414 return 0;
415 }
416 }
417
418 debug_output (NULL, -1, "spool_find_artnum_from_msgid() found no match");
419
420 Spool_Doing_XOver = 0;
421 return -1;
422 }
423
spool_open_article_num(NNTP_Artnum_Type num)424 static FILE *spool_open_article_num (NNTP_Artnum_Type num)
425 {
426 char buf [SLRN_MAX_PATH_LEN];
427
428 slrn_snprintf (buf, sizeof (buf), ("%s/" NNTP_FMT_ARTNUM), Spool_Group, num);
429
430 return fopen (buf,"r");
431 }
432
spool_article_num_exists(NNTP_Artnum_Type num)433 static int spool_article_num_exists (NNTP_Artnum_Type num)
434 {
435 char buf [SLRN_MAX_PATH_LEN];
436
437 slrn_snprintf (buf, sizeof (buf), ("%s/" NNTP_FMT_ARTNUM), Spool_Group, num);
438
439 if (1 == slrn_file_exists (buf))
440 return 0;
441
442 return -1;
443 }
444
spool_get_article_size(NNTP_Artnum_Type num)445 static int spool_get_article_size (NNTP_Artnum_Type num)
446 {
447 char buf [SLRN_MAX_PATH_LEN];
448
449 slrn_snprintf (buf, sizeof (buf), ("%s/" NNTP_FMT_ARTNUM), Spool_Group, num);
450
451 return slrn_file_size (buf);
452 }
453
spool_is_name_all_digits(char * p)454 static int spool_is_name_all_digits (char *p)
455 {
456 char *pmax;
457
458 pmax = p + strlen (p);
459 while (p < pmax)
460 {
461 if (!isdigit (*p))
462 return 0;
463 p++;
464 }
465 return 1;
466 }
467
468 /*{{{ The routines in this fold implement the sv_put_server_cmd */
469
spool_nntp_head(NNTP_Artnum_Type id,char * msgid,NNTP_Artnum_Type * real_id)470 static int spool_nntp_head (NNTP_Artnum_Type id, char *msgid, NNTP_Artnum_Type *real_id)
471 {
472 spool_fclose_local();
473
474 if (id == -1)
475 {
476 if (msgid == NULL)
477 id = Spool_cur_artnum;
478 else
479 {
480 if (-1 == spool_find_artnum_from_msgid (msgid, &id))
481 id = -1;
482 }
483 }
484
485 if (real_id != NULL) *real_id = id;
486
487 if ((id == -1)
488 || (NULL == (Spool_fh_local = spool_open_article_num (id))))
489 return ERR_NOARTIG; /* No such article in this group */
490
491 Spool_cur_artnum = id;
492 Spool_fhead = 1; /* set flag to stop after headers */
493
494 return OK_HEAD; /* Head follows */
495 }
496
spool_nntp_next(NNTP_Artnum_Type * id)497 static int spool_nntp_next (NNTP_Artnum_Type *id)
498 {
499 NNTP_Artnum_Type i;
500
501 spool_fclose_local();
502
503 /* !HACK! better to find value from overview file or active file in case the group grows while we're in it? */
504 for (i = Spool_cur_artnum + 1; i <= Spool_Max_Artnum; i++)
505 {
506 if (-1 != spool_article_num_exists (i))
507 {
508 Spool_cur_artnum = i;
509 if (id != NULL) *id = i;
510
511 debug_output (NULL, -1, ("NEXT found article " NNTP_FMT_ARTNUM), Spool_cur_artnum);
512
513 return OK_NOTEXT; /* No text sent -- stat, next, last */
514 }
515 }
516
517 debug_output (NULL, -1, ("No NEXT -- " NNTP_FMT_ARTNUM " > " NNTP_FMT_ARTNUM),
518 Spool_cur_artnum, Spool_Max_Artnum);
519
520 return ERR_NONEXT; /* No next article in this group */
521 }
522
spool_nntp_newgroups(char * line,char * buf,unsigned int len)523 static int spool_nntp_newgroups (char *line, char *buf, unsigned int len)
524 {
525 /* expect something like "NEWGROUPS 960328 170939 GMT" GMT is optional */
526 char *p;
527 int Y,M,D,h,m,s;
528 int c;
529 int fGMT;
530 int n;
531 time_t threshold;
532 long fpos;
533 char buf1[512];
534 int ch;
535 int found;
536 int first;
537
538 (void) buf; (void) len;
539
540 /* The %n format specification returns the number of charcters parsed up
541 * to that point. I do not know how portable this is.
542 */
543 c = sscanf(line+9,"%02d%02d%02d %02d%02d%02d%n",&Y,&M,&D,&h,&m,&s,&n);
544 assert(c==6); /* group.c should have sanity checked the line */
545
546 p = slrn_skip_whitespace(line + 9 + n);
547 fGMT = (0 == strncmp (p, "GMT", 3));
548
549 /* this !HACK! is good until 2051 -- round at 50 cos the RFC says to */
550 Y += ( Y>50 ? 1900 : 2000 );
551 /* for full version, use this instead:
552 * if (Y<=50) Y+=100;
553 * yr = this_year();
554 * Y += ((yr-51)/100*100);
555 */
556
557 if (Y < 1970
558 || M < 1 || M > 12
559 || D < 1 || D > 31
560 || h < 0 || h > 24
561 || m < 0 || m > 59
562 || s < 0 || s > 59)
563 return ERR_FAULT;
564
565 /* Compute the number of days since beginning of year.
566 * Beware: the calculations involving leap years, etc... are naive. */
567 D--; M--;
568 D += 31 * M;
569 if (M > 1)
570 {
571 D -= (M * 4 + 27)/10;
572 if ((Y % 4) == 0) D++;
573 }
574
575 /* add that to number of days since beginning of time */
576 Y -= 1970;
577 D += Y * 365 + Y / 4;
578 if ((Y % 4) == 3) D++;
579
580 /* Now convert to secs */
581 s += 60L * (m + 60L * (h + 24L * D));
582 threshold = (time_t) s;
583
584 if (0 == fGMT)
585 {
586 #ifdef HAVE_TIMEZONE
587 struct tm *t;
588 (void) localtime (&threshold); /* This call sets timezone */
589 threshold += timezone;
590 t = localtime (&threshold);
591 if (t->tm_isdst) threshold -= (time_t) 3600;
592 #else
593 # ifdef HAVE_TM_GMTOFF
594 struct tm *t;
595 time_t tt;
596
597 t = localtime (&threshold);
598 tt = threshold - t->tm_gmtoff;
599 if (t->tm_isdst) tt += (time_t) 3600;
600 t = localtime (&tt);
601 threshold -= t->tm_gmtoff;
602 # endif /* else: we're out of luck */
603 #endif /* NOT HAVE_TIMEZONE */
604 }
605
606 debug_output (NULL, -1, "threshold for spool_nntp_newsgrups is %lu", (unsigned long) threshold);
607
608 spool_fclose_local();
609 Spool_fh_local = fopen (Slrn_ActiveTimes_File, "r");
610 if (Spool_fh_local == NULL)
611 {
612 /* !HACK! at this point, it would be nice to be able to check for
613 * recently created directories with readdir() and to return those
614 * which would be useful for reading MH folders, etc. This would
615 * be more than a little slow for a true newsspool though.
616 *
617 * Hmm, looked into this and it seems that you can't use the mtime
618 * or ctime to decide when a directory was created. Hmmm.
619 */
620 debug_output (NULL, -1, "Couldn't open active.times");
621 return ERR_FAULT; /* Program fault, command not performed */
622 }
623
624 /* chunk size to step back through active.times by
625 * when checking for new groups */
626 /* 128 should be enough to find the last line in the probably most
627 * common case of no new groups */
628 #define SLRN_SPOOL_ACTIVETIMES_STEP 128
629
630 /* find start of a line */
631 if (-1 == fseek (Spool_fh_local, 0, SEEK_END ))
632 {
633 debug_output (NULL, -1, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
634 return ERR_FAULT;
635 }
636 if (-1 == (fpos = ftell (Spool_fh_local)))
637 {
638 debug_output (NULL, -1, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
639 return ERR_FAULT;
640 }
641
642 found=0;
643 first=1;
644
645 while (!found)
646 {
647 int i, len1;
648
649 len1 = SLRN_SPOOL_ACTIVETIMES_STEP;
650
651 if (fpos < (long)len1) len1=fpos; /* don't run of the start of the file */
652 fpos -= len1;
653 if (-1 == fseek (Spool_fh_local, fpos, SEEK_SET ))
654 {
655 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
656 return ERR_FAULT;
657 }
658 if (fpos == 0) break;
659
660 if (first)
661 {
662 /* on the first pass, we want to ignore the last byte \n at eof */
663 --len1;
664 first=0;
665 }
666
667 for (i = 0; i < len1; i++)
668 {
669 ch = getc(Spool_fh_local);
670
671 assert(ch!=EOF); /* shouldn't happen */
672 if (ch != '\n') continue;
673
674 while ((i < len1)
675 && (NULL != fgets (buf1, sizeof(buf1), Spool_fh_local)))
676 {
677 i -= strlen(buf1);
678 p = buf1;
679 while (*p && (0 == isspace (*p)))
680 p++;
681
682 if (atol(p) < threshold)/* or <= ? !HACK! */
683 {
684 found = 1;
685 break;
686 }
687 }
688 break;
689 }
690 }
691
692 if (-1 == (fpos = ftell (Spool_fh_local)))
693 {
694 debug_output (NULL, -1, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
695 return ERR_FAULT;
696 }
697
698 while (NULL != fgets( buf1, sizeof(buf1), Spool_fh_local ))
699 {
700 p = buf1;
701 while (*p && (0 == isspace (*p))) p++;
702
703 if (atol(p) >= threshold) /* or just > ? !HACK! */
704 {
705 if (-1 == fseek (Spool_fh_local, fpos, SEEK_SET ))
706 {
707 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
708 return ERR_FAULT;
709 }
710 break;
711 }
712 if (-1 == (fpos = ftell(Spool_fh_local)))
713 {
714 debug_output (__FILE__, __LINE__, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
715 return ERR_FAULT;
716 }
717 }
718
719 return OK_NEWGROUPS; /* New newsgroups follow */
720 }
721
722 typedef struct
723 {
724 char *name;
725 unsigned int len;
726 int (*f) (char *, char *, unsigned int);
727 }
728 Spool_NNTP_Map_Type;
729
730 static Spool_NNTP_Map_Type Spool_NNTP_Maps [] =
731 {
732 {"NEWGROUPS", 9, spool_nntp_newgroups},
733 {NULL, 0, NULL}
734 };
735
spool_put_server_cmd(char * line,char * buf,unsigned int len)736 static int spool_put_server_cmd (char *line, char *buf, unsigned int len)
737 {
738 Spool_NNTP_Map_Type *nntpmap;
739
740 debug_output (NULL, -1, "spool_put_server_cmd('%s')", line);
741
742 nntpmap = Spool_NNTP_Maps;
743 while (nntpmap->name != NULL)
744 {
745 if (!slrn_case_strncmp (nntpmap->name,
746 line, nntpmap->len))
747 return (*nntpmap->f)(line, buf, len);
748
749 nntpmap++;
750 }
751
752 debug_output (NULL, -1, "Hmmm, didn't know about that command ('%s')", line);
753 return ERR_COMMAND;
754 }
755
756 /*}}}*/
757
spool_read_minmax_from_dp(Slrn_Dir_Type * dp,NNTP_Artnum_Type * min,NNTP_Artnum_Type * max)758 static int spool_read_minmax_from_dp (Slrn_Dir_Type *dp, NNTP_Artnum_Type *min, NNTP_Artnum_Type *max)
759 {
760 Slrn_Dirent_Type *ep;
761 char *p;
762 NNTP_Artnum_Type l;
763 NNTP_Artnum_Type hi = 0;
764 NNTP_Artnum_Type lo = NNTP_ARTNUM_TYPE_MAX;
765
766 /* Scan through all the files, checking the ones with numbers for names */
767 while ((ep = slrn_read_dir(dp)) != NULL)
768 {
769 p = ep->name;
770 if (!isdigit(*p)) continue;
771
772 if (0 == spool_is_name_all_digits (p))
773 continue;
774
775 if (0 == (l = NNTP_STR_TO_ARTNUM(p)))
776 continue;
777
778 if (l < lo)
779 lo = l;
780 if (l > hi)
781 hi = l;
782 }
783
784 if ((lo == LONG_MAX)
785 && (hi == 0))
786 return -1;
787
788 *min=lo;
789 *max=hi;
790
791 return 0;
792 }
793
spool_read_minmax_file(NNTP_Artnum_Type * min,NNTP_Artnum_Type * max,char * group_dir)794 static int spool_read_minmax_file (NNTP_Artnum_Type *min, NNTP_Artnum_Type *max, char *group_dir)
795 {
796 char *file;
797 FILE *fp;
798 char buf[512];
799 int status;
800
801 file = slrn_spool_dircat (group_dir, ".minmax", 0);
802 if (file == NULL)
803 return -1;
804
805 fp = fopen (file, "r");
806 if (fp == NULL)
807 {
808 SLFREE (file);
809 return -1;
810 }
811
812 status = 0;
813 if ((NULL == fgets (buf, sizeof (buf), fp))
814 || (2 != sscanf (buf, NNTP_FMT_ARTNUM_2, min, max)))
815 status = -1;
816
817 fclose (fp);
818 SLFREE (file);
819
820 return status;
821 }
822
823 /* Get the lowest and highest article numbers by the simple method
824 * or looking at the files in the directory.
825 * Returns 0 on success, -1 on failure
826 */
spool_read_minmax_from_dir(NNTP_Artnum_Type * min,NNTP_Artnum_Type * max,char * dir)827 static int spool_read_minmax_from_dir (NNTP_Artnum_Type *min, NNTP_Artnum_Type *max, char *dir)
828 {
829 Slrn_Dir_Type *dp;
830
831 if (dir == NULL) dir = ".";
832
833 /* I suspect this is very unlikely to fail */
834 if ((dp = slrn_open_dir (dir)) == NULL)
835 {
836 slrn_error (_("Unable to open directory %s"), dir);
837 return -1;
838 }
839
840 if (-1 == spool_read_minmax_from_dp (dp, min, max))
841 {
842 if (-1 == spool_read_minmax_file (min, max, dir))
843 {
844 *min = 1;
845 *max = 0;
846 }
847 }
848
849 (void) slrn_close_dir(dp);
850 return 0;
851 }
852
853 #if SPOOL_ACTIVE_FOR_ART_RANGE
854 /* Get the lowest and highest article numbers from the active file
855 * Returns 0 on success, -1 on failure
856 * (failure => active file didn't open, or the group wasn't in it)
857 */
spool_read_minmax_from_active(char * name,NNTP_Artnum_Type * min,NNTP_Artnum_Type * max)858 static int spool_read_minmax_from_active( char *name, NNTP_Artnum_Type *min, NNTP_Artnum_Type *max )
859 {
860 char buf[512];
861 unsigned int len;
862
863 spool_fclose_local();
864 Spool_fh_local = fopen(Slrn_Active_File,"r");
865 if (Spool_fh_local == NULL) return -1;
866
867 len = strlen(name);
868 buf[len] = 0; /* init this for test below */
869
870 while (NULL != fgets (buf, sizeof(buf), Spool_fh_local))
871 {
872 /* quick, crude test first to see if it could possibly be a match */
873 if ((buf[len] == ' ')
874 && (0 == memcmp (buf, name, len)))
875 {
876 spool_fclose_local ();
877 if (2 != sscanf (buf + len + 1, NNTP_FMT_ARTNUM NNTP_FMT_ARTNUM, max, min))
878 return -1;
879
880 Spool_Max_Artnum = *max;
881 debug_output (NULL, -1, "from active:%s " NNTP_FMT_ARTNUM_2, name, *min, *max);
882 return 0;
883 }
884 buf[len] = 0;
885 }
886 spool_fclose_local();
887
888 return -1;
889 }
890 #endif
891
892 /* Get the lowest and highest article numbers from the overview file
893 * Returns 0 on success, -1 on failure
894 */
spool_read_minmax_from_overview(char * name,NNTP_Artnum_Type * min,NNTP_Artnum_Type * max)895 static int spool_read_minmax_from_overview (char *name, NNTP_Artnum_Type *min, NNTP_Artnum_Type *max)
896 {
897 /* chunk size to step back through .overview files by
898 * when trying to find start of last line */
899 #define SPOOL_NOV_STEP 1024
900 /* If there's no .overview file, get min/max info from the active file */
901 /* ditto if .overview file is empty */
902 int ch;
903 long fpos;
904 int found;
905 int first;
906
907 (void) name;
908
909 if (Slrn_Prefer_Head == 2)
910 Spool_Server_Obj.sv_has_xover = 0;
911 else
912 /* !HACK! this assumes the overview file is rewound */
913 Spool_Server_Obj.sv_has_xover = ((Spool_fh_nov != NULL)
914 && (1 == fscanf (Spool_fh_nov, NNTP_FMT_ARTNUM, min)));
915
916 if (0 == Spool_Server_Obj.sv_has_xover)
917 return -1;
918
919 /* find start of last line */
920 if (-1 == fseek (Spool_fh_nov, 0, SEEK_END))
921 {
922 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
923 return -1;
924 }
925 if (-1 == (fpos = ftell (Spool_fh_nov)))
926 {
927 debug_output (__FILE__, __LINE__, "ftell returned -1; errno %d (%s).", errno, strerror(errno));
928 return -1;
929 }
930
931 found=0;
932 first=1;
933
934 while (!found && (fpos > 0))
935 {
936 int i, len;
937
938 len = SPOOL_NOV_STEP;
939
940 /* don't run of the start of the file */
941 if (fpos < (long)len) len = fpos;
942
943 fpos -= len;
944 if (-1 == fseek(Spool_fh_nov, fpos, SEEK_SET))
945 {
946 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
947 return -1;
948 }
949
950 if (first)
951 {
952 /* on the first pass, we want to ignore the last byte \n at eof */
953 --len;
954 first = 0;
955 }
956
957 for(i = 0; i < len; i++ )
958 {
959 ch = getc(Spool_fh_nov);
960
961 assert(ch!=EOF); /* shouldn't happen */
962 if (ch =='\n')
963 found = i + 1; /* and keep going in case there's another */
964 }
965 }
966
967 if (-1 == fseek(Spool_fh_nov, fpos + found, SEEK_SET))
968 {
969 debug_output (__FILE__, __LINE__, "fseek returned -1; errno %d (%s).", errno, strerror(errno));
970 return -1;
971 }
972
973 if (1 != fscanf (Spool_fh_nov, NNTP_FMT_ARTNUM, max))
974 {
975 debug_output (__FILE__, __LINE__, "Hmmm, unable to understand overview file - no integer found?");
976 return -1;
977 }
978
979 rewind (Spool_fh_nov);
980
981 debug_output (NULL, -1, ("%s " NNTP_FMT_ARTNUM_2), name,*min,*max);
982 return 0;
983 }
984
spool_select_group(char * name,NNTP_Artnum_Type * min,NNTP_Artnum_Type * max)985 static int spool_select_group (char *name, NNTP_Artnum_Type *min, NNTP_Artnum_Type *max)
986 {
987 /* close any open files */
988 spool_fclose_local();
989
990 if (Spool_fh_nov != NULL)
991 {
992 fclose (Spool_fh_nov);
993 Spool_fh_nov = NULL;
994 }
995
996 slrn_free (Spool_Group);
997 slrn_free (Spool_Group_Name);
998
999 Spool_Group = slrn_spool_dircat (Slrn_Spool_Root, name, 1);
1000 Spool_Group_Name = slrn_safe_strmalloc (name);
1001
1002 Spool_fh_nov = spool_open_nov_file ();
1003
1004 if ((-1 == spool_read_minmax_from_overview (name, min, max))
1005 #if SPOOL_ACTIVE_FOR_ART_RANGE
1006 && (-1 == spool_read_minmax_from_active (name, min, max))
1007 #endif
1008 && (-1 == spool_read_minmax_from_dir (min, max, Spool_Group)))
1009 return -1;
1010
1011 Spool_Max_Artnum = *max;
1012 Spool_Min_Artnum = *min;
1013
1014 debug_output (NULL, -1, "Group: %s " NNTP_FMT_ARTRANGE, name, *min, *max);
1015 return OK_GROUP;
1016 }
1017
spool_refresh_groups(Slrn_Group_Range_Type * gr,int n)1018 static int spool_refresh_groups (Slrn_Group_Range_Type *gr, int n)
1019 {
1020 while (n)
1021 {
1022 if (-1 == spool_select_group (gr->name,
1023 &(gr->min), &(gr->max)))
1024 gr->min = -1;
1025 n--;
1026 gr++;
1027 }
1028 return 0;
1029 }
1030
spool_current_group(void)1031 static char *spool_current_group (void)
1032 {
1033 return Spool_Group_Name == NULL ? "" : Spool_Group_Name;
1034 }
1035
1036 static int Spool_Server_Inited = 0;
1037
spool_close_server(void)1038 static void spool_close_server (void)
1039 {
1040 slrn_free (Spool_Group);
1041 Spool_Group = NULL;
1042
1043 spool_fclose_local();
1044
1045 if (NULL != Spool_fh_nov)
1046 {
1047 fclose (Spool_fh_nov);
1048 Spool_fh_nov = NULL;
1049 }
1050 Spool_Server_Inited = 0;
1051 }
1052
spool_has_cmd(char * cmd)1053 static int spool_has_cmd (char *cmd)
1054 {
1055 (void) cmd;
1056 return 0; /* deny everything */
1057 }
1058
spool_initialize_server(void)1059 static int spool_initialize_server (void)
1060 {
1061 if (Spool_Server_Inited) spool_close_server ();
1062
1063 if (2 != slrn_file_exists (Slrn_Spool_Root))
1064 {
1065 slrn_error (_("Local spool directory '%s' doesn't exist."), Slrn_Spool_Root);
1066 return -1;
1067 }
1068
1069 /* I think it's better to think that the *server* has XOVER, but
1070 * some (or all) groups may not.
1071 * So set this to 1 here, and then to 0 or 1 in spool_select_group if we
1072 * find an overview file
1073 */
1074 Spool_Server_Obj.sv_has_xover = 1;
1075 Spool_Server_Inited = 1;
1076 return 0;
1077 }
1078
spool_read_fhlocal(char * line,unsigned int len)1079 static int spool_read_fhlocal (char *line, unsigned int len)
1080 {
1081 do
1082 {
1083 if ((NULL == Spool_fh_local)
1084 || (NULL == fgets (line, len, Spool_fh_local))
1085 || (Spool_fhead && (line[0]=='\n')))
1086 {
1087 spool_fclose_local();
1088 return 0;
1089 }
1090 }
1091 while (((Spool_Output_Regexp != NULL) &&
1092 (NULL == slrn_regexp_match (Spool_Output_Regexp, line))) ||
1093 (Spool_Ignore_Comments && (*line == '#')));
1094
1095 len = strlen(line);
1096
1097 bytes_read += len;
1098
1099 if (len && (line [len - 1] == '\n'))
1100 line [len-1] = '\0';
1101
1102 return 1;
1103 }
1104
spool_read_line(char * line,unsigned int len)1105 static int spool_read_line (char *line, unsigned int len)
1106 {
1107 if (Spool_Doing_XPat) return spool_read_xpat (line, len);
1108
1109 if (Spool_Doing_XOver) return spool_read_xover (line, len);
1110
1111 if (NULL != Spool_XHdr_Field)
1112 {
1113 char buf[NNTP_BUFFER_SIZE];
1114 int retval = -1;
1115
1116 /* if (buf == NULL) return -1; */
1117 if ((len > 33) &&
1118 (1 == (retval = spool_read_xhdr (buf, sizeof (buf)))))
1119 {
1120 unsigned int numlen;
1121 (void) slrn_snprintf (line, len, (NNTP_FMT_ARTNUM " "), (Spool_XOver_Next - 1));
1122 numlen = strlen (line);
1123 if (len > numlen)
1124 slrn_strncpy (line + numlen, buf, len - numlen);
1125 }
1126 return retval;
1127 }
1128
1129 if (Spool_fFakingActive) return spool_fakeactive_read_line (line, len);
1130
1131 return spool_read_fhlocal (line, len);
1132 }
1133
1134 typedef struct
1135 {
1136 int xover_field;
1137 NNTP_Artnum_Type rmin, rmax;
1138 char header[80];
1139 char pat[256];
1140 }
1141 Spool_XPat_Type;
1142
1143 static Spool_XPat_Type Spool_XPat_Struct;
1144
spool_xpat_match(char * str,char * pat)1145 static int spool_xpat_match (char *str, char *pat)
1146 {
1147 /* HACK. This needs fixed for more general patterns. */
1148 if (NULL == strstr (str, pat))
1149 return -1;
1150
1151 return 0;
1152 }
1153
spool_read_xpat(char * buf,unsigned int len)1154 static int spool_read_xpat (char *buf, unsigned int len)
1155 {
1156 char tmpbuf [8192];
1157
1158 Spool_Doing_XPat = 0;
1159
1160 if (Spool_XPat_Struct.xover_field == -1)
1161 {
1162 NNTP_Artnum_Type num;
1163
1164 for (num = Spool_XPat_Struct.rmin; num <= Spool_XPat_Struct.rmax; num++)
1165 {
1166 if (-1 == spool_one_xhdr_command (Spool_XPat_Struct.header, num,
1167 tmpbuf, sizeof (tmpbuf)))
1168 continue;
1169
1170 if (-1 != spool_xpat_match (tmpbuf, Spool_XPat_Struct.pat))
1171 {
1172 unsigned int blen;
1173
1174 Spool_Doing_XPat = 1;
1175 Spool_XPat_Struct.rmin = num + 1;
1176
1177 slrn_snprintf (buf, len, (NNTP_FMT_ARTNUM " "), num);
1178 blen = strlen (buf);
1179
1180 strncpy (buf + blen, tmpbuf, len - blen);
1181 buf[len - 1] = 0;
1182
1183 return 1;
1184 }
1185 }
1186
1187 Spool_XPat_Struct.rmin = Spool_XPat_Struct.rmax + 1;
1188 }
1189 else
1190 {
1191 /* read the overview file until the right field matches the pattern */
1192 while (NULL != (fgets (tmpbuf, sizeof (tmpbuf), Spool_fh_local)))
1193 {
1194 /* first field is article number; "+1" skips it */
1195 int field = Spool_XPat_Struct.xover_field + 1;
1196 NNTP_Artnum_Type num = NNTP_STR_TO_ARTNUM (tmpbuf);
1197 char *b = tmpbuf, *end;
1198
1199 if ((num > Spool_XPat_Struct.rmax) || (num < 0))
1200 {
1201 spool_fclose_local ();
1202 return 0;
1203 }
1204
1205 while (field)
1206 {
1207 b = slrn_strbyte (b, '\t');
1208 /* If we reach the end of the overview line before finding
1209 * the correct field, the overview file must be corrupt. */
1210 if (b == NULL)
1211 {
1212 debug_output (NULL, -1, "Overview file corrupt? Unable to find "
1213 "field %d in overview line %s.",
1214 Spool_XPat_Struct.xover_field, tmpbuf);
1215 spool_fclose_local ();
1216 return -1;
1217 }
1218 b++;
1219 field--;
1220 }
1221 end = slrn_strbyte (b, '\t');
1222 if (end != NULL)
1223 *end = '\0';
1224
1225 if (-1 != spool_xpat_match (b, Spool_XPat_Struct.pat))
1226 {
1227 unsigned int blen;
1228
1229 Spool_Doing_XPat = 1;
1230 slrn_snprintf (buf, len, (NNTP_FMT_ARTNUM " "), num);
1231 blen = strlen (buf);
1232
1233 strncpy (buf + blen, b, len - blen);
1234 buf[len - 1] = 0;
1235 return 1;
1236 }
1237 }
1238 spool_fclose_local ();
1239 }
1240
1241 return 0;
1242 }
1243
spool_xpat_cmd(char * hdr,NNTP_Artnum_Type rmin,NNTP_Artnum_Type rmax,char * pat)1244 static int spool_xpat_cmd (char *hdr, NNTP_Artnum_Type rmin, NNTP_Artnum_Type rmax, char *pat)
1245 {
1246 static char *overview_headers [] =
1247 {
1248 "Subject", "From", "Date", "Message-ID",
1249 "References", "Bytes", "Lines",
1250 NULL
1251 };
1252
1253 spool_fclose_local ();
1254 Spool_Doing_XPat = 0;
1255 memset ((char *) &Spool_XPat_Struct, 0, sizeof (Spool_XPat_Struct));
1256
1257 if (rmin < Spool_Min_Artnum)
1258 rmin = Spool_Min_Artnum;
1259 if (rmax > Spool_Max_Artnum)
1260 rmax = Spool_Max_Artnum;
1261
1262 Spool_XPat_Struct.rmin = rmin;
1263 Spool_XPat_Struct.rmax = rmax;
1264
1265 /* The memset will guarantee that these are NULL terminated. */
1266 strncpy (Spool_XPat_Struct.header, hdr, sizeof (Spool_XPat_Struct.header) - 1);
1267 strncpy (Spool_XPat_Struct.pat, pat, sizeof (Spool_XPat_Struct.pat) - 1);
1268
1269 Spool_XPat_Struct.xover_field = -1;
1270
1271 if (Slrn_Server_Obj->sv_has_xover)
1272 {
1273 int field = 0;
1274
1275 while (1)
1276 {
1277 char *h = overview_headers [field];
1278
1279 if (h == NULL) break;
1280 if (0 == slrn_case_strcmp ( h, hdr))
1281 {
1282 Spool_XPat_Struct.xover_field = field;
1283 break;
1284 }
1285 field++;
1286 }
1287 }
1288
1289 if (Spool_XPat_Struct.xover_field != -1)
1290 {
1291 Spool_fh_local = spool_open_nov_file ();
1292 if (Spool_fh_local == NULL)
1293 return ERR_COMMAND;
1294 if (-1 == overview_file_seek (0, -1, Spool_XPat_Struct.rmin))
1295 return -1;
1296 }
1297
1298 Spool_Doing_XPat = 1;
1299
1300 return OK_HEAD;
1301 }
1302
spool_select_article(NNTP_Artnum_Type n,char * msgid)1303 static int spool_select_article (NNTP_Artnum_Type n, char *msgid)
1304 {
1305 /* printf("spool_select_article(%d,%s)\n",n,msgid); */
1306
1307 if (n == -1)
1308 {
1309 if ((msgid == NULL) || (*msgid == 0))
1310 return -1;
1311
1312 if (-1 == spool_find_artnum_from_msgid (msgid, &n))
1313 return ERR_NOARTIG;
1314 }
1315
1316 spool_fclose_local();
1317
1318 if (NULL == (Spool_fh_local = spool_open_article_num (n)))
1319 return ERR_NOARTIG;
1320
1321 Spool_cur_artnum = n;
1322 return OK_ARTICLE;
1323 }
1324
1325 /* The hdr string should NOT include the ':' */
spool_one_xhdr_command(char * hdr,NNTP_Artnum_Type num,char * buf,unsigned int buflen)1326 static int spool_one_xhdr_command (char *hdr, NNTP_Artnum_Type num, char *buf,
1327 unsigned int buflen)
1328 {
1329 char tmpbuf [1024];
1330 unsigned int colon;
1331
1332 spool_fclose_local ();
1333
1334 if (NULL == (Spool_fh_local = spool_open_article_num (num)))
1335 return -1;
1336
1337 Spool_fhead = 1; /* stop after headers */
1338
1339 colon = strlen (hdr);
1340
1341 while (1 == spool_read_fhlocal (tmpbuf, sizeof (tmpbuf)))
1342 {
1343 char *b;
1344 if (slrn_case_strncmp ( tmpbuf, hdr, colon)
1345 || (tmpbuf[colon] != ':'))
1346 continue;
1347
1348 b = tmpbuf + (colon + 1);
1349 if (*b == ' ') b++;
1350 slrn_strncpy (buf, b, buflen);
1351 while ((1 == spool_read_fhlocal (tmpbuf, sizeof (tmpbuf))) &&
1352 ((*tmpbuf == ' ') || (*tmpbuf == '\t')))
1353 {
1354 unsigned int len = strlen (buf);
1355 b = tmpbuf + 1;
1356 slrn_strncpy (buf + len, b, buflen - len);
1357 }
1358 return 0;
1359 }
1360
1361 return -1;
1362 }
1363
spool_xhdr_command(char * field,NNTP_Artnum_Type min,NNTP_Artnum_Type max)1364 static int spool_xhdr_command (char *field, NNTP_Artnum_Type min, NNTP_Artnum_Type max)
1365 {
1366 debug_output (NULL, -1, ("spool_xhdr_command(%s " NNTP_FMT_ARTNUM_2),
1367 field, min, max);
1368
1369 spool_fclose_local ();
1370
1371 if (max > Spool_Max_Artnum)
1372 max = Spool_Max_Artnum;
1373
1374 if (min < Spool_Min_Artnum)
1375 min = Spool_Min_Artnum;
1376
1377 Spool_XOver_Next = Spool_XOver_Min = min;
1378 Spool_XOver_Max = max;
1379 Spool_XHdr_Field = field;
1380
1381 return OK_HEAD;
1382 }
1383
spool_read_xhdr(char * the_buf,unsigned int len)1384 static int spool_read_xhdr (char *the_buf, unsigned int len)
1385 {
1386 int retval = -1;
1387
1388 if (Spool_XHdr_Field == NULL)
1389 return -1;
1390
1391 if (Spool_XOver_Next > Spool_XOver_Max)
1392 {
1393 Spool_XHdr_Field = NULL;
1394 return 0;
1395 }
1396
1397 while ((retval == -1) && (Spool_XOver_Next <= Spool_XOver_Max))
1398 retval = spool_one_xhdr_command (Spool_XHdr_Field, Spool_XOver_Next++,
1399 the_buf, len);
1400
1401 if (Spool_XOver_Next > Spool_XOver_Max)
1402 Spool_XHdr_Field = NULL;
1403
1404 return (retval == -1) ? -1 : 1;
1405 }
1406
spool_list(char * what)1407 static int spool_list (char *what)
1408 {
1409 if (!slrn_case_strcmp (what,"overview.fmt"))
1410 {
1411 spool_fclose_local();
1412 Spool_fh_local=fopen(Slrn_Overviewfmt_File,"r");
1413 if (Spool_fh_local)
1414 {
1415 Spool_Ignore_Comments = 1;
1416 return OK_GROUPS;
1417 }
1418 }
1419 return ERR_FAULT;
1420 }
1421
spool_list_newsgroups(void)1422 static int spool_list_newsgroups (void)
1423 {
1424 spool_fclose_local();
1425 Spool_fh_local=fopen(Slrn_Newsgroups_File,"r");
1426 if (!Spool_fh_local)
1427 {
1428 /* Use readdir() to return a list of newsgroups read from the
1429 * "newsspool" so we can read MH folders, etc. This would be more
1430 * than a little slow for a true newsspool.
1431 */
1432 spool_fake_active(Slrn_Spool_Root);
1433 Spool_fFakingActive=1;
1434 Spool_fakeactive_newsgroups=1;
1435 /* slrn_exit_error("Couldn't open newsgroups file '%s'", NEWSGROUPS); */
1436 }
1437 return OK_GROUPS;
1438 }
1439
1440 /* Having an independant buffer for the spool code might spare us mysterious
1441 * bugs, so I'm not simply using slrn_compile_regexp_pattern */
1442 #if SLANG_VERSION < 20000
spool_compile_regexp_pattern(char * pat)1443 static SLRegexp_Type *spool_compile_regexp_pattern (char *pat)
1444 {
1445 static unsigned char compiled_pattern_buf [512];
1446 static SLRegexp_Type re;
1447
1448 re.pat = (unsigned char *) pat;
1449 re.buf = compiled_pattern_buf;
1450 re.buf_len = sizeof (compiled_pattern_buf);
1451 re.case_sensitive = 1;
1452
1453 if (0 != SLang_regexp_compile (&re))
1454 {
1455 slrn_error (_("Invalid regular expression or expression too long."));
1456 return NULL;
1457 }
1458 return &re;
1459 }
1460 #endif
1461
spool_list_active(char * pat)1462 static int spool_list_active (char *pat)
1463 {
1464 spool_fclose_local();
1465 Spool_fh_local=fopen (Slrn_Active_File,"r");
1466 if (pat != NULL)
1467 {
1468 #if SLANG_VERSION < 20000
1469 Spool_Output_Regexp=spool_compile_regexp_pattern (slrn_fix_regexp (pat));
1470 #else
1471 if (Spool_Output_Regexp != NULL)
1472 SLregexp_free (Spool_Output_Regexp);
1473 Spool_Output_Regexp = SLregexp_compile (slrn_fix_regexp(pat),0);
1474 #endif
1475 }
1476
1477 if (!Spool_fh_local)
1478 {
1479 spool_fake_active(Slrn_Spool_Root);
1480 Spool_fFakingActive=1;
1481 Spool_fakeactive_newsgroups=0;
1482 return OK_GROUPS;
1483 /* Use readdir() to return a list of newsgroups and article ranges read
1484 * from the "newsspool" so we can read MH folders, etc. This would
1485 * be more than a little slow for a true newsspool.
1486 */
1487 /* slrn_exit_error("Couldn't open active file '%s'", ACTIVE);*/
1488 }
1489 return OK_GROUPS;
1490 }
1491
spool_send_authinfo(void)1492 static int spool_send_authinfo (void)
1493 {
1494 return 0;
1495 }
1496
1497 typedef struct _Spool_DirTree_Type
1498 {
1499 struct _Spool_DirTree_Type *parent;
1500 Slrn_Dir_Type *dp;
1501 int len;
1502 long lo, hi;
1503 }
1504 Spool_DirTree_Type;
1505
1506 static Spool_DirTree_Type *Spool_Head;
1507
1508 static char Spool_Buf[256];
1509 static char Spool_nBuf[256];
1510 static int Spool_Is_LeafDir;
1511
spool_fake_active_in(char * dir)1512 static void spool_fake_active_in (char *dir)
1513 {
1514 char *p;
1515 Slrn_Dir_Type *dp;
1516 Spool_DirTree_Type *tmp;
1517
1518 p = Spool_Buf + strlen(Spool_Buf);
1519
1520 if ((dir != NULL) &&
1521 (strlen (dir) < sizeof (Spool_Buf) - (size_t) (Spool_Buf - p + 2)))
1522 {
1523 *p = SLRN_PATH_SLASH_CHAR;
1524 strcpy (p + 1, dir); /* safe */
1525 }
1526
1527 if ((2 != slrn_file_exists (Spool_Buf))
1528 || (NULL == (dp = slrn_open_dir (Spool_Buf))))
1529 {
1530 *p = 0;
1531 return;
1532 }
1533
1534 Spool_Is_LeafDir = 1;
1535 tmp = (Spool_DirTree_Type *) slrn_safe_malloc (sizeof(Spool_DirTree_Type));
1536
1537 tmp->dp = dp;
1538 tmp->parent = Spool_Head;
1539 tmp->hi = 0;
1540 tmp->lo = LONG_MAX;
1541
1542 if (dir == NULL)
1543 tmp->len = 1;
1544 else
1545 {
1546 tmp->len = strlen (dir);
1547
1548 p = Spool_nBuf + strlen (Spool_nBuf);
1549 if (strlen (dir) < sizeof (Spool_nBuf) - (size_t) (Spool_nBuf - p + 2))
1550 {
1551 if (p != Spool_nBuf) *p++ = '.';
1552 strcpy (p, dir); /* safe */
1553 }
1554 }
1555
1556 Spool_Head = tmp;
1557 }
1558
spool_fake_active_out(void)1559 static void spool_fake_active_out (void)
1560 {
1561 Spool_DirTree_Type *tmp;
1562 int i;
1563
1564 (void)slrn_close_dir (Spool_Head->dp);
1565 Spool_Is_LeafDir = 0;
1566
1567 Spool_Buf [strlen(Spool_Buf) - Spool_Head->len - 1] = '\0';
1568
1569 i = strlen(Spool_nBuf) - Spool_Head->len - 1;
1570
1571 if (i < 0) i = 0;
1572 Spool_nBuf[i]='\0';
1573
1574 tmp = Spool_Head;
1575 Spool_Head = Spool_Head->parent;
1576 SLFREE(tmp);
1577 }
1578
spool_fake_active(char * path)1579 static int spool_fake_active (char *path)
1580 {
1581 slrn_strncpy (Spool_Buf, path, sizeof (Spool_Buf));
1582 *Spool_nBuf='\0';
1583 Spool_Head=NULL;
1584 spool_fake_active_in (NULL);
1585 return 0;
1586 }
1587
spool_fakeactive_read_line(char * line,int len)1588 static int spool_fakeactive_read_line(char *line, int len)
1589 {
1590 Slrn_Dirent_Type *ep;
1591 char *p;
1592 long l;
1593
1594 (void) len;
1595
1596 emptydir:
1597
1598 if (!Spool_Head)
1599 {
1600 /* we've reached the end of the road */
1601 Spool_fFakingActive = 0;
1602 return 0;
1603 }
1604
1605 /* Scan through all the files, checking the ones with numbers for names */
1606 while ((ep = slrn_read_dir(Spool_Head->dp)) != NULL)
1607 {
1608 p = ep->name;
1609
1610 if ((0 == spool_is_name_all_digits (p))
1611 || ((l = atol (p)) == 0))
1612 {
1613 if (!(p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0'))))
1614 {
1615 spool_fake_active_in(p);
1616 }
1617 continue;
1618 }
1619 if (l < Spool_Head->lo)
1620 Spool_Head->lo = l;
1621 if (l > Spool_Head->hi)
1622 Spool_Head->hi = l;
1623 }
1624
1625 if (Spool_Head->lo == LONG_MAX && Spool_Head->hi==0)
1626 {
1627 /* assume all leaf directories are valid groups */
1628 /* non-leaf directories aren't groups unless they have articles in */
1629 if (!Spool_Is_LeafDir)
1630 {
1631 spool_fake_active_out();
1632 goto emptydir; /* skip empty "groups" */
1633 }
1634 Spool_Head->lo = 1;
1635 }
1636
1637 if (Spool_fakeactive_newsgroups)
1638 {
1639 /* newsgroups: alt.foo A group about foo */
1640 slrn_snprintf (line, len, "%s ?\n", Spool_nBuf);
1641 }
1642 else
1643 {
1644 /* active: alt.guitar 0000055382 0000055345 y */
1645 slrn_snprintf (line, len, "%s %ld %ld y\n", Spool_nBuf,
1646 Spool_Head->hi, Spool_Head->lo);
1647 }
1648 spool_fake_active_out();
1649 return 1;
1650 }
1651
spool_reset(void)1652 static void spool_reset (void)
1653 {
1654 spool_fclose_local ();
1655 }
1656
spool_get_bytes(int clear)1657 static unsigned int spool_get_bytes (int clear)
1658 {
1659 unsigned int temp;
1660
1661 temp = bytes_read;
1662 if (clear)
1663 bytes_read = 0;
1664
1665 return temp;
1666 }
1667
1668 char *Slrn_Inn_Root;
1669 char *Slrn_Spool_Root;
1670 char *Slrn_Nov_Root;
1671 char *Slrn_Nov_File;
1672 char *Slrn_Headers_File;
1673 char *Slrn_Active_File;
1674 char *Slrn_ActiveTimes_File;
1675 char *Slrn_Newsgroups_File;
1676 int Slrn_Spool_Check_Up_On_Nov;
1677
spool_init_objects(void)1678 static int spool_init_objects (void)
1679 {
1680 char *login;
1681 Spool_Server_Obj.sv_select_group = spool_select_group;
1682 Spool_Server_Obj.sv_refresh_groups = spool_refresh_groups;
1683 Spool_Server_Obj.sv_current_group = spool_current_group;
1684 Spool_Server_Obj.sv_read_line = spool_read_line;
1685 Spool_Server_Obj.sv_close = spool_close_server;
1686 Spool_Server_Obj.sv_reset = spool_reset;
1687 Spool_Server_Obj.sv_initialize = spool_initialize_server;
1688 Spool_Server_Obj.sv_select_article = spool_select_article;
1689 Spool_Server_Obj.sv_get_article_size = spool_get_article_size;
1690 Spool_Server_Obj.sv_put_server_cmd = spool_put_server_cmd;
1691 Spool_Server_Obj.sv_xpat_cmd = spool_xpat_cmd;
1692 Spool_Server_Obj.sv_xhdr_command = spool_one_xhdr_command;
1693 Spool_Server_Obj.sv_has_cmd = spool_has_cmd;
1694 Spool_Server_Obj.sv_list = spool_list;
1695 Spool_Server_Obj.sv_list_newsgroups = spool_list_newsgroups;
1696 Spool_Server_Obj.sv_list_active = spool_list_active;
1697 Spool_Server_Obj.sv_send_authinfo = spool_send_authinfo;
1698
1699 Spool_Server_Obj.sv_has_xhdr = 1;
1700 Spool_Server_Obj.sv_has_xover = 0;
1701 Spool_Server_Obj.sv_nntp_xover = spool_nntp_xover;
1702 Spool_Server_Obj.sv_nntp_xhdr = spool_xhdr_command;
1703 Spool_Server_Obj.sv_nntp_head = spool_nntp_head;
1704 Spool_Server_Obj.sv_nntp_next = spool_nntp_next;
1705 Spool_Server_Obj.sv_nntp_bytes = spool_get_bytes;
1706 Spool_Server_Obj.sv_id = SERVER_ID_UNKNOWN;
1707
1708 Slrn_Inn_Root = slrn_safe_strmalloc (SLRN_SPOOL_INNROOT);
1709 Slrn_Spool_Root = slrn_safe_strmalloc (SLRN_SPOOL_ROOT);
1710 Slrn_Nov_Root = slrn_safe_strmalloc (SLRN_SPOOL_NOV_ROOT);
1711 Slrn_Nov_File = slrn_safe_strmalloc (SLRN_SPOOL_NOV_FILE);
1712 Slrn_Headers_File = slrn_safe_strmalloc (SLRN_SPOOL_HEADERS);
1713 Slrn_Active_File = slrn_safe_strmalloc (SLRN_SPOOL_ACTIVE);
1714 Slrn_ActiveTimes_File = slrn_safe_strmalloc (SLRN_SPOOL_ACTIVETIMES);
1715 Slrn_Newsgroups_File = slrn_safe_strmalloc (SLRN_SPOOL_NEWSGROUPS);
1716 Slrn_Overviewfmt_File = slrn_safe_strmalloc (SLRN_SPOOL_OVERVIEWFMT);
1717 if ((NULL == (login = Slrn_User_Info.login_name)) ||
1718 (*login == 0))
1719 login = "!unknown";
1720 Slrn_Requests_File = slrn_strdup_printf ("%s/%s", SLRNPULL_REQUESTS_DIR, login);
1721
1722 #if defined(IBMPC_SYSTEM)
1723 slrn_os2_convert_path (Slrn_Inn_Root);
1724 slrn_os2_convert_path (Slrn_Spool_Root);
1725 slrn_os2_convert_path (Slrn_Nov_Root);
1726 slrn_os2_convert_path (Slrn_Nov_File);
1727 slrn_os2_convert_path (Slrn_Headers_File);
1728 slrn_os2_convert_path (Slrn_Active_File);
1729 slrn_os2_convert_path (Slrn_ActiveTimes_File);
1730 slrn_os2_convert_path (Slrn_Newsgroups_File);
1731 slrn_os2_convert_path (Slrn_Overviewfmt_File);
1732 slrn_os2_convert_path (Slrn_Requests_File);
1733 #endif
1734 return 0;
1735 }
1736
1737 /* This function is used below. It has a very specific purpose. */
spool_root_dircat(char * file)1738 static char *spool_root_dircat (char *file)
1739 {
1740 char *f;
1741
1742 if (slrn_is_absolute_path (file))
1743 return file;
1744
1745 f = slrn_spool_dircat (Slrn_Inn_Root, file, 0);
1746 SLFREE (file);
1747 return f;
1748 }
1749
spool_select_server_object(void)1750 static int spool_select_server_object (void)
1751 {
1752 Slrn_Server_Obj = &Spool_Server_Obj;
1753 Slrn_Active_File = spool_root_dircat (Slrn_Active_File);
1754 Slrn_ActiveTimes_File = spool_root_dircat (Slrn_ActiveTimes_File);
1755 Slrn_Newsgroups_File = spool_root_dircat (Slrn_Newsgroups_File);
1756 Slrn_Overviewfmt_File = spool_root_dircat (Slrn_Overviewfmt_File);
1757 Slrn_Requests_File = spool_root_dircat (Slrn_Requests_File);
1758
1759 slrn_free (Spool_Server_Obj.sv_name);
1760 Spool_Server_Obj.sv_name = slrn_safe_strmalloc (Slrn_Spool_Root);
1761
1762 return 0;
1763 }
1764
1765 /* Handling of the additional newsrc-style files in true offline mode: */
slrn_spool_get_no_body_ranges(char * group)1766 Slrn_Range_Type *slrn_spool_get_no_body_ranges (char *group)
1767 {
1768 VFILE *vp;
1769 char *vline;
1770 unsigned int vlen;
1771 Slrn_Range_Type *retval = NULL;
1772 char *p, *q;
1773
1774 p = slrn_spool_dircat (Slrn_Spool_Root, group, 1);
1775 q = slrn_spool_dircat (p, Slrn_Headers_File, 0);
1776 SLFREE (p);
1777
1778 vp = vopen (q, 4096, 0);
1779 SLFREE (q);
1780 if (NULL == vp)
1781 return NULL;
1782
1783 if (NULL != (vline = vgets (vp, &vlen)))
1784 {
1785 if (vline[vlen-1] == '\n')
1786 vline[vlen-1] = 0; /* make sure line is NULL terminated */
1787 else
1788 vline[vlen] = 0;
1789 retval = slrn_ranges_from_newsrc_line (vline);
1790 }
1791
1792 vclose (vp);
1793
1794 return retval;
1795 }
1796
slrn_spool_get_requested_ranges(char * group)1797 Slrn_Range_Type *slrn_spool_get_requested_ranges (char *group) /*{{{*/
1798 {
1799 VFILE *vp;
1800 char *vline;
1801 unsigned int vlen;
1802 Slrn_Range_Type *retval = NULL;
1803
1804 if (NULL == (vp = vopen (Slrn_Requests_File, 4096, 0)))
1805 return NULL;
1806
1807 while (NULL != (vline = vgets (vp, &vlen)))
1808 {
1809 char *p = vline;
1810 char *pmax = p + vlen;
1811
1812 while ((p < pmax) && (*p != ':'))
1813 p++;
1814
1815 if ((p == pmax) || (p == vline) ||
1816 (strncmp(vline, group, (p-vline))))
1817 continue;
1818
1819 if (vline[vlen-1] == '\n')
1820 vline[vlen-1] = 0;
1821 else
1822 vline[vlen] = 0;
1823
1824 retval = slrn_ranges_from_newsrc_line (p+1);
1825 break;
1826 }
1827 vclose (vp);
1828 return retval;
1829 }
1830 /*}}}*/
1831
1832 /* Updates the request file with the new ranges
1833 * returns 0 on success, -1 otherwise */
slrn_spool_set_requested_ranges(char * group,Slrn_Range_Type * r)1834 int slrn_spool_set_requested_ranges (char *group, Slrn_Range_Type *r) /*{{{*/
1835 {
1836 char *old_file = NULL;
1837 FILE *fp;
1838 VFILE *vp;
1839 char *vline;
1840 unsigned int vlen;
1841 struct stat filestat;
1842 #ifdef __unix__
1843 int stat_worked = 0;
1844 #endif
1845 int have_old = 0;
1846
1847 slrn_init_hangup_signals (0);
1848
1849 #ifdef __unix__
1850 /* Try to preserve file permissions and owner/group. */
1851 stat_worked = (-1 != stat (Slrn_Requests_File, &filestat));
1852 #endif
1853
1854 /* Save old file (we'll copy most of it; then, it gets deleted) */
1855 have_old = (0 == slrn_create_backup (Slrn_Requests_File));
1856
1857 if (NULL == (fp = fopen (Slrn_Requests_File, "w")))
1858 {
1859 if (have_old) slrn_restore_backup (Slrn_Requests_File);
1860 slrn_init_hangup_signals (1);
1861 return -1;
1862 }
1863
1864 #ifdef __unix__
1865 # if !defined(IBMPC_SYSTEM)
1866 /* Try to preserve file permissions and owner/group */
1867 # ifndef S_IRUSR
1868 # define S_IRUSR 0400
1869 # define S_IWUSR 0200
1870 # define S_IRGRP 0040
1871 # endif
1872 if (stat_worked)
1873 {
1874 if (-1 == chmod (Slrn_Requests_File, filestat.st_mode))
1875 (void) chmod (Slrn_Requests_File, S_IWUSR | S_IRUSR | S_IRGRP);
1876
1877 (void) chown (Slrn_Requests_File, filestat.st_uid, filestat.st_gid);
1878 }
1879 else
1880 (void) chmod (Slrn_Requests_File, S_IWUSR | S_IRUSR | S_IRGRP);
1881 # endif
1882 #endif
1883
1884 /* Write a line for the current group */
1885 if (r != NULL)
1886 {
1887 if ((EOF == fputs (group, fp)) ||
1888 (EOF == fputs (": ",fp)) ||
1889 (-1 == slrn_ranges_to_newsrc_file (r,0,fp)) ||
1890 (EOF == fputc ('\n',fp)))
1891 goto write_error;
1892 }
1893
1894 /* Now, open the old file and append the data of all other groups */
1895 old_file = slrn_make_backup_filename (Slrn_Requests_File);
1896 if (NULL != (vp = vopen (old_file, 4096, 0)))
1897 {
1898 while (NULL != (vline = vgets (vp, &vlen)))
1899 {
1900 char *p = vline;
1901 char *pmax = p + vlen;
1902
1903 while ((p < pmax) && (*p != ':'))
1904 p++;
1905
1906 if ((p != vline) && (((p-vline != (int) strlen(group)) ||
1907 strncmp(vline, group, (p-vline)))))
1908 {
1909 /* It seems we may not write past vline[vlen-1] for vgets
1910 * to work correctly, so save and reset this. */
1911 char ch = vline[vlen];
1912 vline[vlen] = '\0';
1913 if (EOF == fputs (vline, fp))
1914 {
1915 vclose (vp);
1916 goto write_error;
1917 }
1918 vline[vlen] = ch;
1919 }
1920 }
1921 vclose (vp);
1922 }
1923
1924 if (-1 == slrn_fclose (fp))
1925 goto write_error;
1926
1927 if (have_old) slrn_delete_backup (Slrn_Requests_File);
1928
1929 slrn_init_hangup_signals (1);
1930 SLfree (old_file);
1931 return 0;
1932
1933 write_error:
1934
1935 slrn_fclose (fp);
1936 /* Put back orginal file */
1937 if (have_old) slrn_restore_backup (Slrn_Requests_File);
1938
1939 slrn_init_hangup_signals (1);
1940 SLfree (old_file);
1941 return -1;
1942 }
1943 /*}}}*/
1944