2 * Consolidate mail from remote POP3 accounts.
4 * Copyright (c) 2007-2017 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
20 #include <sysconfig.h>
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
27 # include <sys/time.h>
36 #include <sys/types.h>
38 #include <libcitadel.h>
41 #include "citserver.h"
44 #include "ctdl_module.h"
45 #include "clientsocket.h"
47 #include "internet_addressing.h"
49 #include "citadel_dirs.h"
50 #include "event_client.h"
53 #define POP3C_OK (strncasecmp(ChrPtr(RecvMsg->IO.IOBuf), "+OK", 3) == 0)
56 #define N ((pop3aggr*)IO->Data)->n
58 struct CitContext pop3_client_CC;
60 pthread_mutex_t POP3QueueMutex; /* locks the access to the following vars: */
61 HashList *POP3QueueRooms = NULL;
62 HashList *POP3FetchUrls = NULL;
64 typedef struct pop3aggr pop3aggr;
65 typedef eNextState(*Pop3ClientHandler)(pop3aggr* RecvMsg);
67 eNextState POP3_C_Shutdown(AsyncIO *IO);
68 eNextState POP3_C_Timeout(AsyncIO *IO);
69 eNextState POP3_C_ConnFail(AsyncIO *IO);
70 eNextState POP3_C_DNSFail(AsyncIO *IO);
71 eNextState POP3_C_DispatchReadDone(AsyncIO *IO);
72 eNextState POP3_C_DispatchWriteDone(AsyncIO *IO);
73 eNextState POP3_C_Terminate(AsyncIO *IO);
74 eReadState POP3_C_ReadServerStatus(AsyncIO *IO);
75 eNextState POP3_C_ReAttachToFetchMessages(AsyncIO *IO);
77 typedef struct __pop3_room_counter {
82 typedef enum ePOP3_C_States {
89 ReadMessageBodyFollowing,
97 typedef struct _FetchItem {
103 struct CtdlMessage *Msg;
106 void HfreeFetchItem(void *vItem)
108 FetchItem *Item = (FetchItem*) vItem;
109 FreeStrBuf(&Item->MsgUIDL);
110 FreeStrBuf(&Item->MsgUID);
116 typedef enum _POP3State {
130 ConstStr POP3States[] = {
131 {HKEY("Aggregator created")},
132 {HKEY("Reading Greeting")},
133 {HKEY("Sending User")},
134 {HKEY("Sending Password")},
136 {HKEY("Fetching Usetable")},
137 {HKEY("Get MSG ID")},
138 {HKEY("Get Message")},
140 {HKEY("Delete Upstream")},
144 static void SetPOP3State(AsyncIO *IO, POP3State State)
146 CitContext* CCC = IO->CitContext;
148 memcpy(CCC->cs_clientname, POP3States[State].Key, POP3States[State].len + 1);
159 DNSQueryParts HostLookup;
162 HashList *OtherQRnumbers;
168 StrBuf *RoomName; // TODO: fill me
171 ePOP3_C_States State;
172 HashList *MsgNumbers;
177 void DeletePOP3Aggregator(void *vptr)
179 pop3aggr *ptr = vptr;
180 DeleteHashPos(&ptr->Pos);
181 DeleteHash(&ptr->MsgNumbers);
182 // FreeStrBuf(&ptr->rooms);
183 FreeStrBuf(&ptr->pop3user);
184 FreeStrBuf(&ptr->pop3pass);
185 FreeStrBuf(&ptr->Host);
186 FreeStrBuf(&ptr->RoomName);
187 FreeURL(&ptr->IO.ConnectMe);
188 FreeStrBuf(&ptr->Url);
189 FreeStrBuf(&ptr->IO.IOBuf);
190 FreeStrBuf(&ptr->IO.SendBuf.Buf);
191 FreeStrBuf(&ptr->IO.RecvBuf.Buf);
192 DeleteAsyncMsg(&ptr->IO.ReadMsg);
193 if (((struct CitContext*)ptr->IO.CitContext)) {
194 ((struct CitContext*)ptr->IO.CitContext)->state = CON_IDLE;
195 ((struct CitContext*)ptr->IO.CitContext)->kill_me = 1;
197 FreeAsyncIOContents(&ptr->IO);
201 eNextState FinalizePOP3AggrRun(AsyncIO *IO)
204 pop3aggr *cpptr = (pop3aggr *)IO->Data;
207 "%s@%s: fetched %ld new of %d messages in %fs. bye.",
208 ChrPtr(cpptr->pop3user),
211 GetCount(cpptr->MsgNumbers),
212 IO->Now - cpptr->IOStart
215 It = GetNewHashPos(POP3FetchUrls, 0);
216 pthread_mutex_lock(&POP3QueueMutex);
218 if (GetHashPosFromKey(POP3FetchUrls, SKEY(cpptr->Url), It))
219 DeleteEntryFromHash(POP3FetchUrls, It);
221 pthread_mutex_unlock(&POP3QueueMutex);
226 eNextState FailAggregationRun(AsyncIO *IO)
231 eNextState POP3C_ReadGreeting(pop3aggr *RecvMsg)
233 AsyncIO *IO = &RecvMsg->IO;
234 SetPOP3State(IO, eGreeting);
235 /* Read the server greeting */
236 if (!POP3C_OK) return eTerminateConnection;
237 else return eSendReply;
240 eNextState POP3C_SendUser(pop3aggr *RecvMsg)
242 AsyncIO *IO = &RecvMsg->IO;
243 SetPOP3State(IO, eUser);
244 /* Identify ourselves. NOTE: we have to append a CR to each command.
245 * The LF will automatically be appended by sock_puts(). Believe it
246 * or not, leaving out the CR will cause problems if the server happens
247 * to be Exchange, which is so b0rken it actually barfs on
248 * LF-terminated newlines.
250 StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "USER %s\r\n", ChrPtr(RecvMsg->pop3user));
254 eNextState POP3C_GetUserState(pop3aggr *RecvMsg)
256 if (!POP3C_OK) return eTerminateConnection;
257 else return eSendReply;
260 eNextState POP3C_SendPassword(pop3aggr *RecvMsg)
262 AsyncIO *IO = &RecvMsg->IO;
263 SetPOP3State(IO, ePassword);
265 StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "PASS %s\r\n", ChrPtr(RecvMsg->pop3pass));
266 syslog(LOG_DEBUG, "<PASS <password>\n");
270 eNextState POP3C_GetPassState(pop3aggr *RecvMsg)
272 if (!POP3C_OK) return eTerminateConnection;
273 else return eSendReply;
276 eNextState POP3C_SendListCommand(pop3aggr *RecvMsg)
278 AsyncIO *IO = &RecvMsg->IO;
279 SetPOP3State(IO, eListing);
281 /* Get the list of messages */
282 StrBufPlain(RecvMsg->IO.SendBuf.Buf, HKEY("LIST\r\n"));
286 eNextState POP3C_GetListCommandState(pop3aggr *RecvMsg)
288 if (!POP3C_OK) return eTerminateConnection;
289 RecvMsg->MsgNumbers = NewHash(1, NULL);
295 eNextState POP3C_GetListOneLine(pop3aggr *RecvMsg)
301 FetchItem *OneMsg = NULL;
303 if ((StrLength(RecvMsg->IO.IOBuf) == 1) &&
304 (ChrPtr(RecvMsg->IO.IOBuf)[0] == '.'))
306 if (GetCount(RecvMsg->MsgNumbers) == 0)
308 //// RecvMsg->Sate = ReadQuitState;
312 RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
319 * work around buggy pop3 servers which send
320 * empty lines in their listings.
322 if ((StrLength(RecvMsg->IO.IOBuf) == 0) ||
323 !isdigit(ChrPtr(RecvMsg->IO.IOBuf)[0]))
328 OneMsg = (FetchItem*) malloc(sizeof(FetchItem));
329 memset(OneMsg, 0, sizeof(FetchItem));
330 OneMsg->MSGID = atol(ChrPtr(RecvMsg->IO.IOBuf));
332 pch = strchr(ChrPtr(RecvMsg->IO.IOBuf), ' ');
335 OneMsg->MSGSize = atol(pch + 1);
338 rc = TestValidateHash(RecvMsg->MsgNumbers);
340 syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
343 Put(RecvMsg->MsgNumbers, LKEY(OneMsg->MSGID), OneMsg, HfreeFetchItem);
345 rc = TestValidateHash(RecvMsg->MsgNumbers);
347 syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
349 //RecvMsg->State --; /* read next Line */
353 eNextState POP3_FetchNetworkUsetableEntry(AsyncIO *IO)
358 pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
359 time_t seenstamp = 0;
361 SetPOP3State(IO, eUseTable);
363 if((RecvMsg->Pos != NULL) &&
364 GetNextHashPos(RecvMsg->MsgNumbers,
370 if (server_shutting_down)
373 RecvMsg->CurrMsg = (FetchItem*)vData;
375 seenstamp = CheckIfAlreadySeen(RecvMsg->CurrMsg->MsgUID,
377 EvGetNow(IO) - USETABLE_ANTIEXPIRE,
382 /* Item has already been seen */
383 RecvMsg->CurrMsg->NeedFetch = 0;
387 syslog(LOG_DEBUG, "NO\n");
388 RecvMsg->CurrMsg->NeedFetch = 1;
390 return NextDBOperation(&RecvMsg->IO,
391 POP3_FetchNetworkUsetableEntry);
395 /* ok, now we know them all,
396 * continue with reading the actual messages. */
397 DeleteHashPos(&RecvMsg->Pos);
398 return DBQueueEventContext(IO, POP3_C_ReAttachToFetchMessages);
402 eNextState POP3C_GetOneMessagID(pop3aggr *RecvMsg)
404 AsyncIO *IO = &RecvMsg->IO;
409 SetPOP3State(IO, eGetMsgID);
412 rc = TestValidateHash(RecvMsg->MsgNumbers);
414 syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
416 if((RecvMsg->Pos != NULL) &&
417 GetNextHashPos(RecvMsg->MsgNumbers,
422 RecvMsg->CurrMsg = (FetchItem*) vData;
423 /* Find out the UIDL of the message,
424 * to determine whether we've already downloaded it */
425 StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
426 "UIDL %ld\r\n", RecvMsg->CurrMsg->MSGID);
431 DeleteHashPos(&RecvMsg->Pos);
432 /// done receiving uidls.. start looking them up now.
433 RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
434 return EventQueueDBOperation(&RecvMsg->IO,
435 POP3_FetchNetworkUsetableEntry,
438 return eReadMore; /* TODO */
441 eNextState POP3C_GetOneMessageIDState(pop3aggr *RecvMsg)
445 rc = TestValidateHash(RecvMsg->MsgNumbers);
447 syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
450 if (!POP3C_OK) return eTerminateConnection;
451 RecvMsg->CurrMsg->MsgUIDL = NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf));
452 RecvMsg->CurrMsg->MsgUID = NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf) * 2);
454 StrBufExtract_token(RecvMsg->CurrMsg->MsgUIDL, RecvMsg->IO.IOBuf, 2, ' ');
456 StrBufPrintf(RecvMsg->CurrMsg->MsgUID,
458 ChrPtr(RecvMsg->RoomName),
459 ChrPtr(RecvMsg->CurrMsg->MsgUIDL),
460 RecvMsg->IO.ConnectMe->User,
461 RecvMsg->IO.ConnectMe->Host
468 eNextState POP3C_SendGetOneMsg(pop3aggr *RecvMsg)
470 AsyncIO *IO = &RecvMsg->IO;
475 SetPOP3State(IO, eGetMsg);
477 syslog(LOG_DEBUG, "fast forwarding to the next unknown message");
479 RecvMsg->CurrMsg = NULL;
480 while ((RecvMsg->Pos != NULL) &&
481 GetNextHashPos(RecvMsg->MsgNumbers,
485 (RecvMsg->CurrMsg = (FetchItem*) vData,
486 RecvMsg->CurrMsg->NeedFetch == 0))
489 if ((RecvMsg->CurrMsg != NULL ) && (RecvMsg->CurrMsg->NeedFetch == 1))
491 syslog(LOG_DEBUG, "fetching next");
492 /* Message has not been seen.
493 * Tell the server to fetch the message... */
494 StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "RETR %ld\r\n", RecvMsg->CurrMsg->MSGID);
498 syslog(LOG_DEBUG, "no more messages to fetch.");
499 RecvMsg->State = ReadQuitState;
500 return POP3_C_DispatchWriteDone(&RecvMsg->IO);
505 eNextState POP3C_ReadMessageBodyFollowing(pop3aggr *RecvMsg)
507 if (!POP3C_OK) return eTerminateConnection;
508 RecvMsg->IO.ReadMsg = NewAsyncMsg(HKEY("."),
509 RecvMsg->CurrMsg->MSGSize,
510 CtdlGetConfigLong("c_maxmsglen"),
519 eNextState POP3C_StoreMsgRead(AsyncIO *IO)
521 pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
523 SetPOP3State(IO, eStoreMsg);
525 syslog(LOG_DEBUG, "MARKING: %s as seen: ", ChrPtr(RecvMsg->CurrMsg->MsgUID));
526 CheckIfAlreadySeen(RecvMsg->CurrMsg->MsgUID, EvGetNow(IO), EvGetNow(IO) - USETABLE_ANTIEXPIRE, eWrite);
528 return DBQueueEventContext(&RecvMsg->IO, POP3_C_ReAttachToFetchMessages);
532 eNextState POP3C_SaveMsg(AsyncIO *IO)
535 pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
537 /* Do Something With It (tm) */
538 msgnum = CtdlSubmitMsg(RecvMsg->CurrMsg->Msg,
540 ChrPtr(RecvMsg->RoomName),
544 /* Message has been committed to the store
545 * write the uidl to the use table
546 * so we don't fetch this message again
549 CM_Free(RecvMsg->CurrMsg->Msg);
552 return NextDBOperation(&RecvMsg->IO, POP3C_StoreMsgRead);
556 eNextState POP3C_ReadMessageBody(pop3aggr *RecvMsg)
558 syslog(LOG_DEBUG, "Converting message...");
559 RecvMsg->CurrMsg->Msg = convert_internet_message_buf(&RecvMsg->IO.ReadMsg->MsgBuf);
560 return EventQueueDBOperation(&RecvMsg->IO, POP3C_SaveMsg, 0);
564 eNextState POP3C_SendDelete(pop3aggr *RecvMsg)
566 AsyncIO *IO = &RecvMsg->IO;
568 SetPOP3State(IO, eDelete);
570 if (!RecvMsg->keep) {
571 StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "DELE %ld\r\n", RecvMsg->CurrMsg->MSGID);
575 RecvMsg->State = ReadMessageBodyFollowing;
576 return POP3_C_DispatchWriteDone(&RecvMsg->IO);
581 eNextState POP3C_ReadDeleteState(pop3aggr *RecvMsg)
583 RecvMsg->State = GetOneMessageIDState;
584 return POP3_C_DispatchWriteDone(&RecvMsg->IO);
588 eNextState POP3C_SendQuit(pop3aggr *RecvMsg)
590 AsyncIO *IO = &RecvMsg->IO;
591 SetPOP3State(IO, eQuit);
594 StrBufPlain(RecvMsg->IO.SendBuf.Buf,
600 eNextState POP3C_ReadQuitState(pop3aggr *RecvMsg)
602 return eTerminateConnection;
606 const long POP3_C_ConnTimeout = 1000;
607 const long DefaultPOP3Port = 110;
609 Pop3ClientHandler POP3C_ReadHandlers[] = {
613 POP3C_GetListCommandState,
614 POP3C_GetListOneLine,
615 POP3C_GetOneMessageIDState,
616 POP3C_ReadMessageBodyFollowing,
617 POP3C_ReadMessageBody,
618 POP3C_ReadDeleteState,
622 const long POP3_C_SendTimeouts[POP3C_MaxRead] = {
632 const ConstStr POP3C_ReadErrors[POP3C_MaxRead] = {
633 {HKEY("Connection broken during ")},
634 {HKEY("Connection broken during ")},
635 {HKEY("Connection broken during ")},
636 {HKEY("Connection broken during ")},
637 {HKEY("Connection broken during ")},
638 {HKEY("Connection broken during ")},
639 {HKEY("Connection broken during ")},
640 {HKEY("Connection broken during ")}
643 Pop3ClientHandler POP3C_SendHandlers[] = {
644 NULL, /* we don't send a greeting */
647 POP3C_SendListCommand,
649 POP3C_GetOneMessagID,
656 const long POP3_C_ReadTimeouts[] = {
668 /*****************************************************************************/
669 /* POP3 CLIENT DISPATCHER */
670 /*****************************************************************************/
672 void POP3SetTimeout(eNextState NextTCPState, pop3aggr *pMsg)
674 double Timeout = 0.0;
676 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
678 switch (NextTCPState) {
682 Timeout = POP3_C_SendTimeouts[pMsg->State];
684 if (pMsg->State == eDATABody) {
685 / * if we're sending a huge message, we need more time. * /
686 Timeout += StrLength(pMsg->msgtext) / 1024;
692 Timeout = POP3_C_ReadTimeouts[pMsg->State];
694 if (pMsg->State == eDATATerminateBody) {
696 * some mailservers take a nap before accepting the message
697 * content inspection and such.
699 Timeout += StrLength(pMsg->msgtext) / 1024;
710 case eTerminateConnection:
713 case eReadMore://// TODO
716 SetNextTimeout(&pMsg->IO, Timeout);
718 eNextState POP3_C_DispatchReadDone(AsyncIO *IO)
720 /* syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__); to noisy anyways. */
721 pop3aggr *pMsg = IO->Data;
724 rc = POP3C_ReadHandlers[pMsg->State](pMsg);
727 POP3SetTimeout(rc, pMsg);
730 eNextState POP3_C_DispatchWriteDone(AsyncIO *IO)
732 pop3aggr *pMsg = IO->Data;
735 /* syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__); to noisy anyways. */
736 rc = POP3C_SendHandlers[pMsg->State](pMsg);
737 POP3SetTimeout(rc, pMsg);
742 /*****************************************************************************/
743 /* POP3 CLIENT ERROR CATCHERS */
744 /*****************************************************************************/
745 eNextState POP3_C_Terminate(AsyncIO *IO)
747 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
748 FinalizePOP3AggrRun(IO);
751 eNextState POP3_C_TerminateDB(AsyncIO *IO)
753 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
754 FinalizePOP3AggrRun(IO);
757 eNextState POP3_C_Timeout(AsyncIO *IO)
759 pop3aggr *pMsg = IO->Data;
761 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
762 StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
763 return FailAggregationRun(IO);
765 eNextState POP3_C_ConnFail(AsyncIO *IO)
767 pop3aggr *pMsg = (pop3aggr *)IO->Data;
769 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
770 StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
771 return FailAggregationRun(IO);
773 eNextState POP3_C_DNSFail(AsyncIO *IO)
775 pop3aggr *pMsg = (pop3aggr *)IO->Data;
777 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
778 StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
779 return FailAggregationRun(IO);
781 eNextState POP3_C_Shutdown(AsyncIO *IO)
783 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
784 FinalizePOP3AggrRun(IO);
790 * lineread Handler; understands when to read more POP3 lines, and when this is a one-lined reply.
792 eReadState POP3_C_ReadServerStatus(AsyncIO *IO)
794 eReadState Finished = eBufferNotEmpty;
796 switch (IO->NextState) {
801 case eTerminateConnection:
803 Finished = eReadFail;
810 Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
814 Finished = CtdlReadMessageBodyAsync(IO);
820 /*****************************************************************************
821 * So we connect our Server IP here. *
822 *****************************************************************************/
823 eNextState POP3_C_ReAttachToFetchMessages(AsyncIO *IO)
825 pop3aggr *cpptr = IO->Data;
827 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
828 ////??? cpptr->State ++;
829 if (cpptr->Pos == NULL)
830 cpptr->Pos = GetNewHashPos(cpptr->MsgNumbers, 0);
832 POP3_C_DispatchWriteDone(IO);
833 ReAttachIO(IO, cpptr, 0);
834 IO->NextState = eReadMessage;
835 return IO->NextState;
838 eNextState pop3_connect_ip(AsyncIO *IO)
840 pop3aggr *cpptr = IO->Data;
842 if (cpptr->IOStart == 0.0) /* whith or without DNS? */
843 cpptr->IOStart = IO->Now;
845 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
847 return EvConnectSock(IO,
849 POP3_C_ReadTimeouts[0],
853 eNextState pop3_get_one_host_ip_done(AsyncIO *IO)
855 pop3aggr *cpptr = IO->Data;
856 struct hostent *hostent;
860 hostent = cpptr->HostLookup.VParsedDNSReply;
861 if ((cpptr->HostLookup.DNSStatus == ARES_SUCCESS) &&
862 (hostent != NULL) ) {
863 memset(&cpptr->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
864 if (cpptr->IO.ConnectMe->IPv6) {
865 memcpy(&cpptr->IO.ConnectMe->Addr.sin6_addr.s6_addr,
866 &hostent->h_addr_list[0],
867 sizeof(struct in6_addr));
869 cpptr->IO.ConnectMe->Addr.sin6_family =
871 cpptr->IO.ConnectMe->Addr.sin6_port =
872 htons(DefaultPOP3Port);
875 struct sockaddr_in *addr =
876 (struct sockaddr_in*)
877 &cpptr->IO.ConnectMe->Addr;
879 memcpy(&addr->sin_addr.s_addr,
880 hostent->h_addr_list[0],
883 addr->sin_family = hostent->h_addrtype;
884 addr->sin_port = htons(DefaultPOP3Port);
886 return pop3_connect_ip(IO);
892 eNextState pop3_get_one_host_ip(AsyncIO *IO)
894 pop3aggr *cpptr = IO->Data;
896 cpptr->IOStart = IO->Now;
898 syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
901 "POP3 client[%ld]: looking up %s-Record %s : %d ...\n",
903 (cpptr->IO.ConnectMe->IPv6)? "aaaa": "a",
904 cpptr->IO.ConnectMe->Host,
905 cpptr->IO.ConnectMe->Port);
907 QueueQuery((cpptr->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a,
908 cpptr->IO.ConnectMe->Host,
911 pop3_get_one_host_ip_done);
912 IO->NextState = eReadDNSReply;
913 return IO->NextState;
918 int pop3_do_fetching(pop3aggr *cpptr)
920 AsyncIO *IO = &cpptr->IO;
925 POP3_C_ReadServerStatus,
927 POP3_C_DispatchWriteDone,
928 POP3_C_DispatchReadDone,
935 safestrncpy(((CitContext *)cpptr->IO.CitContext)->cs_host,
937 sizeof(((CitContext *)cpptr->IO.CitContext)->cs_host));
939 if (cpptr->IO.ConnectMe->IsIP) {
940 QueueEventContext(&cpptr->IO,
944 QueueEventContext(&cpptr->IO,
945 pop3_get_one_host_ip);
951 * Scan a room's netconfig to determine whether it requires POP3 aggregation
953 void pop3client_scan_room(struct ctdlroom *qrbuf, void *data, OneRoomNetCfg *OneRNCFG)
955 const RoomNetCfgLine *pLine;
958 pthread_mutex_lock(&POP3QueueMutex);
959 if (GetHash(POP3QueueRooms, LKEY(qrbuf->QRnumber), &vptr))
961 pthread_mutex_unlock(&POP3QueueMutex);
963 "pop3client: [%ld] %s already in progress.",
968 pthread_mutex_unlock(&POP3QueueMutex);
970 if (server_shutting_down) return;
972 pLine = OneRNCFG->NetConfigs[pop3client];
974 while (pLine != NULL)
978 // BEGIN diagnostics for ajc 2017mar08
979 syslog(LOG_DEBUG, "pop3client: room=<%s> host=<%s> user=<%s> pass=<%s> keep=<%d> interval=<%ld>",
981 ChrPtr(pLine->Value[0]),
982 ChrPtr(pLine->Value[1]),
983 ChrPtr(pLine->Value[2]),
984 atoi(ChrPtr(pLine->Value[3])),
985 atol(ChrPtr(pLine->Value[4]))
987 // END diagnostics for ajc 2017mar08
991 cptr = (pop3aggr *) malloc(sizeof(pop3aggr));
992 memset(cptr, 0, sizeof(pop3aggr));
993 cptr->RoomName = NewStrBufPlain(qrbuf->QRname, -1);
994 cptr->pop3user = NewStrBufDup(pLine->Value[1]);
995 cptr->pop3pass = NewStrBufDup(pLine->Value[2]);
996 cptr->Url = NewStrBuf();
997 cptr->Host = NewStrBufDup(pLine->Value[0]);
999 cptr->keep = atol(ChrPtr(pLine->Value[3]));
1000 cptr->interval = atol(ChrPtr(pLine->Value[4]));
1002 StrBufAppendBufPlain(cptr->Url, HKEY("pop3://"), 0);
1003 StrBufUrlescUPAppend(cptr->Url, cptr->pop3user, NULL);
1004 StrBufAppendBufPlain(cptr->Url, HKEY(":"), 0);
1005 StrBufUrlescUPAppend(cptr->Url, cptr->pop3pass, NULL);
1006 StrBufAppendBufPlain(cptr->Url, HKEY("@"), 0);
1007 StrBufAppendBuf(cptr->Url, cptr->Host, 0);
1008 StrBufAppendBufPlain(cptr->Url, HKEY("/"), 0);
1009 StrBufUrlescAppend(cptr->Url, cptr->RoomName, NULL);
1011 ParseURL(&cptr->IO.ConnectMe, cptr->Url, 110);
1013 cptr->n = Pop3ClientID++;
1014 pthread_mutex_lock(&POP3QueueMutex);
1018 DeletePOP3Aggregator);
1020 pthread_mutex_unlock(&POP3QueueMutex);
1021 pLine = pLine->next;
1026 static int doing_pop3client = 0;
1028 void pop3client_scan(void) {
1029 static time_t last_run = 0L;
1030 time_t fastest_scan;
1037 become_session(&pop3_client_CC);
1039 if (CtdlGetConfigLong("c_pop3_fastest") < CtdlGetConfigLong("c_pop3_fetch"))
1040 fastest_scan = CtdlGetConfigLong("c_pop3_fastest");
1042 fastest_scan = CtdlGetConfigLong("c_pop3_fetch");
1045 * Run POP3 aggregation no more frequently than once every n seconds
1047 if ( (time(NULL) - last_run) < fastest_scan ) {
1052 * This is a simple concurrency check to make sure only one pop3client
1053 * run is done at a time. We could do this with a mutex, but since we
1054 * don't really require extremely fine granularity here, we'll do it
1055 * with a static variable instead.
1057 if (doing_pop3client) return;
1058 doing_pop3client = 1;
1060 syslog(LOG_DEBUG, "pop3client started");
1061 CtdlForEachNetCfgRoom(pop3client_scan_room, NULL);
1063 pthread_mutex_lock(&POP3QueueMutex);
1064 it = GetNewHashPos(POP3FetchUrls, 0);
1065 while (!server_shutting_down &&
1066 GetNextHashPos(POP3FetchUrls, it, &len, &Key, &vrptr) &&
1068 cptr = (pop3aggr *)vrptr;
1069 if (cptr->RefCount == 0) {
1070 if (!pop3_do_fetching(cptr)) {
1071 DeletePOP3Aggregator(cptr);
1076 pthread_mutex_unlock(&POP3QueueMutex);
1078 syslog(LOG_DEBUG, "pop3client ended");
1079 last_run = time(NULL);
1080 doing_pop3client = 0;
1084 void pop3_cleanup(void)
1086 while (doing_pop3client != 0) ;
1087 DeleteHash(&POP3FetchUrls);
1088 DeleteHash(&POP3QueueRooms);
1093 CTDL_MODULE_INIT(pop3client)
1097 CtdlFillSystemContext(&pop3_client_CC, "POP3aggr");
1098 CtdlREGISTERRoomCfgType(pop3client, ParseGeneric, 0, 5, SerializeGeneric, DeleteGenericCfgLine);
1099 pthread_mutex_init(&POP3QueueMutex, NULL);
1100 POP3QueueRooms = NewHash(1, lFlathash);
1101 POP3FetchUrls = NewHash(1, NULL);
1102 CtdlRegisterSessionHook(pop3client_scan, EVT_TIMER, PRIO_AGGR + 50);
1103 CtdlRegisterEVCleanupHook(pop3_cleanup);
1106 /* return our module id for the log */
1107 return "pop3client";