aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/toprocmail
Commit message (Expand)AuthorAgeFilesLines
* Add files from ESR's dev directory that weren't under version controlRob Funk2004-06-081-0/+46
ref='#n34'>34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
/* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
 * All rights reserved.
 * For license terms, see the file COPYING in this directory.
 */

/***********************************************************************
  module:       pop3.c
  project:      popclient
  programmer:   Carl Harris, ceharris@mal.com
		Hacks and bug fixes by esr.
  description:  POP3 client code.

 ***********************************************************************/

#include  <config.h>

#include  <stdio.h>
#if defined(STDC_HEADERS)
#include  <string.h>
#endif
#if defined(HAVE_UNISTD_H)
#include  <unistd.h>
#endif

#include  <sys/time.h>
#include  <ctype.h>
#include  <errno.h>

#include  "socket.h"
#include  "popclient.h"

#define	  POP3_PORT	110

#ifdef HAVE_PROTOTYPES
/* prototypes for internal functions */
int POP3_OK (char *buf, int socket);
int POP3_auth (struct hostrec *options, int socket);
int POP3_sendQUIT (int socket);
int POP3_sendSTAT (int *msgcount, int socket);
int POP3_sendRETR (int msgnum, int socket);
int POP3_sendDELE (int msgnum, int socket);
int POP3_sendLAST (int *last, int socket);
int POP3_readmsg (int socket, int mboxfd, char *host, int topipe, int rewrite);
int POP3_BuildDigest (char *buf, struct hostrec *options);
#endif


/*********************************************************************
  function:      doPOP3
  description:   retrieve messages from the specified mail server
                 using Post Office Protocol 3.

  arguments:     
    queryctl     fully-specified options (i.e. parsed, defaults invoked,
                 etc).

  return value:  exit code from the set of PS_.* constants defined in 
                 popclient.h
  calls:
  globals:       reads outlevel.
 *********************************************************************/

int doPOP3 (queryctl)
struct hostrec *queryctl;
{
  int ok;
  int mboxfd;
  char buf [POPBUFSIZE];
  int socket;
  int first,number,count;


  /* open stdout or the mailbox, locking it if it is a folder */
  if (queryctl->output == TO_FOLDER || queryctl->output == TO_STDOUT) 
    if ((mboxfd = openuserfolder(queryctl)) < 0) 
      return(PS_IOERR);
    
  /* open the socket and get the greeting */
  if ((socket = Socket(queryctl->servername,POP3_PORT)) < 0) {
    perror("doPOP3: socket");
    ok = PS_SOCKET;
    goto closeUp;
  }

  ok = POP3_OK(buf,socket);
  if (ok != 0) {
    if (ok != PS_SOCKET)
      POP3_sendQUIT(socket);
    close(socket);
    goto closeUp;
  }

  /* print the greeting */
  if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
    fprintf(stderr,"%s\n",buf);
  else 
    ;

#if defined(HAVE_APOP_SUPPORT)
  /* build MD5 digest from greeting timestamp + password */
  if (queryctl->whichpop == P_APOP) 
    if (POP3_BuildDigest(buf,queryctl) != 0) {
      ok = PS_AUTHFAIL;
      goto closeUp;
    } else
      ;
  else
    ;  /* not using APOP protocol this time */
#endif

  /* try to get authorized */
  ok = POP3_auth(queryctl,socket);
  if (ok == PS_ERROR)
    ok = PS_AUTHFAIL;
  if (ok != 0)
    goto cleanUp;

  /* find out how many messages are waiting */
  ok = POP3_sendSTAT(&count,socket);
  if (ok != 0) {
    goto cleanUp;
  }

  /* Ask for number of last message retrieved */
  if (queryctl->fetchall) 
    first = 1;
  else {
    ok = POP3_sendLAST(&first, socket);
    if (ok != 0)
      goto cleanUp;

    first++;
  }
    
  /* show them how many messages we'll be downloading */
  if (outlevel > O_SILENT && outlevel < O_VERBOSE)
    if (first > 1) 
      fprintf(stderr,"%d messages in folder, %d new messages.\n", 
                      count, count - first + 1);
    else
      fprintf(stderr,"%d new messages in folder.\n", count);
  else
    ;

