"Fossies" - the Fresh Open Source Software Archive

Member "postal-0.76/client.cpp" (13 Dec 2016, 10704 Bytes) of package /linux/privat/postal-0.76.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "client.cpp" see the Fossies "Dox" file reference documentation.

    1 #include "client.h"
    2 #include <cstdlib>
    3 #include <cstring>
    4 #include <unistd.h>
    5 #include "userlist.h"
    6 #include "logit.h"
    7 #include "results.h"
    8 #include "mutex.h"
    9 
   10 class clientResults : public results
   11 {
   12 public:
   13   clientResults();
   14 
   15   void imap_connection();
   16 
   17 private:
   18   virtual void childPrint();
   19   int m_imap_connections;
   20 };
   21 
   22 clientResults::clientResults()
   23  : results()
   24  , m_imap_connections(0)
   25 {
   26 }
   27 
   28 void clientResults::childPrint()
   29 {
   30   printf(",%d", m_imap_connections);
   31   m_imap_connections = 0;
   32 }
   33 
   34 void clientResults::imap_connection()
   35 {
   36   Lock l(m_mut);
   37   m_connections++;
   38   m_imap_connections++;
   39 }
   40 
   41 Thread *client::newThread(int threadNum)
   42 {
   43   return new client(threadNum, this);
   44 }
   45 
   46 int client::action(PVOID)
   47 {
   48   bool logAll = false;
   49   if(m_log && m_log->verbose())
   50     logAll = true;
   51   while(1)
   52   {
   53     string user, pass;
   54     getUser(user, pass);
   55     if(CHECK_PERCENT(m_useIMAP))
   56       m_isIMAP = true;
   57     else
   58       m_isIMAP = false;
   59     int rc = Connect(user, pass);
   60     if(rc == 0)
   61     {
   62       int msgs = 0;
   63       if(m_msgsPerConnection != 0)
   64         msgs = list();
   65       if(m_msgsPerConnection > 0 && msgs > m_msgsPerConnection)
   66         msgs = m_msgsPerConnection;
   67       if(msgs > 0)
   68       {
   69         for(int i = 1; i <= msgs && !rc; i++)
   70         {
   71           if(CHECK_PERCENT(m_downloadPercent))
   72             rc = getMsg(i, user, logAll);
   73         }
   74       }
   75       // if msgs < 0 means we already had a serious error
   76       if(!rc && msgs >= 0)
   77       {
   78         rc = disconnect();
   79       }
   80     }
   81     if(rc)
   82       sleep(10);
   83   }
   84   return 0;
   85 }
   86 
   87 client::client(int *exitCount, const char *addr, const char *ourAddr
   88        , UserList &ul, int processes, int msgsPerConnection, Logit *log
   89 #ifdef USE_SSL
   90        , int ssl
   91 #endif
   92        , TRISTATE qmail_pop, int imap, int downloadPercent, int deletePercent
   93        , Logit *debug)
   94  : tcp(exitCount, addr, 110, log
   95 #ifdef USE_SSL
   96      , ssl
   97 #endif
   98      , ourAddr, debug)
   99  , m_ul(ul)
  100  , m_maxNameLen(ul.maxNameLen() + 1)
  101  , m_namesBuf(new char[m_maxNameLen * (processes + 1)])
  102  , m_sem(new Mutex(true) )
  103  , m_res(new clientResults)
  104  , m_msgsPerConnection(msgsPerConnection)
  105  , m_useIMAP(imap)
  106  , m_isIMAP(false)
  107  , m_imapID(0)
  108  , m_qmail_pop(qmail_pop)
  109  , m_downloadPercent(downloadPercent)
  110  , m_deletePercent(deletePercent)
  111 {
  112   go(NULL, processes);
  113 }
  114 
  115 client::client(int threadNum, const client *parent)
  116  : tcp(threadNum, parent)
  117  , m_ul(parent->m_ul)
  118  , m_maxNameLen(parent->m_maxNameLen)
  119  , m_namesBuf(parent->m_namesBuf)
  120  , m_sem(parent->m_sem)
  121  , m_res(parent->m_res)
  122  , m_msgsPerConnection(parent->m_msgsPerConnection)
  123  , m_useIMAP(parent->m_useIMAP)
  124  , m_isIMAP(false)
  125  , m_imapID(0)
  126  , m_qmail_pop(parent->m_qmail_pop)
  127  , m_downloadPercent(parent->m_downloadPercent)
  128  , m_deletePercent(parent->m_deletePercent)
  129 {
  130 }
  131 
  132 client::~client()
  133 {
  134   if(getThreadNum() < 1)
  135   {
  136     delete m_namesBuf;
  137     delete m_sem;
  138   }
  139 }
  140 
  141 void client::sentData(int)
  142 {
  143 }
  144 
  145 void client::receivedData(int bytes)
  146 {
  147   m_res->dataBytes(bytes);
  148 }
  149 
  150 void client::error()
  151 {
  152   m_res->error();
  153   tcp::disconnect();
  154 }
  155 
  156 ERROR_TYPE client::readCommandResp(bool important)
  157 {
  158   char recvBuf[1024];
  159   int rc;
  160   if(m_isIMAP)
  161   {
  162     do
  163     {
  164       rc = readLine(recvBuf, sizeof(recvBuf));
  165       if(rc < 0)
  166         return ERROR_TYPE(rc);
  167     }
  168     while(recvBuf[0] == '*');
  169 
  170     if(strncmp(recvBuf, m_imapIDtxt, strlen(m_imapIDtxt))
  171        || strncmp(&recvBuf[strlen(m_imapIDtxt)], "OK", 2) )
  172     {
  173       if(important)
  174       {
  175         strtok(recvBuf, "\r\n");
  176         fprintf(stderr, "Server error:%s.\n", recvBuf);
  177         error();
  178       }
  179       return eServer;
  180     }
  181   }
  182   else
  183   {
  184     rc = readLine(recvBuf, sizeof(recvBuf));
  185     if(rc < 0)
  186       return ERROR_TYPE(rc);
  187     if(recvBuf[0] != '+')
  188     {
  189       if(important)
  190       {
  191         strtok(recvBuf, "\r\n");
  192         fprintf(stderr, "Server error:%s.\n", recvBuf);
  193         error();
  194       }
  195       return eServer;
  196     }
  197   }
  198   return eNoError;
  199 }
  200 
  201 int client::Connect(const string &user, const string &pass)
  202 {
  203   char aByte;
  204   int rc = Read(&aByte, 1, 0);
  205   if(rc != 1)
  206     return eCorrupt;
  207   if(m_isIMAP)
  208     return connectIMAP(user, pass);
  209   return connectPOP(user, pass);
  210 }
  211 
  212 int client::connectPOP(const string &user, const string &pass)
  213 {
  214   int rc = tcp::Connect();
  215   if(rc)
  216     return rc;
  217   m_res->connection();
  218   rc = readCommandResp();
  219   if(rc)
  220   {
  221     fprintf(stderr, "Can't establish connection.\n");
  222     disconnect();
  223     return rc;
  224   }
  225   // not supporting CAPA is OK.
  226   rc = sendCommandString("CAPA\r\n", false);
  227   if(rc > 1)
  228     return rc;
  229   if(rc == 0) // successful
  230   {
  231     char buf[1024];
  232     // read all lines of the capa field until the ".\r\n"
  233     do
  234     {
  235       rc = readLine(buf, sizeof(buf) - 1);
  236       if(rc < 0)
  237         return 2;
  238       buf[sizeof(buf) - 1] = '\0';
  239       strtok(buf, "\r\n");
  240 #ifdef USE_SSL
  241       if(!strcasecmp(buf, "STLS"))
  242         m_canTLS = true;
  243 #endif
  244     } while(strcmp(".", buf));
  245   }
  246   string u("user ");
  247   u += user;
  248   u += "\r\n";
  249   rc = sendCommandString(u);
  250 //printf("%s\n", u.c_str());
  251   if(rc)
  252     return rc;
  253   string p("pass ");
  254   p += pass;
  255   p += "\r\n";
  256   rc = sendCommandString(p);
  257 //printf("%s\n", p.c_str());
  258   if(rc)
  259     return rc;
  260   return 0;
  261 }
  262 
  263 int client::connectIMAP(const string &user, const string &pass)
  264 {
  265   m_imapID = 0;
  266   int rc = tcp::Connect(143);
  267   if(rc)
  268     return rc;
  269   m_res->connection();
  270   rc = sendCommandString("C CAPABILITY\r\n");
  271 // check for sub-string "STARTTLS"
  272   if(rc)
  273     return rc;
  274   string u("C LOGIN ");
  275   u += user + " " + pass;
  276   u += "\r\n";
  277   rc = sendCommandString(u);
  278   if(rc)
  279     return rc;
  280   return 0;
  281 }
  282 
  283 int client::disconnect()
  284 {
  285   int rc;
  286   if(m_isIMAP)
  287     rc = sendCommandData("C LOGOUT\r\n", 6);
  288   else
  289     rc = sendCommandData("quit\r\n", 6);
  290 
  291   if(!rc)
  292     rc = tcp::disconnect();
  293 // Comment the next line to make it that if the number of threads equals the
  294 // number of accounts then each thread always does the same account.
  295 // This makes things easy to debug and has no real down-side, I don't do this
  296 // by default because it lightens the load a little on mail servers.
  297   memset(&m_namesBuf[getThreadNum() * m_maxNameLen], 0, m_maxNameLen);
  298 // NB The sendCommandData will wait for a +OK or -ERR response, in either
  299 // case the server is (or should be ;) ready for another connection.
  300   return rc;
  301 }
  302 
  303 int client::getMsg(int num, const string &user, bool log)
  304 {
  305   char command[14];
  306   sprintf(command, "retr %d\r\n", num);
  307   int rc;
  308   rc = sendCommandData(command, strlen(command));
  309   if(rc)
  310     return rc + 1;
  311 
  312   char buf[1024];
  313 
  314   enum STATE { eHeader, eBody, eEnd } state = eHeader;
  315   m_md5.init();
  316   bool md5Error = false;
  317   string logData;
  318   char *from = NULL, *to = NULL, *subject = NULL, *msgid = NULL;
  319   char *date = NULL, *postalHash = NULL;
  320   while(state != eEnd)
  321   {
  322     rc = readLine(buf, sizeof(buf));
  323     if(rc < 0)
  324       return rc;
  325     if(log)
  326       logData += string(buf);
  327     switch(state)
  328     {
  329     case eHeader:
  330       if(!from && !strncmp(buf, "From: ", 6))
  331         from = strdup(buf);
  332       else if(!to && !strncmp(buf, "To: ", 4))
  333         to = strdup(buf);
  334       else if(!subject && !strncmp(buf, "Subject: ", 9))
  335         subject = strdup(buf);
  336       else if(!date && !strncmp(buf, "Date: ", 6))
  337         date = strdup(buf);
  338       else if(!msgid && !strncmp(buf, "Message-Id: ", 12))
  339         msgid = strdup(buf);
  340       else if(!postalHash && !strncmp(buf, "X-PostalHash: ", 14))
  341       {
  342         postalHash = strdup(buf + 14);
  343         char *tmp = strchr(postalHash, '\r');
  344         if(tmp)
  345           *tmp = 0;
  346       }
  347       else if(!strcmp(buf, "\r\n"))
  348       {
  349         state = eBody;
  350         if(from)
  351           m_md5.addData(from, strlen(from));
  352         if(to)
  353           m_md5.addData(to, strlen(to));
  354         if(subject)
  355           m_md5.addData(subject, strlen(subject));
  356         if(date)
  357           m_md5.addData(date, strlen(date));
  358         if(msgid)
  359           m_md5.addData(msgid, strlen(msgid));
  360       }
  361     break;
  362     case eBody:
  363       if(strcmp(buf, ".\r\n"))
  364       {
  365         if(postalHash)
  366           m_md5.addData(buf, strlen(buf));
  367       }
  368       else
  369       {
  370         state = eEnd;
  371         if(postalHash)
  372         {
  373           string sum(m_md5.getSum());
  374           if(strcmp(sum.c_str(), postalHash))
  375           {
  376             if(log || !m_log)
  377             {
  378               string errStr("MD5 mis-match, calculated:");
  379               errStr += sum + ", expected " + postalHash + "!  Account name "
  380                       + user + "\n";
  381               if(m_log)
  382                 m_log->Write(errStr);
  383               fprintf(stderr, "%s", errStr.c_str());
  384             }
  385             md5Error = true;
  386           }
  387           else
  388           {
  389             if(m_log)
  390             {
  391               sum += "\n";
  392               m_log->Write(sum);
  393             } 
  394           }
  395         }
  396         break;
  397       } // end else \r\n
  398     break;
  399     case eEnd:
  400     break;
  401     }
  402   }
  403 
  404   if(log)
  405     m_log->Write(logData);
  406 
  407   // if we didn't log this, but we can do logging and there was an error, then
  408   // retrieve the message again with logging so we know what went wrong.
  409   if(!log && m_log && md5Error)
  410     return getMsg(num, user, true);
  411   if(md5Error)
  412     m_res->error();
  413 
  414   if(CHECK_PERCENT(m_deletePercent))
  415   {
  416     sprintf(command, "dele %d\r\n", num);
  417     rc = sendCommandData(command, strlen(command));
  418   }
  419   if(rc)
  420     return rc;
  421   m_res->message();
  422   return 0;
  423 }
  424 
  425 int client::list()
  426 {
  427   int rc = sendCommandData("list\r\n", 6);
  428   if(rc)
  429     return rc;
  430   // The number of messages is the number of lines of response -1 because
  431   // we get one line of ".\r\n" at the end.
  432   int i = -1;
  433   char buf[1024];
  434   do
  435   {
  436     rc = readLine(buf, sizeof(buf));
  437     if(rc < 0)
  438       return rc;
  439     i++;
  440   }
  441   while(buf[0] != '.');
  442   return i;
  443 }
  444 
  445 bool client::checkUser(const char *user)
  446 {
  447   int num = getNumThreads();
  448   if(num == 1)
  449     return true;
  450   for(int i = 1; i <= num; i++)
  451   {
  452     if(i != getThreadNum() && !strcmp(user, &m_namesBuf[i * m_maxNameLen]))
  453     {
  454       return false;
  455     }
  456   }
  457   strcpy(&m_namesBuf[getThreadNum() * m_maxNameLen], user);
  458   return true;
  459 }
  460 
  461 void client::getUser(string &user, string &pass)
  462 {
  463   Lock l(*m_sem);
  464   user = m_ul.randomUser();
  465   while(!checkUser(user.c_str()) )
  466   {
  467     user = m_ul.sequentialUser();
  468   }
  469   pass = m_ul.password();
  470 }
  471 
  472 int client::pollRead()
  473 {
  474   return 0;
  475 }
  476 
  477 int client::WriteWork(PVOID buf, int size, int timeout)
  478 {
  479   return Write(buf, size, timeout);
  480 }
  481 
  482 ERROR_TYPE client::sendCommandString(const string &s, bool important)
  483 {
  484   if(m_isIMAP)
  485   {
  486     m_imapID++;
  487     sprintf(m_imapIDtxt, "%d ", m_imapID);
  488     string command(m_imapIDtxt);
  489     command += s;
  490     return sendCommandData(command.c_str(), command.size(), important);
  491   }
  492   return sendCommandData(s.c_str(), s.size(), important);
  493 }