00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <strstream>
00021 #include <fstream>
00022 #include <string>
00023 #include <vector>
00024 #include <sys/time.h>
00025 #include <stdlib.h>
00026 #include <netinet/in.h>
00027 #include <unistd.h>
00028 #include <arpa/inet.h>
00029 #include <netdb.h>
00030 #include <sys/types.h>
00031 #include <sys/socket.h>
00032 #include <sys/stat.h>
00033 #include <stdio.h>
00034 #include <ctype.h>
00035 #include <regex.h>
00036 #include <fcntl.h>
00037 #include <string.h>
00038 #include "Account.hh"
00039 #include "PopAccount.hh"
00040 #include "Preferences.hh"
00041 #include "Feedback.hh"
00042 #include "mailfilter.hh"
00043 #include "config.h"
00044
00045
00046 using namespace std;
00047 using namespace acc;
00048
00049 namespace pop {
00050
00051 PopAccount::PopAccount(string server, string user, string password, int port, pref::Preferences* newPrefs) {
00052 this->server = server;
00053 this->user = user;
00054 this->pass = password;
00055 this->port = port;
00056 prefs = newPrefs;
00057 mySocket = 0;
00058 report = new fb::Feedback(prefs->getLogfile(), prefs->getMode());
00059 }
00060
00061
00062 PopAccount::~PopAccount() {
00063
00064
00065 delete(report);
00066 }
00067
00068
00069 int PopAccount::check() {
00070 char cmd[32];
00071 string result;
00072 int messages = 0, size = 0, connectError = 0, loginError = 0;
00073
00074
00075 if ( ((connectError = connectSocket()) == 0) && ((loginError = login()) == 0) ) {
00076
00077
00078 try {
00079 if (sendCommand("STAT\r\n") <= 0) {
00080 logout();
00081 disconnectSocket();
00082 return(CMD_STAT_FAILED);
00083 }
00084 else {
00085 result = receiveResult();
00086
00087 if (serverResult(result) == FALSE) {
00088 logout();
00089 disconnectSocket();
00090 return(CMD_STAT_FAILED);
00091 }
00092 else {
00093 messages = atoi(getWord(result, 1).c_str());
00094 char status[MAX_BYTES];
00095 #ifdef HAVE_SNPRINTF
00096 snprintf(status, sizeof(status), "%s: Examining %d message(s).\n", PACKAGE, messages);
00097 #else
00098 sprintf(status, "%s: Examining %d message(s).\n", PACKAGE, messages);
00099 #endif
00100 report->message(status);
00101
00102
00103 for (int i = 0; i < messages; i++) {
00104
00105
00106 #ifdef HAVE_SNPRINTF
00107 snprintf(cmd, sizeof(cmd), "LIST %d\r\n", i + 1);
00108 #else
00109 sprintf(cmd, "LIST %d\r\n", i + 1);
00110 #endif
00111 if (sendCommand(cmd) <= 0) {
00112 logout();
00113 disconnectSocket();
00114 return(CMD_LIST_FAILED);
00115 }
00116 else {
00117 result = receiveResult();
00118
00119 if (serverResult(result) == FALSE) {
00120 disconnectSocket();
00121 return(CMD_LIST_FAILED);
00122 }
00123 else
00124 size = atoi(getWord(result, 2).c_str());
00125 }
00126
00127
00128 #ifdef HAVE_SNPRINTF
00129 snprintf(cmd, sizeof(cmd), "TOP %d 0\r\n", i + 1);
00130 #else
00131 sprintf(cmd, "TOP %d 0\r\n", i + 1);
00132 #endif
00133 if (sendCommand(cmd) <= 0) {
00134 logout();
00135 disconnectSocket();
00136 return(CMD_TOP_FAILED);
00137 }
00138 else {
00139
00140
00141
00142
00143
00144 result = receiveResult(cmd);
00145
00146
00147 if (serverResult(result) == TRUE) {
00148 if (Account::storeMessageHeader(result, i + 1, size) < 0) {
00149 logout();
00150 disconnectSocket();
00151 return(MALFORMED_HEADER_FAILURE);
00152 }
00153 }
00154 else {
00155 logout();
00156 disconnectSocket();
00157 return(CMD_TOP_FAILED);
00158 }
00159 }
00160 }
00161 }
00162 }
00163 }
00164 catch(...) {
00165 disconnectSocket();
00166 throw;
00167 }
00168
00169
00170 vector<Header>::iterator curMessage = Account::headers.begin();
00171 while(curMessage != Account::headers.end()) {
00172
00173 if ( (prefs->getMaxsize() > 0) && (curMessage->size > prefs->getMaxsize()) ) {
00174 if (deleteMessage(curMessage->number) < 0)
00175 return(CMD_DELE_FAILED);
00176 else
00177 report->message((string)PACKAGE + (string)": Deleted " + curMessage->sender.c_str() + (string)": " + curMessage->subject.c_str() + (string)", " + curMessage->date.c_str() + (string)".\n");
00178 }
00179
00180 else {
00181 vector<Line>::iterator curLine = curMessage->lines.begin();
00182
00183 while(curLine != curMessage->lines.end()) {
00184 regex_t compFilter;
00185 vector<string>::iterator curFilter = prefs->getFilters()->begin();
00186
00187
00188
00189
00190 while (curFilter != prefs->getFilters()->end()) {
00191 string currentLine = curLine->descr.c_str();
00192 currentLine.append(": ");
00193 currentLine.append(curLine->content.c_str());
00194
00195 if (prefs->getIcase() == FALSE)
00196 regcomp(&compFilter, curFilter->c_str(), REG_ICASE);
00197 else
00198 regcomp(&compFilter, curFilter->c_str(), 0);
00199
00200
00201 if (regexec(&compFilter, currentLine.c_str(), 0, NULL, 0) == 0) {
00202 try {
00203 if (deleteMessage(curMessage->number) >= 0) {
00204 report->message((string)PACKAGE + (string)": Deleted " + curMessage->sender.c_str() + (string)": " + curMessage->subject.c_str() + (string)", " + curMessage->date.c_str() + (string)".\n");
00205 curFilter = prefs->getFilters()->end();
00206 }
00207 else {
00208 logout();
00209 disconnectSocket();
00210 return(CMD_DELE_FAILED);
00211 }
00212 }
00213 catch(...) {
00214 disconnectSocket();
00215 throw;
00216 }
00217 }
00218
00219 else
00220 curFilter++;
00221 }
00222
00223 curMessage->lines.erase(curLine);
00224 }
00225 }
00226
00227 Account::headers.erase(curMessage);
00228 }
00229
00230
00231 try {
00232 logout();
00233 disconnectSocket();
00234 return 0;
00235 }
00236 catch(...) {
00237 disconnectSocket();
00238 throw;
00239 }
00240 }
00241 else if (loginError != 0) {
00242 disconnectSocket();
00243 return AUTHENTICATION_FAILURE;
00244 }
00245 else if (connectError != 0) {
00246 disconnectSocket();
00247 return connectError;
00248 }
00249 else {
00250
00251 disconnectSocket();
00252 throw UnknownError();
00253 }
00254 }
00255
00256
00257
00258
00259 int PopAccount::connectSocket() {
00260 struct sockaddr_in socketAddress;
00261 struct hostent *myHost;
00262
00263
00264 if ( (mySocket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
00265 return(SOCKET_CONNECTION_FAILURE);
00266 else {
00267 myHost = gethostbyname(server.c_str());
00268
00269 if (!myHost)
00270 return(DNS_LOOKUP_FAILURE);
00271 else {
00272 memset( (char*)&socketAddress, 0, sizeof(struct sockaddr_in) );
00273 socketAddress.sin_family = AF_INET;
00274 memcpy( &(socketAddress.sin_addr.s_addr), myHost->h_addr, myHost->h_length);
00275 socketAddress.sin_port=htons(port);
00276
00277 if (connect(mySocket, (struct sockaddr*)&socketAddress, sizeof(struct sockaddr)) < 0)
00278 return(SOCKET_CONNECTION_FAILURE);
00279 }
00280 }
00281
00282 return 0;
00283 }
00284
00285
00286
00287
00288 int PopAccount::login() {
00289 char command[32];
00290
00291 try {
00292
00293 if (serverResult(receiveResult()) == TRUE) {
00294
00295
00296 #ifdef HAVE_SNPRINTF
00297 snprintf(command, sizeof(command), "USER %s\r\n", user.c_str());
00298 #else
00299 sprintf(command, "USER %s\r\n", user.c_str());
00300 #endif
00301 if (sendCommand(command) > 0) {
00302 if (serverResult(receiveResult()) == FALSE) {
00303 disconnectSocket();
00304 return -1;
00305 }
00306 }
00307 else
00308 return -1;
00309
00310
00311 #ifdef HAVE_SNPRINTF
00312 snprintf(command, sizeof(command), "PASS %s\r\n", pass.c_str());
00313 #else
00314 sprintf(command, "PASS %s\r\n", pass.c_str());
00315 #endif
00316 if (sendCommand(command) > 0) {
00317 if (serverResult(receiveResult()) == FALSE) {
00318 disconnectSocket();
00319 return -1;
00320 }
00321 }
00322 else
00323 return -1;
00324
00325 return 0;
00326 }
00327 else
00328 return -1;
00329 }
00330 catch(...) {
00331 throw;
00332 }
00333 }
00334
00335
00336
00337 int PopAccount::disconnectSocket() {
00338 return (close(mySocket));
00339 }
00340
00341
00342
00343
00344 int PopAccount::logout() {
00345 try {
00346 if ( (sendCommand("QUIT\r\n") > 0) && (serverResult(receiveResult()) == TRUE) )
00347 return 0;
00348 else
00349 return -1;
00350 }
00351 catch(...) {
00352 throw;
00353 }
00354 }
00355
00356
00357
00358
00359
00360 int PopAccount::sendCommand(string command) {
00361 char cmd[32];
00362 int error = 0;
00363
00364 if (command.length() <= 32) {
00365 strcpy(cmd, command.c_str());
00366
00367 if ( (error = write(mySocket, cmd, strlen(cmd))) == -1 )
00368 return -1;
00369 else
00370 return error;
00371 }
00372 else
00373 return -1;
00374 }
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384 string PopAccount::receiveResult(string serverCommand = "irrelevant") {
00385 static const int TIME_OUT = 10;
00386 char buffer[MAX_BYTES + 20];
00387 int flags = 0, error = 0;
00388 struct timeval tv;
00389 fd_set rfds;
00390 string input;
00391 int counter = 0, bytes = 0, last = 0;
00392
00393
00394 if ( (flags = fcntl(mySocket, F_GETFL, 0)) == -1 )
00395 throw IOException();
00396
00397
00398 if (fcntl(mySocket, F_SETFL, flags | O_NONBLOCK) == -1 )
00399 throw IOException();
00400
00401 do {
00402 memset(buffer, 0, sizeof(buffer));
00403 last = bytes;
00404 tv.tv_sec = TIME_OUT;
00405 tv.tv_usec = 0;
00406 FD_ZERO(&rfds);
00407 FD_SET(mySocket, &rfds);
00408
00409
00410 error = select(mySocket + 1, &rfds, NULL, NULL, &tv);
00411
00412 if (error < 0)
00413 throw ConnectionTimeOut();
00414 else if (error > 0 && FD_ISSET(mySocket, &rfds)) {
00415 bytes = read(mySocket, buffer, MAX_BYTES);
00416
00417 if (bytes > 0) {
00418 buffer[bytes] = 0;
00419 counter += bytes;
00420
00421 if (input.length() > 0)
00422 input.append(buffer, bytes);
00423 else
00424 input = (string)buffer;
00425 }
00426 else if (bytes == 0)
00427 break;
00428 else
00429 throw IOException();
00430 }
00431 } while ( (error > 0) &&
00432 (last > 0 && bytes > 0 && !isHeaderEnd(input)) ||
00433 ( (last > 0 || bytes == MAX_BYTES) && !isHeaderEnd(input) ) ||
00434 ( (serverCommand.find("TOP") == 0) && !isHeaderEnd(input) ) );
00435
00436
00437
00438 input[counter] = 0;
00439
00440
00441 if ( (fcntl(mySocket, F_SETFL, flags)) == -1)
00442 throw IOException();
00443
00444
00445 return(input);
00446 }
00447
00448
00449
00450
00451 int PopAccount::deleteMessage(int mess) {
00452 char cmd[32];
00453 #ifdef HAVE_SNPRINTF
00454 snprintf(cmd, sizeof(cmd), "DELE %d\r\n", mess);
00455 #else
00456 sprintf(cmd, "DELE %d\r\n", mess);
00457 #endif
00458 try {
00459 if ( (sendCommand(cmd) > 0) && (serverResult(receiveResult()) == TRUE) )
00460 return 0;
00461 else
00462 return -1;
00463 }
00464 catch(...) {
00465 throw;
00466 }
00467 }
00468
00469
00470
00471
00472 string PopAccount::getWord(const string word, int n) {
00473 int curSpace = 0, nextSpace = 0, wordEnd = 0;
00474
00475
00476 for (int i = 0; i < n; i++) {
00477 nextSpace = word.find(' ', curSpace);
00478 curSpace = nextSpace + 1;
00479 }
00480
00481
00482 if ( (wordEnd = word.find(' ', curSpace)) == -1 )
00483 wordEnd = word.length() - 1;
00484
00485 return(word.substr(curSpace, wordEnd));
00486 }
00487
00488
00489
00490 bool PopAccount::serverResult(const string serverMessage) {
00491 if (serverMessage[0] == '+')
00492 return TRUE;
00493 else
00494 return FALSE;
00495 }
00496
00497
00498
00499
00500
00501 bool PopAccount::isHeaderEnd(const string buffer) {
00502 if (buffer.length() >= 4)
00503 for (unsigned int i = 0; i+4 <= buffer.length(); i++)
00504 if ( (i+4 <= buffer.length()) && (buffer[i] == '\r') && (buffer[i+1] == '\n') && (buffer[i+2] == '.') &&
00505 (buffer[i+3] == '\r') && (buffer[i+4] == '\n') )
00506 return TRUE;
00507
00508 return FALSE;
00509 }
00510
00511 }