  if (count > 0) { 
    for (number = (queryctl->flush || queryctl->fetchall)? 1 : first;  
                   number <= count;  
                   number++) {

      /* open the mail pipe if we're using an MDA */
      if (queryctl->output == TO_MDA
           && (queryctl->fetchall || number >= first)) {
        ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
        if (ok != 0)
          goto cleanUp;
      }
           
      if (queryctl->flush && number < first && !queryctl->fetchall) 
        ok = 0;  /* no command to send here, will delete message below */
      else if (linelimit) 
        ok = POP3_sendTOP(number,linelimit,socket);
      else 
        ok = POP3_sendRETR(number,socket);
      if (ok != 0)
        goto cleanUp;
      
      if (number >= first || queryctl->fetchall)
        ok = POP3_readmsg(socket,mboxfd,
			  queryctl->servername,
			  queryctl->output == TO_MDA, 
			  queryctl->rewrite);
      else
        ok = 0;
      if (ok != 0)
        goto cleanUp;

      if ((number < first && queryctl->flush) || !queryctl->keep) {
        if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
          fprintf(stderr,"flushing message %d\n", number);
        else
          ;
        ok = POP3_sendDELE(number,socket);
        if (ok != 0)
          goto cleanUp;
      }
      else
        ; /* message is kept */

      /* close the mail pipe if we're using the system mailbox */
      if (queryctl->output == TO_MDA
           && (queryctl->fetchall || number >= first)) {
        ok = closemailpipe(mboxfd);
        if (ok != 0)
          goto cleanUp;
      }
    }

    ok = POP3_sendQUIT(socket);
    if (ok == 0)
      ok = PS_SUCCESS;
    close(socket);
    goto closeUp;
  }
  else {
    ok = POP3_sendQUIT(socket);
    if (ok == 0)
      ok = PS_NOMAIL;
    close(socket);
    goto closeUp;
  }

cleanUp:
  if (ok != 0 && ok != PS_SOCKET)
    POP3_sendQUIT(socket);

closeUp:
  if (queryctl->output == TO_FOLDER)
    if (closeuserfolder(mboxfd) < 0 && ok == 0)
      ok = PS_IOERR;
    
  if (ok == PS_IOERR || ok == PS_SOCKET) 
    perror("doPOP3: cleanUp");

  return(ok);
}



/*********************************************************************
  function:      POP3_OK
  description:   get the server's response to a command, and return
                 the extra arguments sent with the response.
  arguments:     
    argbuf       buffer to receive the argument string.
    socket       socket to which the server is connected.

  return value:  zero if okay, else return code.
  calls:         SockGets
  globals:       reads outlevel.
 *********************************************************************/

int POP3_OK (argbuf,socket)
char *argbuf;
int socket;
{
  int ok;
  char buf [POPBUFSIZE];
  char *bufp;

  if (SockGets(socket, buf, sizeof(buf)) == 0) {
    if (outlevel == O_VERBOSE)
      fprintf(stderr,"%s\n",buf);

    bufp = buf;
    if (*bufp == '+' || *bufp == '-')
      bufp++;
    else
      return(PS_PROTOCOL);

    while (isalpha(*bufp))
      bufp++;
    *(bufp++) = '\0';

    if (strcmp(buf,"+OK") == 0)
      ok = 0;
    else if (strcmp(buf,"-ERR") == 0)
      ok = PS_ERROR;
    else
      ok = PS_PROTOCOL;

    if (argbuf != NULL)
      strcpy(argbuf,bufp);
  }
  else 
    ok = PS_SOCKET;

  return(ok);
}



/*********************************************************************
  function:      POP3_auth
  description:   send the USER and PASS commands to the server, and
                 get the server's response.
  arguments:     
    queryctl	 merged options record.
    socket       socket to which the server is connected.

  return value:  zero if success, else status code.
  calls:         SockPrintf, POP3_OK.
  globals:       read outlevel.
 *********************************************************************/

