mixmaster

mixmaster 3.0 patched for libressl
git clone git://parazyd.org/mixmaster.git
Log | Files | Refs | README

rem.c (18892B)


      1 /* Mixmaster version 3.0  --  (C) 1999 - 2006 Anonymizer Inc. and others.
      2 
      3    Mixmaster may be redistributed and modified under certain conditions.
      4    This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
      5    ANY KIND, either express or implied. See the file COPYRIGHT for
      6    details.
      7 
      8    Process remailer messages
      9    $Id: rem.c 934 2006-06-24 13:40:39Z rabbi $ */
     10 
     11 
     12 #include "mix3.h"
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <time.h>
     16 #include <ctype.h>
     17 #include <sys/types.h>
     18 #include <sys/stat.h>
     19 #ifdef POSIX
     20 #include <unistd.h>
     21 #else /* end of POSIX */
     22 #include <io.h>
     23 #endif /* else if not POSIX */
     24 #ifndef _MSC
     25 #include <dirent.h>
     26 #endif /* not _MSC */
     27 #include <assert.h>
     28 
     29 int blockrequest(BUFFER *message);
     30 int create_dummy_mailin();
     31 
     32 #define REQUESTHELP  100
     33 #define REQUESTSTATS 101
     34 #define REQUESTKEY   200
     35 #define REQUESTCONF  201
     36 #define REQUESTOPKEY 202
     37 #define REQUESTOTHER 203
     38 #define BLOCKREQUEST 666
     39 #define DISABLED      99
     40 
     41 #define CPUNKMSG 1
     42 #define MIXMSG 2
     43 
     44 
     45 /** \brief get replies for additional information requests
     46  *
     47  * \param reply The buffer to store the reply in
     48  * \param file	The file or name of information a user requested
     49  * \returns 0 on success; -1 if a ressource has a valid name
     50  *     but doesn't exist; 1 if the ressource name isn't valid.
     51  * 
     52  * This function returns additional information that a
     53  * user may have requested.  One obvious example is help files
     54  * in different languages.  We don't want to hack the source every
     55  * time we or somebody else adds a new language.
     56  *
     57  * Therefore we add a new directory where the operator may
     58  * just create new files (say "remailer-help-de"). If a user
     59  * requests that using this (file|ressource) name in the
     60  * subject line we respond by sending it.
     61  *
     62  * Perhaps we should build something that returns an index of
     63  * available files (FIXME if done).
     64  *
     65  * A ressource name needs to start with the string "remailer-"
     66  * and must only consist of alphanumerical characters and dashes.
     67  * Checking is done by this function and an error returned
     68  * if this is violated.
     69  */
     70 int get_otherrequests_reply(BUFFER *reply, BUFFER *filename)
     71 {
     72   FILE *f = NULL;
     73   int c;
     74   int err;
     75   BUFFER *path;
     76 
     77   path = buf_new();
     78 
     79   assert(filename);
     80   assert(reply);
     81 
     82   buf_rewind(filename);
     83   err = bufileft(filename, "remailer-");
     84   if (! err) {
     85     err = 1;
     86     goto end;
     87   };
     88 
     89   while ((c = buf_getc(filename)) != -1) {
     90     int ok = (c >= 'A' && c <= 'Z') ||
     91 	     (c >= 'a' && c <= 'z') ||
     92 	     (c >= '0' && c <= '9') ||
     93 	     c == '-';
     94     if (!ok) {
     95       err = 1;
     96       goto end;
     97     };
     98   };
     99   buf_rewind(filename);
    100 
    101   buf_appends(path, REQUESTDIR);
    102   buf_appends(path, "/");
    103   buf_cat(path, filename);
    104 
    105   f = mix_openfile(path->data, "r");
    106   if (f == NULL) {
    107     err = -1;
    108     goto end;
    109   };
    110 
    111   buf_read(reply, f);
    112   err = 0;
    113 end:
    114   if (f)
    115     fclose(f);
    116   buf_free(path);
    117   return (err);
    118 }
    119 
    120 int mix_decrypt(BUFFER *message)
    121 {
    122   int type = 0;
    123   BUFFER *field, *content;
    124   BUFFER *to, *subject, *replyto, *reply;
    125   BUFFER *otherrequest;
    126   FILE *f;
    127   BUFFER *block;
    128   int err = 0;
    129   int quoted_printable = 0;     /* is this message quoted printable encoded */
    130 
    131   mix_init(NULL);
    132   field = buf_new();
    133   content = buf_new();
    134   to = buf_new();
    135   replyto = buf_new();
    136   reply = buf_new();
    137   block = buf_new();
    138   subject = buf_new();
    139   otherrequest = buf_new();
    140   buf_sets(subject, "Subject: Re: your mail");
    141 
    142   buf_rewind(message);
    143 
    144   f = mix_openfile(SOURCEBLOCK, "r");
    145   if (f != NULL) {
    146     buf_read(block, f);
    147     fclose(f);
    148   }
    149   for (;;) {
    150     err = buf_getheader(message, field, content);
    151     if (err == 1) {
    152       /* "::" marks for additional header lines */
    153       while (buf_lookahead(message, field) == 1)
    154 	buf_getheader(message, field, content);
    155       if (isline(field, HDRMARK))
    156 	continue;
    157       else
    158 	goto hdrend;
    159     }
    160     if (err == -1)
    161       goto hdrend;
    162 
    163     if ((bufieq(field, "from") || bufieq(field, "sender") || bufieq(field,"received")) &&
    164        doblock(content, block, 1) != 0)
    165       goto end;
    166 
    167     if (bufieq(field, "to"))
    168       buf_cat(to, content);
    169     else if (bufieq(field, "from") && replyto->length == 0)
    170       /* reply to From address if no Reply-To header present */
    171       buf_set(replyto, content);
    172     else if (bufieq(field, "reply-to"))
    173       buf_set(replyto, content);
    174     else if (MIX && bufieq(field, "remailer-type") &&
    175 	     bufileft(content, "mixmaster"))
    176       type = MIXMSG;
    177     else if (bufieq(field, "subject")) {
    178       if (bufieq(content, "help") || bufieq(content, "remailer-help"))
    179 	type = REQUESTHELP;
    180       else if (bufieq(content, "remailer-stats"))
    181 	type = REQUESTSTATS;
    182       else if (bufieq(content, "remailer-key"))
    183 	type = REQUESTKEY;
    184       else if (bufieq(content, "remailer-adminkey"))
    185 	type = REQUESTOPKEY;
    186       else if (bufieq(content, "remailer-conf"))
    187 	type = REQUESTCONF;
    188       else if (bufileft(content, "remailer-")) {
    189 	type = REQUESTOTHER;
    190 	buf_set(otherrequest, content);
    191       } else if (bufileft(content, "destination-block"))
    192 	type = BLOCKREQUEST;
    193       else {
    194 	buf_sets(subject, "Subject: ");
    195 	if (!bufileft(content, "re:"))
    196 	  buf_appends(subject, "Re: ");
    197 	buf_cat(subject, content);
    198       }
    199     } else if (bufieq(field, "test-to") || bufieq(field, "encrypted") ||
    200 	       bufieq(field, "anon-to") ||
    201 	       bufieq(field, "request-remailing-to") ||
    202 	       bufieq(field, "remail-to") || bufieq(field, "anon-post-to") ||
    203 	       bufieq(field, "post-to") || bufieq(field, "anon-send-to") ||
    204 	       bufieq(field, "send-to") || bufieq(field, "remix-to") ||
    205 	       bufieq(field, "encrypt-to"))
    206       type = CPUNKMSG;
    207     else if (bufieq(field, "content-transfer-encoding")
    208 	     && bufieq(content, "quoted-printable")) {
    209       quoted_printable = 1;
    210     }
    211 
    212   }
    213 hdrend:
    214   if (quoted_printable)
    215     qp_decode_message(message);
    216 
    217   if (type > 0 && REMAIL == 0)
    218     type = DISABLED;
    219   switch (type) {
    220   case REQUESTHELP:
    221     if (sendinfofile(HELPFILE, NULL, replyto, NULL) == -1)
    222       errlog(WARNING, "No help file available.\n");
    223     break;
    224   case REQUESTKEY:
    225     err = key(reply);
    226     if (err == 0)
    227       err = sendmail(reply, REMAILERNAME, replyto);
    228     break;
    229   case REQUESTOPKEY:
    230     err = adminkey(reply);
    231     if (err == 0)
    232       err = sendmail(reply, REMAILERNAME, replyto);
    233     break;
    234   case REQUESTSTATS:
    235     err = stats(reply);
    236     if (err == 0)
    237       err = sendmail(reply, REMAILERNAME, replyto);
    238     break;
    239   case REQUESTCONF:
    240     err = conf(reply);
    241     if (err == 0)
    242       err = sendmail(reply, REMAILERNAME, replyto);
    243     break;
    244   case REQUESTOTHER:
    245     err = get_otherrequests_reply(reply, otherrequest);
    246     if (err == 0)
    247       err = sendmail(reply, REMAILERNAME, replyto);
    248     break;
    249   case CPUNKMSG:
    250     err = t1_decrypt(message);
    251     if (err != 0) {
    252       errlog(LOG, "Invalid type 1 message from %b\n", replyto);
    253       sendinfofile(USAGEFILE, USAGELOG, replyto, NULL);
    254       logmail(err == -2 ? MAILUSAGE : MAILERROR, message);
    255     } else
    256       create_dummy_mailin();
    257     break;
    258   case MIXMSG:
    259     err = t2_decrypt(message);
    260     if (err == -1) {
    261       errlog(LOG, "Invalid type 2 message from %b\n", replyto);
    262       sendinfofile(USAGEFILE, USAGELOG, replyto, NULL);
    263       logmail(MAILERROR, message);
    264     } else
    265       create_dummy_mailin();
    266     break;
    267   case BLOCKREQUEST:
    268     blockrequest(message);
    269     /* Already wrote a log entry in blockrequest() */
    270     logmail(MAILBLOCK, message);
    271     break;
    272   case DISABLED:
    273     errlog(ERRORMSG, "Remailer is disabled.\n");
    274     buf_sets(reply, "Subject: remailer error\n\nThe remailer is disabled.\n");
    275     sendmail(reply, REMAILERNAME, replyto);
    276     logmail(MAILERROR, message);
    277     break;
    278   default:
    279     if (strifind
    280 	(replyto->data, "mailer-daemon")) {
    281       errlog(LOG, "Bounce mail from %b\n", replyto);
    282       logmail(MAILBOUNCE, message);
    283     } else if (bufifind(to, REMAILERADDR) && blockrequest(message)) {
    284       /* Already wrote a log entry in blockrequest() */
    285       logmail(MAILBLOCK, message);
    286     } else if (bufifind(to, REMAILERADDR)) {
    287       errlog(LOG, "Non-remailer message from %b\n", replyto);
    288       if (AUTOREPLY)
    289 	sendinfofile(USAGEFILE, USAGELOG, replyto, NULL);
    290       logmail(MAILUSAGE, message);
    291     } else if (bufifind(to, COMPLAINTS)) {
    292       errlog(WARNING, "Abuse complaint from %b\n", replyto);
    293       if (AUTOREPLY)
    294 	sendinfofile(ABUSEFILE, NULL, replyto, subject);
    295       logmail(MAILABUSE, message);
    296     } else if (ANONADDR[0] && bufifind(to, ANONADDR)) {
    297       errlog(LOG, "Reply to anonymous message from %b\n", replyto);
    298       if (AUTOREPLY)
    299 	sendinfofile(REPLYFILE, NULL, replyto, subject);
    300       logmail(MAILANON, message);
    301     } else {
    302       errlog(DEBUGINFO, "Mail from %b\n", replyto);
    303       logmail(MAILBOX, message);
    304     }
    305     err = 1;
    306   }
    307 end:
    308   buf_free(field);
    309   buf_free(content);
    310   buf_free(to);
    311   buf_free(replyto);
    312   buf_free(reply);
    313   buf_free(block);
    314   buf_free(subject);
    315   buf_free(otherrequest);
    316   return (err);
    317 }
    318 
    319 int create_dummy_mailin()
    320 {
    321   while (rnd_number(100) < INDUMMYP) {
    322     errlog(DEBUGINFO, "Generating dummy message with incoming mail.\n");
    323     if (mix_encrypt(MSG_NULL, NULL, NULL, 1, NULL) == -1)
    324       return -1;
    325   }
    326   return 0;
    327 }
    328 
    329 int t2_decrypt(BUFFER *in)
    330 {
    331   int err = 0;
    332   BUFFER *msg;
    333 
    334   msg = buf_new();
    335   do {
    336     err = mix_dearmor(in, msg);
    337     if (err != -1) {
    338       err = mix2_decrypt(msg);
    339     }
    340   }
    341   while (in->ptr + 1000 < in->length);	/* accept several packets in one message */
    342 
    343   buf_free(msg);
    344   return (err);
    345 }
    346 
    347 int mix_pool(BUFFER *msg, int type, long latent)
    348 {
    349   char path[PATHMAX], pathtmp[PATHMAX];
    350   FILE *f;
    351   int err = -1;
    352 
    353   f = pool_new(latent > 0 ? "lat" : "msg", pathtmp, path);
    354   if (f != NULL) {
    355     if (latent > 0)
    356       fprintf(f, "%d %ld\n", type, latent + time(NULL));
    357     else
    358       fprintf(f, "%d 0\n", type);
    359     err = buf_write_sync(msg, f);
    360   }
    361   if (err == 0) {
    362     rename(pathtmp, path);
    363     errlog(DEBUGINFO, "Added message to pool.\n");
    364   }
    365   return (err);
    366 }
    367 
    368 int pool_packetfile(char *fname, BUFFER *mid, int packetnum)
    369      /* create a filename */
    370 {
    371 #ifdef SHORTNAMES
    372   sprintf(fname, "%s%cp%02x%02x%02x%01x.%02x", POOLDIR, DIRSEP,
    373 	  mid->data[0], mid->data[1], mid->data[2], mid->data[3] & 15,
    374 	  packetnum);
    375 #else /* end of SHORTNAMES */
    376   sprintf(fname, "%s%cp%02x%02x%02x%02x%02x%02x%01x", POOLDIR, DIRSEP,
    377 	  packetnum, mid->data[0], mid->data[1], mid->data[2], mid->data[3],
    378 	  mid->data[4], mid->data[5] & 15);
    379 #endif /* else if not SHORTNAMES */
    380   return (0);
    381 }
    382 
    383 void pool_packetexp(void)
    384 {
    385   char *path;
    386   DIR *d;
    387   struct dirent *e;
    388   struct stat sb;
    389 
    390   d = opendir(POOLDIR);
    391   errlog(DEBUGINFO, "Checking for old parts.\n");
    392   if (d != NULL)
    393     for (;;) {
    394       e = readdir(d);
    395       if (e == NULL)
    396 	break;
    397       if (e->d_name[0] == 'p' || e->d_name[0] == 'e' || e->d_name[0] == 't') {
    398 	path=malloc(strlen(POOLDIR)+strlen(e->d_name)+strlen(DIRSEPSTR)+1);
    399 	if (path) {
    400 	 strcpy(path, POOLDIR);
    401 	  strcat(path, DIRSEPSTR);
    402 	  strcat(path, e->d_name);
    403 	  if (stat(path, &sb) == 0 && time(NULL) - sb.st_mtime > PACKETEXP) {
    404 	     if (e->d_name[0] == 'p') {
    405 	        errlog(NOTICE, "Expiring incomplete partial message %s.\n",
    406 	        e->d_name);
    407 	     }
    408 	     else if (e->d_name[0] == 'e') {
    409 	        errlog(NOTICE, "Expiring old error message %s.\n",
    410 	        e->d_name);
    411 	     }
    412 	     else if (e->d_name[0] == 't') {
    413 	        errlog(NOTICE, "Expiring moldy temporary message %s.\n",
    414 	        e->d_name);
    415 	     }
    416 	     unlink(path);
    417 	  }
    418 	free(path);
    419 	}
    420       }
    421     }
    422   closedir(d);
    423 }
    424 
    425 void logmail(char *mailbox, BUFFER *message)
    426 {
    427   time_t t;
    428   struct tm *tc;
    429   char line[LINELEN];
    430 
    431   /* mailbox is "|program", "user@host", "stdout", "Maildir/" or "filename" */
    432   buf_rewind(message);
    433   if (mailbox[0] == '\0')	/* default action */
    434     mailbox = MAILBOX;
    435   if (strieq(mailbox, "stdout"))
    436     buf_write(message, stdout);
    437   else if (mailbox[0] == '|') {
    438     FILE *p;
    439 
    440     errlog(DEBUGINFO, "Piping message to %s.\n", mailbox + 1);
    441     p = openpipe(mailbox + 1);
    442     if (p != NULL) {
    443       buf_write(message, p);
    444       closepipe(p);
    445     }
    446   } else if (strchr(mailbox, '@')) {
    447     BUFFER *field, *content;
    448 
    449     field = buf_new();
    450     content = buf_new();
    451     while (buf_getheader(message, field, content) == 0)
    452       if (bufieq(field, "x-loop") && bufifind(content, REMAILERADDR)) {
    453 	errlog(WARNING, "Loop detected! Message not sent to %s.\n", mailbox);
    454 	goto isloop;
    455       }
    456     buf_sets(content, mailbox);
    457     sendmail_loop(message, NULL, content);
    458   isloop:
    459     buf_free(field);
    460     buf_free(content);
    461   } else if (mailbox[strlen(mailbox)-1] == DIRSEP) {
    462     /* the user is requesting Maildir delivery */
    463     if(maildirWrite(mailbox, message, 1) != 0) {
    464       errlog(ERRORMSG, "Can't write to maildir %s\n", mailbox);
    465       return;
    466     }
    467   } else {
    468     FILE *mbox;
    469 
    470     mbox = mix_openfile(mailbox, "a");
    471     if (mbox == NULL) {
    472       errlog(ERRORMSG, "Can't write to mail folder %s\n", mailbox);
    473       return;
    474     }
    475     lock(mbox);
    476     if (!bufileft(message, "From ")) {
    477       t = time(NULL);
    478       tc = localtime(&t);
    479       strftime(line, LINELEN, "From Mixmaster %a %b %d %H:%M:%S %Y\n", tc);
    480       fprintf(mbox, line);
    481     }
    482     buf_write(message, mbox);
    483     fprintf(mbox, "\n\n");
    484     unlock(mbox);
    485     fclose(mbox);
    486   }
    487 }
    488 
    489 int blockrequest(BUFFER *message)
    490 {
    491   int request = 0, num, i;
    492   BUFFER *from, *line, *field, *content, *addr, *remailer_addr, *copy_addr;
    493   REMAILER remailer[MAXREM];
    494   FILE *f;
    495   char *destblklst = (char *)malloc( strlen(DESTBLOCK)+1 );
    496   char *destblk;
    497 
    498   from = buf_new();
    499   line = buf_new();
    500   field = buf_new();
    501   content = buf_new();
    502   addr = buf_new();
    503   remailer_addr = buf_new();
    504   copy_addr = buf_new();
    505 
    506   if (destblklst == NULL) {
    507     errlog(ERRORMSG, "Can't malloc %n bytes for destblklst.\n", strlen(DESTBLOCK)+1);
    508     goto end;
    509   };
    510 
    511   buf_rewind(message);
    512   while (buf_getheader(message, field, content) == 0)
    513     if (bufieq(field, "from"))
    514       buf_set(from, content);
    515     else if (bufieq(field, "subject"))
    516       buf_cat(message, content);
    517    /* Append the subject to the message body so destination block requests
    518       in the subject line work too (we process the body a few lines down) */
    519   while (buf_getline(message, line) != -1)
    520     if (bufifind(line, "destination-block")) {
    521       buf_clear(addr);
    522       request = 1;
    523       {
    524 	int c = 0;
    525 
    526 	while (!strileft(line->data + line->ptr, "block"))
    527 	  line->ptr++;
    528 	while (c != ' ' && c != -1)
    529 	  c = tolower(buf_getc(line));
    530 	while (c == ' ')
    531 	  c = buf_getc(line);
    532 	if (c != -1)
    533 	  do {
    534 	    buf_appendc(addr, c);
    535 	    c = buf_getc(line);
    536 	  } while (c > ' ');
    537       }
    538       if (addr->length == 0) {
    539 	rfc822_addr (from, addr);
    540 	buf_chop(addr);
    541       }
    542       /* Check whether somebody wants us to block ourselves */
    543       buf_set(copy_addr, addr);
    544       buf_sets(remailer_addr, REMAILERADDR);
    545       if (doblock(remailer_addr, copy_addr, 1)) {
    546 	errlog(LOG, "Ignoring blocking request for %b from %b.\n", addr, from);
    547 	request = 2;
    548 	goto end;
    549       }
    550       /* Check if some evil person tries to block a known type II remailer */
    551       num = mix2_rlist(remailer, NULL);
    552       for (i = 0; i < num; i++) {
    553 	buf_sets(remailer_addr, remailer[i].addr);
    554 	if (doblock(remailer_addr, copy_addr, 1)) {
    555 	  errlog(LOG, "Ignoring blocking request for %b from %b.\n", addr, from);
    556 	  request = 2;
    557 	  goto end;
    558 	}
    559       }
    560       /* Check if some evil person tries to block a known type I remailer */
    561       num = t1_rlist(remailer, NULL);
    562       for (i = 0; i < num; i++) {
    563 	buf_sets(remailer_addr, remailer[i].addr);
    564 	if (doblock(remailer_addr, copy_addr, 1)) {
    565 	  errlog(LOG, "Ignoring blocking request for %b from %b.\n", addr, from);
    566 	  request = 2;
    567 	  goto end;
    568 	}
    569       }
    570 
    571       if (buf_ieq(addr, from))
    572 	errlog(NOTICE, "Blocking request for %b\n", addr);
    573       else
    574 	errlog(NOTICE, "Blocking request for %b from %b\n", addr, from);
    575       if (AUTOBLOCK) {
    576 	buf_clear(line);
    577 	rfc822_addr(addr, line);
    578 	if (line->length == 0) {
    579 	  errlog(LOG, "Nothing to block after rfc822_addr().\n");
    580 	} else
    581 	  if (bufleft(line, "/")) {
    582 	    errlog(LOG, "Ignoring blocking request: %b is a regex.\n", addr);
    583 	  } else {
    584 	    if (strchr(line->data, '@') && strchr(strchr(line->data, '@'), '.')) {
    585 	      strcpy( destblklst, DESTBLOCK );
    586 	      destblk = strtok( destblklst, " " );
    587 	      f = mix_openfile( destblk, "a" );
    588 	      if (f != NULL) {
    589 		lock(f);
    590 
    591 		buf_chop(line);
    592 		sendinfofile(BLOCKFILE, NULL, line, NULL);
    593 		if (line->length) {
    594 		  fprintf(f, "%s\n", line->data);
    595 		} else
    596 		  errlog(NOTICE, "%b already blocked.\n", addr);
    597 		unlock(f);
    598 		fclose(f);
    599 	      } else
    600 	        errlog(ERRORMSG, "Can't write to %s.\n", DESTBLOCK);
    601 	    } else
    602 	      errlog(WARNING, "Invalid address not added to %s: %b\n", DESTBLOCK,
    603 		     addr);
    604 	  }
    605       }
    606     }
    607 
    608 end:
    609   free( destblklst );
    610   buf_free(from);
    611   buf_free(line);
    612   buf_free(field);
    613   buf_free(content);
    614   buf_free(addr);
    615   buf_free(remailer_addr);
    616   buf_free(copy_addr);
    617 
    618   return (request);
    619 }
    620 
    621 
    622 int idexp(void)
    623 {
    624   FILE *f;
    625   long now, then;
    626   LOCK *i;
    627   idlog_t idbuf;
    628   long fpi = sizeof(idlog_t), fpo = sizeof(idlog_t);
    629 
    630   if (IDEXP == 0)
    631     return (0);
    632 
    633   f = mix_openfile(IDLOG, "rb+");
    634   if (f == NULL)
    635     return (-1);
    636   i = lockfile(IDLOG);
    637   now = time(NULL);
    638   if (fread(&idbuf, 1, sizeof(idlog_t), f) != sizeof(idlog_t)) { /* replace first line */
    639     fclose(f);
    640     unlockfile(i);
    641     return (-1);
    642   }
    643   then = idbuf.time;
    644   memset(idbuf.id,0,sizeof(idbuf.id));
    645   idbuf.time = now - IDEXP;
    646   fseek(f,0,SEEK_SET);
    647   fwrite(&idbuf,1,sizeof(idlog_t),f);
    648   fseek(f,fpi,SEEK_SET); /* this fseek does nothing, but MSVC CRT happilly reads past EOF (!!!) if we do not fseek here :-/ */
    649   while (fread(&idbuf, 1, sizeof(idlog_t), f) == sizeof(idlog_t)) {
    650     fpi+=sizeof(idlog_t);
    651     then = idbuf.time;
    652     if (now - then < IDEXP &&
    653       now - then > - SECONDSPERDAY * 180 )
    654       /* also expire packets that are dated more than half a year in the future.
    655        * That way we get rid of invalid packets introduced by the switch to a
    656        * binary id.log. */
    657     {
    658       fseek(f,fpo,SEEK_SET);
    659       fwrite(&idbuf,1,sizeof(idlog_t),f);
    660       fpo += sizeof(idlog_t);
    661       fseek(f,fpi,SEEK_SET);
    662     }
    663   }
    664 #ifdef _MSC
    665     chsize(fileno(f),fpo);
    666 #else /* end of _MSC */
    667     ftruncate(fileno(f),fpo);
    668 #endif /* else if not _MSC */
    669   fclose(f);
    670   unlockfile(i);
    671   return (0);
    672 }
    673 
    674 
    675 int pgpmaxexp(void)
    676 {
    677   FILE *f;
    678   BUFFER *b;
    679   long now, then;
    680   LOCK *i;
    681   char temp[LINELEN];
    682 
    683   f = mix_openfile(PGPMAXCOUNT, "rb+");
    684   if (f == NULL)
    685     return (-1);
    686   i = lockfile(PGPMAXCOUNT);
    687   b = buf_new();
    688   now = time(NULL);
    689 
    690   while (fgets(temp, sizeof(temp), f) != NULL)
    691     if (sscanf(temp, "%ld", &then) &&
    692 	then >= now - SECONDSPERDAY)
    693       buf_appends(b, temp);
    694 
    695   fseek(f,0,SEEK_SET);
    696 
    697   buf_write(b, f);
    698 
    699 #ifdef _MSC
    700     chsize(fileno(f),b->length);
    701 #else /* end of _MSC */
    702     ftruncate(fileno(f),b->length);
    703 #endif /* else if not _MSC */
    704 
    705   fclose(f);
    706   unlockfile(i);
    707   buf_free(b);
    708   return (0);
    709 }