+const long POP3_C_ConnTimeout = 1000;
+const long DefaultPOP3Port = 110;
+
+Pop3ClientHandler POP3C_ReadHandlers[] = {
+ POP3C_ReadGreeting,
+ POP3C_GetUserState,
+ POP3C_GetPassState,
+ POP3C_GetListCommandState,
+ POP3C_GetOneMessageIDState,
+ POP3C_ReadMessageBodyFollowing,
+ POP3C_ReadMessageBody,
+ 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,
+ POP3C_GetListOneLine,
+ POP3C_GetOneMessagID,
+ POP3C_SendGetOneMsg,
+ POP3C_SendDelete,
+ POP3C_SendQuit
+};
+
+const long POP3_C_ReadTimeouts[] = {
+ 100,
+ 100,
+ 100,
+ 100,
+ 100,
+ 100,
+ 100,
+ 100,
+ 100,
+ 100
+};
+/*****************************************************************************/
+/* POP3 CLIENT DISPATCHER */
+/*****************************************************************************/
+
+void POP3SetTimeout(eNextState NextTCPState, pop3aggr *pMsg)
+{
+ double Timeout = 0.0;
+
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+
+ switch (NextTCPState) {
+ case eSendReply:
+ case eSendMore:
+ Timeout = POP3_C_SendTimeouts[pMsg->State];
+/*
+ if (pMsg->State == eDATABody) {
+ / * if we're sending a huge message, we need more time. * /
+ Timeout += StrLength(pMsg->msgtext) / 1024;
+ }
+*/
+ break;
+ case eReadMessage:
+ Timeout = POP3_C_ReadTimeouts[pMsg->State];
+/*
+ if (pMsg->State == eDATATerminateBody) {
+ / *
+ * some mailservers take a nap before accepting the message
+ * content inspection and such.
+ * /
+ Timeout += StrLength(pMsg->msgtext) / 1024;
+ }
+*/
+ break;
+ case eSendDNSQuery:
+ case eReadDNSReply:
+ case eConnect:
+ case eTerminateConnection:
+ case eAbort:
+ case eReadMore://// TODO
+ return;
+ }
+ SetNextTimeout(&pMsg->IO, Timeout);
+}
+eNextState POP3_C_DispatchReadDone(AsyncIO *IO)
+{
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+ pop3aggr *pMsg = IO->Data;
+ eNextState rc;
+
+ rc = POP3C_ReadHandlers[pMsg->State](pMsg);
+ pMsg->State++;
+ POP3SetTimeout(rc, pMsg);
+ return rc;
+}
+eNextState POP3_C_DispatchWriteDone(AsyncIO *IO)
+{
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+ pop3aggr *pMsg = IO->Data;
+ eNextState rc;
+
+ rc = POP3C_SendHandlers[pMsg->State](pMsg);
+ POP3SetTimeout(rc, pMsg);
+ return rc;
+}
+
+
+/*****************************************************************************/
+/* POP3 CLIENT ERROR CATCHERS */
+/*****************************************************************************/
+eNextState POP3_C_Terminate(AsyncIO *IO)
+{
+/// pop3aggr *pMsg = (pop3aggr *)IO->Data;
+
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+ FinalizePOP3AggrRun(IO);
+ return eAbort;
+}
+eNextState POP3_C_Timeout(AsyncIO *IO)
+{
+ pop3aggr *pMsg = IO->Data;
+
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+ StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
+ return FailAggregationRun(IO);
+}
+eNextState POP3_C_ConnFail(AsyncIO *IO)
+{
+ pop3aggr *pMsg = (pop3aggr *)IO->Data;
+
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+ StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State]));
+ return FailAggregationRun(IO);
+}
+eNextState POP3_C_Shutdown(AsyncIO *IO)
+{
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+//// pop3aggr *pMsg = IO->Data;
+
+ ////pMsg->MyQEntry->Status = 3;
+ ///StrBufPlain(pMsg->MyQEntry->StatusMessage, HKEY("server shutdown during message retrieval."));
+ FinalizePOP3AggrRun(IO);
+ return eAbort;
+}
+
+
+/**
+ * @brief lineread Handler; understands when to read more POP3 lines, and when this is a one-lined reply.
+ */
+eReadState POP3_C_ReadServerStatus(AsyncIO *IO)
+{
+ eReadState Finished = eBufferNotEmpty;
+
+ while (Finished == eBufferNotEmpty) {
+ Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
+
+ switch (Finished) {
+ case eMustReadMore: /// read new from socket...
+ return Finished;
+ break;
+ case eBufferNotEmpty: /* shouldn't happen... */
+ case eReadSuccess: /// done for now...
+ if (StrLength(IO->IOBuf) < 4)
+ continue;
+ if (ChrPtr(IO->IOBuf)[3] == '-')
+ Finished = eBufferNotEmpty;
+ else
+ return Finished;
+ break;
+ case eReadFail: /// WHUT?
+ ///todo: shut down!
+ break;
+ }
+ }
+ return Finished;
+}
+
+/*****************************************************************************
+ * So we connect our Server IP here. *
+ *****************************************************************************/
+eNextState connect_ip(AsyncIO *IO)
+{
+ pop3aggr *cpptr = IO->Data;
+
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+
+//// IO->ConnectMe = &cpptr->Pop3Host;
+ /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
+
+ /////// SetConnectStatus(IO);
+
+ return InitEventIO(IO, cpptr,
+ POP3_C_ConnTimeout,
+ POP3_C_ReadTimeouts[0],
+ 1);
+}
+
+eNextState get_one_host_ip_done(AsyncIO *IO)
+{
+ pop3aggr *cpptr = IO->Data;
+ struct hostent *hostent;
+
+ QueryCbDone(IO);
+
+ hostent = cpptr->HostLookup.VParsedDNSReply;
+ if ((cpptr->HostLookup.DNSStatus == ARES_SUCCESS) &&
+ (hostent != NULL) ) {
+ memset(&cpptr->Pop3Host.Addr, 0, sizeof(struct in6_addr));
+ if (cpptr->Pop3Host.IPv6) {
+ memcpy(&cpptr->Pop3Host.Addr.sin6_addr.s6_addr,
+ &hostent->h_addr_list[0],
+ sizeof(struct in6_addr));
+
+ cpptr->Pop3Host.Addr.sin6_family = hostent->h_addrtype;
+ cpptr->Pop3Host.Addr.sin6_port = htons(DefaultPOP3Port);
+ }
+ else {
+ struct sockaddr_in *addr = (struct sockaddr_in*) &cpptr->Pop3Host.Addr;
+ /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
+// addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
+ memcpy(&addr->sin_addr.s_addr,
+ hostent->h_addr_list[0],
+ sizeof(uint32_t));
+
+ addr->sin_family = hostent->h_addrtype;
+ addr->sin_port = htons(DefaultPOP3Port);
+
+ }
+ return connect_ip(IO);
+ }
+ else
+ return eAbort;
+}
+
+eNextState get_one_host_ip(AsyncIO *IO)
+{
+ pop3aggr *cpptr = IO->Data;
+ /*
+ * here we start with the lookup of one host. it might be...
+ * - the relay host *sigh*
+ * - the direct hostname if there was no mx record
+ * - one of the mx'es
+ */
+
+ InitC_ares_dns(IO);
+
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s\n", __FUNCTION__);
+
+ CtdlLogPrintf(CTDL_DEBUG,
+ "POP3 client[%ld]: looking up %s-Record %s : %d ...\n",
+ cpptr->n,
+ (cpptr->Pop3Host.IPv6)? "aaaa": "a",
+ cpptr->Pop3Host.Host,
+ cpptr->Pop3Host.Port);
+
+ if (!QueueQuery((cpptr->Pop3Host.IPv6)? ns_t_aaaa : ns_t_a,
+ cpptr->Pop3Host.Host,
+ &cpptr->IO,
+ &cpptr->HostLookup,
+ get_one_host_ip_done))
+ {
+// cpptr->MyQEntry->Status = 5;
+// StrBufPrintf(SendMsg->MyQEntry->StatusMessage,
+// "No MX hosts found for <%s>", SendMsg->node);
+ cpptr->IO.NextState = eTerminateConnection;
+ return IO->NextState;
+ }
+ IO->NextState = eReadDNSReply;
+ return IO->NextState;
+}
+
+
+
+int pop3_do_fetching(pop3aggr *cpptr)
+{
+ CitContext *SubC;
+
+ cpptr->IO.Data = cpptr;
+
+ cpptr->IO.SendDone = POP3_C_DispatchWriteDone;
+ cpptr->IO.ReadDone = POP3_C_DispatchReadDone;
+ cpptr->IO.Terminate = POP3_C_Terminate;
+ cpptr->IO.LineReader = POP3_C_ReadServerStatus;
+ cpptr->IO.ConnFail = POP3_C_ConnFail;
+ cpptr->IO.Timeout = POP3_C_Timeout;
+ cpptr->IO.ShutdownAbort = POP3_C_Shutdown;
+
+ cpptr->IO.SendBuf.Buf = NewStrBufPlain(NULL, 1024);
+ cpptr->IO.RecvBuf.Buf = NewStrBufPlain(NULL, 1024);
+ cpptr->IO.IOBuf = NewStrBuf();
+
+ cpptr->IO.NextState = eReadMessage;
+/* TODO
+ CtdlLogPrintf(CTDL_DEBUG, "POP3: %s %s %s <password>\n", roomname, pop3host, pop3user);
+ CtdlLogPrintf(CTDL_NOTICE, "Connecting to <%s>\n", pop3host);
+*/
+
+ SubC = CloneContext (CC);
+ SubC->session_specific_data = (char*) cpptr;
+ cpptr->IO.CitContext = SubC;
+
+ if (cpptr->IO.ConnectMe->IsIP) {
+ QueueEventContext(&cpptr->IO,
+ connect_ip);
+ }
+ else { /* uneducated admin has chosen to add DNS to the equation... */
+ QueueEventContext(&cpptr->IO,
+ get_one_host_ip);
+ }
+ return 1;
+}