X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fpop3client%2Fserv_pop3client.c;h=8a674c77f4df847f429c2ad479490c23ff795c43;hb=0387f48886a9395d89eaca01cd40ab751610426f;hp=658644d57ff39376b3a512e8f1cc8afef52c23c0;hpb=86e4b759e84c17ef8e035af2695c081660aa80d5;p=citadel.git diff --git a/citadel/modules/pop3client/serv_pop3client.c b/citadel/modules/pop3client/serv_pop3client.c index 658644d57..8a674c77f 100644 --- a/citadel/modules/pop3client/serv_pop3client.c +++ b/citadel/modules/pop3client/serv_pop3client.c @@ -1,7 +1,7 @@ /* * Consolidate mail from remote POP3 accounts. * - * Copyright (c) 2007-2017 by the citadel.org team + * Copyright (c) 2007-2020 by the citadel.org team * * This program is open source software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -49,46 +49,47 @@ #include "database.h" #include "citadel_dirs.h" +struct p3cq { // module-local queue of pop3 client work that needs processing + struct p3cq *next; + char *room; + char *host; + char *user; + char *pass; + int keep; + long interval; +}; -struct CitContext pop3_client_CC; static int doing_pop3client = 0; - - -// This is how we form a USETABLE record for pop3 client -// -// StrBufPrintf(RecvMsg->CurrMsg->MsgUID, -// "pop3/%s/%s:%s@%s", -// ChrPtr(RecvMsg->RoomName), -// ChrPtr(RecvMsg->CurrMsg->MsgUIDL), -// RecvMsg->IO.ConnectMe->User, -// RecvMsg->IO.ConnectMe->Host -// ); - +struct p3cq *p3cq = NULL; /* * Process one mailbox. */ -void pop3client_one_mailbox(struct ctdlroom *qrbuf, const char *host, const char *user, const char *pass, int keep, long interval) +void pop3client_one_mailbox(char *room, const char *host, const char *user, const char *pass, int keep, long interval) { - syslog(LOG_DEBUG, "\033[33mpop3client: room=<%s> host=<%s> user=<%s> pass=<%s> keep=<%d> interval=<%ld>\033[0m", - qrbuf->QRname, host, user, pass, keep, interval - ); + syslog(LOG_DEBUG, "pop3client: room=<%s> host=<%s> user=<%s> keep=<%d> interval=<%ld>", room, host, user, keep, interval); char url[SIZ]; CURL *curl; CURLcode res = CURLE_OK; - int is_pop3s = 0; + StrBuf *Uidls = NULL; + int i; + char cmd[1024]; curl = curl_easy_init(); if (!curl) { return; } + Uidls = NewStrBuf(); + curl_easy_setopt(curl, CURLOPT_USERNAME, user); curl_easy_setopt(curl, CURLOPT_PASSWORD, pass); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlFillStrBuf_callback); // What to do with downloaded data + curl_easy_setopt(curl, CURLOPT_WRITEDATA, Uidls); // Give it our StrBuf to work with curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "UIDL"); /* Try POP3S (SSL encrypted) first */ @@ -96,23 +97,74 @@ void pop3client_one_mailbox(struct ctdlroom *qrbuf, const char *host, const char curl_easy_setopt(curl, CURLOPT_URL, url); res = curl_easy_perform(curl); if (res == CURLE_OK) { - is_pop3s = 1; } else { - syslog(LOG_DEBUG, "POP3S client failed: %s , trying POP3 next", curl_easy_strerror(res)); + syslog(LOG_DEBUG, "pop3client: POP3S connection failed: %s , trying POP3 next", curl_easy_strerror(res)); snprintf(url, sizeof url, "pop3://%s", host); // try unencrypted next curl_easy_setopt(curl, CURLOPT_URL, url); + FlushStrBuf(Uidls); res = curl_easy_perform(curl); } if (res != CURLE_OK) { - syslog(LOG_DEBUG, "pop3 client failed: %s", curl_easy_strerror(res)); + syslog(LOG_DEBUG, "pop3client: POP3 connection failed: %s", curl_easy_strerror(res)); curl_easy_cleanup(curl); + FreeStrBuf(&Uidls); return; } - // FIXME finish this + // If we got this far, a connection was established, we know whether it's pop3s or pop3, and UIDL is supported. + // Now go through the UIDL list and look for messages. + + int num_msgs = num_tokens(ChrPtr(Uidls), '\n'); + syslog(LOG_DEBUG, "pop3client: there are %d messages", num_msgs); + for (i=0; i 2) { + if (oneuidl[strlen(oneuidl)-1] == '\r') { + oneuidl[strlen(oneuidl)-1] = 0; + } + int this_msg = atoi(oneuidl); + char *c = strchr(oneuidl, ' '); + if (c) strcpy(oneuidl, ++c); + + // Make up the Use Table record so we can check if we've already seen this message. + StrBuf *UT = NewStrBuf(); + StrBufPrintf(UT, "pop3/%s/%s:%s@%s", room, oneuidl, user, host); + int already_seen = CheckIfAlreadySeen(UT); + FreeStrBuf(&UT); + + // Only fetch the message if we haven't seen it before. + if (already_seen == 0) { + StrBuf *TheMsg = NewStrBuf(); + snprintf(cmd, sizeof cmd, "RETR %d", this_msg); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, cmd); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, TheMsg); + res = curl_easy_perform(curl); + if (res == CURLE_OK) { + struct CtdlMessage *msg = convert_internet_message_buf(&TheMsg); + CtdlSubmitMsg(msg, NULL, room); + CM_Free(msg); + } + else { + FreeStrBuf(&TheMsg); + } + + // Unless the configuration says to keep the message on the server, delete it. + if (keep == 0) { + snprintf(cmd, sizeof cmd, "DELE %d", this_msg); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, cmd); + res = curl_easy_perform(curl); + } + } + else { + syslog(LOG_DEBUG, "pop3client: %s has already been retrieved", oneuidl); + } + } + } curl_easy_cleanup(curl); + FreeStrBuf(&Uidls); return; } @@ -123,6 +175,7 @@ void pop3client_one_mailbox(struct ctdlroom *qrbuf, const char *host, const char void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCFG) { const RoomNetCfgLine *pLine; + struct p3cq *pptr = NULL; if (server_shutting_down) return; @@ -130,15 +183,17 @@ void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *One while (pLine != NULL) { - pop3client_one_mailbox(qrbuf, - ChrPtr(pLine->Value[0]), - ChrPtr(pLine->Value[1]), - ChrPtr(pLine->Value[2]), - atoi(ChrPtr(pLine->Value[3])), - atol(ChrPtr(pLine->Value[4])) - ); + pptr = malloc(sizeof(struct p3cq)); + pptr->next = p3cq; + p3cq = pptr; + pptr->room = strdup(qrbuf->QRname); + pptr->host = strdup(ChrPtr(pLine->Value[0])); + pptr->user = strdup(ChrPtr(pLine->Value[1])); + pptr->pass = strdup(ChrPtr(pLine->Value[2])); + pptr->keep = atoi(ChrPtr(pLine->Value[3])); + pptr->interval = atol(ChrPtr(pLine->Value[4])); + pLine = pLine->next; - } } @@ -146,8 +201,7 @@ void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *One void pop3client_scan(void) { static time_t last_run = 0L; time_t fastest_scan; - - become_session(&pop3_client_CC); + struct p3cq *pptr = NULL; if (CtdlGetConfigLong("c_pop3_fastest") < CtdlGetConfigLong("c_pop3_fetch")) { fastest_scan = CtdlGetConfigLong("c_pop3_fastest"); @@ -172,9 +226,27 @@ void pop3client_scan(void) { if (doing_pop3client) return; doing_pop3client = 1; - syslog(LOG_DEBUG, "pop3client started"); + syslog(LOG_DEBUG, "pop3client: scan started"); CtdlForEachNetCfgRoom(pop3client_scan_room, NULL); - syslog(LOG_DEBUG, "pop3client ended"); + + /* + * We have to queue and process in separate phases, otherwise we leave a cursor open + */ + syslog(LOG_DEBUG, "pop3client: processing started"); + while (p3cq != NULL) { + pptr = p3cq; + p3cq = p3cq->next; + + pop3client_one_mailbox(pptr->room, pptr->host, pptr->user, pptr->pass, pptr->keep, pptr->interval); + + free(pptr->room); + free(pptr->host); + free(pptr->user); + free(pptr->pass); + free(pptr); + } + + syslog(LOG_DEBUG, "pop3client: ended"); last_run = time(NULL); doing_pop3client = 0; } @@ -184,7 +256,6 @@ CTDL_MODULE_INIT(pop3client) { if (!threading) { - CtdlFillSystemContext(&pop3_client_CC, "POP3aggr"); CtdlREGISTERRoomCfgType(pop3client, ParseGeneric, 0, 5, SerializeGeneric, DeleteGenericCfgLine); CtdlRegisterSessionHook(pop3client_scan, EVT_TIMER, PRIO_AGGR + 50); }