int POP3_auth (queryctl,socket) 
struct hostrec *queryctl;
int socket;
{
  char buf [POPBUFSIZE];

  switch (queryctl->protocol) {
    case P_POP3:
      SockPrintf(socket,"USER %s\r\n",queryctl->remotename);
      if (outlevel == O_VERBOSE)
        fprintf(stderr,"> USER %s\n",queryctl->remotename);
      if (POP3_OK(buf,socket) != 0)
        goto badAuth;

      SockPrintf(socket,"PASS %s\r\n",queryctl->password);
      if (outlevel == O_VERBOSE)
        fprintf(stderr,"> PASS password\n");
      if (POP3_OK(buf,socket) != 0)
        goto badAuth;
    
      break;

#if defined(HAVE_APOP_SUPPORT)
    case P_APOP:
      SockPrintf(socket,"APOP %s %s\r\n", 
                 queryctl->remotename, queryctl->digest);
      if (outlevel == O_VERBOSE)
        fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest);
      if (POP3_OK(buf,socket) != 0) 
        goto badAuth;
      break;
#endif  /* HAVE_APOP_SUPPORT */

#if defined(HAVE_RPOP_SUPPORT)
    case P_RPOP:
      SockPrintf(socket, "RPOP %s\r\n", queryctl->remotename);
      if (POP3_OK(buf,socket) != 0)
         goto badAuth;
      if (outlevel == O_VERBOSE)
        fprintf(stderr,"> RPOP %s %s\n",queryctl->remotename);
      break;
#endif  /* HAVE_RPOP_SUPPORT */

    default:
      fprintf(stderr,"Undefined protocol request in POP3_auth\n");
  }

  /* we're approved */
  return(0);

  /*NOTREACHED*/

badAuth:
  if (outlevel > O_SILENT && outlevel < O_VERBOSE)
    fprintf(stderr,"%s\n",buf);
  else
    ; /* say nothing */

  return(PS_ERROR);
}




/*********************************************************************
  function:      POP3_sendQUIT
  description:   send the QUIT command to the server and close 
                 the socket.

  arguments:     
    socket       socket to which the server is connected.

  return value:  none.
  calls:         SockPuts, POP3_OK.
  globals:       reads outlevel.
 *********************************************************************/

int POP3_sendQUIT (socket)
int socket;
{
  int ok;
  char buf [POPBUFSIZE];

  SockPuts(socket,"QUIT");

  if (outlevel == O_VERBOSE)
    fprintf(stderr,"> QUIT\n");
  else
    ;

  ok = POP3_OK(buf,socket);
  if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
    fprintf(stderr,"%s\n",buf);

  return(ok);
}



/*********************************************************************
  function:      POP3_sendSTAT
  description:   send the STAT command to the POP3 server to find
                 out how many messages are waiting.
  arguments:     
    count        pointer to an integer to receive the message count.
    socket       socket to which the POP3 server is connected.

  return value:  return code from POP3_OK.
  calls:         POP3_OK, SockPrintf
  globals:       reads outlevel.
 *********************************************************************/

int POP3_sendSTAT (msgcount,socket)
int *msgcount;
int socket;
{
  int ok;
  char buf [POPBUFSIZE];
  int totalsize;

  SockPrintf(socket,"STAT\r\n");
  if (outlevel == O_VERBOSE)
    fprintf(stderr,"> STAT\n");
  
  ok = POP3_OK(buf,socket);
  if (ok == 0)
    sscanf(buf,"%d %d",msgcount,&totalsize);
  else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
    fprintf(stderr,"%s\n",buf);

  return(ok);
}




/*********************************************************************
  function:      POP3_sendRETR
  description:   send the RETR command to the POP3 server.
  arguments:     
    msgnum       message ID number
    socket       socket to which the POP3 server is connected.

  return value:  return code from POP3_OK.
  calls:         POP3_OK, SockPrintf
  globals:       reads outlevel.
 *********************************************************************/

