"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 }