-pthread_mutex_t POP3QueueMutex; /* locks the access to the following vars: */
-HashList *POP3QueueRooms = NULL;
-HashList *POP3FetchUrls = NULL;
-
-typedef struct pop3aggr pop3aggr;
-typedef eNextState(*Pop3ClientHandler)(pop3aggr* RecvMsg);
-
-eNextState POP3_C_Shutdown(AsyncIO *IO);
-eNextState POP3_C_Timeout(AsyncIO *IO);
-eNextState POP3_C_ConnFail(AsyncIO *IO);
-eNextState POP3_C_DNSFail(AsyncIO *IO);
-eNextState POP3_C_DispatchReadDone(AsyncIO *IO);
-eNextState POP3_C_DispatchWriteDone(AsyncIO *IO);
-eNextState POP3_C_Terminate(AsyncIO *IO);
-eReadState POP3_C_ReadServerStatus(AsyncIO *IO);
-eNextState POP3_C_ReAttachToFetchMessages(AsyncIO *IO);
-
-typedef struct __pop3_room_counter {
- int count;
- long QRnumber;
-}pop3_room_counter;
-
-typedef enum ePOP3_C_States {
- ReadGreeting,
- GetUserState,
- GetPassState,
- GetListCommandState,
- GetListOneLine,
- GetOneMessageIDState,
- ReadMessageBodyFollowing,
- ReadMessageBody,
- GetDeleteState,
- ReadQuitState,
- POP3C_MaxRead
-}ePOP3_C_States;
-
-
-typedef struct _FetchItem {
- long MSGID;
- long MSGSize;
- StrBuf *MsgUIDL;
- StrBuf *MsgUID;
- int NeedFetch;
- struct CtdlMessage *Msg;
-} FetchItem;
-
-void HfreeFetchItem(void *vItem)
-{
- FetchItem *Item = (FetchItem*) vItem;
- FreeStrBuf(&Item->MsgUIDL);
- FreeStrBuf(&Item->MsgUID);
- free(Item);
-}
-
-
-
-typedef enum _POP3State {
- eCreated,
- eGreeting,
- eUser,
- ePassword,
- eListing,
- eUseTable,
- eGetMsgID,
- eGetMsg,
- eStoreMsg,
- eDelete,
- eQuit
-} POP3State;
-
-ConstStr POP3States[] = {
- {HKEY("Aggregator created")},
- {HKEY("Reading Greeting")},
- {HKEY("Sending User")},
- {HKEY("Sending Password")},
- {HKEY("Listing")},
- {HKEY("Fetching Usetable")},
- {HKEY("Get MSG ID")},
- {HKEY("Get Message")},
- {HKEY("Store Msg")},
- {HKEY("Delete Upstream")},
- {HKEY("Quit")}
-};
-
-static void SetPOP3State(AsyncIO *IO, POP3State State)
-{
- CitContext* CCC = IO->CitContext;
- if (CCC != NULL)
- memcpy(CCC->cs_clientname, POP3States[State].Key, POP3States[State].len + 1);
-}
-
-
-struct pop3aggr {
- AsyncIO IO;
-
- long n;
- double IOStart;
- long count;
- long RefCount;
- DNSQueryParts HostLookup;
-
- long QRnumber;
- HashList *OtherQRnumbers;
-
- StrBuf *Url;
- StrBuf *pop3user;
- StrBuf *pop3pass;
- StrBuf *Host;
- StrBuf *RoomName; // TODO: fill me
- int keep;
- time_t interval;
- ePOP3_C_States State;
- HashList *MsgNumbers;
- HashPos *Pos;
- FetchItem *CurrMsg;
-};
-
-void DeletePOP3Aggregator(void *vptr)
-{
- pop3aggr *ptr = vptr;
- DeleteHashPos(&ptr->Pos);
- DeleteHash(&ptr->MsgNumbers);
-// FreeStrBuf(&ptr->rooms);
- FreeStrBuf(&ptr->pop3user);
- FreeStrBuf(&ptr->pop3pass);
- FreeStrBuf(&ptr->Host);
- FreeStrBuf(&ptr->RoomName);
- FreeURL(&ptr->IO.ConnectMe);
- FreeStrBuf(&ptr->Url);
- FreeStrBuf(&ptr->IO.IOBuf);
- FreeStrBuf(&ptr->IO.SendBuf.Buf);
- FreeStrBuf(&ptr->IO.RecvBuf.Buf);
- DeleteAsyncMsg(&ptr->IO.ReadMsg);
- if (((struct CitContext*)ptr->IO.CitContext)) {
- ((struct CitContext*)ptr->IO.CitContext)->state = CON_IDLE;
- ((struct CitContext*)ptr->IO.CitContext)->kill_me = 1;
- }
- FreeAsyncIOContents(&ptr->IO);
- free(ptr);
-}
-
-eNextState FinalizePOP3AggrRun(AsyncIO *IO)
-{
- HashPos *It;
- pop3aggr *cpptr = (pop3aggr *)IO->Data;
-
- syslog(LOG_INFO,
- "%s@%s: fetched %ld new of %d messages in %fs. bye.",
- ChrPtr(cpptr->pop3user),
- ChrPtr(cpptr->Host),
- cpptr->count,
- GetCount(cpptr->MsgNumbers),
- IO->Now - cpptr->IOStart
- );
-
- It = GetNewHashPos(POP3FetchUrls, 0);
- pthread_mutex_lock(&POP3QueueMutex);
- {
- if (GetHashPosFromKey(POP3FetchUrls, SKEY(cpptr->Url), It))
- DeleteEntryFromHash(POP3FetchUrls, It);
- }
- pthread_mutex_unlock(&POP3QueueMutex);
- DeleteHashPos(&It);
- return eAbort;
-}
-
-eNextState FailAggregationRun(AsyncIO *IO)
-{
- return eAbort;
-}
-
-eNextState POP3C_ReadGreeting(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- SetPOP3State(IO, eGreeting);
- /* Read the server greeting */
- if (!POP3C_OK) return eTerminateConnection;
- else return eSendReply;
-}
-
-eNextState POP3C_SendUser(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- SetPOP3State(IO, eUser);
- /* Identify ourselves. NOTE: we have to append a CR to each command.
- * The LF will automatically be appended by sock_puts(). Believe it
- * or not, leaving out the CR will cause problems if the server happens
- * to be Exchange, which is so b0rken it actually barfs on
- * LF-terminated newlines.
- */
- StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "USER %s\r\n", ChrPtr(RecvMsg->pop3user));
- return eReadMessage;
-}
-
-eNextState POP3C_GetUserState(pop3aggr *RecvMsg)
-{
- if (!POP3C_OK) return eTerminateConnection;
- else return eSendReply;
-}
-
-eNextState POP3C_SendPassword(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- SetPOP3State(IO, ePassword);
- /* Password */
- StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "PASS %s\r\n", ChrPtr(RecvMsg->pop3pass));
- syslog(LOG_DEBUG, "<PASS <password>\n");
- return eReadMessage;
-}
-
-eNextState POP3C_GetPassState(pop3aggr *RecvMsg)
-{
- if (!POP3C_OK) return eTerminateConnection;
- else return eSendReply;
-}
-
-eNextState POP3C_SendListCommand(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- SetPOP3State(IO, eListing);
-
- /* Get the list of messages */
- StrBufPlain(RecvMsg->IO.SendBuf.Buf, HKEY("LIST\r\n"));
- return eReadMessage;
-}
-
-eNextState POP3C_GetListCommandState(pop3aggr *RecvMsg)
-{
- if (!POP3C_OK) return eTerminateConnection;
- RecvMsg->MsgNumbers = NewHash(1, NULL);
- RecvMsg->State++;
- return eReadMore;
-}
-
-
-eNextState POP3C_GetListOneLine(pop3aggr *RecvMsg)
-{
-#if 0
- int rc;
-#endif
- const char *pch;
- FetchItem *OneMsg = NULL;
-
- if ((StrLength(RecvMsg->IO.IOBuf) == 1) &&
- (ChrPtr(RecvMsg->IO.IOBuf)[0] == '.'))
- {
- if (GetCount(RecvMsg->MsgNumbers) == 0)
- {
- //// RecvMsg->Sate = ReadQuitState;
- }
- else
- {
- RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
- }
- return eSendReply;
-
- }
-
- /*
- * work around buggy pop3 servers which send
- * empty lines in their listings.
- */
- if ((StrLength(RecvMsg->IO.IOBuf) == 0) ||
- !isdigit(ChrPtr(RecvMsg->IO.IOBuf)[0]))
- {
- return eReadMore;
- }
-
- OneMsg = (FetchItem*) malloc(sizeof(FetchItem));
- memset(OneMsg, 0, sizeof(FetchItem));
- OneMsg->MSGID = atol(ChrPtr(RecvMsg->IO.IOBuf));
-
- pch = strchr(ChrPtr(RecvMsg->IO.IOBuf), ' ');
- if (pch != NULL)
- {
- OneMsg->MSGSize = atol(pch + 1);
- }
-#if 0
- rc = TestValidateHash(RecvMsg->MsgNumbers);
- if (rc != 0)
- syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
-#endif
-
- Put(RecvMsg->MsgNumbers, LKEY(OneMsg->MSGID), OneMsg, HfreeFetchItem);
-#if 0
- rc = TestValidateHash(RecvMsg->MsgNumbers);
- if (rc != 0)
- syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
-#endif
- //RecvMsg->State --; /* read next Line */
- return eReadMore;
-}
-
-eNextState POP3_FetchNetworkUsetableEntry(AsyncIO *IO)
-{
- long HKLen;
- const char *HKey;
- void *vData;
- pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
- time_t seenstamp = 0;
-
- SetPOP3State(IO, eUseTable);
-
- if((RecvMsg->Pos != NULL) &&
- GetNextHashPos(RecvMsg->MsgNumbers,
- RecvMsg->Pos,
- &HKLen,
- &HKey,
- &vData))
- {
- if (server_shutting_down)
- return eAbort;
-
- RecvMsg->CurrMsg = (FetchItem*)vData;
-
- seenstamp = CheckIfAlreadySeen(RecvMsg->CurrMsg->MsgUID,
- EvGetNow(IO),
- EvGetNow(IO) - USETABLE_ANTIEXPIRE,
- eCheckUpdate
- );
- if (seenstamp != 0)
- {
- /* Item has already been seen */
- RecvMsg->CurrMsg->NeedFetch = 0;
- }
- else
- {
- syslog(LOG_DEBUG, "NO\n");
- RecvMsg->CurrMsg->NeedFetch = 1;
- }
- return NextDBOperation(&RecvMsg->IO,
- POP3_FetchNetworkUsetableEntry);
- }
- else
- {
- /* ok, now we know them all,
- * continue with reading the actual messages. */
- DeleteHashPos(&RecvMsg->Pos);
- return DBQueueEventContext(IO, POP3_C_ReAttachToFetchMessages);
- }
-}
-
-eNextState POP3C_GetOneMessagID(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- long HKLen;
- const char *HKey;
- void *vData;
-
- SetPOP3State(IO, eGetMsgID);
-#if 0
- int rc;
- rc = TestValidateHash(RecvMsg->MsgNumbers);
- if (rc != 0)
- syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
-#endif
- if((RecvMsg->Pos != NULL) &&
- GetNextHashPos(RecvMsg->MsgNumbers,
- RecvMsg->Pos,
- &HKLen, &HKey,
- &vData))
- {
- RecvMsg->CurrMsg = (FetchItem*) vData;
- /* Find out the UIDL of the message,
- * to determine whether we've already downloaded it */
- StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
- "UIDL %ld\r\n", RecvMsg->CurrMsg->MSGID);
- }
- else
- {
- RecvMsg->State++;
- DeleteHashPos(&RecvMsg->Pos);
- /// done receiving uidls.. start looking them up now.
- RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
- return EventQueueDBOperation(&RecvMsg->IO,
- POP3_FetchNetworkUsetableEntry,
- 0);
- }
- return eReadMore; /* TODO */
-}
-
-eNextState POP3C_GetOneMessageIDState(pop3aggr *RecvMsg)
-{
-#if 0
- int rc;
- rc = TestValidateHash(RecvMsg->MsgNumbers);
- if (rc != 0)
- syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
-#endif
-
- if (!POP3C_OK) return eTerminateConnection;
- RecvMsg->CurrMsg->MsgUIDL = NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf));
- RecvMsg->CurrMsg->MsgUID = NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf) * 2);
-
- StrBufExtract_token(RecvMsg->CurrMsg->MsgUIDL, RecvMsg->IO.IOBuf, 2, ' ');
-
- StrBufPrintf(RecvMsg->CurrMsg->MsgUID,
- "pop3/%s/%s:%s@%s",
- ChrPtr(RecvMsg->RoomName),
- ChrPtr(RecvMsg->CurrMsg->MsgUIDL),
- RecvMsg->IO.ConnectMe->User,
- RecvMsg->IO.ConnectMe->Host
- );
- RecvMsg->State --;
- return eSendReply;
-}
-
-
-eNextState POP3C_SendGetOneMsg(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- long HKLen;
- const char *HKey;
- void *vData;
-
- SetPOP3State(IO, eGetMsg);
-
- syslog(LOG_DEBUG, "fast forwarding to the next unknown message");
-
- RecvMsg->CurrMsg = NULL;
- while ((RecvMsg->Pos != NULL) &&
- GetNextHashPos(RecvMsg->MsgNumbers,
- RecvMsg->Pos,
- &HKLen, &HKey,
- &vData) &&
- (RecvMsg->CurrMsg = (FetchItem*) vData,
- RecvMsg->CurrMsg->NeedFetch == 0))
- {}
-
- if ((RecvMsg->CurrMsg != NULL ) && (RecvMsg->CurrMsg->NeedFetch == 1))
- {
- syslog(LOG_DEBUG, "fetching next");
- /* Message has not been seen.
- * Tell the server to fetch the message... */
- StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "RETR %ld\r\n", RecvMsg->CurrMsg->MSGID);
- return eReadMessage;
- }
- else {
- syslog(LOG_DEBUG, "no more messages to fetch.");
- RecvMsg->State = ReadQuitState;
- return POP3_C_DispatchWriteDone(&RecvMsg->IO);
- }
-}
-
-
-eNextState POP3C_ReadMessageBodyFollowing(pop3aggr *RecvMsg)
-{
- if (!POP3C_OK) return eTerminateConnection;
- RecvMsg->IO.ReadMsg = NewAsyncMsg(HKEY("."),
- RecvMsg->CurrMsg->MSGSize,
- CtdlGetConfigLong("c_maxmsglen"),
- NULL, -1,
- 1
- );
-
- return eReadPayload;
-}
-
-
-eNextState POP3C_StoreMsgRead(AsyncIO *IO)
-{
- pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
-
- SetPOP3State(IO, eStoreMsg);
-
- syslog(LOG_DEBUG, "MARKING: %s as seen: ", ChrPtr(RecvMsg->CurrMsg->MsgUID));
- CheckIfAlreadySeen(RecvMsg->CurrMsg->MsgUID, EvGetNow(IO), EvGetNow(IO) - USETABLE_ANTIEXPIRE, eWrite);
-
- return DBQueueEventContext(&RecvMsg->IO, POP3_C_ReAttachToFetchMessages);
-}
-
-
-eNextState POP3C_SaveMsg(AsyncIO *IO)
-{
- long msgnum;
- pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
-
- /* Do Something With It (tm) */
- msgnum = CtdlSubmitMsg(RecvMsg->CurrMsg->Msg,
- NULL,
- ChrPtr(RecvMsg->RoomName),
- 0);
- if (msgnum > 0L)
- {
- /* Message has been committed to the store
- * write the uidl to the use table
- * so we don't fetch this message again
- */
- }
- CM_Free(RecvMsg->CurrMsg->Msg);
-
- RecvMsg->count ++;
- return NextDBOperation(&RecvMsg->IO, POP3C_StoreMsgRead);
-}
-
-
-eNextState POP3C_ReadMessageBody(pop3aggr *RecvMsg)
-{
- syslog(LOG_DEBUG, "Converting message...");
- RecvMsg->CurrMsg->Msg = convert_internet_message_buf(&RecvMsg->IO.ReadMsg->MsgBuf);
- return EventQueueDBOperation(&RecvMsg->IO, POP3C_SaveMsg, 0);
-}
-
-
-eNextState POP3C_SendDelete(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
-
- SetPOP3State(IO, eDelete);
-
- if (!RecvMsg->keep) {
- StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "DELE %ld\r\n", RecvMsg->CurrMsg->MSGID);
- return eReadMessage;
- }
- else {
- RecvMsg->State = ReadMessageBodyFollowing;
- return POP3_C_DispatchWriteDone(&RecvMsg->IO);
- }
-}
-
-
-eNextState POP3C_ReadDeleteState(pop3aggr *RecvMsg)
-{
- RecvMsg->State = GetOneMessageIDState;
- return POP3_C_DispatchWriteDone(&RecvMsg->IO);
-}
-
-
-eNextState POP3C_SendQuit(pop3aggr *RecvMsg)
-{
- AsyncIO *IO = &RecvMsg->IO;
- SetPOP3State(IO, eQuit);
-
- /* Log out */
- StrBufPlain(RecvMsg->IO.SendBuf.Buf,
- HKEY("QUIT\r\n3)"));
- return eReadMessage;
-}
-
-
-eNextState POP3C_ReadQuitState(pop3aggr *RecvMsg)
-{
- return eTerminateConnection;
-}
-
-
-const long POP3_C_ConnTimeout = 1000;
-const long DefaultPOP3Port = 110;
-
-Pop3ClientHandler POP3C_ReadHandlers[] = {
- POP3C_ReadGreeting,
- POP3C_GetUserState,
- POP3C_GetPassState,
- POP3C_GetListCommandState,
- POP3C_GetListOneLine,
- POP3C_GetOneMessageIDState,
- POP3C_ReadMessageBodyFollowing,
- POP3C_ReadMessageBody,
- POP3C_ReadDeleteState,
- POP3C_ReadQuitState,
-};
-
-const long POP3_C_SendTimeouts[POP3C_MaxRead] = {
- 100,
- 100,
- 100,
- 100,
- 100,
- 100,
- 100,
- 100
-};
-const ConstStr POP3C_ReadErrors[POP3C_MaxRead] = {
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")},
- {HKEY("Connection broken during ")}
-};
-
-Pop3ClientHandler POP3C_SendHandlers[] = {
- NULL, /* we don't send a greeting */
- POP3C_SendUser,
- POP3C_SendPassword,
- POP3C_SendListCommand,
- NULL,
- POP3C_GetOneMessagID,
- POP3C_SendGetOneMsg,
- NULL,
- POP3C_SendDelete,
- POP3C_SendQuit
-};