int POP3_sendRETR (msgnum,socket)
int msgnum;
int socket;
{
  int ok;
  char buf [POPBUFSIZE];

  SockPrintf(socket,"RETR %d\r\n",msgnum);
  if (outlevel == O_VERBOSE)
    fprintf(stderr,"> RETR %d\n",msgnum);
  
  ok = POP3_OK(buf,socket);
  if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
    fprintf(stderr,"%s\n",buf);

  return(ok);
}


/*********************************************************************
  function:      POP3_sendTOP
  description:   send the TOP command to the POP3 server.
  arguments:     
    msgnum       message ID number
    limit        maximum number of message body lines to retrieve.
    socket       socket to which the POP3 server is connected.

  return value:  return code from POP3_OK.
  calls:         POP3_OK, SockPrintf
  globals:       reads outlevel.
 *********************************************************************/

int POP3_sendTOP (msgnum,limit,socket)
int msgnum;
int socket;
{
  int ok;
  char buf [POPBUFSIZE];

  SockPrintf(socket,"TOP %d %d\r\n",msgnum,limit);
  if (outlevel == O_VERBOSE)
    fprintf(stderr,"> TOP %d %d\n",msgnum,limit);
  
  ok = POP3_OK(buf,socket);
  if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
    fprintf(stderr,"option --limit failed; server says '%s'\n",buf);

  return(ok);
}




/*********************************************************************
  function:      POP3_sendDELE
  description:   send the DELE command to the POP3 server.
  arguments:     
    msgnum       message ID number
    socket       socket to which the POP3 server is connected.

  return value:  return code from POP3_OK.
  calls:         POP3_OK, SockPrintF.
  globals:       reads outlevel.
 *********************************************************************/

int POP3_sendDELE (msgnum,socket)
int msgnum;
int socket;
{
  int ok;
  char buf [POPBUFSIZE];

  SockPrintf(socket,"DELE %d\r\n",msgnum);
  if (outlevel == O_VERBOSE)
    fprintf(stderr,"> DELE %d\n",msgnum);
  
  ok = POP3_OK(buf,socket);
  if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
    fprintf(stderr,"%s\n",buf);

  return(ok);
}



/*********************************************************************
  function:      POP3_readmsg
  description:   Read the message content as described in RFC 1225.
  arguments:     
    socket       ... to which the server is connected.
    mboxfd       open file descriptor to which the retrieved message will
                 be written. 
    pophost      name of the POP host 
    topipe       true if we're writing to the system mailbox pipe.

  return value:  zero if success else PS_* return code.
  calls:         SockGets.
  globals:       reads outlevel. 
 *********************************************************************/

int POP3_readmsg (socket,mboxfd,pophost,topipe,rewrite)
int socket;
int mboxfd;
char *pophost;
int topipe;
int rewrite;
{ 
  char buf [MSGBUFSIZE]; 
  char *bufp;
  char savec;
  char fromBuf[MSGBUFSIZE];
  int needFrom;
  int inheaders;
  int lines,sizeticker;
  time_t now;
  /* This keeps the retrieved message count for display purposes */
  static int msgnum = 0;  

  /* set up for status message if outlevel allows it */
  if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
    fprintf(stderr,"reading message %d",++msgnum);
    /* won't do the '...' if retrieved messages are being sent to stdout */
    if (mboxfd == 1)
      fputs(".\n",stderr);
    else
      ;
  }
  else
    ;

  /* read the message content from the server */
  inheaders = 1;
  lines = 0;
  sizeticker = MSGBUFSIZE;
  while (1) {
    if (SockGets(socket,buf,sizeof(buf)) < 0)
      return(PS_SOCKET);
    bufp = buf;
    if (buf[0] == '\r' || buf[0] == '\n')
      inheaders = 0;
    if (*bufp == '.') {
      bufp++;
      if (*bufp == 0)
        break;  /* end of message */
    }
    strcat(bufp,"\n");
     
    /* Check for Unix 'From' header, and add a bogus one if it's not
       present -- only if not using an MDA.
       XXX -- should probably parse real From: header and use its 
              address field instead of bogus 'POPmail' string. 
    */
    if (!topipe && lines == 0) {
      if (strlen(bufp) >= strlen("From ")) {
        savec = *(bufp + 5);
        *(bufp + 5) = 0;
        needFrom = strcmp(bufp,"From ") != 0;
        *(bufp + 5) = savec;
      }
      else
        needFrom = 1;
      if (needFrom) {
        now = time(NULL);
        sprintf(fromBuf,"From POPmail %s",ctime(&now));
        if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
          perror("POP3_readmsg: write");
          return(PS_IOERR);
        }
      }
    }

    /*
     * Edit some headers so that replies will work properly.
     */
    if (inheaders && rewrite)
      reply_hack(bufp, pophost);

    /* write this line to the file */
    if (write(mboxfd,bufp,strlen(bufp)) < 0) {
      perror("POP3_readmsg: write");
      return(PS_IOERR);
    }

    sizeticker -= strlen(bufp);
    if (sizeticker <= 0) {
      if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
        fputc('.',stderr);
      sizeticker = MSGBUFSIZE;
    }
    lines++;
  }

  if (!topipe) {
    /* The server may not write the extra newline required by the Unix
       mail folder format, so we write one here just in case */
    if (write(mboxfd,"\n",1) < 0) {
      perror("POP3_readmsg: write");
      return(PS_IOERR);
    }
  }
  else {
    /* The mail delivery agent may require a terminator.  Write it if
       it has been defined */
#ifdef BINMAIL_TERM
    if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
      perror("POP3_readmsg: write");
      return(PS_IOERR);
    }
