Attachment 'dbackfeed.c'

Download

   1 /*
   2  * dbackfeed.c
   3  *
   4  * 2007, Keith Bare <kbare@club.cc.cmu.edu>
   5  *
   6  * This program aids in backfeeding articles from one reader to another,
   7  * while preserving article numbering.
   8  *
   9  * Many parts of this code were taken from parts of the Diablo
  10  * distribution, notably dexpireover.c.
  11  */
  12 
  13 /*
  14  * UTIL/DEXPIREOVER.C
  15  *
  16  * (c)Copyright 1998, Matthew Dillon, All Rights Reserved.  Refer to
  17  *    the COPYRIGHT file in the base directory of this distribution
  18  *    for specific rights granted.
  19  *
  20  *	LOCKING INFO
  21  *
  22  *	The data file will have an advisory lock at offset 4 from dreaderd
  23  *	when it has the file open (can't rewrite data)
  24  *	The info file will have an advisory lock at offset 4 from dreaderd
  25  *	when it has the file open (can't resize)
  26  *
  27  *	od_HFd = data. file
  28  *	ov_OFd = over. file
  29  *
  30  */
  31 
  32 
  33 #include <assert.h>
  34 #include <dreaderd/defs.h>
  35 
  36 
  37 /*
  38  * Types and constants
  39  */
  40 
  41 typedef struct Group {
  42     struct Group *gr_Next;
  43     int		gr_State;
  44     int		gr_StartNo;
  45     int		gr_EndNo;
  46     int		gr_CTS;
  47     int		gr_LMTS;
  48     int		gr_Iter;
  49     char	*gr_GroupName;
  50     char	*gr_Flags;
  51     char	*gr_Hash;
  52     int		gr_UpdateFlag;
  53 } Group;
  54 
  55 #define GRF_DESCRIPTION 0x00000001
  56 #define GRF_STARTNO     0x00000002
  57 #define GRF_ENDNO       0x00000004
  58 #define GRF_FLAGS       0x00000008
  59 #define GRF_FROMLOCAL   0x00000800
  60 #define GRF_NEW         0x00001000
  61 #define GRF_FROMREMOTE  0x00002000
  62 #define GRF_MODIFIED    0x00008000
  63 #define GRF_EDITEDBEG   0x00010000
  64 
  65 #define GHSIZE		1024
  66 #define GHMASK		(GHSIZE-1)
  67 
  68 #define IO_BUFSIZE      2048
  69 
  70 
  71 /*
  72  * Globals
  73  */
  74 
  75 KPDB  *KDBActive;
  76 Group *GHash[GHSIZE];
  77 
  78 int VerboseOpt = -1;
  79 int ForReal = 1;
  80 int MustExit = 0;
  81 char *Wild;
  82 char *OutgoingPath = "/tmp/dbackfeed-outgoing";
  83 char *QueuePath = "/tmp/dbackfeed-queue";
  84 size_t OutgoingTargetSize = 2UL * (2UL << 20); /* 2 MB */
  85 char *HostName = NULL;
  86 int HostPort = 119;
  87 
  88 FILE *outgoingStream = NULL;
  89 FILE *queueStream = NULL;
  90 size_t outgoingBytes;
  91 
  92 char DnewslinkCommand[1024];
  93 
  94 
  95 /*
  96  * Prototypes
  97  */
  98 
  99 void Usage(char *progname);
 100 void ScanDirectories(void);
 101 void scanDirectory(const char *dirpath, char *dirname, int *level);
 102 void ProcessOverviewFile(const char *dirPath, const char *name);
 103 void ProcessArticle(OverArt *oa, Group *group, const char *dirPath, int iter);
 104 void ReadArticle(const char *msgid, char *headBase, int headLen, char *artPath, History *h);
 105 void WriteArticleAndQueueFileEntry(const char *msgid, SpoolArtHdr *spoolArtHdr, char *headBase, int headLen, FILE *artStream);
 106 void RunDnewslink(void);
 107 char *allocTmpCopy(const char *buf, int bufLen);
 108 int SetField(char **pptr, const char *str);
 109 Group *EnterGroup(const char *groupName, int begNo, int endNo, int lmts, int cts, int iter, const char *flags);
 110 Group *FindGroupByHash(char *Hash, int iter);
 111 void ExtractMessageId(const char *scan, int scanLen, const char **pmsgid);
 112 
 113 
 114 /*
 115  * Function definitions
 116  */
 117 
 118 int
 119 main(int ac, char **av)
 120 {
 121     int i;
 122     char *dbfile = NULL;
 123 
 124     LoadDiabloConfig(ac, av);
 125 
 126     for (i = 1; i < ac; ++i) {
 127 	char *ptr = av[i];
 128 	if (*ptr != '-') {
 129 	    fprintf(stderr, "Unexpected argument: %s\n", ptr);
 130 	    Usage(av[0]);
 131 	}
 132 	ptr += 2;
 133 	switch(ptr[-1]) {
 134 	case 'f':
 135 	    dbfile = (*ptr) ? ptr : av[++i];
 136 	    break;
 137         case 'h':
 138             HostName = (*ptr) ? ptr : av[++i];
 139             break;
 140 	case 'n':
 141 	    ForReal = 0;
 142 	    if (VerboseOpt < 0)
 143 		VerboseOpt = 1;
 144 	    break;
 145         case 'o':
 146             OutgoingPath = (*ptr) ? ptr : av[++i];
 147             break;
 148         case 'p':
 149             if (*ptr) {
 150                 errno = 0;
 151                 HostPort = (int)strtol(ptr, NULL, 0);
 152                 if (errno != 0) {
 153                     fprintf(stderr, "invalid number format for -p option\n");
 154                     Usage(av[0]);
 155                     exit(1);
 156                 }
 157             } else {
 158                 fprintf(stderr, "the -p option requires an argument\n");
 159                 Usage(av[0]);
 160                 exit(1);
 161             }
 162             break;
 163         case 'q':
 164             QueuePath = (*ptr) ? ptr : av[++i];
 165             break;
 166         case 't':
 167             if (*ptr) {
 168                 errno = 0;
 169                 OutgoingTargetSize = (size_t)strtoul(ptr, NULL, 0);
 170                 if (errno != 0) {
 171                     fprintf(stderr, "invalid number format for -t option\n");
 172                     Usage(av[0]);
 173                     exit(1);
 174                 }
 175             } else {
 176                 fprintf(stderr, "the -t option requires an argument\n");
 177                 Usage(av[0]);
 178                 exit(1);
 179             }
 180             break;
 181 	case 'v':
 182 	    VerboseOpt = (*ptr) ? strtol(ptr, NULL, 0) : 1;
 183 	    break;
 184 	case 'w':
 185 	    Wild = (*ptr) ? ptr : av[++i];
 186 	    break;
 187 	/* Common options */
 188 	case 'C':           /* parsed by LoadDiabloConfig */
 189 	    if (*ptr == 0)
 190 		++i;
 191 	    break;
 192 	case 'd':
 193 	    DebugOpt = (*ptr) ? strtol(ptr, NULL, 0) : 1;
 194 	    break;
 195 	case 'V':
 196 	    PrintVersion();
 197 	    break;
 198 	default:
 199 	    fprintf(stderr, "unknown option: %s\n", ptr - 2);
 200 	    Usage(av[0]);
 201 	}
 202     }
 203 
 204     if (!HostName) {
 205         fprintf(stderr, "the -h option is required\n");
 206         Usage(av[0]);
 207         exit(1);
 208     }
 209 
 210     /*
 211      * Construct the dnewlink invocation
 212      */
 213     if (snprintf(DnewslinkCommand, sizeof(DnewslinkCommand),
 214                  "dnewslink -b %s -h %s -P %d",
 215                  QueuePath, HostName, HostPort) >=
 216         sizeof(DnewslinkCommand))
 217     {
 218         printf("dnewslink command is too long\n");
 219         exit(1);
 220     }
 221 
 222     /*
 223      * Open active file database
 224      */
 225 
 226     if (VerboseOpt)
 227 	printf("Loading active file\n");
 228     if (dbfile) {
 229 	KDBActive = KPDBOpen(dbfile, O_RDONLY);
 230     } else {
 231 	KDBActive = KPDBOpen(PatDbExpand(ReaderDActivePat), O_RDONLY);
 232     }
 233     if (KDBActive == NULL) {
 234 	fprintf(stderr, "Unable to open dactive.kp\n");
 235 	exit(1);
 236     }
 237 
 238     /*
 239      * scan dactive.kp
 240      */
 241 
 242     if (VerboseOpt)
 243 	printf("Hashing newsgroups\n");
 244     {
 245 	int recLen;
 246 	int recOff;
 247 	int cts0 = (int)time(NULL);
 248 
 249 	for (recOff = KPDBScanFirst(KDBActive, 0, &recLen);
 250 	     recOff;
 251 	     recOff = KPDBScanNext(KDBActive, recOff, 0, &recLen)
 252 	) {
 253 	    int groupLen;
 254 	    int flagsLen;
 255 	    const char *rec = KPDBReadRecordAt(KDBActive, recOff, 0, NULL);
 256 	    const char *group = KPDBGetField(rec, recLen, NULL, &groupLen, NULL);
 257 	    const char *flags = KPDBGetField(rec, recLen, "S", &flagsLen, "y");
 258 	    int begNo = strtol(KPDBGetField(rec, recLen, "NB", NULL, "-1"), NULL, 10);
 259 	    int endNo = strtol(KPDBGetField(rec, recLen, "NE", NULL, "-1"), NULL, 10);
 260 	    int lmts = (int)strtoul(KPDBGetField(rec, recLen, "LMTS", NULL, "0"), NULL, 16);
 261 	    int cts = (int)strtoul(KPDBGetField(rec, recLen, "CTS", NULL, "0"), NULL, 16);
 262 	    int iter = (int)strtoul(KPDBGetField(rec, recLen, "ITER", NULL, "0"), NULL, 16);
 263 	    Group *grp;
 264 
 265 	    if (cts == 0)	/* enter non-zero cts only if group has no CTS field */
 266 		cts = cts0;
 267 	    else
 268 		cts = 0;
 269 
 270 	    if (group)
 271 		group = allocTmpCopy(group, groupLen);
 272 	    if (flags)
 273 		flags = allocTmpCopy(flags, flagsLen);
 274 
 275 	    /*
 276 	     * ignore bad group or group that does not match the wildcard
 277 	     */
 278 
 279 	    if (group == NULL)
 280 		continue;
 281 	    if (Wild && WildCmp(Wild, group) != 0)
 282 		continue;
 283 
 284 	    grp = EnterGroup(
 285 		group,
 286 		begNo,
 287 		endNo,
 288 		lmts,
 289 		cts,
 290 		iter,
 291 		flags
 292 	    );
 293 	    grp->gr_State &= ~(GRF_NEW|GRF_MODIFIED);
 294 	}
 295     }
 296 
 297     /*
 298      * Open the history
 299      */
 300 
 301     if (HistoryOpen(NULL, HGF_READONLY) < 0) {
 302         printf("failed to open history\n");
 303         return(-1);
 304     }
 305 
 306     /*
 307      * Process the spool control file
 308      */
 309 
 310     LoadSpoolCtl(0, 1);
 311 
 312     /*
 313      * Do work
 314      */
 315 
 316     ScanDirectories();
 317 
 318     if (outgoingStream != NULL) {
 319         fclose(outgoingStream);
 320         fclose(queueStream);
 321 
 322         RunDnewslink();
 323     }
 324 
 325     /*
 326      * Close the active file
 327      */
 328 
 329     if (KDBActive)
 330         KPDBClose(KDBActive);
 331 
 332     return(0);
 333 }
 334 
 335 void
 336 Usage(char *progname)
 337 {
 338     fprintf(stderr, "Backfeed articles in the reader header database\n");
 339     fprintf(stderr, "dbackfeed [-f active] -h host [-n] [-o outgoingfile] [-p#] [-q queuefile] [-t#] [-v#] [-w wildmat] [-C diablo.config] [-d[n]] [-V]\n");
 340     fprintf(stderr, "\t-f file\t\tSpecify the name of the active file\n");
 341     fprintf(stderr, "\t-h host\t\tName of host to backfeed to\n");
 342     fprintf(stderr, "\t-n\t\tDon't actually make any changes (dry run)\n");
 343     fprintf(stderr, "\t-o file\t\tSpecify location to store outgoing articles\n");
 344     fprintf(stderr, "\t-p#\t\tPort number to backfeed to\n");
 345     fprintf(stderr, "\t-q file\t\tSpecify location to store outgoing queue file\n");
 346     fprintf(stderr, "\t-t#\t\tAccumulate at least this many bytes before sending\n");
 347     fprintf(stderr, "\t-v[#]\t\tVerbose mode\n");
 348     fprintf(stderr, "\t-w wildmat\tSpecify a wildmat of groups to expire\n");
 349     fprintf(stderr, "\t-C file\tspecify diablo.config to use\n");
 350     fprintf(stderr, "\t-d[n]\tset debug [with optional level]\n");
 351     fprintf(stderr, "\t-V\tprint version and exit\n");
 352     exit(1);
 353 }
 354 
 355 /*
 356  * Scan /news/spool/group/ and
 357  *
 358  * This works by scanning all files in the directories, mapping the
 359  * filename (hash) to a newsgroup name and performing the following,
 360  * depending on file type:
 361  *
 362  * over.* :
 363  * *.o.* :
 364  *
 365  * data.*:
 366  * *.d.*:
 367  *
 368  * If a newsgroup for a file cannot be found, the file is ignored
 369  */
 370 
 371 void
 372 ScanDirectories(void)
 373 {
 374     DIR *dir;
 375     int level = 0;
 376 
 377     if (VerboseOpt)
 378 	printf("Scanning group directories\n");
 379 
 380     chdir(PatExpand(GroupHomePat));
 381     if ((dir = opendir(".")) != NULL) {
 382 	den_t *den;
 383 	struct stat st;
 384 
 385 	while ((den = readdir(dir)) != NULL) {
 386 	    if (isalnum((int)den->d_name[0]) &&
 387 	    	stat(den->d_name, &st) == 0 && S_ISDIR(st.st_mode)
 388 		/*
 389 		 * We explicitly use the first char, because overview
 390 		 * sizes appear to be not evenly distributed wrt second
 391 		 * char.
 392 		 */
 393 	    )
 394 		scanDirectory(PatExpand(GroupHomePat), den->d_name, &level);
 395 	}
 396 	closedir(dir);
 397     }
 398 }
 399 
 400 void
 401 scanDirectory(const char *dirpath, char *dirname, int *level)
 402 {
 403     DIR *dir2;
 404     char path[PATH_MAX];
 405     char origpath[PATH_MAX];
 406 
 407     sprintf(path, "%s/%s", dirpath, dirname);
 408 
 409     if (getcwd(origpath, sizeof(origpath)) == NULL) {
 410 	printf("Unable to getcwd(%s): %s\n", dirname, strerror(errno));
 411 	return;
 412     }
 413     if (chdir(dirname) != 0) {
 414 	printf("Unable to chdir(%s)\n", dirname);
 415 	return;
 416     }
 417 
 418     if ((dir2 = opendir(".")) != NULL) {
 419 	den_t *den2;
 420 	struct stat st;
 421 
 422 	while ((den2 = readdir(dir2)) != NULL) {
 423 	    if (strcmp(den2->d_name, ".") == 0 || strcmp(den2->d_name, "..") == 0)
 424 		continue;
 425 	    if (stat(den2->d_name, &st) == 0 && S_ISDIR(st.st_mode)) {
 426 		if (*level <= 2)
 427 		    scanDirectory(path, den2->d_name, level);
 428 	    } else if (strncmp(den2->d_name, "over.", 5) == 0 ||
 429 		strncmp(den2->d_name, "o.", 2) == 0 ||
 430 		strstr(den2->d_name, ".o.") != NULL)
 431 		ProcessOverviewFile(path, den2->d_name);
 432 	    if (MustExit)
 433 		exit(1);
 434 	}
 435     }
 436     if (chdir(origpath) != 0) {
 437 	printf("FATAL: Unable to return with chdir(%s)\n", dirname);
 438 	exit(1);
 439     }
 440 
 441     closedir(dir2);
 442 }
 443 
 444 /*
 445  * ProcessOverviewFile() - process over. and data. files.  All over. files
 446  *			   are processed first. 
 447  *
 448  *	When processing over. files, we may resize the index array (-s)
 449  *	and/or cleanup the file (-R).
 450  *
 451  *	When processing data. files, we typically remove whole files. 
 452  *	If the -R option was used, however, we rewrite the files.  We can
 453  *	safely copy/rename-over data. files as long as we are able to
 454  *	lock the associated over. file.
 455  */
 456 
 457 void
 458 ProcessOverviewFile(const char *dirPath, const char *name)
 459 {
 460     long artBase = -1;
 461     Group *group;
 462     char path[PATH_MAX];
 463     char Hash[PATH_MAX];
 464     int iter = 0;
 465     int oaFd = -1;
 466     OverHead oh;
 467     struct stat st;
 468     int r;
 469     OverArt *oaBase = NULL;
 470     size_t oaSize;
 471     int i;
 472 
 473     snprintf(path, sizeof(path), "%s/%s", dirPath, name);
 474 
 475     if (DebugOpt > 0)
 476 	printf("ProcessOverviewFile(%s)\n", path);
 477 
 478     bzero(Hash, sizeof(Hash));
 479 
 480     if (ExtractGroupHashInfo(name, Hash, &artBase, &iter) == HASHGRP_NONE)
 481 	return;
 482 
 483     if (DebugOpt > 2)
 484 	printf("File: %s  artBase=%ld  iter=%d  Hash=%s\n",
 485 						name, artBase, iter, Hash);
 486 
 487     if ((group = FindGroupByHash(Hash, iter)) == NULL) {
 488         if (!Wild) {
 489             printf("Group for over file not found (%s)\n", path);
 490         }
 491 	return;
 492     }
 493 
 494     if (DebugOpt > 2)
 495 	printf("Maps to group: %s\n", group->gr_GroupName);
 496 
 497     /*
 498      * over. file	(fixed length file)
 499      */
 500 
 501 again:
 502 
 503     oaFd = open(path, O_RDONLY);
 504     if (oaFd < 0) {
 505         return;
 506     }
 507 
 508     /*
 509      * Leave a shared lock on the over.* file so expireover knows when
 510      * it is OK to resize the file.  If the file was renamed-over,
 511      * we have to re-open it.
 512      */
 513 
 514     hflock(oaFd, 4, XLOCK_SH);
 515 
 516     if (fstat(oaFd, &st) < 0 || st.st_nlink == 0) {
 517         hflock(oaFd, 4, XLOCK_UN);
 518         close(oaFd);
 519         oaFd = -1;
 520         goto again;
 521     }
 522 
 523     r = (read(oaFd, &oh, sizeof(oh)) == sizeof(oh));
 524     if (oh.oh_Version > OH_VERSION ||
 525         oh.oh_ByteOrder != OH_BYTEORDER)
 526         r = 0;
 527     if (r && oh.oh_Version > 1 &&
 528         strcmp(oh.oh_Gname, group->gr_GroupName) != 0)
 529         r = 0;
 530     if (r) {
 531         int n;
 532 
 533         oaSize = st.st_size - oh.oh_HeadSize;
 534         n = oaSize / sizeof(OverArt);
 535 
 536         if (lseek(oaFd, 0, SEEK_SET) == (off_t)-1) {
 537             printf("error rewinding overview file \"%s\" fd = %d (%s)\n",
 538                    path, oaFd, strerror(errno));
 539             goto out;
 540         }
 541 
 542         oaBase = xmap(NULL, oaSize, PROT_READ, MAP_SHARED, oaFd, oh.oh_HeadSize);
 543         if (oaBase == NULL) {
 544             printf("error on overview mmap for group %s, file \"%s\" fd = %d (%s)\n",
 545                    group->gr_GroupName, path, oaFd, strerror(errno));
 546             goto out;
 547         }
 548 
 549         for (i = 0; i < n; i++) {
 550             if (oaBase[i].oa_ArtNo > 0) {
 551                 ProcessArticle(&oaBase[i], group, dirPath, iter);
 552             }
 553         }
 554 
 555     } else {
 556         printf("group %s, file \"%s\" bad file header\n",
 557                group->gr_GroupName,
 558                path
 559             );
 560         if (oh.oh_Version > OH_VERSION)
 561             printf("   expected version %d, got version %d\n",
 562                    OH_VERSION, oh.oh_Version);
 563     }
 564 
 565 out:
 566 
 567     if (oaBase) {
 568         xunmap(oaBase, oaSize);
 569     }
 570     if (oaFd > 0) {
 571         hflock(oaFd, 4, XLOCK_UN);
 572         close(oaFd);
 573     }
 574 
 575 }
 576 
 577 void
 578 ProcessArticle(OverArt *oa, Group *group, const char *dirPath, int iter)
 579 {
 580     int artBase;
 581     const char *dfName;
 582     char dfPath[PATH_MAX];
 583     int ohFd = -1;
 584     struct stat st;
 585     char *ohBase = NULL;
 586     const char *msgid;
 587     History h;
 588     char artPath[PATH_MAX];
 589 
 590     artBase = oa->oa_ArtNo & ~OD_HMASK;
 591 
 592     /* Construct the path to the data file */
 593 
 594     dfName = GFName(group->gr_GroupName, GRPFTYPE_DATA, artBase, 0, iter,
 595                     &DOpts.ReaderGroupHashMethod);
 596 
 597     if (snprintf(dfPath, sizeof(dfPath), "%s/%s", dirPath, dfName) >=
 598         sizeof(dfPath))
 599     {
 600         printf("error getting data file name, name too long\n");
 601         goto out;
 602     }
 603 
 604     ohFd = open(dfPath, O_RDONLY);
 605     if (ohFd < 0) {
 606         printf("error opening data file for group %s, file \"%s\" (%s)\n",
 607                group->gr_GroupName, dfPath, strerror(errno));
 608         goto out;
 609     }
 610 
 611     if (fstat(ohFd, &st) < 0) {
 612         goto out;
 613     }
 614 
 615     if (lseek(ohFd, 0, SEEK_SET) == (off_t)-1) {
 616         printf("error rewinding overview file \"%s\" fd = %d (%s)\n",
 617                dfPath, ohFd, strerror(errno));
 618         goto out;
 619     }
 620 
 621     ohBase = xmap(NULL, st.st_size, PROT_READ, MAP_SHARED, ohFd, 0);
 622     if (!ohBase) {
 623         printf("error on data mmap for group %s, file \"%s\" fd = %d (%s)\n",
 624                group->gr_GroupName, dfPath, ohFd, strerror(errno));
 625         goto out;
 626     }
 627 
 628     ExtractMessageId(ohBase + oa->oa_SeekPos, oa->oa_Bytes, &msgid);
 629 
 630     if (!strncmp(msgid, "<>", 2)) {
 631         printf("failed to extract message id, group \"%s\", artNum %d\n",
 632                group->gr_GroupName, oa->oa_ArtNo);
 633         goto out;
 634     }
 635 
 636     if (HistoryLookup(msgid, &h) < 0) {
 637         printf("message \"%s\" is in the overview, but not in "
 638                "the history\n", msgid);
 639         goto out;
 640     }
 641 
 642     ArticleFileName(artPath, sizeof(artPath), &h, ARTFILE_FILE);
 643     if (!strncmp(artPath, "/dev/null", 9)) {
 644         printf("message \"%s\" is in the history, but not in "
 645                "the spool\n", msgid);
 646         goto out;
 647     }
 648 
 649     if (ForReal) {
 650         ReadArticle(msgid, ohBase + oa->oa_SeekPos, oa->oa_Bytes, artPath, &h);
 651     }
 652 
 653 out:
 654 
 655     if (ohBase) {
 656         xunmap(ohBase, st.st_size);
 657     }
 658     if (ohFd > 0) {
 659         close(ohFd);
 660     }
 661 }
 662 
 663 void
 664 ReadArticle(const char *msgid, char *headBase, int headLen,
 665             char *artPath, History *h)
 666 {
 667     FILE *artStream;
 668     SpoolArtHdr spoolArtHdr;
 669 
 670     if (VerboseOpt) {
 671         printf("processing \"%s\" offset %d\n", artPath, h->boffset);
 672     }
 673 
 674     artStream = fopen(artPath, "rb");
 675     if (!artStream) {
 676         printf("couldn't open article file \"%s\"\n", artPath);
 677         goto out;
 678     }
 679 
 680     if (fseek(artStream, h->boffset, SEEK_SET) < 0) {
 681         printf("couldn't seek article file \"%s\" to %d\n",
 682                artPath, h->boffset);
 683         goto out;
 684     }
 685 
 686     if (fread(&spoolArtHdr, sizeof(SpoolArtHdr), 1, artStream) < 1) {
 687         printf("io error reading article header from \"%s\" offset %d\n",
 688                artPath, h->boffset);
 689         goto out;
 690     }
 691 
 692     if (spoolArtHdr.Magic1 != STORE_MAGIC1 ||
 693         spoolArtHdr.Magic2 != STORE_MAGIC2)
 694     {
 695         printf("bad magic in \"%s\" offset %d\n", artPath, h->boffset);
 696         goto out;
 697     }
 698 
 699     if (spoolArtHdr.Version != STOREAPI_REVISION) {
 700         printf("wrong version in \"%s\" offset %d\n", artPath, h->boffset);
 701         goto out;
 702     }
 703 
 704     if (fseek(artStream, spoolArtHdr.ArtHdrLen, SEEK_CUR) < 0)
 705     {
 706         printf("couldn't seek article file\"%s\" past SpoolArtHdr "
 707                "at offset %d\n", artPath, h->boffset);
 708         goto out;
 709     }
 710 
 711     WriteArticleAndQueueFileEntry(msgid, &spoolArtHdr, headBase,
 712                                   headLen, artStream);
 713 
 714 out:
 715 
 716     if (artStream) {
 717         fclose(artStream);
 718     }
 719 }
 720 
 721 void
 722 WriteArticleAndQueueFileEntry(const char *msgid, SpoolArtHdr *spoolArtHdr,
 723                               char *headBase, int headLen, FILE *artStream)
 724 {
 725     long artOffset;
 726     SpoolArtHdr outgoingArtHdr;
 727     size_t bytesWritten;
 728     char buf[IO_BUFSIZE];
 729     size_t bytesToRead;
 730     size_t bytesRead;
 731 
 732     char *headbuf = NULL;
 733     size_t headbufLen = 0;
 734     int i;
 735 
 736     headbuf = alloca(headLen);
 737 
 738     for (i = 0; i < headLen - 1; i++) {
 739         if (headBase[i] == '\r' && headBase[i + 1] == '\n') {
 740             headbuf[headbufLen++] = '\n';
 741             i++;
 742         } else {
 743             headbuf[headbufLen++] = headBase[i];
 744         }
 745     }
 746 
 747     if (i < headLen) {
 748         headbuf[headbufLen++] = headBase[i];
 749     }
 750 
 751     if (!outgoingStream) {
 752         assert(queueStream == NULL);
 753 
 754         outgoingStream = fopen(OutgoingPath, "wb");
 755         if (!outgoingStream) {
 756             printf("couldn't open output spool file \"%s\"\n",
 757                    OutgoingPath);
 758             exit(1);
 759         }
 760         queueStream = fopen(QueuePath, "w");
 761         if (!queueStream) {
 762             printf("couldn't open output queue file \"%s\"\n",
 763                    QueuePath);
 764             exit(1);
 765         }
 766         outgoingBytes = 0;
 767     }
 768 
 769     artOffset = ftell(outgoingStream);
 770     if (artOffset == -1) {
 771         printf("couldn't find current position in outgoingStream\n");
 772         return;
 773     }
 774 
 775     memcpy(&outgoingArtHdr, spoolArtHdr, sizeof(SpoolArtHdr));
 776     outgoingArtHdr.HeadLen = sizeof(SpoolArtHdr);
 777     outgoingArtHdr.ArtHdrLen = headbufLen;
 778     outgoingArtHdr.ArtLen += headbufLen - spoolArtHdr->ArtHdrLen;
 779     outgoingArtHdr.StoreLen = sizeof(SpoolArtHdr) + outgoingArtHdr.ArtLen + 1;
 780 
 781     bytesWritten = fwrite(&outgoingArtHdr, 1, sizeof(SpoolArtHdr),
 782                           outgoingStream);
 783     if (bytesWritten < sizeof(SpoolArtHdr)) {
 784         printf("io error writing SpoolArtHdr to output spool file \"%s\"\n",
 785                OutgoingPath);
 786         exit(1);
 787     }
 788     outgoingBytes += bytesWritten;
 789 
 790     {
 791 
 792         bytesWritten = fwrite(headbuf, 1, headbufLen, outgoingStream);
 793         if (bytesWritten < headbufLen) {
 794             printf("io error writing headers to output spool file \"%s\"\n",
 795                    OutgoingPath);
 796             exit(1);
 797         }
 798         outgoingBytes += bytesWritten;
 799     }
 800 
 801     bytesToRead = spoolArtHdr->ArtLen - spoolArtHdr->ArtHdrLen;
 802     while (bytesToRead > 0) {
 803         bytesRead = fread(buf, 1, bytesToRead > sizeof(buf) ?
 804                           sizeof(buf) : bytesToRead, artStream);
 805         if (bytesRead == 0) {
 806             printf("io error reading input spool file\n");
 807             exit(1);
 808         }
 809         bytesWritten = fwrite(buf, 1, bytesRead, outgoingStream);
 810         if (bytesWritten < bytesRead) {
 811             printf("io error writing article to output spool file \"%s\"\n",
 812                    OutgoingPath);
 813             exit(1);
 814         }
 815         outgoingBytes += bytesWritten;
 816         bytesToRead -= bytesRead;
 817     }
 818 
 819     if (fputc('\0', outgoingStream) == EOF) {
 820         printf("io error writing nul to output spool file \"%s\"\n",
 821                OutgoingPath);
 822         exit(1);
 823     }
 824     outgoingBytes += 1;
 825 
 826     if (fprintf(queueStream, "%s %s %ld,%u\n", OutgoingPath, msgid,
 827                 artOffset, outgoingArtHdr.StoreLen - 1) == 0)
 828     {
 829         printf("io error writing entry to queue file \"%s\"\n",
 830                QueuePath);
 831         exit(1);
 832     }
 833 
 834     if (outgoingBytes > OutgoingTargetSize) {
 835         /* temporarily just close files, and wait for user to hit enter. */
 836         fclose(outgoingStream);
 837         outgoingStream = NULL;
 838         fclose(queueStream);
 839         queueStream = NULL;
 840 
 841         RunDnewslink();
 842     }
 843 }
 844 
 845 void
 846 RunDnewslink(void)
 847 {
 848     int val;
 849 
 850     if (VerboseOpt) {
 851         printf("running dnewslink\n");
 852     }
 853     val = system(DnewslinkCommand);
 854     if (!WIFEXITED(val) || WEXITSTATUS(val) != 0) {
 855         printf("dnewslink failed\n");
 856         exit(1);
 857     }
 858 }
 859 
 860 
 861 int
 862 SetField(char **pptr, const char *str)
 863 {
 864     if (*pptr && strcmp(*pptr, str) == 0)
 865 	return(0);
 866     if (*pptr)
 867 	free(*pptr);
 868     *pptr = strcpy(malloc(strlen(str) + 1), str);
 869     return(1);
 870 }
 871 
 872 char *
 873 allocTmpCopy(const char *buf, int bufLen)
 874 {
 875     static char *SaveAry[8];
 876     static int SaveCnt;
 877     char **pptr;
 878 
 879     SaveCnt = (SaveCnt + 1) % arysize(SaveAry);
 880     pptr = &SaveAry[SaveCnt];
 881     if (*pptr)
 882 	free(*pptr);
 883     *pptr = malloc(bufLen + 1);
 884     memcpy(*pptr, buf, bufLen);
 885     (*pptr)[bufLen] = 0;
 886     return(*pptr);
 887 }
 888 
 889 Group *
 890 EnterGroup(const char *groupName, int begNo, int endNo, int lmts, int cts, int iter, const char *flags)
 891 {
 892     hash_t hv = hhash(GFHash(groupName, &DOpts.ReaderGroupHashMethod));
 893     Group **pgroup = &GHash[hv.h1 & GHMASK];
 894     Group *group;
 895 
 896     if (DebugOpt > 0)
 897 	printf("EnterGroup(%s)\n", groupName);
 898 
 899     if (DebugOpt > 1)
 900         printf("Group: %s  Hash: %08x.%08x\n", groupName, hv.h1, hv.h2);
 901 
 902     while ((group = *pgroup) != NULL) {
 903 	if (strcmp(groupName, group->gr_GroupName) == 0)
 904 	    break;
 905 	pgroup = &group->gr_Next;
 906     }
 907     if (group == NULL) {
 908 	*pgroup = group = calloc(sizeof(Group) + strlen(groupName) + 1, 1);
 909 	group->gr_State = GRF_NEW;
 910 	group->gr_GroupName = (char *)(group + 1);
 911 	group->gr_Hash = strdup(GFHash(groupName, &DOpts.ReaderGroupHashMethod));
 912 	group->gr_Iter = iter;
 913 	strcpy(group->gr_GroupName, groupName);
 914     }
 915 
 916     /*
 917      * update fields
 918      */
 919     if (begNo >= 0) {
 920 	group->gr_State |= GRF_STARTNO;
 921 	if (group->gr_StartNo != begNo) {
 922 	    group->gr_State |= GRF_MODIFIED;
 923 	    group->gr_StartNo = begNo;
 924 	}
 925     }
 926     if (endNo >= 0) {
 927 	group->gr_State |= GRF_ENDNO;
 928 	if (endNo < group->gr_EndNo) {
 929 	    printf("*** Would be adjusting NE down - not doing it ***\n");
 930 	    printf("adjust %s NE from %d to %d\n", group->gr_GroupName,
 931 				endNo, group->gr_EndNo);
 932 	} else if (group->gr_EndNo != endNo) {
 933 	    group->gr_EndNo = endNo;
 934 	    group->gr_State |= GRF_MODIFIED;
 935 	}
 936     }
 937     group->gr_LMTS = lmts;
 938 
 939     if (cts) {
 940 	group->gr_CTS = cts;
 941     }
 942 
 943     if (flags) {
 944 	group->gr_State |= GRF_FLAGS;
 945 	if (SetField(&group->gr_Flags, flags))
 946 	    group->gr_State |= GRF_MODIFIED;
 947     }
 948     return(group);
 949 }
 950 
 951 Group *
 952 FindGroupByHash(char *Hash, int iter)
 953 {
 954     Group *group;
 955     hash_t hv = hhash(Hash);
 956 
 957     for (group = GHash[hv.h1 & GHMASK]; group; group = group->gr_Next) {
 958 	if (strcmp(group->gr_Hash, Hash) == 0 && group->gr_Iter == iter)
 959 	    break;
 960     }
 961     return(group);
 962 }
 963 
 964 /*
 965  * ExtractMessageId()
 966  *
 967  * This code was taken from dreaderd/group.c.
 968  */
 969 
 970 void
 971 ExtractMessageId(const char *scan, int scanLen, const char **pmsgid)
 972 {
 973     while (scanLen > 0) {
 974         int i;
 975         char ch = tolower(scan[0]);
 976         for (i = 0; i < scanLen && scan[i] != '\n'; ++i)
 977             ;
 978         if (pmsgid != NULL && ch == 'm' &&
 979             strncasecmp(scan, "Message-ID:", 11) == 0) {
 980             int b = 11;
 981             int e;
 982             char buf[MAXMSGIDLEN];
 983 
 984             while (b < scanLen && (scan[b] == ' ' || scan[b] == '\t'))
 985                 ++b;
 986             e = b;
 987             while (e < scanLen && (scan[e] != '>'))
 988                 ++e;
 989             if (e < scanLen)
 990                 ++e;
 991             if (e - b < MAXMSGIDLEN) {
 992                 bcopy(scan + b, buf, e - b);
 993                 buf[e-b] = 0;
 994                 *pmsgid = MsgId(buf, NULL);
 995             }
 996             /*
 997              * Stop scanning through headers
 998              */
 999             break;
1000         }
1001 
1002         if (i < scanLen)
1003             ++i;
1004         scanLen -= i;
1005         scan += i;
1006     }
1007 }

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.