home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / vsoup128.zip / pop3.cc < prev    next >
C/C++ Source or Header  |  1997-04-20  |  15KB  |  562 lines

  1. /* $Id: pop3.cc 1.19 1997/04/20 19:20:34 hardy Exp $
  2.  *
  3.  * This module has been modified for souper.
  4.  */
  5.  
  6. /* Copyright 1993,1994 by Carl Harris, Jr.
  7.  * All rights reserved
  8.  *
  9.  * Distribute freely, except: don't remove my name from the source or
  10.  * documentation (don't take credit for my work), mark your changes (don't
  11.  * get me blamed for your possible bugs), don't alter or remove this
  12.  * notice.  May be sold if buildable source is provided to buyer.  No
  13.  * warrantee of any kind, express or implied, is included with this
  14.  * software; use at your own risk, responsibility for damages (if any) to
  15.  * anyone resulting from the use of this software rests entirely with the
  16.  * user.
  17.  *
  18.  * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
  19.  * I'll try to keep a version up to date.  I can be reached as follows:
  20.  * Carl Harris <ceharris@vt.edu>
  21.  */
  22.  
  23. //
  24. //  This progam/module was written by Hardy Griech based on ideas and
  25. //  pieces of code from Chin Huang (cthuang@io.org).  Bug reports should
  26. //  be submitted to rgriech@swol.de.
  27. //
  28. //  This file is part of VSoup for OS/2.  VSoup including this file
  29. //  is freeware.  There is no warranty of any kind implied.  The terms
  30. //  of the GNU Gernal Public Licence are valid for this piece of software.
  31. //
  32. //  NNTP client routines
  33. //
  34.  
  35. /***********************************************************************
  36.   module:       pop3.c
  37.   program:      popclient
  38.   SCCS ID:      @(#)pop3.c      2.4  3/31/94
  39.   programmer:   Carl Harris, ceharris@vt.edu
  40.   date:         29 December 1993
  41.   compiler:     DEC RISC C compiler (Ultrix 4.1)
  42.   environment:  DEC Ultrix 4.3 
  43.   description:  POP2 client code.
  44.  ***********************************************************************/
  45.  
  46.  
  47. #include <assert.h> 
  48. #include <ctype.h>
  49. #include <string.h>
  50.  
  51. #include "areas.hh"
  52. #include "global.hh"
  53. #include "mts.hh"
  54. #include "pop3.hh"
  55. #include "socket.hh"
  56. #include "util.hh"
  57.  
  58.  
  59. /* exit code values */
  60.  
  61. enum PopRetCode {ps_success,      // successful receipt of messages
  62.                  ps_socket,       // socket I/O woes
  63.                  ps_protocol,     // protocol violation
  64.                  ps_error         // some kind of POP3 error condition
  65. };
  66.  
  67.  
  68. /*********************************************************************
  69.   function:      POP3_ok
  70.   description:   get the server's response to a command, and return
  71.                  the extra arguments sent with the response.
  72.   arguments:     
  73.     argbuf       buffer to receive the argument string (==NULL -> no return)
  74.     socket       socket to which the server is connected.
  75.  
  76.   return value:  zero if okay, else return code.
  77.   calls:         SockGets
  78.  *********************************************************************/
  79.  
  80. static PopRetCode POP3_ok(char *argbuf, TSocket &socket)
  81. {
  82.     PopRetCode ok;
  83.     char buf[BUFSIZ];
  84.     char *bufp;
  85.  
  86. #ifdef TRACE
  87.     printfT( "POP3_ok()\n" );
  88. #endif
  89.     if (socket.gets(buf, sizeof(buf))) {
  90. #ifdef DEBUG
  91.     printfT( "POP3_ok() -> %s\n",buf );
  92. #endif
  93.     bufp = buf;
  94.     if (*bufp == '+' || *bufp == '-')
  95.         bufp++;
  96.     else
  97.         return(ps_protocol);
  98.  
  99.     while (isalpha(*bufp))
  100.         bufp++;
  101.     *(bufp++) = '\0';
  102.     
  103.     if (strcmp(buf,"+OK") == 0)
  104.         ok = ps_success;
  105.     else if (strcmp(buf,"-ERR") == 0)
  106.         ok = ps_error;
  107.     else
  108.         ok = ps_protocol;
  109.     
  110.     if (argbuf != NULL)
  111.         strcpy(argbuf,bufp);
  112.     }
  113.     else {
  114.     ok = ps_socket;
  115.     if (argbuf != NULL)
  116.         *argbuf = '\0';
  117.     }
  118.     
  119.     return(ok);
  120. }   // POP3_ok
  121.  
  122.  
  123.  
  124. /*********************************************************************
  125.   function:      POP3_Auth
  126.   description:   send the USER and PASS commands to the server, and
  127.                  get the server's response.
  128.   arguments:     
  129.     userid       user's mailserver id.
  130.     password     user's mailserver password.
  131.     socket       socket to which the server is connected.
  132.  
  133.   return value:  non-zero if success, else zero.
  134.   calls:         SockPrintf, POP3_ok.
  135.  *********************************************************************/
  136.  
  137. static PopRetCode POP3_Auth(const char *userid, const char *password, TSocket &socket) 
  138. {
  139.     PopRetCode ok;
  140.     char buf[BUFSIZ];
  141.  
  142. #ifdef TRACE
  143.     printfT( "POP3_Auth(%s,.,.)\n", userid);
  144. #endif
  145.     socket.printf("USER %s\n",userid);
  146.     if ((ok = POP3_ok(buf,socket)) == ps_success) {
  147.     socket.printf("PASS %s\n",password);
  148.     if ((ok = POP3_ok(buf,socket)) == ps_success) 
  149.         ;  //  okay, we're approved.. 
  150.     else
  151.         areas.mailPrintf1( 1,"%s\n",buf);
  152.     }
  153.     else
  154.     areas.mailPrintf1(1,"%s\n",buf);
  155.     
  156.     return(ok);
  157. }   // POP3_Auth
  158.  
  159.  
  160.  
  161.  
  162. /*********************************************************************
  163.   function:      POP3_sendQuit
  164.   description:   send the QUIT command to the server and close 
  165.                  the socket.
  166.  
  167.   arguments:     
  168.     socket       socket to which the server is connected.
  169.  
  170.   return value:  none.
  171.   calls:         SockPuts, POP3_ok.
  172.  *********************************************************************/
  173.  
  174. static PopRetCode POP3_sendQuit(TSocket &socket)
  175. {
  176.     char buf[BUFSIZ];
  177.     PopRetCode ok;
  178.  
  179. #ifdef TRACE
  180.     printfT( "POP3_sendQuit(): QUIT\n" );
  181. #endif
  182.     socket.printf("QUIT\n");
  183.     ok = POP3_ok(buf,socket);
  184.     if (ok != ps_success)
  185.     areas.mailPrintf1( 1,"%s\n",buf);
  186.  
  187.     return(ok);
  188. }   // POP3_sendQuit
  189.  
  190.  
  191.  
  192. /*********************************************************************
  193.   function:      POP3_sendStat
  194.   description:   send the STAT command to the POP3 server to find
  195.                  out how many messages are waiting.
  196.   arguments:     
  197.     count        pointer to an integer to receive the message count.
  198.     socket       socket to which the POP3 server is connected.
  199.  
  200.   return value:  return code from POP3_ok.
  201.   calls:         POP3_ok, SockPrintf
  202.  *********************************************************************/
  203.  
  204. static PopRetCode POP3_sendStat(int *msgcount, TSocket &socket)
  205. {
  206.     PopRetCode ok;
  207.     char buf[BUFSIZ];
  208.     int totalsize;
  209.     
  210.     socket.printf("STAT\n");
  211.     ok = POP3_ok(buf,socket);
  212.     if (ok == ps_success)
  213.     sscanfT(buf,"%d %d",msgcount,&totalsize);
  214.     else
  215.     areas.mailPrintf1( 1,"%s\n",buf);
  216.  
  217. #ifdef DEBUG
  218.     printfT( "POP3_sendStat: %d, %d\n", *msgcount,totalsize );
  219. #endif
  220.  
  221.     return(ok);
  222. }   // POP3_sendStat
  223.  
  224.  
  225.  
  226.  
  227. /*********************************************************************
  228.   function:      POP3_sendRetr
  229.   description:   send the RETR command to the POP3 server.
  230.   arguments:     
  231.     msgnum       message ID number
  232.     socket       socket to which the POP3 server is connected.
  233.  
  234.   return value:  return code from POP3_ok.
  235.   calls:         POP3_ok, SockPrintf
  236.  *********************************************************************/
  237.  
  238. static PopRetCode POP3_sendRetr(int msgnum, TSocket &socket)
  239. {
  240.     PopRetCode ok;
  241.     char buf[BUFSIZ];
  242.  
  243. #ifdef TRACE
  244.     printfT( "POP3_sendRetr(%d,.)\n",msgnum );
  245. #endif
  246.     socket.printf("RETR %d\n",msgnum);
  247.     ok = POP3_ok(buf,socket);
  248.     if (ok != ps_success)
  249.     areas.mailPrintf1( 1,"%s\n",buf);
  250.  
  251.     return(ok);
  252. }   // POP3_sendRetr
  253.  
  254.  
  255.  
  256. /*********************************************************************
  257.   function:      POP3_sendDele
  258.   description:   send the DELE command to the POP3 server.
  259.   arguments:     
  260.     msgnum       message ID number
  261.     socket       socket to which the POP3 server is connected.
  262.  
  263.   return value:  return code from POP3_ok.
  264.   calls:         POP3_ok, SockPrintF.
  265.  *********************************************************************/
  266.  
  267. static PopRetCode POP3_sendDele(int msgnum, TSocket &socket)
  268. {
  269.     PopRetCode ok;
  270.     char buf[BUFSIZ];
  271.  
  272. #ifdef TRACE
  273.     printfT( "POP3_sendDele(%d,.)\n", msgnum );
  274. #endif
  275.     socket.printf("DELE %d\n",msgnum);
  276.     ok = POP3_ok(buf,socket);
  277.     if (ok != ps_success)
  278.     areas.mailPrintf1(1,"%s\n",buf);
  279.  
  280.     return(ok);
  281. }   // POP3_sendDele
  282.  
  283.  
  284.  
  285. /*********************************************************************
  286.   function:      POP3_readmsg
  287.   description:   Read the message content as described in RFC 1225.
  288.                  RETR with reply evaluation has been done before
  289.   arguments:     
  290.     socket       ... to which the server is connected.
  291.     mboxfd       open file descriptor to which the retrieved message will
  292.                  be written.  
  293.     topipe       true if we're writing to the system mailbox pipe.
  294.  
  295.   return value:  zero if success else PS_* return code.
  296.   calls:         SockGets.
  297.  *********************************************************************/
  298.  
  299. static PopRetCode POP3_readmsg(TSocket &socket, TFile &outf)
  300. {
  301.     char buf[BUFSIZ];
  302.     char *bufp;
  303.     int KbRead = 0;
  304.     int OldKbRead = -1;
  305.     int inHeader = 1;
  306.     int firstFromTo = 1;
  307.  
  308.     //
  309.     //  read the message content from the server
  310.     //
  311.     OldKbRead = -1;
  312.     outf.seek(0L,SEEK_SET);
  313.     for (;;) {
  314.     if (socket.gets(buf,sizeof(buf)) == NULL) {
  315.         return ps_socket;
  316.     }
  317.     bufp = buf;
  318.     if (*bufp == '\0')
  319.         inHeader = 0;
  320.     else if (*bufp == '.') {
  321.         bufp++;
  322.         if (*bufp == 0)
  323.         break;     // end of message
  324.     }
  325.     outf.printf( "%s\n",bufp );
  326.  
  327.     if (inHeader  &&  (isHeader(bufp,"From")  ||  isHeader(bufp,"To"))) {
  328.         areas.mailPrintf1( 0,"%s: %s%s\n", progname,firstFromTo ? "" : "  ", bufp );
  329.         firstFromTo = 0;
  330.         OldKbRead = -1;
  331.     }
  332.  
  333.     KbRead = (TSocket::getBytesRcvd() + TSocket::getBytesXmtd()) / 1000;
  334.     if (KbRead != OldKbRead) {
  335.         printfT( "(%05dk)\b\b\b\b\b\b\b\b", KbRead );
  336.         OldKbRead = KbRead;
  337.     }
  338.     }
  339.  
  340.     return ps_success;
  341. }   // POP3_readmsg
  342.  
  343.  
  344.  
  345. ////////////////////////////////////////////////////////////////////////////////
  346. ////////////////////////////////////////////////////////////////////////////////
  347.  
  348.  
  349.  
  350. static void pop3Cleanup( TSocket &socket, PopRetCode ok )
  351. {
  352. #ifdef TRACE
  353.     printfT( "pop3CleanUp(.,%d)\n",ok );
  354. #endif
  355.     if (ok != ps_success  &&  ok != ps_socket)
  356.     POP3_sendQuit(socket);
  357.     
  358.     if (ok == ps_socket) 
  359.     perror("doPOP3: cleanUp");
  360.  
  361.     socket.close();
  362. }   // pop3Cleanup
  363.  
  364.  
  365.  
  366. static int pop3Connect( TSocket &socket, const char *host, const char *userid,
  367.             const char *password, int port )
  368. //
  369. //  opens the socket and returns number of messages in mailbox,
  370. //  on error -1 is returned
  371. //
  372. {
  373.     static int firstCall = 1;
  374.     PopRetCode ok;
  375.     int count;
  376.  
  377. #ifdef TRACE
  378.     printfT( "pop3Connect(.,%s,%s,.,%d )\n", host,userid,port );
  379. #endif
  380.     if (socket.open( host,"pop3","tcp",port ) < 0) {
  381.     if (firstCall)
  382.         areas.mailPrintf1( 1,"%s: cannot connect to pop3 server %s\n", progname,host);
  383.     firstCall = 0;
  384.     return -1;
  385.     }
  386.  
  387.     if (firstCall)
  388.     areas.mailPrintf1( 1,"%s: connected to pop3 server %s\n",progname,host );
  389.     
  390.     ok = POP3_ok(NULL, socket);
  391.     if (ok != ps_success) {
  392.     if (ok != ps_socket)
  393.         POP3_sendQuit(socket);
  394.     socket.close();
  395.     firstCall = 0;
  396.     return -1;
  397.     }
  398.  
  399.     /* try to get authorized */
  400.     ok = POP3_Auth(userid, password, socket);
  401.     if (ok != ps_success) {
  402.     pop3Cleanup( socket,ok );
  403.     return -1;
  404.     }
  405.  
  406.     /* find out how many messages are waiting */
  407.     ok = POP3_sendStat(&count, socket);
  408.     if (ok != ps_success) {
  409.     pop3Cleanup( socket,ok );
  410.     return -1;
  411.     }
  412.  
  413.     /* show them how many messages we'll be downloading */
  414.     if (firstCall)
  415.     areas.mailPrintf1( 1,"%s: you have %d mail message%s\n", progname, count,
  416.                (count == 1) ? "" : "s");
  417.     firstCall = 0;
  418.     return count;
  419. }   // pop3Connect
  420.  
  421.  
  422.  
  423. static int pop3WriteMail( TAreasMail &msgF, TFile &inF )
  424. //
  425. //  Copy the mail content from the temporary to the SOUP file
  426. //  returns 1 on success, 0 otherwise
  427. //
  428. {
  429.     long msgSize;
  430.     long toRead, wasRead;
  431.     char buf[4096];   // 4096 = good size for file i/o
  432.     int  res;
  433.     TFileTmp tmpF;
  434.  
  435.     //
  436.     //  Get message size.
  437.     //
  438.     msgSize = inF.tell();
  439.     if (msgSize <= 0)
  440.     return 0;    // Skip empty messages
  441.  
  442.     msgF.msgStart( "Email","bn" );
  443.     
  444.     //
  445.     //  Copy article body.
  446.     //
  447.     inF.seek(0L, SEEK_SET);
  448.     res = 1;
  449.     while (msgSize > 0) {
  450.     toRead = ((size_t)msgSize < sizeof(buf)) ? msgSize : sizeof(buf);
  451.     wasRead = inF.read(buf, toRead);
  452.     if (wasRead != toRead) {
  453.         perror("read mail");
  454.         res = 0;
  455.         break;
  456.     }
  457.     assert( wasRead > 0 );
  458.     if (msgF.msgWrite(buf, wasRead) != wasRead) {
  459.         perror("write mail");
  460.         res = 0;
  461.         break;
  462.     }
  463.     msgSize -= wasRead;
  464.     }
  465.  
  466.     msgF.msgStop();
  467.     return res;
  468. }   // pop3WriteMail
  469.  
  470.  
  471.     
  472. /*********************************************************************
  473.   function:      getMail
  474.   description:   retrieve messages from the specified mail server
  475.                  using Post Office Protocol 3.
  476.  
  477.   arguments:     
  478.     options      fully-specified options (i.e. parsed, defaults invoked,
  479.                  etc).
  480.  
  481.   return value:  exit code from the set of PS_.* constants defined in 
  482.                  popclient.h
  483.   calls:
  484.  *********************************************************************/
  485.  
  486. int getMail( const char *host, const char *userid, const char *password, int port )
  487. {
  488.     PopRetCode ok;
  489.     TSocket socket;
  490.     int count;
  491.     int percent;
  492.     int msgTotal;
  493.     int msgNumber;
  494.     TFileTmp tmpF;
  495.  
  496. #ifdef TRACE
  497.     printfT( "getMail(%s,%s,.)\n", host,userid );
  498. #endif
  499.  
  500.     if (host == NULL) {
  501.     areas.mailPrintf1( 1,"%s: no pop3 server defined\n", progname );
  502.     return 0;
  503.     }
  504.  
  505.     if ( !tmpF.open("soup")) {
  506.     areas.mailPrintf1( 1,"%s: cannot open temporary mail file\n", progname );
  507.     return 0;
  508.     }
  509.  
  510.     msgTotal = pop3Connect( socket,host,userid,password,port );
  511.     if (msgTotal < 0)
  512.     return 0;
  513.  
  514.     for (count = 1, msgNumber = 1;  count <= msgTotal;  ++count, ++msgNumber) {
  515.     percent = (count * 100) / msgTotal;
  516.     printfT("\r%d%%  ", percent);
  517.  
  518.     ok = POP3_sendRetr(msgNumber,socket);
  519.     if (ok != ps_success) {
  520.         pop3Cleanup( socket,ok );
  521.         return 0;
  522.     }
  523.         
  524.     ok = POP3_readmsg(socket, tmpF);
  525.     if (ok != ps_success) {
  526.         pop3Cleanup( socket,ok );
  527.         return 0;
  528.     }
  529.     if ( !pop3WriteMail( areas,tmpF )) {
  530.         pop3Cleanup( socket, ps_success );
  531.         areas.mailPrintf1( 1,"%s: cannot copy mail into SOUP file\n", progname );
  532.         return 0;
  533.     }
  534.         
  535.     if ( !readOnly) {
  536.         ok = POP3_sendDele(msgNumber,socket);
  537.         if (ok != ps_success) {
  538.         pop3Cleanup( socket,ok );
  539.         return 0;
  540.         }
  541.     }
  542.     if (forceMailDelete  &&  count < msgTotal) {
  543.         int res;
  544.         
  545.         POP3_sendQuit( socket );
  546.         socket.close();
  547.         res = pop3Connect(socket,host,userid,password,port);
  548.         if (res < 0) {
  549.         areas.mailPrintf1( 1,"%s: cannot reconnect to pop3 server %s\n",progname,host );
  550.         return 0;
  551.         }
  552.         else if (res == 0)
  553.         msgTotal = 0;    // msg vanished??
  554.         msgNumber = 0;
  555.     }
  556.     }
  557.     printfT( "\r                    \r" );
  558.     ok = POP3_sendQuit(socket);
  559.     socket.close();
  560.     return 1;
  561. }   // getMail
  562.