#endif
    }

  /* finish up display output */
  if (outlevel == O_VERBOSE)
    fprintf(stderr,"(%d lines of message content)\n",lines);
  else if (outlevel > O_SILENT && mboxfd != 1) 
    fputs(".\n",stderr);
  else
    ;
  return(0);
}




/******************************************************************
  function:	POP3_sendLAST
  description:	send the LAST command to the server, which should
                return the number of the last message number retrieved 
                from the server.
  arguments:
    last	integer buffer to receive last message# 

  ret. value:	non-zero on success, else zero.
  globals:	SockPrintf, POP3_OK.
  calls:	reads outlevel.
 *****************************************************************/

int POP3_sendLAST (last, socket)
int *last;
int socket;
{
  int ok;
  char buf [POPBUFSIZE];

  SockPrintf(socket,"LAST\r\n");
  if (outlevel == O_VERBOSE)
    fprintf(stderr,"> LAST\n");

  ok = POP3_OK(buf,socket);
  if (ok == 0 && sscanf(buf,"%d",last) == 0)
    ok = PS_ERROR;

  if (ok != 0 && outlevel > O_SILENT) 
    fprintf(stderr,"Server says '%s' to LAST command.\n",buf);

  return(ok);
}


/******************************************************************
  function:	POP3_BuildDigest
  description:	Construct the MD5 digest for the current session,
	        using the user-specified password, and the time
                stamp in the POP3 greeting.
  arguments:
    buf		greeting string
    queryctl	merged options record.

  ret. value:	zero on success, nonzero if no timestamp found in
	        greeting.
  globals:	none.
  calls:	MD5Digest.
 *****************************************************************/

#if defined(HAVE_APOP_SUPPORT)
POP3_BuildDigest (buf,queryctl)
char *buf;
struct hostrec *queryctl;
{
  char *start,*end;
  char *msg;

  /* find start of timestamp */
  for (start = buf;  *start != 0 && *start != '<';  start++)
    ;
  if (*start == 0) {
    fprintf(stderr,"Required APOP timestamp not found in greeting\n");
    return(-1);
  }

  /* find end of timestamp */
  for (end = start;  *end != 0  && *end != '>';  end++)
    ;
  if (*end == 0 || (end - start - 1) == 1) {
    fprintf(stderr,"Timestamp syntax error in greeting\n");
    return(-1);
  }

  /* copy timestamp and password into digestion buffer */
  msg = (char *) malloc((end-start-1) + strlen(queryctl->password) + 1);
  *(++end) = 0;
  strcpy(msg,start);
  strcat(msg,queryctl->password);

  strcpy(queryctl->digest, MD5Digest(msg));
  free(msg);
  return(0);
}
#endif  /* HAVE_APOP_SUPPORT */