Back to index

citadel  8.12
serv_pop3client.c
Go to the documentation of this file.
00001 /*
00002  * Consolidate mail from remote POP3 accounts.
00003  *
00004  * Copyright (c) 2007-2011 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License as published
00008  * by the Free Software Foundation; either version 3 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00019  */
00020 
00021 #include <stdlib.h>
00022 #include <unistd.h>
00023 #include <stdio.h>
00024 
00025 #if TIME_WITH_SYS_TIME
00026 # include <sys/time.h>
00027 # include <time.h>
00028 #else
00029 # if HAVE_SYS_TIME_H
00030 #  include <sys/time.h>
00031 # else
00032 #  include <time.h>
00033 # endif
00034 #endif
00035 
00036 #include <ctype.h>
00037 #include <string.h>
00038 #include <errno.h>
00039 #include <sys/types.h>
00040 #include <sys/stat.h>
00041 #include <libcitadel.h>
00042 #include "citadel.h"
00043 #include "server.h"
00044 #include "citserver.h"
00045 #include "support.h"
00046 #include "config.h"
00047 #include "ctdl_module.h"
00048 #include "clientsocket.h"
00049 #include "msgbase.h"
00050 #include "internet_addressing.h"
00051 #include "database.h"
00052 #include "citadel_dirs.h"
00053 #include "event_client.h"
00054 
00055 
00056 #define POP3C_OK (strncasecmp(ChrPtr(RecvMsg->IO.IOBuf), "+OK", 3) == 0)
00057 int Pop3ClientID = 0;
00058 int POP3ClientDebugEnabled = 0;
00059 
00060 #define N ((pop3aggr*)IO->Data)->n
00061 
00062 #define DBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (POP3ClientDebugEnabled != 0))
00063 
00064 #define EVP3C_syslog(LEVEL, FORMAT, ...)                       \
00065        DBGLOG(LEVEL) syslog(LEVEL,                             \
00066                           "IO[%ld]CC[%d][%ld]" FORMAT,         \
00067                           IO->ID, CCID, N, __VA_ARGS__)
00068 
00069 #define EVP3CM_syslog(LEVEL, FORMAT)                                  \
00070        DBGLOG(LEVEL) syslog(LEVEL,                             \
00071                           "IO[%ld]CC[%d][%ld]" FORMAT,         \
00072                           IO->ID, CCID, N)
00073 
00074 #define EVP3CQ_syslog(LEVEL, FORMAT, ...)                      \
00075        DBGLOG(LEVEL) syslog(LEVEL,                             \
00076                           "P3Q:" FORMAT,                       \
00077                           __VA_ARGS__)
00078 
00079 #define EVP3CQM_syslog(LEVEL, FORMAT)                                 \
00080        DBGLOG(LEVEL) syslog(LEVEL,                             \
00081                           "P3Q" FORMAT                         \
00082               )
00083 
00084 #define EVP3CCS_syslog(LEVEL, FORMAT, ...)                            \
00085        DBGLOG(LEVEL) syslog(LEVEL, "IO[%ld][%ld]" FORMAT,             \
00086                           IO->ID, N, __VA_ARGS__)
00087 
00088 #define EVP3CCSM_syslog(LEVEL, FORMAT)                         \
00089        DBGLOG(LEVEL) syslog(LEVEL, "IO[%ld][%ld]" FORMAT,      \
00090                           IO->ID, N)
00091 
00092 #define POP3C_DBG_SEND()                                       \
00093        EVP3C_syslog(LOG_DEBUG,                                        \
00094                    "POP3: > %s\n",                             \
00095                    ChrPtr(RecvMsg->IO.SendBuf.Buf))
00096 
00097 #define POP3C_DBG_READ()                         \
00098        EVP3C_syslog(LOG_DEBUG,                          \
00099                    "POP3: < %s\n",               \
00100                    ChrPtr(RecvMsg->IO.IOBuf))
00101 
00102 
00103 struct CitContext pop3_client_CC;
00104 
00105 pthread_mutex_t POP3QueueMutex; /* locks the access to the following vars: */
00106 HashList *POP3QueueRooms = NULL;
00107 HashList *POP3FetchUrls = NULL;
00108 
00109 typedef struct pop3aggr pop3aggr;
00110 typedef eNextState(*Pop3ClientHandler)(pop3aggr* RecvMsg);
00111 
00112 eNextState POP3_C_Shutdown(AsyncIO *IO);
00113 eNextState POP3_C_Timeout(AsyncIO *IO);
00114 eNextState POP3_C_ConnFail(AsyncIO *IO);
00115 eNextState POP3_C_DNSFail(AsyncIO *IO);
00116 eNextState POP3_C_DispatchReadDone(AsyncIO *IO);
00117 eNextState POP3_C_DispatchWriteDone(AsyncIO *IO);
00118 eNextState POP3_C_Terminate(AsyncIO *IO);
00119 eReadState POP3_C_ReadServerStatus(AsyncIO *IO);
00120 eNextState POP3_C_ReAttachToFetchMessages(AsyncIO *IO);
00121 
00122 typedef struct __pop3_room_counter {
00123        int count;
00124        long QRnumber;
00125 }pop3_room_counter;
00126 
00127 typedef enum ePOP3_C_States {
00128        ReadGreeting,
00129        GetUserState,
00130        GetPassState,
00131        GetListCommandState,
00132        GetListOneLine,
00133        GetOneMessageIDState,
00134        ReadMessageBodyFollowing,
00135        ReadMessageBody,
00136        GetDeleteState,
00137        ReadQuitState,
00138        POP3C_MaxRead
00139 }ePOP3_C_States;
00140 
00141 
00142 typedef struct _FetchItem {
00143        long MSGID;
00144        long MSGSize;
00145        StrBuf *MsgUIDL;
00146        StrBuf *MsgUID;
00147        int NeedFetch;
00148        struct CtdlMessage *Msg;
00149 } FetchItem;
00150 
00151 void HfreeFetchItem(void *vItem)
00152 {
00153        FetchItem *Item = (FetchItem*) vItem;
00154        FreeStrBuf(&Item->MsgUIDL);
00155        FreeStrBuf(&Item->MsgUID);
00156        free(Item);
00157 }
00158 
00159 struct pop3aggr {
00160        AsyncIO        IO;
00161 
00162        long n;
00163        double IOStart;
00164        long count;
00165        long RefCount;
00166        DNSQueryParts HostLookup;
00167 
00168        long           QRnumber;
00169        HashList      *OtherQRnumbers;
00170 
00171        StrBuf        *Url;
00172        StrBuf *pop3user;
00173        StrBuf *pop3pass;
00174        StrBuf *RoomName; // TODO: fill me
00175        int keep;
00176        time_t interval;
00177        ePOP3_C_States State;
00178        HashList *MsgNumbers;
00179        HashPos *Pos;
00180        FetchItem *CurrMsg;
00181 };
00182 
00183 void DeletePOP3Aggregator(void *vptr)
00184 {
00185        pop3aggr *ptr = vptr;
00186        DeleteHashPos(&ptr->Pos);
00187        DeleteHash(&ptr->MsgNumbers);
00188 //     FreeStrBuf(&ptr->rooms);
00189        FreeStrBuf(&ptr->pop3user);
00190        FreeStrBuf(&ptr->pop3pass);
00191        FreeStrBuf(&ptr->RoomName);
00192        FreeURL(&ptr->IO.ConnectMe);
00193        FreeStrBuf(&ptr->Url);
00194        FreeStrBuf(&ptr->IO.IOBuf);
00195        FreeStrBuf(&ptr->IO.SendBuf.Buf);
00196        FreeStrBuf(&ptr->IO.RecvBuf.Buf);
00197        DeleteAsyncMsg(&ptr->IO.ReadMsg);
00198        ((struct CitContext*)ptr->IO.CitContext)->state = CON_IDLE;
00199        ((struct CitContext*)ptr->IO.CitContext)->kill_me = 1;
00200        FreeAsyncIOContents(&ptr->IO);
00201        free(ptr);
00202 }
00203 
00204 eNextState FinalizePOP3AggrRun(AsyncIO *IO)
00205 {
00206        HashPos  *It;
00207        pop3aggr *cpptr = (pop3aggr *)IO->Data;
00208 
00209        EVP3C_syslog(LOG_INFO,
00210                    "%s@%s: fetched %ld new of %d messages in %fs. bye.",
00211                    ChrPtr(cpptr->pop3user),
00212                    ChrPtr(cpptr->pop3pass),
00213                    cpptr->count,
00214                    GetCount(cpptr->MsgNumbers), 
00215                    IO->Now - cpptr->IOStart 
00216               );
00217 
00218        It = GetNewHashPos(POP3FetchUrls, 0);
00219        pthread_mutex_lock(&POP3QueueMutex);
00220        {
00221               if (GetHashPosFromKey(POP3FetchUrls, SKEY(cpptr->Url), It))
00222                      DeleteEntryFromHash(POP3FetchUrls, It);
00223        }
00224        pthread_mutex_unlock(&POP3QueueMutex);
00225        DeleteHashPos(&It);
00226        return eAbort;
00227 }
00228 
00229 eNextState FailAggregationRun(AsyncIO *IO)
00230 {
00231        return eAbort;
00232 }
00233 
00234 eNextState POP3C_ReadGreeting(pop3aggr *RecvMsg)
00235 {
00236        AsyncIO *IO = &RecvMsg->IO;
00237        POP3C_DBG_READ();
00238        /* Read the server greeting */
00239        if (!POP3C_OK) return eTerminateConnection;
00240        else return eSendReply;
00241 }
00242 
00243 eNextState POP3C_SendUser(pop3aggr *RecvMsg)
00244 {
00245        AsyncIO *IO = &RecvMsg->IO;
00246        /* Identify ourselves.  NOTE: we have to append a CR to each command.
00247         *  The LF will automatically be appended by sock_puts().  Believe it
00248         * or not, leaving out the CR will cause problems if the server happens
00249         * to be Exchange, which is so b0rken it actually barfs on
00250         * LF-terminated newlines.
00251         */
00252        StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
00253                    "USER %s\r\n", ChrPtr(RecvMsg->pop3user));
00254        POP3C_DBG_SEND();
00255        return eReadMessage;
00256 }
00257 
00258 eNextState POP3C_GetUserState(pop3aggr *RecvMsg)
00259 {
00260        AsyncIO *IO = &RecvMsg->IO;
00261        POP3C_DBG_READ();
00262        if (!POP3C_OK) return eTerminateConnection;
00263        else return eSendReply;
00264 }
00265 
00266 eNextState POP3C_SendPassword(pop3aggr *RecvMsg)
00267 {
00268        AsyncIO *IO = &RecvMsg->IO;
00269        /* Password */
00270        StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
00271                    "PASS %s\r\n", ChrPtr(RecvMsg->pop3pass));
00272        EVP3CM_syslog(LOG_DEBUG, "<PASS <password>\n");
00273 //     POP3C_DBG_SEND(); No, we won't write the passvoid to syslog...
00274        return eReadMessage;
00275 }
00276 
00277 eNextState POP3C_GetPassState(pop3aggr *RecvMsg)
00278 {
00279        AsyncIO *IO = &RecvMsg->IO;
00280        POP3C_DBG_READ();
00281        if (!POP3C_OK) return eTerminateConnection;
00282        else return eSendReply;
00283 }
00284 
00285 eNextState POP3C_SendListCommand(pop3aggr *RecvMsg)
00286 {
00287        AsyncIO *IO = &RecvMsg->IO;
00288        /* Get the list of messages */
00289        StrBufPlain(RecvMsg->IO.SendBuf.Buf, HKEY("LIST\r\n"));
00290        POP3C_DBG_SEND();
00291        return eReadMessage;
00292 }
00293 
00294 eNextState POP3C_GetListCommandState(pop3aggr *RecvMsg)
00295 {
00296        AsyncIO *IO = &RecvMsg->IO;
00297        POP3C_DBG_READ();
00298        if (!POP3C_OK) return eTerminateConnection;
00299        RecvMsg->MsgNumbers = NewHash(1, NULL);
00300        RecvMsg->State++;
00301        return eReadMore;
00302 }
00303 
00304 
00305 eNextState POP3C_GetListOneLine(pop3aggr *RecvMsg)
00306 {
00307        AsyncIO *IO = &RecvMsg->IO;
00308 #if 0
00309        int rc;
00310 #endif
00311        const char *pch;
00312        FetchItem *OneMsg = NULL;
00313        POP3C_DBG_READ();
00314 
00315        if ((StrLength(RecvMsg->IO.IOBuf) == 1) &&
00316            (ChrPtr(RecvMsg->IO.IOBuf)[0] == '.'))
00317        {
00318               if (GetCount(RecvMsg->MsgNumbers) == 0)
00319               {
00321               }
00322               else
00323               {
00324                      RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
00325               }
00326               return eSendReply;
00327 
00328        }
00329        OneMsg = (FetchItem*) malloc(sizeof(FetchItem));
00330        memset(OneMsg, 0, sizeof(FetchItem));
00331        OneMsg->MSGID = atol(ChrPtr(RecvMsg->IO.IOBuf));
00332 
00333        pch = strchr(ChrPtr(RecvMsg->IO.IOBuf), ' ');
00334        if (pch != NULL)
00335        {
00336               OneMsg->MSGSize = atol(pch + 1);
00337        }
00338 #if 0
00339        rc = TestValidateHash(RecvMsg->MsgNumbers);
00340        if (rc != 0)
00341               EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
00342 #endif
00343 
00344        Put(RecvMsg->MsgNumbers, LKEY(OneMsg->MSGID), OneMsg, HfreeFetchItem);
00345 #if 0
00346        rc = TestValidateHash(RecvMsg->MsgNumbers);
00347        if (rc != 0)
00348               EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
00349 #endif
00350        //RecvMsg->State --; /* read next Line */
00351        return eReadMore;
00352 }
00353 
00354 eNextState POP3_FetchNetworkUsetableEntry(AsyncIO *IO)
00355 {
00356        long HKLen;
00357        const char *HKey;
00358        void *vData;
00359        struct cdbdata *cdbut;
00360        pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
00361 
00362        if(GetNextHashPos(RecvMsg->MsgNumbers,
00363                        RecvMsg->Pos,
00364                        &HKLen,
00365                        &HKey,
00366                        &vData))
00367        {
00368               struct UseTable ut;
00369               if (server_shutting_down)
00370                      return eAbort;
00371 
00372               RecvMsg->CurrMsg = (FetchItem*) vData;
00373               EVP3CCS_syslog(LOG_DEBUG,
00374                             "CHECKING: whether %s has already been seen: ",
00375                             ChrPtr(RecvMsg->CurrMsg->MsgUID));
00376 
00377               /* Find out if we've already seen this item */
00378               safestrncpy(ut.ut_msgid,
00379                          ChrPtr(RecvMsg->CurrMsg->MsgUID),
00380                          sizeof(ut.ut_msgid));
00381               ut.ut_timestamp = time(NULL);
00382 
00383               cdbut = cdb_fetch(CDB_USETABLE, SKEY(RecvMsg->CurrMsg->MsgUID));
00384               if (cdbut != NULL) {
00385                      /* Item has already been seen */
00386                      EVP3CCSM_syslog(LOG_DEBUG, "YES\n");
00387                      cdb_free(cdbut);
00388 
00389                      /* rewrite the record anyway, to update the timestamp */
00390                      cdb_store(CDB_USETABLE,
00391                               SKEY(RecvMsg->CurrMsg->MsgUID),
00392                               &ut, sizeof(struct UseTable) );
00393                      RecvMsg->CurrMsg->NeedFetch = 0; 
00394               }
00395               else
00396               {
00397                      EVP3CCSM_syslog(LOG_DEBUG, "NO\n");
00398                      RecvMsg->CurrMsg->NeedFetch = 1;
00399               }
00400               return NextDBOperation(&RecvMsg->IO,
00401                                    POP3_FetchNetworkUsetableEntry);
00402        }
00403        else
00404        {
00405               /* ok, now we know them all,
00406                * continue with reading the actual messages. */
00407               DeleteHashPos(&RecvMsg->Pos);
00408               StopDBWatchers(IO);
00409               return QueueEventContext(IO, POP3_C_ReAttachToFetchMessages);
00410        }
00411 }
00412 
00413 eNextState POP3C_GetOneMessagID(pop3aggr *RecvMsg)
00414 {
00415        AsyncIO *IO = &RecvMsg->IO;
00416        long HKLen;
00417        const char *HKey;
00418        void *vData;
00419 
00420 #if 0
00421        int rc;
00422        rc = TestValidateHash(RecvMsg->MsgNumbers);
00423        if (rc != 0)
00424               EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
00425 #endif
00426        if(GetNextHashPos(RecvMsg->MsgNumbers,
00427                        RecvMsg->Pos,
00428                        &HKLen, &HKey,
00429                        &vData))
00430        {
00431               RecvMsg->CurrMsg = (FetchItem*) vData;
00432               /* Find out the UIDL of the message,
00433                * to determine whether we've already downloaded it */
00434               StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
00435                           "UIDL %ld\r\n", RecvMsg->CurrMsg->MSGID);
00436               POP3C_DBG_SEND();
00437        }
00438        else
00439        {
00440            RecvMsg->State++;
00441               DeleteHashPos(&RecvMsg->Pos);
00443               RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0);
00444               return QueueDBOperation(&RecvMsg->IO,
00445                                    POP3_FetchNetworkUsetableEntry);
00446        }
00447        return eReadMore; /* TODO */
00448 }
00449 
00450 eNextState POP3C_GetOneMessageIDState(pop3aggr *RecvMsg)
00451 {
00452        AsyncIO *IO = &RecvMsg->IO;
00453 #if 0
00454        int rc;
00455        rc = TestValidateHash(RecvMsg->MsgNumbers);
00456        if (rc != 0)
00457               EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc);
00458 #endif
00459 
00460        POP3C_DBG_READ();
00461        if (!POP3C_OK) return eTerminateConnection;
00462        RecvMsg->CurrMsg->MsgUIDL =
00463               NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf));
00464        RecvMsg->CurrMsg->MsgUID =
00465               NewStrBufPlain(NULL, StrLength(RecvMsg->IO.IOBuf) * 2);
00466 
00467        StrBufExtract_token(RecvMsg->CurrMsg->MsgUIDL,
00468                          RecvMsg->IO.IOBuf, 2, ' ');
00469 
00470        StrBufPrintf(RecvMsg->CurrMsg->MsgUID,
00471                    "pop3/%s/%s:%s@%s",
00472                    ChrPtr(RecvMsg->RoomName),
00473                    ChrPtr(RecvMsg->CurrMsg->MsgUIDL),
00474                    RecvMsg->IO.ConnectMe->User,
00475                    RecvMsg->IO.ConnectMe->Host);
00476        RecvMsg->State --;
00477        return eSendReply;
00478 }
00479 
00480 
00481 eNextState POP3C_SendGetOneMsg(pop3aggr *RecvMsg)
00482 {
00483        AsyncIO *IO = &RecvMsg->IO;
00484        long HKLen;
00485        const char *HKey;
00486        void *vData;
00487 
00488        RecvMsg->CurrMsg = NULL;
00489        while (GetNextHashPos(RecvMsg->MsgNumbers,
00490                            RecvMsg->Pos,
00491                            &HKLen, &HKey,
00492                            &vData) &&
00493               (RecvMsg->CurrMsg = (FetchItem*) vData,
00494               RecvMsg->CurrMsg->NeedFetch == 0))
00495        {}
00496 
00497        if ((RecvMsg->CurrMsg != NULL ) && (RecvMsg->CurrMsg->NeedFetch == 1))
00498        {
00499               /* Message has not been seen.
00500                * Tell the server to fetch the message... */
00501               StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
00502                           "RETR %ld\r\n", RecvMsg->CurrMsg->MSGID);
00503               POP3C_DBG_SEND();
00504               return eReadMessage;
00505        }
00506        else {
00507               RecvMsg->State = ReadQuitState;
00508               return POP3_C_DispatchWriteDone(&RecvMsg->IO);
00509        }
00510 }
00511 
00512 
00513 eNextState POP3C_ReadMessageBodyFollowing(pop3aggr *RecvMsg)
00514 {
00515        AsyncIO *IO = &RecvMsg->IO;
00516        POP3C_DBG_READ();
00517        if (!POP3C_OK) return eTerminateConnection;
00518        RecvMsg->IO.ReadMsg = NewAsyncMsg(HKEY("."),
00519                                      RecvMsg->CurrMsg->MSGSize,
00520                                      config.c_maxmsglen,
00521                                      NULL, -1,
00522                                      1);
00523 
00524        return eReadPayload;
00525 }
00526 
00527 
00528 eNextState POP3C_StoreMsgRead(AsyncIO *IO)
00529 {
00530        pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
00531        struct UseTable ut;
00532 
00533        EVP3CCS_syslog(LOG_DEBUG,
00534                      "MARKING: %s as seen: ",
00535                      ChrPtr(RecvMsg->CurrMsg->MsgUID));
00536 
00537        safestrncpy(ut.ut_msgid,
00538                   ChrPtr(RecvMsg->CurrMsg->MsgUID),
00539                   sizeof(ut.ut_msgid));
00540        ut.ut_timestamp = time(NULL); /* TODO: use libev time */
00541        cdb_store(CDB_USETABLE,
00542                 ChrPtr(RecvMsg->CurrMsg->MsgUID),
00543                 StrLength(RecvMsg->CurrMsg->MsgUID),
00544                 &ut,
00545                 sizeof(struct UseTable) );
00546        StopDBWatchers(IO);
00547        return QueueEventContext(&RecvMsg->IO, POP3_C_ReAttachToFetchMessages);
00548 }
00549 eNextState POP3C_SaveMsg(AsyncIO *IO)
00550 {
00551        long msgnum;
00552        pop3aggr *RecvMsg = (pop3aggr *) IO->Data;
00553 
00554        /* Do Something With It (tm) */
00555        msgnum = CtdlSubmitMsg(RecvMsg->CurrMsg->Msg,
00556                             NULL,
00557                             ChrPtr(RecvMsg->RoomName),
00558                             0);
00559        if (msgnum > 0L)
00560        {
00561               /* Message has been committed to the store
00562                * write the uidl to the use table
00563                * so we don't fetch this message again
00564                */
00565        }
00566        CtdlFreeMessage(RecvMsg->CurrMsg->Msg);
00567 
00568        RecvMsg->count ++;
00569        return NextDBOperation(&RecvMsg->IO, POP3C_StoreMsgRead);
00570 }
00571 
00572 eNextState POP3C_ReadMessageBody(pop3aggr *RecvMsg)
00573 {
00574        AsyncIO *IO = &RecvMsg->IO;
00575        EVP3CM_syslog(LOG_DEBUG, "Converting message...");
00576        RecvMsg->CurrMsg->Msg =
00577               convert_internet_message_buf(&RecvMsg->IO.ReadMsg->MsgBuf);
00578        StopClientWatchers(IO, 0);
00579        return QueueDBOperation(&RecvMsg->IO, POP3C_SaveMsg);
00580 }
00581 
00582 eNextState POP3C_SendDelete(pop3aggr *RecvMsg)
00583 {
00584        AsyncIO *IO = &RecvMsg->IO;
00585        if (!RecvMsg->keep) {
00586               StrBufPrintf(RecvMsg->IO.SendBuf.Buf,
00587                           "DELE %ld\r\n", RecvMsg->CurrMsg->MSGID);
00588               POP3C_DBG_SEND();
00589               return eReadMessage;
00590        }
00591        else {
00592               RecvMsg->State = ReadMessageBodyFollowing;
00593               return POP3_C_DispatchWriteDone(&RecvMsg->IO);
00594        }
00595 }
00596 eNextState POP3C_ReadDeleteState(pop3aggr *RecvMsg)
00597 {
00598        AsyncIO *IO = &RecvMsg->IO;
00599        POP3C_DBG_READ();
00600        RecvMsg->State = GetOneMessageIDState;
00601        return eReadMessage;
00602 }
00603 
00604 eNextState POP3C_SendQuit(pop3aggr *RecvMsg)
00605 {
00606        AsyncIO *IO = &RecvMsg->IO;
00607        /* Log out */
00608        StrBufPlain(RecvMsg->IO.SendBuf.Buf,
00609                   HKEY("QUIT\r\n3)"));
00610        POP3C_DBG_SEND();
00611        return eReadMessage;
00612 }
00613 
00614 
00615 eNextState POP3C_ReadQuitState(pop3aggr *RecvMsg)
00616 {
00617        AsyncIO *IO = &RecvMsg->IO;
00618        POP3C_DBG_READ();
00619        return eTerminateConnection;
00620 }
00621 
00622 const long POP3_C_ConnTimeout = 1000;
00623 const long DefaultPOP3Port = 110;
00624 
00625 Pop3ClientHandler POP3C_ReadHandlers[] = {
00626        POP3C_ReadGreeting,
00627        POP3C_GetUserState,
00628        POP3C_GetPassState,
00629        POP3C_GetListCommandState,
00630        POP3C_GetListOneLine,
00631        POP3C_GetOneMessageIDState,
00632        POP3C_ReadMessageBodyFollowing,
00633        POP3C_ReadMessageBody,
00634        POP3C_ReadDeleteState,
00635        POP3C_ReadQuitState,
00636 };
00637 
00638 const long POP3_C_SendTimeouts[POP3C_MaxRead] = {
00639        100,
00640        100,
00641        100,
00642        100,
00643        100,
00644        100,
00645        100,
00646        100
00647 };
00648 const ConstStr POP3C_ReadErrors[POP3C_MaxRead] = {
00649        {HKEY("Connection broken during ")},
00650        {HKEY("Connection broken during ")},
00651        {HKEY("Connection broken during ")},
00652        {HKEY("Connection broken during ")},
00653        {HKEY("Connection broken during ")},
00654        {HKEY("Connection broken during ")},
00655        {HKEY("Connection broken during ")},
00656        {HKEY("Connection broken during ")}
00657 };
00658 
00659 Pop3ClientHandler POP3C_SendHandlers[] = {
00660        NULL, /* we don't send a greeting */
00661        POP3C_SendUser,
00662        POP3C_SendPassword,
00663        POP3C_SendListCommand,
00664        NULL,
00665        POP3C_GetOneMessagID,
00666        POP3C_SendGetOneMsg,
00667        NULL,
00668        POP3C_SendDelete,
00669        POP3C_SendQuit
00670 };
00671 
00672 const long POP3_C_ReadTimeouts[] = {
00673        100,
00674        100,
00675        100,
00676        100,
00677        100,
00678        100,
00679        100,
00680        100,
00681        100,
00682        100
00683 };
00684 /*****************************************************************************/
00685 /*                     POP3 CLIENT DISPATCHER                                */
00686 /*****************************************************************************/
00687 
00688 void POP3SetTimeout(eNextState NextTCPState, pop3aggr *pMsg)
00689 {
00690        AsyncIO *IO = &pMsg->IO;
00691        double Timeout = 0.0;
00692 
00693        EVP3C_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00694 
00695        switch (NextTCPState) {
00696        case eSendFile:
00697        case eSendReply:
00698        case eSendMore:
00699               Timeout = POP3_C_SendTimeouts[pMsg->State];
00700 /*
00701   if (pMsg->State == eDATABody) {
00702   / * if we're sending a huge message, we need more time. * /
00703   Timeout += StrLength(pMsg->msgtext) / 1024;
00704   }
00705 */
00706               break;
00707        case eReadFile:
00708        case eReadMessage:
00709               Timeout = POP3_C_ReadTimeouts[pMsg->State];
00710 /*
00711   if (pMsg->State == eDATATerminateBody) {
00712   / *
00713   * some mailservers take a nap before accepting the message
00714   * content inspection and such.
00715   * /
00716   Timeout += StrLength(pMsg->msgtext) / 1024;
00717   }
00718 */
00719               break;
00720        case eReadPayload:
00721               Timeout = 100000;
00722               /* TODO!!! */
00723               break;
00724        case eSendDNSQuery:
00725        case eReadDNSReply:
00726        case eConnect:
00727        case eTerminateConnection:
00728        case eDBQuery:
00729        case eAbort:
00730        case eReadMore:
00731               return;
00732        }
00733        SetNextTimeout(&pMsg->IO, Timeout);
00734 }
00735 eNextState POP3_C_DispatchReadDone(AsyncIO *IO)
00736 {
00737 /*     EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__); to noisy anyways. */
00738        pop3aggr *pMsg = IO->Data;
00739        eNextState rc;
00740 
00741        rc = POP3C_ReadHandlers[pMsg->State](pMsg);
00742        if (rc != eReadMore)
00743            pMsg->State++;
00744        POP3SetTimeout(rc, pMsg);
00745        return rc;
00746 }
00747 eNextState POP3_C_DispatchWriteDone(AsyncIO *IO)
00748 {
00749        pop3aggr *pMsg = IO->Data;
00750        eNextState rc;
00751 
00752 /*     EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__); to noisy anyways. */
00753        rc = POP3C_SendHandlers[pMsg->State](pMsg);
00754        POP3SetTimeout(rc, pMsg);
00755        return rc;
00756 }
00757 
00758 
00759 /*****************************************************************************/
00760 /*                     POP3 CLIENT ERROR CATCHERS                            */
00761 /*****************************************************************************/
00762 eNextState POP3_C_Terminate(AsyncIO *IO)
00763 {
00765 
00766        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00767        FinalizePOP3AggrRun(IO);
00768        return eAbort;
00769 }
00770 eNextState POP3_C_TerminateDB(AsyncIO *IO)
00771 {
00773 
00774        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00775        FinalizePOP3AggrRun(IO);
00776        return eAbort;
00777 }
00778 eNextState POP3_C_Timeout(AsyncIO *IO)
00779 {
00780        pop3aggr *pMsg = IO->Data;
00781 
00782        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00783        StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
00784        return FailAggregationRun(IO);
00785 }
00786 eNextState POP3_C_ConnFail(AsyncIO *IO)
00787 {
00788        pop3aggr *pMsg = (pop3aggr *)IO->Data;
00789 
00790        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00791        StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
00792        return FailAggregationRun(IO);
00793 }
00794 eNextState POP3_C_DNSFail(AsyncIO *IO)
00795 {
00796        pop3aggr *pMsg = (pop3aggr *)IO->Data;
00797 
00798        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00799        StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
00800        return FailAggregationRun(IO);
00801 }
00802 eNextState POP3_C_Shutdown(AsyncIO *IO)
00803 {
00804        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00806 
00809        FinalizePOP3AggrRun(IO);
00810        return eAbort;
00811 }
00812 
00813 
00818 eReadState POP3_C_ReadServerStatus(AsyncIO *IO)
00819 {
00820        eReadState Finished = eBufferNotEmpty;
00821 
00822        switch (IO->NextState) {
00823        case eSendDNSQuery:
00824        case eReadDNSReply:
00825        case eDBQuery:
00826        case eConnect:
00827        case eTerminateConnection:
00828        case eAbort:
00829               Finished = eReadFail;
00830               break;
00831        case eSendFile:
00832        case eSendReply:
00833        case eSendMore:
00834        case eReadMore:
00835        case eReadMessage:
00836               Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
00837               break;
00838        case eReadFile:
00839        case eReadPayload:
00840               Finished = CtdlReadMessageBodyAsync(IO);
00841               break;
00842        }
00843        return Finished;
00844 }
00845 
00846 /*****************************************************************************
00847  * So we connect our Server IP here.                                         *
00848  *****************************************************************************/
00849 eNextState POP3_C_ReAttachToFetchMessages(AsyncIO *IO)
00850 {
00851        pop3aggr *cpptr = IO->Data;
00852 
00853        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00855        if (cpptr->Pos == NULL)
00856               cpptr->Pos = GetNewHashPos(cpptr->MsgNumbers, 0);
00857 
00858        POP3_C_DispatchWriteDone(IO);
00859        ReAttachIO(IO, cpptr, 0);
00860        IO->NextState = eReadMessage;
00861        return IO->NextState;
00862 }
00863 
00864 eNextState pop3_connect_ip(AsyncIO *IO)
00865 {
00866        pop3aggr *cpptr = IO->Data;
00867 
00868        if (cpptr->IOStart == 0.0) /* whith or without DNS? */
00869               cpptr->IOStart = IO->Now;
00870 
00871        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00872 
00873        return EvConnectSock(IO,
00874                           POP3_C_ConnTimeout,
00875                           POP3_C_ReadTimeouts[0],
00876                           1);
00877 }
00878 
00879 eNextState pop3_get_one_host_ip_done(AsyncIO *IO)
00880 {
00881        pop3aggr *cpptr = IO->Data;
00882        struct hostent *hostent;
00883 
00884        QueryCbDone(IO);
00885 
00886        hostent = cpptr->HostLookup.VParsedDNSReply;
00887        if ((cpptr->HostLookup.DNSStatus == ARES_SUCCESS) && 
00888            (hostent != NULL) ) {
00889               memset(&cpptr->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
00890               if (cpptr->IO.ConnectMe->IPv6) {
00891                      memcpy(&cpptr->IO.ConnectMe->Addr.sin6_addr.s6_addr, 
00892                             &hostent->h_addr_list[0],
00893                             sizeof(struct in6_addr));
00894 
00895                      cpptr->IO.ConnectMe->Addr.sin6_family =
00896                             hostent->h_addrtype;
00897                      cpptr->IO.ConnectMe->Addr.sin6_port   =
00898                             htons(DefaultPOP3Port);
00899               }
00900               else {
00901                      struct sockaddr_in *addr =
00902                             (struct sockaddr_in*)
00903                             &cpptr->IO.ConnectMe->Addr;
00904 
00905                      memcpy(&addr->sin_addr.s_addr,
00906                             hostent->h_addr_list[0],
00907                             sizeof(uint32_t));
00908 
00909                      addr->sin_family = hostent->h_addrtype;
00910                      addr->sin_port   = htons(DefaultPOP3Port);
00911               }
00912               return pop3_connect_ip(IO);
00913        }
00914        else
00915               return eAbort;
00916 }
00917 
00918 eNextState pop3_get_one_host_ip(AsyncIO *IO)
00919 {
00920        pop3aggr *cpptr = IO->Data;
00921 
00922        cpptr->IOStart = IO->Now;
00923 
00924        EVP3CCS_syslog(LOG_DEBUG, "POP3: %s\n", __FUNCTION__);
00925 
00926        EVP3CCS_syslog(LOG_DEBUG, 
00927                      "POP3 client[%ld]: looking up %s-Record %s : %d ...\n",
00928                      cpptr->n,
00929                      (cpptr->IO.ConnectMe->IPv6)? "aaaa": "a",
00930                      cpptr->IO.ConnectMe->Host,
00931                      cpptr->IO.ConnectMe->Port);
00932 
00933        QueueQuery((cpptr->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a,
00934                  cpptr->IO.ConnectMe->Host,
00935                  &cpptr->IO,
00936                  &cpptr->HostLookup,
00937                  pop3_get_one_host_ip_done);
00938        IO->NextState = eReadDNSReply;
00939        return IO->NextState;
00940 }
00941 
00942 
00943 
00944 int pop3_do_fetching(pop3aggr *cpptr)
00945 {
00946        AsyncIO *IO = &cpptr->IO;
00947 
00948        InitIOStruct(IO,
00949                    cpptr,
00950                    eReadMessage,
00951                    POP3_C_ReadServerStatus,
00952                    POP3_C_DNSFail,
00953                    POP3_C_DispatchWriteDone,
00954                    POP3_C_DispatchReadDone,
00955                    POP3_C_Terminate,
00956                    POP3_C_TerminateDB,
00957                    POP3_C_ConnFail,
00958                    POP3_C_Timeout,
00959                    POP3_C_Shutdown);
00960 
00961        safestrncpy(((CitContext *)cpptr->IO.CitContext)->cs_host,
00962                   ChrPtr(cpptr->Url),
00963                   sizeof(((CitContext *)cpptr->IO.CitContext)->cs_host));
00964 
00965        if (cpptr->IO.ConnectMe->IsIP) {
00966               QueueEventContext(&cpptr->IO,
00967                               pop3_connect_ip);
00968        }
00969        else {
00970               QueueEventContext(&cpptr->IO,
00971                               pop3_get_one_host_ip);
00972        }
00973        return 1;
00974 }
00975 
00976 /*
00977  * Scan a room's netconfig to determine whether it requires POP3 aggregation
00978  */
00979 void pop3client_scan_room(struct ctdlroom *qrbuf, void *data)
00980 {
00981        StrBuf *CfgData;
00982        StrBuf *CfgType;
00983        StrBuf *Line;
00984 
00985        struct stat statbuf;
00986        char filename[PATH_MAX];
00987        int  fd;
00988        int Done;
00989        void *vptr;
00990        const char *CfgPtr, *lPtr;
00991        const char *Err;
00992 
00993 //     pop3_room_counter *Count = NULL;
00994 //     pop3aggr *cpptr;
00995 
00996        pthread_mutex_lock(&POP3QueueMutex);
00997        if (GetHash(POP3QueueRooms, LKEY(qrbuf->QRnumber), &vptr))
00998        {
00999               pthread_mutex_unlock(&POP3QueueMutex);
01000               EVP3CQ_syslog(LOG_DEBUG,
01001                            "pop3client: [%ld] %s already in progress.",
01002                            qrbuf->QRnumber,
01003                            qrbuf->QRname);
01004               return;
01005        }
01006        pthread_mutex_unlock(&POP3QueueMutex);
01007 
01008        if (server_shutting_down) return;
01009 
01010        assoc_file_name(filename, sizeof filename, qrbuf, ctdl_netcfg_dir);
01011 
01012        if (server_shutting_down)
01013               return;
01014 
01015        /* Only do net processing for rooms that have netconfigs */
01016        fd = open(filename, 0);
01017        if (fd <= 0) {
01018               return;
01019        }
01020        if (server_shutting_down)
01021               return;
01022        if (fstat(fd, &statbuf) == -1) {
01023               EVP3CQ_syslog(LOG_INFO,
01024                            "ERROR: could not stat configfile '%s' - %s",
01025                            filename,
01026                            strerror(errno));
01027               return;
01028        }
01029        if (server_shutting_down)
01030               return;
01031        CfgData = NewStrBufPlain(NULL, statbuf.st_size + 1);
01032        if (StrBufReadBLOB(CfgData, &fd, 1, statbuf.st_size, &Err) < 0) {
01033               close(fd);
01034               FreeStrBuf(&CfgData);
01035               EVP3CQ_syslog(LOG_INFO,
01036                            "ERROR: reading config '%s' - %s",
01037                            filename, strerror(errno));
01038               return;
01039        }
01040        close(fd);
01041        if (server_shutting_down)
01042               return;
01043 
01044        CfgPtr = NULL;
01045        CfgType = NewStrBuf();
01046        Line = NewStrBufPlain(NULL, StrLength(CfgData));
01047        Done = 0;
01048 
01049        while (!Done)
01050        {
01051               Done = StrBufSipLine(Line, CfgData, &CfgPtr) == 0;
01052               if (StrLength(Line) > 0)
01053               {
01054                      lPtr = NULL;
01055                      StrBufExtract_NextToken(CfgType, Line, &lPtr, '|');
01056                      if (!strcasecmp("pop3client", ChrPtr(CfgType)))
01057                      {
01058                             pop3aggr *cptr;
01059                             StrBuf *Tmp;
01060 /*
01061                             if (Count == NULL)
01062                             {
01063                             Count = malloc(sizeof(pop3_room_counter));
01064                                    Count->count = 0;
01065                             }
01066                             Count->count ++;
01067 */
01068                             cptr = (pop3aggr *) malloc(sizeof(pop3aggr));
01069                             memset(cptr, 0, sizeof(pop3aggr));
01071                             cptr->RoomName =
01072                                    NewStrBufPlain(qrbuf->QRname, -1);
01073                             cptr->pop3user =
01074                                    NewStrBufPlain(NULL, StrLength(Line));
01075                             cptr->pop3pass =
01076                                    NewStrBufPlain(NULL, StrLength(Line));
01077                             cptr->Url = NewStrBuf();
01078                             Tmp = NewStrBuf();
01079 
01080                             StrBufExtract_NextToken(Tmp, Line, &lPtr, '|');
01081                             StrBufExtract_NextToken(cptr->pop3user,
01082                                                  Line,
01083                                                  &lPtr,
01084                                                  '|');
01085 
01086                             StrBufExtract_NextToken(cptr->pop3pass,
01087                                                  Line,
01088                                                  &lPtr,
01089                                                  '|');
01090 
01091                             cptr->keep = StrBufExtractNext_long(Line,
01092                                                             &lPtr,
01093                                                             '|');
01094 
01095                             cptr->interval = StrBufExtractNext_long(Line,
01096                                                                &lPtr,
01097                                                                '|');
01098 
01099                             StrBufAppendBufPlain(cptr->Url, HKEY("pop3://"), 0);
01100                             StrBufUrlescUPAppend(cptr->Url, cptr->pop3user, NULL);
01101                             StrBufAppendBufPlain(cptr->Url, HKEY(":"), 0);
01102                             StrBufUrlescUPAppend(cptr->Url, cptr->pop3pass, NULL);
01103                             StrBufAppendBufPlain(cptr->Url, HKEY("@"), 0);
01104                             StrBufAppendBuf(cptr->Url, Tmp, 0);
01105                             StrBufAppendBufPlain(cptr->Url, HKEY("/"), 0);
01106                             StrBufUrlescAppend(cptr->Url, cptr->RoomName, NULL);
01107 
01108                             FreeStrBuf(&Tmp);
01109                             ParseURL(&cptr->IO.ConnectMe, cptr->Url, 110);
01110 
01111 
01112 #if 0
01113 /* todo: we need to reunite the url to be shure. */
01114 
01115                             pthread_mutex_lock(&POP3ueueMutex);
01116                             GetHash(POP3FetchUrls, SKEY(ptr->Url), &vptr);
01117                             use_this_cptr = (pop3aggr *)vptr;
01118 
01119                             if (use_this_rncptr != NULL)
01120                             {
01121                                    /* mustn't attach to an active session */
01122                                    if (use_this_cptr->RefCount > 0)
01123                                    {
01124                                           DeletePOP3Cfg(cptr);
01126                                    }
01127                                    else
01128                                    {
01129                                           long *QRnumber;
01130                                           StrBufAppendBufPlain(
01131                                                  use_this_cptr->rooms,
01132                                                  qrbuf->QRname,
01133                                                  -1, 0);
01134                                           if (use_this_cptr->roomlist_parts == 1)
01135                                           {
01136                                                  use_this_cptr->OtherQRnumbers
01137                                                         = NewHash(1, lFlathash);
01138                                           }
01139                                           QRnumber = (long*)malloc(sizeof(long));
01140                                           *QRnumber = qrbuf->QRnumber;
01141                                           Put(use_this_cptr->OtherQRnumbers,
01142                                               LKEY(qrbuf->QRnumber),
01143                                               QRnumber,
01144                                               NULL);
01145 
01146                                           use_this_cptr->roomlist_parts++;
01147                                    }
01148                                    pthread_mutex_unlock(&POP3QueueMutex);
01149                                    continue;
01150                             }
01151                             pthread_mutex_unlock(&RSSQueueMutex);
01152 #endif
01153                             cptr->n = Pop3ClientID++;
01154                             pthread_mutex_lock(&POP3QueueMutex);
01155                             Put(POP3FetchUrls,
01156                                 SKEY(cptr->Url),
01157                                 cptr,
01158                                 DeletePOP3Aggregator);
01159 
01160                             pthread_mutex_unlock(&POP3QueueMutex);
01161 
01162                      }
01163 
01164               }
01165 
01167 
01168        }
01169        FreeStrBuf(&Line);
01170        FreeStrBuf(&CfgType);
01171        FreeStrBuf(&CfgData);
01172 }
01173 
01174 static int doing_pop3client = 0;
01175 
01176 void pop3client_scan(void) {
01177        static time_t last_run = 0L;
01178        time_t fastest_scan;
01179        HashPos *it;
01180        long len;
01181        const char *Key;
01182        void *vrptr;
01183        pop3aggr *cptr;
01184 
01185        become_session(&pop3_client_CC);
01186 
01187        if (config.c_pop3_fastest < config.c_pop3_fetch)
01188               fastest_scan = config.c_pop3_fastest;
01189        else
01190               fastest_scan = config.c_pop3_fetch;
01191 
01192        /*
01193         * Run POP3 aggregation no more frequently than once every n seconds
01194         */
01195        if ( (time(NULL) - last_run) < fastest_scan ) {
01196               return;
01197        }
01198 
01199        /*
01200         * This is a simple concurrency check to make sure only one pop3client
01201         * run is done at a time.  We could do this with a mutex, but since we
01202         * don't really require extremely fine granularity here, we'll do it
01203         * with a static variable instead.
01204         */
01205        if (doing_pop3client) return;
01206        doing_pop3client = 1;
01207 
01208        EVP3CQM_syslog(LOG_DEBUG, "pop3client started");
01209        CtdlForEachRoom(pop3client_scan_room, NULL);
01210 
01211        pthread_mutex_lock(&POP3QueueMutex);
01212        it = GetNewHashPos(POP3FetchUrls, 0);
01213        while (!server_shutting_down &&
01214               GetNextHashPos(POP3FetchUrls, it, &len, &Key, &vrptr) &&
01215               (vrptr != NULL)) {
01216               cptr = (pop3aggr *)vrptr;
01217               if (cptr->RefCount == 0)
01218                      if (!pop3_do_fetching(cptr))
01219                             DeletePOP3Aggregator(cptr);
01220 
01221 /*
01222        if ((palist->interval && time(NULL) > (last_run + palist->interval))
01223                      || (time(NULL) > last_run + config.c_pop3_fetch))
01224                      pop3_do_fetching(palist->roomname, palist->pop3host,
01225                      palist->pop3user, palist->pop3pass, palist->keep);
01226               pptr = palist;
01227               palist = palist->next;
01228               free(pptr);
01229 */
01230        }
01231        DeleteHashPos(&it);
01232        pthread_mutex_unlock(&POP3QueueMutex);
01233 
01234        EVP3CQM_syslog(LOG_DEBUG, "pop3client ended");
01235        last_run = time(NULL);
01236        doing_pop3client = 0;
01237 }
01238 
01239 
01240 void pop3_cleanup(void)
01241 {
01242        /* citthread_mutex_destroy(&POP3QueueMutex); TODO */
01243        while (doing_pop3client != 0) ;
01244        DeleteHash(&POP3FetchUrls);
01245        DeleteHash(&POP3QueueRooms);
01246 }
01247 
01248 
01249 
01250 void LogDebugEnablePOP3Client(const int n)
01251 {
01252        POP3ClientDebugEnabled = n;
01253 }
01254 
01255 CTDL_MODULE_INIT(pop3client)
01256 {
01257        if (!threading)
01258        {
01259               CtdlFillSystemContext(&pop3_client_CC, "POP3aggr");
01260               pthread_mutex_init(&POP3QueueMutex, NULL);
01261               POP3QueueRooms = NewHash(1, lFlathash);
01262               POP3FetchUrls = NewHash(1, NULL);
01263               CtdlRegisterSessionHook(pop3client_scan, EVT_TIMER, PRIO_AGGR + 50);
01264               CtdlRegisterEVCleanupHook(pop3_cleanup);
01265               CtdlRegisterDebugFlagHook(HKEY("pop3client"), LogDebugEnablePOP3Client, &POP3ClientDebugEnabled);
01266        }
01267 
01268        /* return our module id for the log */
01269        return "pop3client";
01270 }