1 /* $Id: imap_connection.c 7103 2004-12-23 22:36:27Z rra $
3 ** Feed articles to an IMAP server via LMTP and IMAP.
5 ** Written by Tim Martin.
7 ** Instead of feeding articles via nntp to another host this feeds the
8 ** messages via lmtp to a host and the control messages (cancel's etc..) it
9 ** performs via IMAP. This means it has 2 active connections at any given
12 ** When an article comes in it is immediatly placed in the lmtp queue. When
13 ** an article is picked off the lmtp queue for processing first check if it's
14 ** a control message. If so, place it in the IMAP queue. If not, attempt to
17 ** This attempts to follow the exact same api as connection.c.
22 ** security layers? <--punt on for now
23 ** authname/password per connection object
24 ** untagged IMAP messages
29 #include "portable/socket.h"
37 #include "inn/messages.h"
41 #include "connection.h"
46 #include "configfile.h"
49 # include <sasl/sasl.h>
52 #ifndef MAXHOSTNAMELEN
53 #define MAXHOSTNAMELEN 1024
61 # define LMTP_PORT 2003
64 #define IMAP_TAGLENGTH 6
66 #define QUEUE_MAX_SIZE 250
68 #define DOSOMETHING_TIMEOUT 60
73 extern char *deliver_username;
74 extern char *deliver_authname;
75 extern char *deliver_password;
76 extern char *deliver_realm;
77 extern char *deliver_rcpt_to;
78 extern char *deliver_to_header;
81 char hostname[MAXHOSTNAMELEN];
82 char *mailfrom_name = NULL; /* default to no return path */
85 static int initialized_sasl = 0; /* weather sasl_client_init() has been called */
88 /* states the imap connection may be in */
91 IMAP_DISCONNECTED = 1,
94 IMAP_CONNECTED_NOTAUTH,
98 IMAP_WRITING_CAPABILITY,
99 IMAP_READING_CAPABILITY,
101 IMAP_WRITING_STARTAUTH,
102 IMAP_READING_STEPAUTH,
103 IMAP_WRITING_STEPAUTH,
133 LMTP_DISCONNECTED = 1,
136 LMTP_CONNECTED_NOTAUTH,
143 LMTP_WRITING_STARTAUTH,
144 LMTP_READING_STEPAUTH,
145 LMTP_WRITING_STEPAUTH,
152 LMTP_READING_MAILFROM,
155 LMTP_READING_CONTENTS,
157 LMTP_WRITING_UPTODATA,
158 LMTP_WRITING_CONTENTS,
165 typedef struct imap_capabilities_s {
167 int imap4; /* does server support imap4bis? */
168 int logindisabled; /* does the server allow the login command? */
170 char *saslmechs; /* supported SASL mechanisms */
172 } imap_capabilities_t;
174 typedef struct lmtp_capabilities_s {
177 int EnhancedStatusCodes;
182 } lmtp_capabilities_t;
199 typedef struct control_item_s {
203 char *msgid; /* only for cancel's */
204 unsigned long uid; /* only for cancel's */
208 typedef struct article_queue_s {
213 time_t nextsend; /* time we should next try to send article */
217 int counts_toward_size;
221 control_item_t *control;
225 struct article_queue_s *next;
231 article_queue_t *head;
233 article_queue_t *tail;
239 typedef struct connection_s {
244 char *lmtp_respBuffer; /* buffer all responses are read into */
245 Buffer lmtp_rBuffer; /* buffer all responses are read into */
247 Host myHost ; /* the host who owns the connection */
249 time_t timeCon ; /* the time the connect happened
250 (last auth succeeded) */
252 int issue_quit; /* Three states:
253 * 0 - don't do anything
254 * 1 - after issue quit enter wait state
255 * 2 - after issue quit reconnect
256 * 3 - after issue quit delete connection
264 int cancel_succeeded;
267 int create_succeeded;
270 int remove_succeeded;
276 lmtp_state_t lmtp_state;
278 sasl_conn_t *saslconn_lmtp;
279 #endif /* HAVE_SASL */
282 time_t lmtp_timeCon ;
284 EndPoint lmtp_endpoint;
285 unsigned int ident ; /* an identifier for syslogging. */
287 lmtp_capabilities_t *lmtp_capabilities;
289 int lmtp_disconnects;
290 char *lmtp_tofree_str;
292 article_queue_t *current_article;
293 Buffer *current_bufs;
294 int current_rcpts_issued;
295 int current_rcpts_okayed;
297 /* Timer for the max amount of time to wait for a response from the
299 unsigned int lmtp_readTimeout ;
300 TimeoutId lmtp_readBlockedTimerId ;
302 /* Timer for the max amount of time to wait for a any amount of data
303 to be written to the remote */
304 unsigned int lmtp_writeTimeout ;
305 TimeoutId lmtp_writeBlockedTimerId ;
307 /* Timer for the number of seconds to sleep before attempting a
309 unsigned int lmtp_sleepTimeout ;
310 TimeoutId lmtp_sleepTimerId ;
312 /* Timer for max amount between queueing some articles and trying to send them */
313 unsigned int dosomethingTimeout ;
314 TimeoutId dosomethingTimerId ;
316 Q_t lmtp_todeliver_q;
323 sasl_conn_t *imap_saslconn;
324 #endif /* HAVE_SASL */
326 char *imap_respBuffer;
328 EndPoint imap_endpoint;
330 imap_capabilities_t *imap_capabilities;
334 time_t imap_timeCon ;
336 imap_state_t imap_state;
337 int imap_disconnects;
338 char *imap_tofree_str;
340 char imap_currentTag[IMAP_TAGLENGTH];
343 /* Timer for the max amount of time to wait for a response from the
345 unsigned int imap_readTimeout ;
346 TimeoutId imap_readBlockedTimerId ;
348 /* Timer for the max amount of time to wait for a any amount of data
349 to be written to the remote */
350 unsigned int imap_writeTimeout ;
351 TimeoutId imap_writeBlockedTimerId ;
353 /* Timer for the number of seconds to sleep before attempting a
355 unsigned int imap_sleepTimeout ;
356 TimeoutId imap_sleepTimerId ;
358 Q_t imap_controlMsg_q;
360 article_queue_t *current_control;
362 struct connection_s *next;
366 static Connection gCxnList = NULL ;
367 static unsigned int gCxnCount= 0 ;
368 static unsigned int max_reconnect_period = MAX_RECON_PER ;
369 static unsigned int init_reconnect_period = INIT_RECON_PER;
382 /********** Private Function Declarations *************/
384 static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d);
385 static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d);
386 static void imap_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d);
387 static void lmtp_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d);
389 static conn_ret lmtp_Connect(connection_t *cxn);
390 static conn_ret imap_Connect(connection_t *cxn);
392 static void prepareReopenCbk (Connection cxn, int type);
394 static void lmtp_readTimeoutCbk (TimeoutId id, void *data);
395 static void imap_readTimeoutCbk (TimeoutId id, void *data);
397 static void dosomethingTimeoutCbk (TimeoutId id, void *data);
399 static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen);
400 static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen);
402 static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback,
403 EndPoint endp, Buffer *array);
404 static void lmtp_sendmessage(connection_t *cxn, Article justadded);
405 static void imap_ProcessQueue(connection_t *cxn);
407 static conn_ret FindHeader(Buffer *bufs, const char *header, char **start, char **end);
408 static conn_ret PopFromQueue(Q_t *q, article_queue_t **item);
412 MSG_FAIL_DELIVER = 1,
417 static void QueueForgetAbout(connection_t *cxn, article_queue_t *item,
418 enum failure_type failed);
420 static void delConnection (Connection cxn);
421 static void DeleteIfDisconnected(Connection cxn);
422 static void DeferAllArticles(connection_t *cxn, Q_t *q);
424 static void lmtp_Disconnect(connection_t *cxn);
425 static void imap_Disconnect(connection_t *cxn);
426 static conn_ret imap_listenintro(connection_t *cxn);
428 static void imap_writeTimeoutCbk (TimeoutId id, void *data);
429 static void lmtp_writeTimeoutCbk (TimeoutId id, void *data);
431 /******************** PRIVATE FUNCTIONS ***************************/
433 static const char *imap_stateToString(int state)
437 case IMAP_DISCONNECTED: return "disconnected";
438 case IMAP_WAITING: return "waiting";
439 case IMAP_CONNECTED_NOTAUTH: return "connected (unauthenticated)";
440 case IMAP_READING_INTRO: return "reading intro";
441 case IMAP_WRITING_CAPABILITY: return "writing CAPABILITY";
442 case IMAP_READING_CAPABILITY: return "reading CAPABILITY";
443 case IMAP_WRITING_STARTAUTH: return "writing AUTHENTICATE";
444 case IMAP_READING_STEPAUTH: return "reading stepauth";
445 case IMAP_WRITING_STEPAUTH: return "writing stepauth";
446 case IMAP_IDLE_AUTHED: return "idle (authenticated)";
447 case IMAP_WRITING_NOOP: return "writing NOOP";
448 case IMAP_READING_NOOP: return "reading NOOP response";
449 case IMAP_WRITING_CREATE: return "writing CREATE";
450 case IMAP_READING_CREATE: return "reading CREATE response";
451 case IMAP_WRITING_DELETE: return "writing DELETE command";
452 case IMAP_READING_DELETE: return "reading DELETE response";
453 case IMAP_WRITING_SELECT: return "writing SELECT";
454 case IMAP_READING_SELECT: return "reading SELECT response";
455 case IMAP_WRITING_SEARCH: return "writing SEARCH";
456 case IMAP_READING_SEARCH: return "reading SEARCH response";
457 case IMAP_WRITING_STORE: return "writing STORE";
458 case IMAP_READING_STORE: return "reading STORE response";
459 case IMAP_WRITING_CLOSE: return "writing CLOSE";
460 case IMAP_READING_CLOSE: return "reading CLOSE response";
461 case IMAP_WRITING_QUIT: return "writing LOGOUT";
462 case IMAP_READING_QUIT: return "reading LOGOUT response";
463 default: return "Unknown state";
467 static const char *lmtp_stateToString(int state)
471 case LMTP_DISCONNECTED: return "disconnected";
472 case LMTP_WAITING: return "waiting";
473 case LMTP_CONNECTED_NOTAUTH: return "connected (unauthenticated)";
474 case LMTP_READING_INTRO: return "reading intro";
475 case LMTP_WRITING_LHLO: return "writing LHLO";
476 case LMTP_READING_LHLO: return "reading LHLO response";
477 case LMTP_WRITING_STARTAUTH: return "writing AUTH";
478 case LMTP_READING_STEPAUTH: return "reading stepauth";
479 case LMTP_WRITING_STEPAUTH: return "writing stepauth";
480 case LMTP_AUTHED_IDLE: return "idle (authenticated)";
481 case LMTP_WRITING_NOOP: return "writing NOOP";
482 case LMTP_READING_NOOP: return "reading NOOP response";
483 case LMTP_READING_RSET: return "reading RSET response";
484 case LMTP_READING_MAILFROM: return "reading MAIL FROM response";
485 case LMTP_READING_RCPTTO: return "reading RCPT TO response";
486 case LMTP_READING_DATA: return "reading DATA response";
487 case LMTP_READING_CONTENTS: return "reading contents response";
488 case LMTP_WRITING_UPTODATA:
489 return "writing RSET, MAIL FROM, RCPT TO, DATA commands";
490 case LMTP_WRITING_CONTENTS: return "writing contents of message";
491 case LMTP_WRITING_QUIT: return "writing QUIT";
492 case LMTP_READING_QUIT: return "reading QUIT";
493 default: return "unknown state";
497 /******************************* Queue functions ***********************************/
500 * Add a message to a generic queue
502 * q - the queue adding to
503 * item - the data to add to the queue
504 * type - the type of item it is (i.e. cancel,lmtp,etc..)
505 * addsmsg - weather this should be counted toward the queue size
506 * this is for control msg's that create multiple queue items.
507 * For example a cancel message canceling a message in multiple
508 * newsgroups will create >1 queue item but we only want it to count
509 * once towards the queue
510 * must - wheather we must take it even though it may put us over our max size
513 static conn_ret AddToQueue(Q_t *q, void *item,
514 control_type_t type, int addsmsg, bool must)
516 article_queue_t *newentry;
520 if (q->size >= QUEUE_MAX_SIZE)
522 return RET_EXCEEDS_SIZE;
525 if (q->size >= QUEUE_MAX_SIZE * 10)
527 d_printf(0, "Queue has grown way too much. Dropping article\n");
532 /* add to the end of our queue */
533 newentry = xmalloc(sizeof(article_queue_t));
535 newentry->type = type;
537 /* send as soon as possible */
538 newentry->nextsend = newentry->arrived = time(NULL);
542 newentry->data.generic = item;
543 newentry->next = NULL;
544 newentry->counts_toward_size = addsmsg;
546 /* add to end of queue */
553 q->tail->next = newentry;
563 * Pop an item from the queue
565 * q - the queue to pop from
566 * item - where the item shall be placed upon sucess
570 static conn_ret PopFromQueue(Q_t *q, article_queue_t **item)
572 /* if queue empty return error */
573 if ( q->head == NULL)
575 return RET_QUEUE_EMPTY;
578 /* set what we return */
581 q->head = q->head->next;
582 if (q->head == NULL) q->tail = NULL;
584 q->size-=(*item)->counts_toward_size;
590 * ReQueue an item. Will either put it back in the queue for another try
593 * cxn - our connection object (needed so forget about things)
594 * q - the queue to requeue to
595 * entry - the item to put back
598 static void ReQueue(connection_t *cxn, Q_t *q, article_queue_t *entry)
600 /* look at the time it's been here */
601 entry->nextsend = time(NULL) + (entry->trys *30); /* xxx better formula? */
605 /* give up after 5 tries xxx configurable??? */
606 if (entry->trys >= 5)
608 QueueForgetAbout(cxn, entry, MSG_FAIL_DELIVER);
613 /* ok let's add back to the end of the queue */
616 /* add to end of queue */
622 q->tail->next = entry;
626 q->size+=entry->counts_toward_size;
632 * Forget about an item. Tells host object if we succeeded/failed/etc with the message
634 * cxn - connection object
636 * failed - type of failure (see below)
639 * 0 - succeeded delivering message
640 * 1 - failed delivering message
641 * 2 - Try to give back to host
642 * 3 - Article missing (i.e. can't find on disk)
644 static void QueueForgetAbout(connection_t *cxn, article_queue_t *item,
645 enum failure_type failed)
654 art = item->data.article;
659 cxn->cancel_failed++;
660 free(item->data.control->msgid);
661 free(item->data.control->folder);
663 if (item->counts_toward_size == 1)
664 art = item->data.control->article;
666 free(item->data.control );
671 cxn->create_failed++;
672 free(item->data.control->folder);
674 art = item->data.control->article;
676 free(item->data.control );
681 cxn->remove_failed++;
682 free(item->data.control->folder);
684 art = item->data.control->article;
686 free(item->data.control );
690 d_printf(0, "%s:%d QueueForgetAbout(): "
691 "Unknown type to forget about\n",
692 hostPeerName (cxn->myHost), cxn->ident);
699 hostArticleAccepted (cxn->myHost, cxn, art);
702 case MSG_FAIL_DELIVER:
703 hostArticleRejected (cxn->myHost, cxn, art);
707 hostTakeBackArticle (cxn->myHost, cxn, art);
711 hostArticleIsMissing(cxn->myHost, cxn, art);
714 d_printf(0,"%s:%d QueueForgetAbout(): failure type unknown\n",
715 hostPeerName (cxn->myHost), cxn->ident);
724 * How much space is available in the queue
727 static int QueueSpace(Q_t *q)
729 int ret = QUEUE_MAX_SIZE - q->size;
730 if (ret < 0) ret = 0;
735 * How many items are in the queue
738 static int QueueItems(Q_t *q)
744 /***************************** END Queue functions ***********************************/
746 /***************************** Generic Parse Functions *******************************/
748 /* returns the end of the header */
750 static char *GetUntil(char *str)
752 while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n'))
760 static char *GotoNextLine(char *str)
762 while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n'))
767 if (*str == '\r') str++;
768 if (*str == '\n') str++;
774 * Finds the given header in the message
775 * Returns NULL if not found
777 static conn_ret FindHeader(Buffer *bufs, const char *header, char **start,
784 int headerlen = strlen(header);
790 return RET_ARTICLE_BAD;
794 size = bufferSize(b);
795 str_base = bufferBase(b);
798 while ((str - str_base) < size - headerlen)
800 if (*str == header[0])
802 if ((strncasecmp(header, str, headerlen)==0) && ( *(str + headerlen)==':'))
807 *start = str+headerlen+1;
809 /* get rid of leading whitespace */
810 while ( isspace((int) **start))
815 *end = GetUntil(str+headerlen+1);
819 } else if (*str == '\n') {
823 str = GotoNextLine(str);
829 static conn_ret GetLine(char *buf, char *ret, int retmaxsize)
834 int size = strlen(buf);
838 while ( (*str) != '\0')
842 if (str-str_base > retmaxsize)
844 d_printf(0, "Max size exceeded! %s\n",str_base);
848 /* fill in the return string */
849 memcpy(ret, str_base, str-str_base);
850 ret[ str - str_base -1] = '\0';
852 memcpy( str_base, str_base + (str-str_base)+1, size - (str-str_base));
853 str_base[size - (str-str_base)]='\0';
861 /* couldn't find a full line */
862 return RET_NO_FULLLINE;
867 /************************** END Generic Parse Functions *******************************/
869 /************************ Writing to Network functions *****************/
871 static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback,
872 EndPoint endp, Buffer *array)
875 if (array == NULL) return RET_FAIL;
886 static conn_ret WriteToWire_str(connection_t *cxn, EndpRWCB callback,
887 EndPoint endp, char *str, int slen)
893 if (slen==-1) slen = strlen(str);
895 buff = newBufferByCharP(str, slen+1, slen);
896 ASSERT (buff != NULL);
898 writeArr = makeBufferArray (buff, NULL) ;
900 result = WriteToWire(cxn, callback, endp, writeArr);
905 static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen)
907 /* prepare the timeouts */
908 clearTimer (cxn->imap_readBlockedTimerId) ;
910 /* set up the write timer. */
911 clearTimer (cxn->imap_writeBlockedTimerId) ;
913 if (cxn->imap_writeTimeout > 0)
914 cxn->imap_writeBlockedTimerId = prepareSleep (imap_writeTimeoutCbk, cxn->imap_writeTimeout,
916 cxn->imap_tofree_str = str;
917 return WriteToWire_str(cxn, imap_writeCB, cxn->imap_endpoint, str, slen);
920 static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen)
922 /* prepare the timeouts */
923 clearTimer (cxn->lmtp_readBlockedTimerId) ;
925 /* set up the write timer. */
926 clearTimer (cxn->lmtp_writeBlockedTimerId) ;
928 if (cxn->lmtp_writeTimeout > 0)
929 cxn->lmtp_writeBlockedTimerId = prepareSleep (lmtp_writeTimeoutCbk, cxn->lmtp_writeTimeout,
934 cxn->lmtp_tofree_str = str;
935 return WriteToWire_str(cxn, lmtp_writeCB, cxn->lmtp_endpoint, str, slen);
938 static conn_ret WriteArticle(connection_t *cxn, Buffer *array)
940 int array_len = bufferArrayLen (array);
944 for (lup=0;lup<array_len;lup++)
950 current_buf = array[lup];
952 current_size = bufferDataSize( current_buf );
954 current_start = bufferBase( current_buf );
958 /* just call writetowire since it's easy */
959 result = WriteToWire(cxn, lmtp_writeCB, cxn->lmtp_endpoint, array);
966 cxn->lmtp_state = LMTP_WRITING_CONTENTS;
971 /************************ END Writing to Network functions *****************/
976 * Adds a cancel item to the control queue
977 * Cancel item to delete message with <msgid> in <folder>
979 * cxn - connection object
980 * folder - pointer to start of folder string (this is a pointer into the actual message buffer)
981 * folderlen - length of folder string
982 * msgid - pointer to start of msgid string (this is a pointer into the actual message buffer)
983 * msgidlen - length of msgid string
984 * art - the article for this control message (NULL if this cancel object lacks one)
985 * must - if must be accepted into queue
988 static conn_ret addCancelItem(connection_t *cxn,
989 char *folder, int folderlen,
990 char *msgid, int msgidlen,
991 Article art, int must)
993 control_item_t *item;
997 ASSERT(folder); ASSERT(msgid); ASSERT(cxn);
999 /* sanity check folder, msgid */
1000 for (i = 0; i < folderlen; i++) ASSERT(!isspace((int) folder[i]));
1001 for (i = 0; i < msgidlen; i++) ASSERT(!isspace((int) msgid[i]));
1003 /* create the object */
1004 item = xcalloc (1, sizeof(control_item_t));
1006 item->folder = xcalloc(folderlen+1, 1);
1007 memcpy(item->folder, folder, folderlen);
1008 item->folder[folderlen] = '\0';
1010 item->msgid = xcalloc (msgidlen+1, 1);
1011 memcpy(item->msgid, msgid, msgidlen);
1012 item->msgid[msgidlen] = '\0';
1014 item->article = art;
1016 /* try to add to the queue (counts if art isn't null) */
1017 result = AddToQueue(&(cxn->imap_controlMsg_q), item, CANCEL_MSG, (art != NULL), must);
1018 if (result != RET_OK) {
1019 d_printf(1,"%s:%d addCancelItem(): "
1020 "I thought we had in space in [imap] queue "
1021 "but apparently not\n",
1022 hostPeerName (cxn->myHost), cxn->ident);
1035 static conn_ret AddControlMsg(connection_t *cxn,
1038 char *control_header,
1039 char *control_header_end,
1042 char *rcpt_list = NULL, *rcpt_list_end;
1043 control_item_t *item;
1044 conn_ret res = RET_OK;
1047 /* make sure contents ok; this also should load it into memory */
1048 if (!artContentsOk (art)) {
1049 d_printf(0, "%s:%d AddControlMsg(): "
1050 "artContentsOk() said article was bad\n",
1051 hostPeerName (cxn->myHost), cxn->ident);
1052 hostArticleIsMissing (cxn->myHost, cxn, art);
1057 /* now let's look at the control to see what it is */
1058 if (!strncasecmp(control_header,"newgroup",8)) {
1059 control_header += 8;
1061 } else if (!strncasecmp(control_header,"rmgroup",7)) {
1062 /* jump past "rmgroup" */
1063 control_header += 7;
1065 } else if (!strncasecmp(control_header,"cancel",6)) {
1067 control_header += 6;
1069 /* unrecognized type */
1074 endstr = strchr(control_header,'\n');
1075 clen = endstr - control_header;
1077 if (clen > sizeof(tmp)-1) clen = sizeof(tmp)-1;
1079 memcpy(tmp,control_header, clen);
1082 d_printf(0,"%s:%d Don't understand control header [%s]\n",
1083 hostPeerName (cxn->myHost), cxn->ident,tmp);
1093 /* go past all white space */
1094 while ((*control_header == ' ') &&
1095 (control_header != control_header_end)) {
1099 /* trim trailing whitespace */
1100 while (control_header_end[-1] == ' ') {
1101 control_header_end--;
1104 if (control_header >= control_header_end) {
1105 d_printf(0,"%s:%d addControlMsg(): "
1106 "newgroup/rmgroup header has no group specified\n",
1107 hostPeerName (cxn->myHost), cxn->ident);
1111 folderlen = control_header_end - control_header;
1113 item = xcalloc(1, sizeof(control_item_t));
1115 item->folder = xcalloc(folderlen + 1, 1);
1116 memcpy(item->folder, control_header, folderlen);
1117 item->folder[folderlen] = '\0';
1119 item->article = art;
1121 if (AddToQueue(&(cxn->imap_controlMsg_q), item, t, 1, must) != RET_OK) {
1122 d_printf(1,"%s:%d addCancelItem(): "
1123 "I thought we had in space in [imap] queue"
1124 " but apparently not\n",
1125 hostPeerName (cxn->myHost), cxn->ident);
1136 char *str, *laststart;
1138 while (((*control_header) == ' ') &&
1139 (control_header != control_header_end))
1144 if (control_header == control_header_end)
1146 d_printf(0, "%s:%d Control header contains cancel "
1147 "with no msgid specified\n",
1148 hostPeerName (cxn->myHost), cxn->ident);
1152 if (FindHeader(bufs, "Newsgroups", &rcpt_list, &rcpt_list_end)!=RET_OK)
1154 d_printf(0,"%s:%d Cancel msg contains no newsgroups header\n",
1155 hostPeerName (cxn->myHost), cxn->ident);
1160 laststart = rcpt_list;
1162 while (str != rcpt_list_end)
1165 /* eliminate leading whitespace */
1166 while (((*laststart) ==' ') || ((*laststart)=='\t'))
1171 res = addCancelItem(cxn, laststart,
1174 control_header_end - control_header,
1176 if (res!=RET_OK) return res;
1187 res = addCancelItem(cxn, laststart, str - laststart,
1189 control_header_end - control_header,
1191 if (res!=RET_OK) return res;
1197 d_printf(0, "%s:%d internal error in addControlMsg()\n",
1198 hostPeerName (cxn->myHost), cxn->ident);
1204 * Show msg handling statistics
1207 static void show_stats(connection_t *cxn)
1209 d_printf(0, "%s:%d\n",hostPeerName (cxn->myHost), cxn->ident);
1210 d_printf(0, " imap queue = %d lmtp queue = %d\n",
1211 QueueItems(&(cxn->imap_controlMsg_q)),
1212 QueueItems(&(cxn->lmtp_todeliver_q)));
1213 d_printf(0," imap state = %s\n", imap_stateToString(cxn->imap_state));
1214 d_printf(0," lmtp state = %s\n", lmtp_stateToString(cxn->lmtp_state));
1215 d_printf(0," delivered: yes: %d no: %d\n",
1216 cxn->lmtp_succeeded,
1218 d_printf(0," cancel: yes: %d no: %d\n",
1219 cxn->cancel_succeeded, cxn->cancel_failed);
1220 d_printf(0," create: yes: %d no: %d\n",
1221 cxn->create_succeeded, cxn->create_failed);
1222 d_printf(0," remove: yes: %d no: %d\n",
1223 cxn->remove_succeeded, cxn->remove_failed);
1226 /**************************** SASL helper functions ******************************/
1229 /* callback to get userid or authid */
1230 static int getsimple(void *context __attribute__((unused)),
1232 const char **result,
1239 return SASL_BADPARAM;
1243 case SASL_CB_GETREALM:
1244 *result = deliver_realm;
1246 *len = deliver_realm ? strlen(deliver_realm) : 0;
1250 *result = deliver_username;
1252 *len = deliver_username ? strlen(deliver_username) : 0;
1254 case SASL_CB_AUTHNAME:
1255 authid=deliver_authname;
1258 *len = authid ? strlen(authid) : 0;
1260 case SASL_CB_LANGUAGE:
1266 return SASL_BADPARAM;
1271 /* callback to get password */
1273 getsecret(sasl_conn_t *conn,
1274 void *context __attribute__((unused)),
1276 sasl_secret_t **psecret)
1280 if (! conn || ! psecret || id != SASL_CB_PASS)
1281 return SASL_BADPARAM;
1283 if (deliver_password==NULL)
1285 d_printf(0,"SASL requested a password but I don't have one\n");
1289 passlen = strlen(deliver_password);
1290 *psecret = xmalloc(sizeof(sasl_secret_t) + passlen + 1);
1294 strlcpy((*psecret)->data, deliver_password, passlen + 1);
1295 (*psecret)->len = passlen;
1301 /* callbacks we support */
1302 static sasl_callback_t saslcallbacks[] = {
1304 SASL_CB_GETREALM, &getsimple, NULL
1306 SASL_CB_USER, &getsimple, NULL
1308 SASL_CB_AUTHNAME, &getsimple, NULL
1310 SASL_CB_PASS, &getsecret, NULL
1312 SASL_CB_LIST_END, NULL, NULL
1316 static sasl_security_properties_t *make_secprops(int min,int max)
1318 sasl_security_properties_t *ret=
1319 xmalloc(sizeof(sasl_security_properties_t));
1321 ret->maxbufsize=1024;
1325 ret->security_flags=0;
1326 ret->property_names=NULL;
1327 ret->property_values=NULL;
1332 #ifndef NI_WITHSCOPEID
1333 #define NI_WITHSCOPEID 0
1336 #define NI_MAXHOST 1025
1339 #define NI_MAXSERV 32
1342 static int iptostring(const struct sockaddr *addr, socklen_t addrlen,
1343 char *out, unsigned outlen) {
1344 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
1346 if(!addr || !out) return SASL_BADPARAM;
1348 getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
1349 NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);
1351 if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
1352 return SASL_BUFOVER;
1354 snprintf(out, outlen, "%s;%s", hbuf, pbuf);
1359 static conn_ret SetSASLProperties(sasl_conn_t *conn, int sock, int minssf, int maxssf)
1362 sasl_security_properties_t *secprops=NULL;
1363 int addrsize=sizeof(struct sockaddr_in);
1364 char localip[60], remoteip[60];
1365 struct sockaddr_in saddr_l;
1366 struct sockaddr_in saddr_r;
1368 /* create a security structure and give it to sasl */
1369 secprops = make_secprops(minssf, maxssf);
1370 if (secprops != NULL)
1372 sasl_setprop(conn, SASL_SEC_PROPS, secprops);
1376 if (getpeername(sock,(struct sockaddr *)&saddr_r,&addrsize)!=0)
1379 if (iptostring((struct sockaddr *)&saddr_r, sizeof(struct sockaddr_in),
1380 remoteip, sizeof(remoteip)))
1383 if (sasl_setprop(conn, SASL_IPREMOTEPORT, remoteip)!=SASL_OK)
1386 addrsize=sizeof(struct sockaddr_in);
1387 if (getsockname(sock,(struct sockaddr *) &saddr_l,&addrsize)!=0)
1390 if (iptostring((struct sockaddr *)&saddr_l, sizeof(struct sockaddr_in),
1391 localip, sizeof(localip)))
1394 if (sasl_setprop(conn, SASL_IPLOCALPORT, localip)!=SASL_OK)
1399 #endif /* HAVE_SASL */
1401 /************************** END SASL helper functions ******************************/
1403 /************************* Startup functions **********************************/
1405 static conn_ret Initialize(connection_t *cxn, int respTimeout)
1408 conn_ret saslresult;
1409 #endif /* HAVE_SASL */
1411 d_printf(1,"%s:%d initializing....\n",
1412 hostPeerName (cxn->myHost), cxn->ident);
1415 /* only call sasl_client_init() once */
1416 if (initialized_sasl == 0)
1418 /* Initialize SASL */
1419 saslresult=sasl_client_init(saslcallbacks);
1421 if (saslresult!=SASL_OK)
1424 "%s:%d Error initializing SASL (sasl_client_init) (%s)\n",
1425 hostPeerName (cxn->myHost), cxn->ident,
1426 sasl_errstring(saslresult, NULL, NULL));
1429 initialized_sasl = 1;
1432 #endif /* HAVE_SASL */
1434 cxn->lmtp_rBuffer = newBuffer(4096);
1435 if (cxn->lmtp_rBuffer == NULL)
1437 d_printf(0, "%s:%d Failure allocating buffer for lmtp_rBuffer\n",
1438 hostPeerName (cxn->myHost), cxn->ident);
1441 bufferAddNullByte(cxn->lmtp_rBuffer);
1444 cxn->imap_rBuffer = newBuffer(4096);
1445 if (cxn->imap_rBuffer == NULL)
1447 d_printf(0, "%s:%d Failure allocating buffer for imap_rBuffer \n",
1448 hostPeerName (cxn->myHost), cxn->ident);
1451 bufferAddNullByte(cxn->imap_rBuffer);
1453 /* Initialize timeouts */
1454 cxn->lmtp_writeTimeout = respTimeout;
1455 cxn->lmtp_readTimeout = respTimeout;
1456 cxn->imap_writeTimeout = respTimeout;
1457 cxn->imap_readTimeout = respTimeout;
1458 cxn->lmtp_sleepTimerId = 0 ;
1459 cxn->lmtp_sleepTimeout = init_reconnect_period ;
1460 cxn->imap_sleepTimerId = 0 ;
1461 cxn->imap_sleepTimeout = init_reconnect_period ;
1463 cxn->dosomethingTimeout = DOSOMETHING_TIMEOUT;
1465 /* set up the write timer. */
1466 clearTimer (cxn->dosomethingTimerId) ;
1468 if (cxn->dosomethingTimeout > 0)
1469 cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk,
1470 cxn->dosomethingTimeout, cxn);
1478 /* initialize the network */
1479 static conn_ret init_net(char *serverFQDN,
1483 struct sockaddr_in addr;
1486 if ((hp = gethostbyname(serverFQDN)) == NULL) {
1487 d_printf(0, "gethostbyname(): %s\n", strerror(errno));
1491 if (( (*sock) = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1492 d_printf(0, "socket(): %s\n", strerror(errno));
1496 addr.sin_family = AF_INET;
1497 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
1498 addr.sin_port = htons(port);
1500 if (connect( (*sock), (struct sockaddr *) &addr, sizeof (addr)) < 0) {
1501 d_printf(0,"connect(): %s\n",
1511 static conn_ret SetupLMTPConnection(connection_t *cxn,
1517 #endif /* HAVE_SASL */
1520 cxn->lmtp_port = port;
1522 if (serverName==NULL)
1524 d_printf(0, "%s:%d serverName is null\n",
1525 hostPeerName (cxn->myHost), cxn->ident);
1530 /* Free the SASL connection if we already had one */
1531 if (cxn->saslconn_lmtp!=NULL)
1533 sasl_dispose(&cxn->saslconn_lmtp);
1537 saslresult=sasl_client_new("lmtp",
1543 &cxn->saslconn_lmtp);
1545 if (saslresult != SASL_OK)
1548 d_printf(0, "%s:%d:LMTP Error creating a new SASL connection (%s)\n",
1549 hostPeerName (cxn->myHost), cxn->ident,
1550 sasl_errstring(saslresult,NULL,NULL));
1553 #endif /* HAVE_SASL */
1555 /* Connect the Socket */
1556 result = init_net(serverName,
1557 LMTP_PORT, /*port,*/
1558 &(cxn->sockfd_lmtp));
1560 if (result != RET_OK)
1562 d_printf(0, "%s:%d unable to connect to lmtp host\n",
1563 hostPeerName (cxn->myHost), cxn->ident);
1567 if (cxn->lmtp_respBuffer) free(cxn->lmtp_respBuffer);
1568 cxn->lmtp_respBuffer = xmalloc (4096);
1569 cxn->lmtp_respBuffer[0]='\0';
1571 /* Free if we had an existing one */
1572 if (cxn->lmtp_endpoint != NULL)
1574 delEndPoint(cxn->lmtp_endpoint);
1575 cxn->lmtp_endpoint = NULL;
1578 cxn->lmtp_endpoint = newEndPoint(cxn->sockfd_lmtp);
1579 if (cxn->lmtp_endpoint == NULL)
1581 d_printf(0, "%s:%d:LMTP failure creating endpoint\n",
1582 hostPeerName (cxn->myHost), cxn->ident);
1587 /* Set the SASL properties */
1588 result = SetSASLProperties(cxn->saslconn_lmtp, cxn->sockfd_lmtp,
1591 if (result != RET_OK)
1593 d_printf(0,"%s:%d:LMTP error setting SASL properties\n",
1594 hostPeerName (cxn->myHost), cxn->ident);
1597 #endif /* HAVE_SASL */
1603 static conn_ret SetupIMAPConnection(connection_t *cxn,
1609 #endif /* HAVE_SASL */
1612 cxn->imap_port = port;
1614 if (serverName==NULL)
1616 d_printf(0,"%s:%d:IMAP Servername is null",
1617 hostPeerName (cxn->myHost), cxn->ident);
1622 /* Free the SASL connection if we already had one */
1623 if (cxn->imap_saslconn!=NULL)
1625 sasl_dispose(&cxn->imap_saslconn);
1629 saslresult=sasl_client_new("imap",
1635 &cxn->imap_saslconn);
1637 if (saslresult != SASL_OK)
1639 d_printf(0,"%s:%d:IMAP Error creating a new SASL connection (%s)",
1640 hostPeerName (cxn->myHost), cxn->ident,
1641 sasl_errstring(saslresult,NULL,NULL));
1644 #endif /* HAVE_SASL */
1646 /* Connect the Socket */
1647 result = init_net(serverName,
1649 &(cxn->imap_sockfd));
1651 if (result != RET_OK)
1653 d_printf(0,"%s:%d:IMAP Unable to start network connection for IMAP",
1654 hostPeerName (cxn->myHost), cxn->ident);
1658 if (cxn->imap_respBuffer) free(cxn->imap_respBuffer);
1659 cxn->imap_respBuffer = xmalloc (4096);
1660 cxn->imap_respBuffer[0]='\0';
1662 /* Free if we had an existing one */
1663 if (cxn->imap_endpoint != NULL)
1665 delEndPoint(cxn->imap_endpoint);
1666 cxn->imap_endpoint = NULL;
1669 cxn->imap_endpoint = newEndPoint(cxn->imap_sockfd);
1670 if (cxn->imap_endpoint == NULL)
1672 d_printf(0,"%s:%d:IMAP Failure creating imap endpoint\n",
1673 hostPeerName (cxn->myHost), cxn->ident);
1678 /* Set the SASL properties */
1679 result = SetSASLProperties(cxn->imap_saslconn, cxn->imap_sockfd,
1681 if (result != RET_OK)
1683 d_printf(0,"%s:%d:IMAP Error setting sasl properties",
1684 hostPeerName (cxn->myHost), cxn->ident);
1687 #endif /* HAVE_SASL */
1693 /************************* END Startup functions **********************************/
1695 /* Return the response code for this line
1696 -1 if it doesn't seem to have one
1698 static int ask_code(char *str)
1702 if (str==NULL) return -1;
1704 if (strlen(str) < 3) return -1;
1706 /* check to make sure 0-2 are digits */
1707 if ((isdigit((int) str[0])==0) ||
1708 (isdigit((int) str[1])==0) ||
1709 (isdigit((int) str[2])==0))
1712 "Parse error: response does not begin with a code [%s]\n",
1718 ret = ((str[0]-'0')*100)+
1725 /* is this a continuation or not?
1727 220 fdsfs is not (0)
1730 static int ask_keepgoing(char *str)
1732 if (str==NULL) return 0;
1733 if (strlen(str) < 4) return 0;
1735 if (str[3]=='-') return 1;
1741 static conn_ret lmtp_listenintro(connection_t *cxn)
1743 Buffer *readBuffers;
1745 /* set up to receive */
1746 readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
1747 prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
1749 cxn->lmtp_state = LMTP_READING_INTRO;
1756 /************************** IMAP functions ***********************/
1758 static conn_ret imap_Connect(connection_t *cxn)
1762 ASSERT(cxn->imap_sleepTimerId == 0);
1764 /* make the IMAP connection */
1765 result = SetupIMAPConnection(cxn,
1769 /* Listen to the intro and start the authenticating process */
1770 result = imap_listenintro(cxn);
1776 * This is called when the data write timeout for the remote
1777 * goes off. We tear down the connection and notify our host.
1779 static void imap_writeTimeoutCbk (TimeoutId id UNUSED, void *data)
1781 connection_t *cxn = (Connection) data ;
1782 const char *peerName ;
1784 peerName = hostPeerName (cxn->myHost) ;
1786 syslog (LOG_WARNING, "timeout for %s", peerName);
1787 d_printf(0, "%s: shutting down non-responsive IMAP connection (%s)\n",
1788 hostPeerName (cxn->myHost),
1789 imap_stateToString(cxn->imap_state));
1791 cxnLogStats (cxn,true) ;
1793 imap_Disconnect(cxn);
1797 * This is called when the timeout for the reponse from the remote
1798 * goes off. We tear down the connection and notify our host.
1800 static void imap_readTimeoutCbk (TimeoutId id, void *data)
1802 Connection cxn = (Connection) data ;
1803 const char *peerName ;
1805 ASSERT (id == cxn->imap_readBlockedTimerId) ;
1807 peerName = hostPeerName (cxn->myHost) ;
1809 warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ;
1810 d_printf(0, "%s:%d shutting down non-responsive IMAP connection (%s)\n",
1811 hostPeerName (cxn->myHost), cxn->ident,
1812 imap_stateToString(cxn->imap_state));
1814 cxnLogStats (cxn,true);
1816 if (cxn->imap_state == IMAP_DISCONNECTED)
1818 imap_Disconnect(cxn);
1819 lmtp_Disconnect(cxn);
1820 delConnection (cxn) ;
1823 imap_Disconnect(cxn);
1828 * Called by the EndPoint class when the timer goes off
1830 static void imap_reopenTimeoutCbk (TimeoutId id, void *data)
1832 Connection cxn = (Connection) data ;
1834 ASSERT (id == cxn->imap_sleepTimerId) ;
1836 cxn->imap_sleepTimerId = 0 ;
1838 d_printf(1,"%s:%d:IMAP Reopen timer rang. Try to make new connection now\n",
1839 hostPeerName (cxn->myHost), cxn->ident) ;
1841 if (cxn->imap_state != IMAP_DISCONNECTED)
1843 warn ("%s:%d cxnsleep connection in bad state: %s",
1844 hostPeerName (cxn->myHost), cxn->ident,
1845 imap_stateToString (cxn->imap_state)) ;
1848 if (imap_Connect(cxn) != RET_OK)
1849 prepareReopenCbk(cxn, 0);
1853 static void imap_Disconnect(connection_t *cxn)
1855 clearTimer (cxn->imap_sleepTimerId) ;
1856 cxn->imap_sleepTimerId = 0;
1857 clearTimer (cxn->imap_readBlockedTimerId) ;
1858 clearTimer (cxn->imap_writeBlockedTimerId) ;
1860 DeferAllArticles(cxn, &(cxn->imap_controlMsg_q)) ; /* give any articles back to Host */
1862 cxn->imap_state = IMAP_DISCONNECTED;
1864 cxn->imap_disconnects++;
1866 cxn->imap_respBuffer[0]='\0';
1868 if (cxn->issue_quit == 0)
1869 prepareReopenCbk(cxn,0);
1871 DeleteIfDisconnected(cxn);
1874 /************************** END IMAP functions ***********************/
1876 /************************ LMTP functions **************************/
1879 * Create a network lmtp connection
1880 * and start listening for the intro string
1884 static conn_ret lmtp_Connect(connection_t *cxn)
1888 ASSERT(cxn->lmtp_sleepTimerId == 0);
1890 /* make the LMTP connection */
1891 result = SetupLMTPConnection(cxn,
1895 if (result!=RET_OK) return result;
1897 /* Listen to the intro */
1898 result = lmtp_listenintro(cxn);
1905 static void lmtp_Disconnect(connection_t *cxn)
1907 clearTimer (cxn->lmtp_sleepTimerId) ;
1908 cxn->lmtp_sleepTimerId = 0;
1909 clearTimer (cxn->lmtp_readBlockedTimerId) ;
1910 clearTimer (cxn->lmtp_writeBlockedTimerId) ;
1912 /* give any articles back to Host */
1913 DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q)) ;
1915 cxn->lmtp_state = LMTP_DISCONNECTED;
1917 cxn->lmtp_disconnects++;
1919 cxn->lmtp_respBuffer[0]='\0';
1921 if (cxn->issue_quit == 0)
1922 prepareReopenCbk(cxn,1);
1924 DeleteIfDisconnected(cxn);
1930 * Called by the EndPoint class when the timer goes off
1932 static void lmtp_reopenTimeoutCbk (TimeoutId id, void *data)
1934 Connection cxn = (Connection) data ;
1936 ASSERT (id == cxn->lmtp_sleepTimerId) ;
1938 cxn->lmtp_sleepTimerId = 0 ;
1940 d_printf(1,"%s:%d:LMTP Reopen timer rang. Try to make new connection now\n",
1941 hostPeerName (cxn->myHost), cxn->ident) ;
1943 if (cxn->lmtp_state != LMTP_DISCONNECTED)
1945 warn ("%s:%d cxnsleep connection in bad state: %s",
1946 hostPeerName (cxn->myHost), cxn->ident,
1947 lmtp_stateToString (cxn->lmtp_state)) ;
1950 if (lmtp_Connect(cxn) != RET_OK)
1951 prepareReopenCbk(cxn, 1);
1956 * Set up the callback used when the Connection is sleeping (i.e. will try
1957 * to reopen the connection).
1959 * type (0 = imap, 1 = lmtp)
1961 static void prepareReopenCbk (Connection cxn, int type)
1963 /* xxx check state */
1969 cxn->imap_sleepTimerId = prepareSleep (imap_reopenTimeoutCbk,
1970 cxn->imap_sleepTimeout, cxn) ;
1971 d_printf (1,"%s:%d IMAP connection error\n"
1972 " will try to reconnect in %d seconds\n",
1973 hostPeerName (cxn->myHost), cxn->ident,
1974 cxn->imap_sleepTimeout) ;
1976 cxn->lmtp_sleepTimerId = prepareSleep (lmtp_reopenTimeoutCbk,
1977 cxn->lmtp_sleepTimeout, cxn) ;
1978 d_printf (1,"%s:%d:LMTP connection error\n"
1979 "will try to reconnect in %d seconds\n",
1980 hostPeerName (cxn->myHost), cxn->ident,
1981 cxn->lmtp_sleepTimeout) ;
1984 /* bump the sleep timer amount each time to wait longer and longer. Gets
1985 reset in resetConnection() */
1987 cxn->imap_sleepTimeout *= 2 ;
1988 if (cxn->imap_sleepTimeout > max_reconnect_period)
1989 cxn->imap_sleepTimeout = max_reconnect_period ;
1991 cxn->lmtp_sleepTimeout *= 2 ;
1992 if (cxn->lmtp_sleepTimeout > max_reconnect_period)
1993 cxn->lmtp_sleepTimeout = max_reconnect_period ;
1998 * This is called when the timeout for the reponse from the remote
1999 * goes off. We tear down the connection and notify our host.
2001 static void lmtp_readTimeoutCbk (TimeoutId id, void *data)
2003 Connection cxn = (Connection) data ;
2004 const char *peerName ;
2006 ASSERT (id == cxn->lmtp_readBlockedTimerId) ;
2008 peerName = hostPeerName (cxn->myHost) ;
2010 warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ;
2011 d_printf(0,"%s:%d shutting down non-responsive LMTP connection (%s)\n",
2012 hostPeerName (cxn->myHost), cxn->ident,
2013 lmtp_stateToString(cxn->lmtp_state));
2015 cxnLogStats (cxn,true) ;
2017 if (cxn->lmtp_state == LMTP_DISCONNECTED) {
2018 imap_Disconnect(cxn);
2019 lmtp_Disconnect(cxn);
2020 delConnection (cxn) ;
2022 lmtp_Disconnect(cxn);
2029 * This is called when the data write timeout for the remote
2030 * goes off. We tear down the connection and notify our host.
2032 static void lmtp_writeTimeoutCbk (TimeoutId id UNUSED, void *data)
2034 connection_t *cxn = (Connection) data ;
2035 const char *peerName ;
2037 peerName = hostPeerName (cxn->myHost) ;
2039 syslog (LOG_WARNING, "timeout for %s", peerName);
2040 d_printf(0, "%s:%d shutting down non-responsive LMTP connection (%s)\n",
2041 hostPeerName (cxn->myHost), cxn->ident,
2042 lmtp_stateToString(cxn->lmtp_state)) ;
2044 cxnLogStats (cxn,true);
2046 lmtp_Disconnect(cxn);
2049 /************************ END LMTP functions **************************/
2051 /************************** LMTP write functions ********************/
2053 static conn_ret lmtp_noop(connection_t *cxn)
2058 p = xstrdup("NOOP\r\n");
2059 result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2060 if (result!=RET_OK) return result;
2062 cxn->lmtp_state = LMTP_WRITING_NOOP;
2067 static conn_ret lmtp_IssueQuit(connection_t *cxn)
2072 p = xstrdup("QUIT\r\n");
2073 result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2074 if (result!=RET_OK) return result;
2076 cxn->lmtp_state = LMTP_WRITING_QUIT;
2081 static conn_ret lmtp_getcapabilities(connection_t *cxn)
2086 if (cxn->lmtp_capabilities != NULL)
2088 if (cxn->lmtp_capabilities->saslmechs) {
2089 free( cxn->lmtp_capabilities->saslmechs);
2091 free( cxn->lmtp_capabilities );
2092 cxn->lmtp_capabilities = NULL;
2095 cxn->lmtp_capabilities = xcalloc (1, sizeof(lmtp_capabilities_t));
2096 cxn->lmtp_capabilities->saslmechs = NULL;
2099 p = concat("EHLO ", hostname, "\r\n", (char *) 0);
2101 p = concat("LHLO ", hostname, "\r\n", (char *) 0);
2102 #endif /* SMTPMODE */
2104 result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2105 if (result!=RET_OK) return result;
2107 cxn->lmtp_state = LMTP_WRITING_LHLO;
2113 static conn_ret lmtp_authenticate(connection_t *cxn)
2117 const char *mechusing;
2119 unsigned int outlen;
2127 sasl_interact_t *client_interact=NULL;
2130 saslresult=sasl_client_start(cxn->saslconn_lmtp,
2131 cxn->lmtp_capabilities->saslmechs,
2138 if ((saslresult != SASL_OK) &&
2139 (saslresult != SASL_CONTINUE)) {
2141 d_printf(0,"%s:%d:LMTP Error calling sasl_client_start (%s)\n",
2142 hostPeerName (cxn->myHost), cxn->ident,
2143 sasl_errstring(saslresult, NULL, NULL));
2147 d_printf(1,"%s:%d:LMTP Decided to try to authenticate with SASL mechanism=%s\n",
2148 hostPeerName (cxn->myHost), cxn->ident,mechusing);
2152 /* no initial client response */
2153 p = concat("AUTH ", mechusing, "\r\n", (char *) 0);
2154 } else if (!outlen) {
2155 /* empty initial client response */
2156 p = concat("AUTH ", mechusing, " =\r\n", (char *) 0);
2158 /* initial client response - convert to base64 */
2159 inbase64 = xmalloc(outlen*2+10);
2161 saslresult = sasl_encode64(out, outlen,
2162 inbase64, outlen*2+10,
2163 (unsigned *) &inbase64len);
2164 if (saslresult != SASL_OK) return RET_FAIL;
2165 p = concat("AUTH ", mechusing, " ", inbase64, "\r\n", (char *) 0);
2169 result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2171 cxn->lmtp_state = LMTP_WRITING_STARTAUTH;
2176 static imt_stat lmtp_getauthline(char *str, char **line, int *linelen)
2180 int response_code = -1;
2182 response_code = ask_code(str);
2184 if (response_code == 334) {
2188 } else if (response_code == 235) {
2190 /* woohoo! authentication complete */
2194 /* failure of some sort */
2195 d_printf(0,"?:?:LMTP Authentication failure (%d) [%s]\n",
2200 str += 4; /* jump past the "334 " */
2202 *line = xmalloc(strlen(str)+30);
2203 if ((*line)==NULL) {
2207 /* decode this line */
2208 saslresult = sasl_decode64(str, strlen(str),
2209 *line, strlen(str)+1, (unsigned *) linelen);
2210 if (saslresult != SASL_OK) {
2211 d_printf(0,"?:?:LMTP base64 decoding error\n");
2217 #endif /* HAVE_SASL */
2219 static void lmtp_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b,
2222 connection_t *cxn = (connection_t *) d;
2223 Buffer *readBuffers;
2225 clearTimer (cxn->lmtp_writeBlockedTimerId) ;
2227 /* Free the string that was written */
2228 freeBufferArray (b);
2229 if (cxn->lmtp_tofree_str!=NULL)
2231 free(cxn->lmtp_tofree_str);
2232 cxn->lmtp_tofree_str=NULL;
2235 /* set up to receive */
2236 readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
2237 prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
2239 /* set up the response timer. */
2240 clearTimer (cxn->lmtp_readBlockedTimerId) ;
2242 if (cxn->lmtp_readTimeout > 0)
2243 cxn->lmtp_readBlockedTimerId = prepareSleep (lmtp_readTimeoutCbk,
2244 cxn->lmtp_readTimeout, cxn) ;
2247 switch (cxn->lmtp_state)
2250 case LMTP_WRITING_LHLO:
2251 cxn->lmtp_state = LMTP_READING_LHLO;
2254 case LMTP_WRITING_STARTAUTH:
2255 case LMTP_WRITING_STEPAUTH:
2257 cxn->lmtp_state = LMTP_READING_STEPAUTH;
2261 case LMTP_WRITING_UPTODATA:
2262 /* expect result to rset */
2263 cxn->lmtp_state = LMTP_READING_RSET;
2266 case LMTP_WRITING_CONTENTS:
2267 /* so we sent the whole DATA command
2268 let's see what the server responded */
2270 cxn->lmtp_state = LMTP_READING_CONTENTS;
2274 case LMTP_WRITING_NOOP:
2275 cxn->lmtp_state = LMTP_READING_NOOP;
2278 case LMTP_WRITING_QUIT:
2279 cxn->lmtp_state = LMTP_READING_QUIT;
2284 d_printf(0,"%s:%d:LMTP Unknown state. Internal error\n",
2285 hostPeerName (cxn->myHost), cxn->ident) ;
2291 /************************** END LMTP write functions ********************/
2293 /************************** IMAP sending functions ************************/
2296 static void imap_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b,
2299 connection_t *cxn = (connection_t *) d;
2300 Buffer *readBuffers;
2302 clearTimer (cxn->imap_writeBlockedTimerId) ;
2304 /* free the string we just wrote out */
2305 freeBufferArray (b);
2306 if (cxn->imap_tofree_str!=NULL)
2308 free(cxn->imap_tofree_str);
2309 cxn->imap_tofree_str=NULL;
2312 /* set up to receive */
2313 readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
2314 prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
2316 /* set up the response timer. */
2317 clearTimer (cxn->imap_readBlockedTimerId) ;
2319 if (cxn->imap_readTimeout > 0)
2320 cxn->imap_readBlockedTimerId = prepareSleep(imap_readTimeoutCbk,
2321 cxn->imap_readTimeout,
2324 switch (cxn->imap_state) {
2325 case IMAP_WRITING_CAPABILITY:
2326 cxn->imap_state = IMAP_READING_CAPABILITY;
2329 case IMAP_WRITING_STEPAUTH:
2330 case IMAP_WRITING_STARTAUTH:
2331 cxn->imap_state = IMAP_READING_STEPAUTH;
2334 case IMAP_WRITING_CREATE:
2335 cxn->imap_state = IMAP_READING_CREATE;
2338 case IMAP_WRITING_DELETE:
2339 cxn->imap_state = IMAP_READING_DELETE;
2342 case IMAP_WRITING_SELECT:
2343 cxn->imap_state = IMAP_READING_SELECT;
2346 case IMAP_WRITING_SEARCH:
2347 cxn->imap_state = IMAP_READING_SEARCH;
2350 case IMAP_WRITING_STORE:
2351 cxn->imap_state = IMAP_READING_STORE;
2354 case IMAP_WRITING_CLOSE:
2355 cxn->imap_state = IMAP_READING_CLOSE;
2358 case IMAP_WRITING_NOOP:
2359 cxn->imap_state = IMAP_READING_NOOP;
2362 case IMAP_WRITING_QUIT:
2363 cxn->imap_state = IMAP_READING_QUIT;
2367 d_printf(0,"%s:%d:IMAP invalid connection state\n",
2368 hostPeerName (cxn->myHost), cxn->ident) ;
2369 imap_Disconnect(cxn);
2375 * Tag is already allocated
2378 static void imap_GetTag(connection_t *cxn)
2380 sprintf(cxn->imap_currentTag,"%06d",cxn->imap_tag_num);
2381 cxn->imap_tag_num++;
2382 if (cxn->imap_tag_num >= 999999)
2384 cxn->imap_tag_num = 0;
2389 static conn_ret imap_sendAuthStep(connection_t *cxn, char *str)
2396 unsigned int outlen;
2398 unsigned int inbase64len;
2400 /* base64 decode it */
2402 saslresult = sasl_decode64(str, strlen(str),
2403 in, strlen(str)+1, &inlen);
2404 if (saslresult != SASL_OK) {
2405 d_printf(0,"%s:%d:IMAP base64 decoding error\n",
2406 hostPeerName (cxn->myHost), cxn->ident) ;
2410 saslresult=sasl_client_step(cxn->imap_saslconn,
2417 /* check if sasl succeeded */
2418 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
2420 d_printf(0,"%s:%d:IMAP sasl_client_step failed with %s\n",
2421 hostPeerName (cxn->myHost), cxn->ident,
2422 sasl_errstring(saslresult,NULL,NULL));
2423 cxn->imap_state = IMAP_CONNECTED_NOTAUTH;
2427 inbase64 = xmalloc(outlen * 2 + 10);
2429 /* convert to base64 */
2430 saslresult = sasl_encode64(out, outlen,
2431 inbase64, outlen*2, (unsigned *) &inbase64len);
2433 if (saslresult != SASL_OK) return RET_FAIL;
2435 /* append endline */
2436 strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10 - inbase64len);
2439 /* send to server */
2440 result = WriteToWire_imapstr(cxn,inbase64, inbase64len);
2442 cxn->imap_state = IMAP_WRITING_STEPAUTH;
2446 #endif /* HAVE_SASL */
2448 static conn_ret imap_sendAuthenticate(connection_t *cxn)
2455 const char *mechusing;
2458 int saslresult=SASL_NOMECH;
2460 sasl_interact_t *client_interact=NULL;
2462 if (cxn->imap_capabilities->saslmechs) {
2463 saslresult=sasl_client_start(cxn->imap_saslconn,
2464 cxn->imap_capabilities->saslmechs,
2472 /* If no mechs try "login" */
2473 if (saslresult == SASL_NOMECH)
2476 #else /* HAVE_SASL */
2478 { /* always do login */
2480 #endif /* HAVE_SASL */
2481 d_printf(1,"%s:%d:IMAP No mechanism found. Trying login method\n",
2482 hostPeerName (cxn->myHost), cxn->ident) ;
2484 if (cxn->imap_capabilities->logindisabled==1)
2486 d_printf(0,"%s:%d:IMAP Login command w/o security layer not allowed on this server\n",
2487 hostPeerName (cxn->myHost), cxn->ident);
2491 if (deliver_authname==NULL)
2493 d_printf(0,"%s:%d:IMAP Unable to log in because can't find a authname\n",
2494 hostPeerName (cxn->myHost), cxn->ident) ;
2498 if (deliver_password==NULL)
2500 d_printf(0,"%s:%d:IMAP Unable to log in because can't find a password\n",
2501 hostPeerName (cxn->myHost), cxn->ident) ;
2507 p = concat(cxn->imap_currentTag, " LOGIN ", deliver_authname, " \"",
2508 deliver_password, "\"\r\n", (char *) 0);
2510 result = WriteToWire_imapstr(cxn, p, strlen(p));
2512 cxn->imap_state = IMAP_WRITING_STARTAUTH;
2518 if ((saslresult != SASL_OK) &&
2519 (saslresult != SASL_CONTINUE)) {
2521 d_printf(0,"%s:%d:IMAP Error calling sasl_client_start (%s) mechusing = %s\n",
2522 hostPeerName (cxn->myHost), cxn->ident,
2523 sasl_errstring(saslresult, NULL, NULL), mechusing);
2527 d_printf(1,"%s:%d:IMAP Trying to authenticate to imap with %s mechanism\n",
2528 hostPeerName (cxn->myHost), cxn->ident,
2533 p = concat(cxn->imap_currentTag, " AUTHENTICATE ", mechusing, "\r\n",
2535 result = WriteToWire_imapstr(cxn, p, strlen(p));
2537 cxn->imap_state = IMAP_WRITING_STARTAUTH;
2540 #endif /* HAVE_SASL */
2543 static conn_ret imap_CreateGroup(connection_t *cxn, char *bboard)
2548 d_printf(1,"%s:%d:IMAP Ok creating group [%s]\n",
2549 hostPeerName (cxn->myHost), cxn->ident,
2554 tosend = concat(cxn->imap_currentTag, " CREATE ", bboard, "\r\n",
2557 result = WriteToWire_imapstr(cxn, tosend, -1);
2558 if (result!=RET_OK) return result;
2560 cxn->imap_state = IMAP_WRITING_CREATE;
2565 static conn_ret imap_DeleteGroup(connection_t *cxn, char *bboard)
2570 d_printf(1,"%s:%d:IMAP Ok removing bboard [%s]\n",
2571 hostPeerName (cxn->myHost), cxn->ident, bboard);
2575 tosend = concat(cxn->imap_currentTag, " DELETE ", bboard, "\r\n",
2577 result = WriteToWire_imapstr(cxn, tosend, -1);
2578 if (result!=RET_OK) return result;
2580 cxn->imap_state = IMAP_WRITING_DELETE;
2585 static conn_ret imap_CancelMsg(connection_t *cxn, char *newsgroup)
2595 tosend = concat(cxn->imap_currentTag, " SELECT ", newsgroup, "\r\n",
2597 result = WriteToWire_imapstr(cxn, tosend, -1);
2598 if (result != RET_OK) return result;
2600 cxn->imap_state = IMAP_WRITING_SELECT;
2602 hostArticleOffered (cxn->myHost, cxn);
2607 static conn_ret imap_sendSearch(connection_t *cxn, char *msgid)
2616 /* preform search */
2617 tosend = concat(cxn->imap_currentTag,
2618 " UID SEARCH header \"Message-ID\" \"", msgid, "\"\r\n",
2620 result = WriteToWire_imapstr(cxn, tosend, -1);
2621 if (result != RET_OK) return result;
2623 cxn->imap_state = IMAP_WRITING_SEARCH;
2628 static conn_ret imap_sendKill(connection_t *cxn, unsigned uid)
2636 length = 7 + 50 + 20;
2637 tosend = xmalloc(length);
2638 snprintf(tosend,length,"%s UID STORE %d +FLAGS.SILENT (\\Deleted)\r\n",
2639 cxn->imap_currentTag, uid);
2641 result = WriteToWire_imapstr(cxn, tosend, -1);
2642 if (result != RET_OK) return result;
2644 cxn->imap_state = IMAP_WRITING_STORE;
2649 static conn_ret imap_sendSimple(connection_t *cxn, const char *atom, int st)
2655 tosend = concat(cxn->imap_currentTag, " ", atom, "\r\n", (char *) 0);
2657 result = WriteToWire_imapstr(cxn, tosend, -1);
2658 if (result != RET_OK) return result;
2660 cxn->imap_state = st;
2665 static conn_ret imap_sendClose(connection_t *cxn)
2667 return imap_sendSimple(cxn, "CLOSE", IMAP_WRITING_CLOSE);
2670 static conn_ret imap_sendQuit(connection_t *cxn)
2672 return imap_sendSimple(cxn, "LOGOUT", IMAP_WRITING_QUIT);
2675 static conn_ret imap_noop(connection_t *cxn)
2677 return imap_sendSimple(cxn, "NOOP", IMAP_WRITING_NOOP);
2681 static conn_ret imap_sendCapability(connection_t *cxn)
2683 return imap_sendSimple(cxn, "CAPABILITY", IMAP_WRITING_CAPABILITY);
2686 /************************** END IMAP sending functions ************************/
2688 /************************** IMAP reading functions ***************************/
2690 static conn_ret imap_listenintro(connection_t *cxn)
2692 Buffer *readBuffers;
2694 /* set up to receive */
2695 readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
2696 prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
2698 cxn->imap_state = IMAP_READING_INTRO;
2703 static conn_ret imap_ParseCapability(char *string, imap_capabilities_t **caps)
2709 /* allocate the caps structure if it doesn't already exist */
2710 if ( (*caps) == NULL)
2711 (*caps) = xcalloc(1, sizeof(imap_capabilities_t));
2713 while ( (*str) != '\0')
2716 while (((*str) != '\0') && ((*str)!=' '))
2721 if ( (*str) != '\0')
2727 if ( strcasecmp(start,"IMAP4")==0)
2730 } else if (strcasecmp(start,"LOGINDISABLED")==0) {
2731 (*caps)->logindisabled = 1;
2732 } else if ( strncmp(start, "AUTH=", 5)==0) {
2734 if ( (*caps)->saslmechs == NULL)
2736 (*caps)->saslmechs = xstrdup (start + 5);
2738 mechlen = strlen((*caps)->saslmechs) + 1;
2739 mechlen += strlen(start + 5) + 1;
2740 (*caps)->saslmechs = xrealloc((*caps)->saslmechs, mechlen);
2741 strlcat((*caps)->saslmechs, " ", mechlen);
2742 strlcat((*caps)->saslmechs, start + 5, mechlen);
2750 if ((*caps)->saslmechs) {
2751 d_printf(1,"?:?:IMAP parsed capabilities: saslmechs = %s\n",
2752 (*caps)->saslmechs);
2759 static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d)
2761 connection_t *cxn = (connection_t *) d;
2762 Buffer *readBuffers ;
2771 p = bufferBase(b[0]);
2773 /* Add what we got to our internal read buffer */
2774 bufferAddNullByte (b[0]) ;
2777 errno = endPointErrno(e);
2779 syslog(LOG_ERR, "%s:%d IMAP i/o failed: %m",
2780 hostPeerName (cxn->myHost), cxn->ident);
2781 freeBufferArray (b);
2782 imap_Disconnect(cxn);
2786 if (strchr (p, '\n') == NULL) {
2787 /* partial read. expand buffer and retry */
2789 if (expandBuffer (b[0], BUFFER_EXPAND_AMOUNT)==false) {
2790 d_printf(0,"%s:%d:IMAP expanding buffer returned false\n",
2791 hostPeerName (cxn->myHost), cxn->ident);
2793 imap_Disconnect(cxn);
2796 readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ;
2798 if (!prepareRead (e, readBuffers, imap_readCB, cxn, 1)) {
2799 imap_Disconnect(cxn);
2802 freeBufferArray (b);
2806 clearTimer (cxn->imap_readBlockedTimerId) ;
2808 /* we got something. add to our buffer and free b */
2810 strcat(cxn->imap_respBuffer, p);
2812 bufferSetDataSize( b[0], 0);
2814 freeBufferArray (b);
2818 /* goto here to take another step */
2821 /* see if we have a full line */
2822 ret = GetLine( cxn->imap_respBuffer , strbuf, sizeof(strbuf));
2826 /* if we don't have a full line */
2827 if ( ret == RET_NO_FULLLINE)
2830 readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
2832 if ( !prepareRead (e, readBuffers, imap_readCB, cxn, 1) )
2834 imap_Disconnect(cxn);
2838 } else if (ret!=RET_OK)
2844 if ((str[0]=='*') && (str[1]==' '))
2848 /* now figure out what kind of untagged it is */
2849 if (strncasecmp(str,"CAPABILITY ",11)==0)
2853 imap_ParseCapability(str,&(cxn->imap_capabilities));
2855 } else if (strncasecmp(str,"SEARCH",6)==0) {
2863 cxn->current_control->data.control->uid = atoi(str);
2865 d_printf(1,"%s:%d:IMAP i think the UID = %ld\n",
2866 hostPeerName (cxn->myHost), cxn->ident,
2867 cxn->current_control->data.control->uid);
2869 /* it's probably a blank uid (i.e. message doesn't exist) */
2870 cxn->current_control->data.control->uid = (unsigned long)-1;
2874 } else if (strncasecmp(str,"OK ",3)==0) {
2876 if (cxn->imap_state==IMAP_READING_INTRO)
2878 imap_sendCapability(cxn); /* xxx errors */
2887 /* untagged command not understood */
2890 /* always might be more to look at */
2893 } else if ((str[0]=='+') && (str[1]==' ')) {
2897 if (cxn->imap_state == IMAP_READING_STEPAUTH)
2900 if (imap_sendAuthStep(cxn, str)!=RET_OK)
2902 imap_Disconnect(cxn);
2905 d_printf(0,"%s:%d:IMAP got a '+ ...' without SASL. Something's wrong\n",
2906 hostPeerName (cxn->myHost), cxn->ident);
2907 imap_Disconnect(cxn);
2908 #endif /* HAVE_SASL */
2912 d_printf(0,"%s:%d:IMAP got a '+ ...' in state where not expected\n",
2913 hostPeerName (cxn->myHost), cxn->ident);
2914 imap_Disconnect(cxn);
2920 } else if (strncmp(str, cxn->imap_currentTag, IMAP_TAGLENGTH)==0) {
2921 /* matches our tag */
2922 str += IMAP_TAGLENGTH;
2926 d_printf(0,"%s:%d:IMAP Parse error: tag with no space afterward\n",
2927 hostPeerName (cxn->myHost), cxn->ident);
2928 imap_Disconnect(cxn);
2933 /* should be OK/NO */
2934 if (strncmp(str,"OK",2)==0)
2941 switch(cxn->imap_state)
2943 case IMAP_READING_CAPABILITY:
2946 if (imap_sendAuthenticate(cxn)!=RET_OK)
2948 d_printf(0,"%s:%d:IMAP sendauthenticate failed\n",
2949 hostPeerName (cxn->myHost), cxn->ident);
2950 imap_Disconnect(cxn);
2954 d_printf(0,"%s:%d:IMAP CAPABILITY gave a NO response\n",
2955 hostPeerName (cxn->myHost), cxn->ident);
2956 imap_Disconnect(cxn);
2961 case IMAP_READING_STEPAUTH:
2965 cxn->imap_sleepTimeout = init_reconnect_period ;
2967 cxn->imap_timeCon = theTime () ;
2968 cxn->timeCon = theTime () ;
2970 d_printf(0,"%s:%d IMAP authentication succeeded\n",
2971 hostPeerName (cxn->myHost), cxn->ident);
2973 cxn->imap_disconnects=0;
2975 cxn->imap_state = IMAP_IDLE_AUTHED;
2977 /* try to send a message if we have one */
2979 imap_ProcessQueue(cxn);
2981 d_printf(0,"%s:%d:IMAP Authentication failed with [%s]\n",
2982 hostPeerName (cxn->myHost), cxn->ident,str);
2983 imap_Disconnect(cxn);
2990 case IMAP_READING_CREATE:
2994 d_printf(1,"%s:%d:IMAP Create of bboard successful\n",
2995 hostPeerName (cxn->myHost), cxn->ident);
2997 cxn->create_succeeded++;
2999 /* we can delete article now */
3000 QueueForgetAbout(cxn, cxn->current_control,
3003 d_printf(1,"%s:%d:IMAP Create failed with [%s] for %s\n",
3004 hostPeerName (cxn->myHost), cxn->ident,str,
3005 cxn->current_control->data.control->folder);
3007 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3010 imap_ProcessQueue(cxn);
3014 case IMAP_READING_DELETE:
3017 d_printf(1,"%s:%d:IMAP Delete successful\n",
3018 hostPeerName (cxn->myHost), cxn->ident);
3019 cxn->remove_succeeded++;
3021 /* we can delete article now */
3022 QueueForgetAbout(cxn, cxn->current_control,
3025 d_printf(1,"%s:%d:IMAP Delete mailbox failed with [%s] for %s\n",
3026 hostPeerName (cxn->myHost), cxn->ident,str,
3027 cxn->current_control->data.control->folder);
3029 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3033 imap_ProcessQueue(cxn);
3038 case IMAP_READING_SELECT:
3042 imap_sendSearch(cxn, cxn->current_control->data.control->msgid);
3046 d_printf(1,"%s:%d:IMAP Select failed with [%s] for %s\n",
3047 hostPeerName (cxn->myHost), cxn->ident,str,
3048 cxn->current_control->data.control->folder);
3050 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3052 cxn->imap_state = IMAP_IDLE_AUTHED;
3054 imap_ProcessQueue(cxn);
3060 case IMAP_READING_SEARCH:
3061 /* if no message let's forget about it */
3062 if (cxn->current_control->data.control->uid
3063 == (unsigned long)-1) {
3064 d_printf(2, "%s:%d:IMAP Search didn't find the message\n",
3065 hostPeerName (cxn->myHost), cxn->ident);
3066 QueueForgetAbout(cxn, cxn->current_control,
3068 if (imap_sendClose(cxn) != RET_OK)
3069 imap_Disconnect(cxn);
3074 /* we got a uid. let's delete it */
3075 if (imap_sendKill(cxn,
3076 cxn->current_control->data.control->uid)
3078 imap_Disconnect(cxn);
3081 d_printf(0, "%s:%d IMAP Received NO response to SEARCH\n",
3082 hostPeerName (cxn->myHost), cxn->ident);
3083 ReQueue(cxn, &(cxn->imap_controlMsg_q),
3084 cxn->current_control);
3086 if (imap_sendClose(cxn) != RET_OK)
3087 imap_Disconnect(cxn);
3092 case IMAP_READING_STORE:
3096 d_printf(1,"%s:%d:IMAP Processed a Cancel fully\n",
3097 hostPeerName (cxn->myHost), cxn->ident);
3099 /* we can delete article now */
3100 QueueForgetAbout(cxn, cxn->current_control,
3103 cxn->cancel_succeeded++;
3105 if (imap_sendClose(cxn) != RET_OK)
3106 imap_Disconnect(cxn);
3111 d_printf(1,"%s:%d:IMAP Store failed\n",
3112 hostPeerName (cxn->myHost), cxn->ident);
3113 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3115 if (imap_sendClose(cxn) != RET_OK)
3116 imap_Disconnect(cxn);
3122 case IMAP_READING_NOOP:
3123 cxn->imap_state = IMAP_IDLE_AUTHED;
3127 case IMAP_READING_CLOSE:
3129 /* we can't do anything about it */
3130 d_printf(1,"%s:%d:IMAP Close failed\n",
3131 hostPeerName (cxn->myHost), cxn->ident);
3134 cxn->imap_state = IMAP_IDLE_AUTHED;
3136 imap_ProcessQueue(cxn);
3140 case IMAP_READING_QUIT:
3142 /* we don't care if the server said OK or NO just
3143 that it said something */
3145 d_printf(1,"%s:%d:IMAP Read quit response\n",
3146 hostPeerName (cxn->myHost), cxn->ident);
3148 cxn->imap_state = IMAP_DISCONNECTED;
3150 DeleteIfDisconnected(cxn);
3155 d_printf(0,"%s:%d:IMAP I don't understand state %d [%s]\n",
3156 hostPeerName (cxn->myHost), cxn->ident,
3157 cxn->imap_state,str);
3158 imap_Disconnect(cxn);
3164 d_printf(0,"%s:%d:IMAP tag (%s) doesn't match what we gave (%s). What's up with that??\n",
3165 hostPeerName (cxn->myHost), cxn->ident, str, cxn->imap_currentTag);
3166 imap_Disconnect(cxn);
3171 /************************** END IMAP reading functions ***************************/
3173 /*************************** LMTP reading functions ****************************/
3175 static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d)
3177 connection_t *cxn = (connection_t *) d;
3179 Buffer *readBuffers;
3192 sasl_interact_t *client_interact=NULL;
3193 #endif /* HAVE_SASL */
3195 char *p = bufferBase(b[0]);
3197 bufferAddNullByte (b[0]) ;
3200 errno = endPointErrno(e);
3201 syslog(LOG_ERR, "%s:%d LMTP i/o failed: %m",
3202 hostPeerName (cxn->myHost), cxn->ident);
3204 freeBufferArray (b);
3205 lmtp_Disconnect(cxn);
3209 if (strchr (p, '\n') == NULL)
3211 /* partial read. expand buffer and retry */
3213 d_printf(0,"%s:%d:LMTP Partial. retry\n",
3214 hostPeerName (cxn->myHost),cxn->ident);
3215 expandBuffer (b[0], BUFFER_EXPAND_AMOUNT) ;
3216 readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ;
3218 if ( !prepareRead (e, readBuffers, lmtp_readCB, cxn, 1) )
3220 lmtp_Disconnect(cxn);
3223 freeBufferArray (b);
3227 clearTimer (cxn->lmtp_readBlockedTimerId) ;
3229 /* Add what we got to our internal read buffer */
3230 strcat(cxn->lmtp_respBuffer, p);
3232 bufferSetDataSize( b[0], 0);
3234 freeBufferArray (b);
3237 /* see if we have a full line */
3238 ret = GetLine( cxn->lmtp_respBuffer, str, sizeof(str));
3243 if (ret!=RET_NO_FULLLINE)
3245 /* was a more serious error */
3246 d_printf(0,"%s:%d:LMTP Internal error getting line from server\n",
3247 hostPeerName (cxn->myHost),cxn->ident);
3248 lmtp_Disconnect(cxn);
3252 /* set up to receive some more */
3253 readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
3254 prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
3258 switch (cxn->lmtp_state)
3261 case LMTP_READING_INTRO:
3263 if (ask_code(str)!=220)
3265 d_printf(0,"%s:%d:LMTP Initial server msg does not start with 220 (began with %d)\n",
3266 hostPeerName (cxn->myHost),cxn->ident,
3268 lmtp_Disconnect(cxn);
3272 /* the initial intro could have many lines via
3273 continuations. see if we need to read more */
3274 if (ask_keepgoing(str)==1)
3279 result = lmtp_getcapabilities(cxn);
3281 if (result != RET_OK)
3283 d_printf(0,"%s:%d:LMTP lmtp_getcapabilities() failure\n",
3284 hostPeerName (cxn->myHost),cxn->ident);
3285 lmtp_Disconnect(cxn);
3291 case LMTP_READING_LHLO:
3292 /* recieve the response(s) */
3293 response_code = ask_code(str);
3295 if (response_code != 250) /* was none */
3297 d_printf(0,"%s:%d:LMTP Response code unexpected (%d)\n",
3298 hostPeerName (cxn->myHost),cxn->ident,
3300 lmtp_Disconnect(cxn);
3304 /* look for one we know about; ignore all others */
3305 if (strncmp(str+4,"8BITMIME",strlen("8BITMIME"))==0)
3307 cxn->lmtp_capabilities->Eightbitmime = 1;
3308 } else if (strncmp(str+4, "ENHANCEDSTATUSCODES",
3309 strlen("ENHANCEDSTATUSCODES"))==0) {
3310 cxn->lmtp_capabilities->EnhancedStatusCodes = 1;
3311 } else if (strncmp(str+4, "AUTH",4)==0) {
3312 cxn->lmtp_capabilities->saslmechs = xstrdup(str + 4 + 5);
3313 } else if (strncmp(str+4,"PIPELINING",strlen("PIPELINING"))==0) {
3314 cxn->lmtp_capabilities->pipelining = 1;
3316 /* don't care; ignore */
3319 /* see if this is the last line of the capability */
3320 if (ask_keepgoing(str)==1)
3324 /* we require a few capabilities */
3325 if (!cxn->lmtp_capabilities->pipelining) {
3326 d_printf(0,"%s:%d:LMTP We require PIPELINING\n",
3327 hostPeerName (cxn->myHost),cxn->ident);
3329 lmtp_Disconnect(cxn);
3333 if (cxn->lmtp_capabilities->saslmechs) {
3334 /* start the authentication */
3335 result = lmtp_authenticate(cxn);
3337 if (result != RET_OK) {
3338 d_printf(0,"%s:%d:LMTP lmtp_authenticate() error\n",
3339 hostPeerName (cxn->myHost),cxn->ident);
3340 lmtp_Disconnect(cxn);
3348 /* either we can't authenticate or the remote server
3349 doesn't support it */
3350 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3351 d_printf(1,"%s:%d:LMTP Even though we can't authenticate"
3352 " we're going to try to feed anyway\n",
3353 hostPeerName (cxn->myHost),cxn->ident);
3354 /* We just assume we don't need to authenticate
3355 (great assumption huh?) */
3356 hostRemoteStreams (cxn->myHost, cxn, true) ;
3358 cxn->lmtp_timeCon = theTime () ;
3359 cxn->timeCon = theTime () ;
3361 /* try to send a message if we have one */
3362 lmtp_sendmessage(cxn,NULL);
3369 case LMTP_READING_STEPAUTH:
3371 status = lmtp_getauthline(str, &in, &inlen);
3378 saslresult=sasl_client_step(cxn->saslconn_lmtp,
3387 /* check if sasl succeeded */
3388 if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
3389 d_printf(0,"%s:%d:LMTP sasl_client_step(): %s\n",
3390 hostPeerName (cxn->myHost),cxn->ident,
3391 sasl_errstring(saslresult,NULL,NULL));
3393 lmtp_Disconnect(cxn);
3397 /* convert to base64 */
3398 inbase64 = xmalloc(outlen*2+10);
3400 saslresult = sasl_encode64(out, outlen,
3401 inbase64, outlen*2+10,
3402 (unsigned *) &inbase64len);
3404 if (saslresult != SASL_OK)
3406 d_printf(0,"%s:%d:LMTP sasl_encode64(): %s\n",
3407 hostPeerName (cxn->myHost),cxn->ident,
3408 sasl_errstring(saslresult,NULL,NULL));
3410 lmtp_Disconnect(cxn);
3414 /* add an endline */
3415 strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10);
3417 /* send to server */
3418 result = WriteToWire_lmtpstr(cxn,inbase64, inbase64len+2);
3420 if (result != RET_OK)
3422 d_printf(0,"%s:%d:LMTP WriteToWire() failure\n",
3423 hostPeerName (cxn->myHost),cxn->ident);
3424 lmtp_Disconnect(cxn);
3428 cxn->lmtp_state = LMTP_WRITING_STEPAUTH;
3432 cxn->lmtp_sleepTimeout = init_reconnect_period ;
3434 d_printf(0,"%s:%d LMTP authentication succeeded\n",
3435 hostPeerName (cxn->myHost), cxn->ident);
3437 cxn->lmtp_disconnects=0;
3439 hostRemoteStreams (cxn->myHost, cxn, true) ;
3441 cxn->lmtp_timeCon = theTime () ;
3442 cxn->timeCon = theTime () ;
3444 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3447 /* try to send a message if we have one */
3448 lmtp_sendmessage(cxn,NULL);
3454 d_printf(0,"%s:%d:LMTP failed authentication\n",
3455 hostPeerName (cxn->myHost),cxn->ident);
3456 lmtp_Disconnect(cxn);
3460 #endif /* HAVE_SASL */
3462 case LMTP_READING_RSET:
3463 if (ask_keepgoing(str)) {
3466 if (ask_code(str) != 250) {
3467 d_printf(0,"%s:%d:LMTP RSET failed with (%d)\n",
3468 hostPeerName (cxn->myHost),cxn->ident,
3470 lmtp_Disconnect(cxn);
3474 /* we pipelined so next we recieve the mail from response */
3475 cxn->lmtp_state = LMTP_READING_MAILFROM;
3478 case LMTP_READING_MAILFROM:
3479 if (ask_keepgoing(str)) {
3482 if (ask_code(str) != 250) {
3483 d_printf(0,"%s:%d:LMTP MAILFROM failed with (%d)\n",
3484 hostPeerName (cxn->myHost),cxn->ident,
3486 lmtp_Disconnect(cxn);
3490 /* we pipelined so next we recieve the rcpt's */
3491 cxn->lmtp_state = LMTP_READING_RCPTTO;
3495 case LMTP_READING_RCPTTO:
3496 if (ask_keepgoing(str)) {
3499 if (ask_code(str)!=250) {
3500 d_printf(1,"%s:%d:LMTP RCPT TO failed with (%d) %s\n",
3501 hostPeerName (cxn->myHost),cxn->ident,
3502 ask_code(str), str);
3504 /* if got a 5xx don't try to send anymore */
3505 cxn->current_article->trys=100;
3507 cxn->current_rcpts_issued--;
3509 cxn->current_rcpts_okayed++;
3512 /* if issued equals number okayed then we're done */
3513 if ( cxn->current_rcpts_okayed == cxn->current_rcpts_issued) {
3514 cxn->lmtp_state = LMTP_READING_DATA;
3516 /* stay in same state */
3521 case LMTP_READING_DATA:
3522 if (ask_keepgoing(str)) {
3525 if (cxn->current_rcpts_issued == 0) {
3526 if (cxn->current_article->trys < 100) {
3527 d_printf(1, "%s:%d:LMTP None of the rcpts "
3528 "were accepted for this message. Re-queueing\n",
3529 hostPeerName (cxn->myHost),cxn->ident);
3532 ReQueue(cxn, &(cxn->lmtp_todeliver_q), cxn->current_article);
3534 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3535 lmtp_sendmessage(cxn,NULL);
3537 if (WriteArticle(cxn, cxn->current_bufs) != RET_OK)
3539 d_printf(0, "%s:%d:LMTP Error writing article\n",
3540 hostPeerName (cxn->myHost),cxn->ident);
3541 lmtp_Disconnect(cxn);
3545 cxn->lmtp_state = LMTP_WRITING_CONTENTS;
3550 case LMTP_READING_CONTENTS:
3551 if (ask_keepgoing(str)) {
3555 /* need 1 response from server for every rcpt */
3556 cxn->current_rcpts_issued--;
3558 if (ask_code(str) != 250) {
3559 d_printf(1, "%s:%d:LMTP DATA failed with %d (%s)\n",
3560 hostPeerName (cxn->myHost),cxn->ident,
3561 ask_code(str), str);
3562 cxn->current_rcpts_okayed--;
3565 if (cxn->current_rcpts_issued>0) {
3570 * current_rcpts_okayed is number that succeeded
3573 if (cxn->current_rcpts_okayed == 0) {
3574 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3576 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3577 cxn->lmtp_succeeded++;
3578 d_printf(1, "%s:%d:LMTP Woohoo! message accepted\n",
3579 hostPeerName (cxn->myHost),cxn->ident);
3582 /* we can delete article now */
3583 QueueForgetAbout(cxn, cxn->current_article, MSG_SUCCESS);
3585 /* try to send another if we have one and we're still idle
3586 * forgetting the msg might have made us unidle
3588 if (cxn->lmtp_state == LMTP_AUTHED_IDLE) {
3589 lmtp_sendmessage(cxn,NULL);
3594 case LMTP_READING_NOOP:
3595 if (ask_keepgoing(str)) {
3598 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3601 case LMTP_READING_QUIT:
3602 d_printf(1,"%s:%d:LMTP read quit\n",
3603 hostPeerName (cxn->myHost),cxn->ident);
3605 cxn->lmtp_state = LMTP_DISCONNECTED;
3607 DeleteIfDisconnected(cxn);
3612 d_printf(0,"%s:%d:LMTP Bad state in lmtp_readCB %d\n",
3613 hostPeerName (cxn->myHost),cxn->ident,
3615 lmtp_Disconnect(cxn);
3623 * Add a rcpt to:<foo> to the string
3627 static void addrcpt(char *newrcpt, int newrcptlen, char **out, int *outalloc)
3629 int size = strlen(*out);
3631 int newsize = size + 9+strlen(deliver_rcpt_to)+newrcptlen+3;
3634 /* see if we need to grow the string */
3635 if (newsize > *outalloc) {
3636 (*outalloc) = newsize;
3637 (*out) = xrealloc(*out, *outalloc);
3640 strlcpy((*out) + size,"RCPT TO:<", newsize - size);
3643 c = newrcpt[newrcptlen];
3644 newrcpt[newrcptlen] = '\0';
3645 size += snprintf((*out) + size, newsize - size, deliver_rcpt_to, newrcpt);
3646 newrcpt[newrcptlen] = c;
3648 strlcpy((*out) + size, ">\r\n", newsize - size);
3650 /* has embedded '\n' */
3651 d_printf(2, "Attempting to send to: %s", (*out) + fsize);
3655 * Takes the newsgroups header value and makes it into a list of RCPT TO:'s we can send over the wire
3657 * in - newsgroups header start
3658 * in_end - end of newsgroups header
3659 * num - number of rcpt's we created
3662 static char *ConvertRcptList(char *in, char *in_end, int *num)
3665 char *ret = xmalloc(retalloc);
3667 char *laststart = in;
3671 /* start it off empty */
3672 strlcpy(ret, "", retalloc);
3674 while ( str != in_end)
3678 /* eliminate leading whitespace */
3679 while (((*laststart) ==' ') || ((*laststart)=='\t'))
3685 addrcpt(laststart, str - laststart, &ret, &retalloc);
3687 #endif /* SMTPMODE */
3696 addrcpt(laststart, str - laststart, &ret, &retalloc);
3703 static void addto(char *newrcpt, int newrcptlen, const char *sep,
3704 char **out, int *outalloc)
3706 int size = strlen(*out);
3707 int newsize = size + strlen(sep)+1+strlen(deliver_to_header)+newrcptlen+1;
3710 /* see if we need to grow the string */
3711 if (newsize > *outalloc) {
3712 (*outalloc) = newsize;
3713 (*out) = xrealloc(*out, *outalloc);
3716 size += snprintf((*out) + size, newsize - size, "%s<", sep);
3718 c = newrcpt[newrcptlen];
3719 newrcpt[newrcptlen] = '\0';
3720 size += snprintf((*out) + size, newsize - size, deliver_to_header,newrcpt);
3721 newrcpt[newrcptlen] = c;
3723 strlcpy((*out) + size, ">", newsize - size);
3727 * Takes the newsgroups header value and makes it into a To: header
3729 * in - newsgroups header start
3730 * in_end - end of newsgroups header
3733 static char *BuildToHeader(char *in, char *in_end)
3736 char *ret = xmalloc(retalloc);
3738 char *laststart = in;
3739 const char *sep = "";
3741 /* start it off with the header name */
3742 strlcpy(ret,"To: ", retalloc);
3744 while ( str != in_end)
3748 /* eliminate leading whitespace */
3749 while (((*laststart) ==' ') || ((*laststart)=='\t'))
3754 addto(laststart, str - laststart, sep, &ret, &retalloc);
3757 /* separate multiple addresses with a comma */
3766 addto(laststart, str - laststart, sep, &ret, &retalloc);
3769 /* terminate the header */
3770 strlcat(ret, "\n\r", retalloc);
3774 /*************************** END LMTP reading functions ****************************/
3779 * Process the control message queue. If we run out ask the host for more.
3781 * cxn - connection object
3784 static void imap_ProcessQueue(connection_t *cxn)
3786 article_queue_t *item;
3791 /* pull an article off the queue */
3792 result = PopFromQueue(&(cxn->imap_controlMsg_q), &item);
3794 if (result==RET_QUEUE_EMPTY)
3796 if (cxn->issue_quit)
3802 cxn->imap_state = IMAP_IDLE_AUTHED;
3804 /* now we wait for articles from our Host, or we have some
3805 articles already. On infrequently used connections, the
3806 network link is torn down and rebuilt as needed. So we may
3807 be rebuilding the connection here in which case we have an
3810 /* make sure imap has _lots_ of space too */
3811 if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0) &&
3812 (QueueItems(&(cxn->imap_controlMsg_q)) == 0))
3814 if (hostGimmeArticle (cxn->myHost,cxn)==true)
3821 cxn->current_control = item;
3826 imap_CreateGroup(cxn, item->data.control->folder);
3830 imap_CancelMsg(cxn, item->data.control->folder);
3834 imap_DeleteGroup(cxn, item->data.control->folder);
3847 * Pulls a message off the queue and trys to start sending it. If the
3848 * message is a control message put it in the control queue and grab
3849 * another message. If the message doesn't exist on disk or something
3850 * is wrong with it tell the host and try again. If we run out of
3851 * messages to get tell the host we want more
3853 * cxn - connection object
3854 * justadded - the article that was just added to the queue
3857 static void lmtp_sendmessage(connection_t *cxn, Article justadded)
3863 char *control_header = NULL;
3864 char *control_header_end = NULL;
3866 article_queue_t *item;
3867 char *rcpt_list, *rcpt_list_end;
3872 /* pull an article off the queue */
3873 result = PopFromQueue(&(cxn->lmtp_todeliver_q), &item);
3875 if (result==RET_QUEUE_EMPTY)
3877 if (cxn->issue_quit) {
3878 lmtp_IssueQuit(cxn);
3881 /* now we wait for articles from our Host, or we have some
3882 articles already. On infrequently used connections, the
3883 network link is torn down and rebuilt as needed. So we may
3884 be rebuilding the connection here in which case we have an
3887 /* make sure imap has space too */
3888 d_printf(1,"%s:%d stalled waiting for articles\n",
3889 hostPeerName (cxn->myHost),cxn->ident);
3890 if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0) &&
3891 (QueueItems(&(cxn->imap_controlMsg_q)) == 0)) {
3892 if (hostGimmeArticle (cxn->myHost,cxn)==true)
3899 /* make sure contents ok; this also should load it into memory */
3900 res = artContentsOk (item->data.article);
3903 if (justadded == item->data.article) {
3904 ReQueue(cxn, &(cxn->lmtp_todeliver_q), item);
3907 /* tell to reject taking this message */
3908 QueueForgetAbout(cxn,item, MSG_MISSING);
3914 /* Check if it's a control message */
3915 bufs = artGetNntpBuffers (item->data.article);
3918 /* tell to reject taking this message */
3919 QueueForgetAbout(cxn,item, MSG_MISSING);
3923 result = FindHeader(bufs, "Control", &control_header, &control_header_end);
3924 if (result == RET_OK) {
3925 result = AddControlMsg(cxn, item->data.article, bufs,
3926 control_header,control_header_end, 1);
3927 if (result != RET_OK) {
3928 d_printf(1,"%s:%d Error adding to [imap] control queue\n",
3929 hostPeerName (cxn->myHost),cxn->ident) ;
3930 ReQueue(cxn, &(cxn->lmtp_todeliver_q), item);
3934 switch(cxn->imap_state) {
3935 case IMAP_IDLE_AUTHED:
3936 /* we're idle. let's process the queue */
3937 imap_ProcessQueue(cxn);
3939 case IMAP_DISCONNECTED:
3941 /* Let's connect. Once we're connected we can
3942 worry about the message */
3943 if (cxn->imap_sleepTimerId == 0) {
3944 if (imap_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,0);
3948 /* we're doing something right now */
3952 /* all we did was add a control message.
3953 we still want to get an lmtp message */
3957 if (cxn->current_bufs != NULL) {
3958 /* freeBufferArray(cxn->current_bufs); */
3959 cxn->current_bufs = NULL;
3961 cxn->current_bufs = bufs;
3962 cxn->current_article = item;
3964 /* we make use of pipelining here
3972 /* find out who it's going to */
3973 result = FindHeader(cxn->current_bufs, "Newsgroups",
3974 &rcpt_list, &rcpt_list_end);
3976 if ((result != RET_OK) || (rcpt_list == NULL)) {
3977 d_printf(1,"%s:%d Didn't find Newsgroups header\n",
3978 hostPeerName (cxn->myHost),cxn->ident) ;
3979 QueueForgetAbout(cxn, cxn->current_article, MSG_FAIL_DELIVER);
3983 /* free's original rcpt_list */
3984 rcpt_list = ConvertRcptList(rcpt_list, rcpt_list_end,
3985 &cxn->current_rcpts_issued);
3986 cxn->current_rcpts_okayed = 0;
3988 if(mailfrom_name == NULL)
3989 mailfrom_name = xstrdup("");
3990 p = concat("RSET\r\n"
3991 "MAIL FROM:<", mailfrom_name, ">\r\n",
3993 "DATA\r\n", (char *) 0);
3995 cxn->lmtp_state = LMTP_WRITING_UPTODATA;
3996 result = WriteToWire_lmtpstr(cxn, p, strlen(p));
3998 if (result != RET_OK) {
3999 d_printf(0,"%s:%d failed trying to write\n",
4000 hostPeerName (cxn->myHost),cxn->ident) ;
4001 lmtp_Disconnect(cxn);
4005 /* prepend To: header to article */
4006 if (deliver_to_header) {
4007 char *to_list, *to_list_end;
4010 result = FindHeader(cxn->current_bufs, "Followup-To",
4011 &to_list, &to_list_end);
4013 if ((result != RET_OK) || (to_list == NULL)) {
4014 result = FindHeader(cxn->current_bufs, "Newsgroups",
4015 &to_list, &to_list_end);
4018 /* free's original to_list */
4019 to_list = BuildToHeader(to_list, to_list_end);
4021 len = bufferArrayLen(cxn->current_bufs);
4022 cxn->current_bufs = xrealloc(cxn->current_bufs,
4023 sizeof(Buffer) * (len+2));
4024 cxn->current_bufs[len+1] = NULL;
4026 for (i = len; i > 0; i--) {
4027 cxn->current_bufs[i] = cxn->current_bufs[i-1];
4030 cxn->current_bufs[0] = newBufferByCharP(to_list, strlen(to_list+1),
4034 hostArticleOffered (cxn->myHost, cxn);
4038 * Called by the EndPoint class when the timer goes off
4040 static void dosomethingTimeoutCbk (TimeoutId id, void *data)
4042 Connection cxn = (Connection) data ;
4044 ASSERT (id == cxn->dosomethingTimerId) ;
4048 /* we're disconnected but there are things to send */
4049 if ((cxn->lmtp_state == LMTP_DISCONNECTED) &&
4050 (cxn->lmtp_sleepTimerId == 0) &&
4051 QueueItems(&(cxn->lmtp_todeliver_q)) > 0)
4053 if (lmtp_Connect(cxn) != RET_OK)
4054 prepareReopenCbk(cxn, 1);
4057 if ((cxn->imap_state == IMAP_DISCONNECTED) &&
4058 (cxn->imap_sleepTimerId == 0) &&
4059 (QueueItems(&(cxn->imap_controlMsg_q)) > 0))
4061 if (imap_Connect(cxn) != RET_OK)
4062 prepareReopenCbk(cxn, 0);
4066 /* if we're idle and there are items to send let's send them */
4067 if ((cxn->lmtp_state == LMTP_AUTHED_IDLE) &&
4068 QueueItems(&(cxn->lmtp_todeliver_q)) > 0) {
4069 lmtp_sendmessage(cxn,NULL);
4070 } else if (cxn->lmtp_state == LMTP_AUTHED_IDLE) {
4074 if ((cxn->imap_state == IMAP_IDLE_AUTHED) &&
4075 (QueueItems(&(cxn->imap_controlMsg_q)) > 0)) {
4076 imap_ProcessQueue(cxn);
4077 } else if (cxn->imap_state == IMAP_IDLE_AUTHED) {
4081 /* set up the timer. */
4082 clearTimer (cxn->dosomethingTimerId) ;
4084 cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk,
4085 cxn->dosomethingTimeout, cxn);
4088 /* Give all articles in the queue back to the host. We're probably
4089 * going to exit soon.
4092 static void DeferAllArticles(connection_t *cxn, Q_t *q)
4094 article_queue_t *cur;
4099 ret = PopFromQueue(q, &cur);
4100 if (ret == RET_QUEUE_EMPTY) return;
4104 QueueForgetAbout(cxn, cur, MSG_GIVE_BACK);
4106 d_printf(0,"%s:%d Error emptying queue (deffering all articles)\n",
4107 hostPeerName (cxn->myHost),cxn->ident);
4114 * Does the actual deletion of a connection and all its private data.
4116 static void delConnection (Connection cxn)
4124 d_printf (1,"Deleting connection: %s:%d\n",
4125 hostPeerName (cxn->myHost),cxn->ident) ;
4127 for (c = gCxnList, q = NULL ; c != NULL ; q = c, c = c->next)
4131 gCxnList = gCxnList->next ;
4137 ASSERT (c != NULL) ;
4139 if (cxn->lmtp_endpoint != NULL)
4140 delEndPoint (cxn->lmtp_endpoint) ;
4141 if (cxn->imap_endpoint != NULL)
4142 delEndPoint (cxn->imap_endpoint) ;
4144 delBuffer (cxn->imap_rBuffer) ;
4145 delBuffer (cxn->lmtp_rBuffer) ;
4147 /* tell the Host we're outta here. */
4148 shutDown = hostCxnGone (cxn->myHost, cxn) ;
4153 free (cxn->ServerName) ;
4155 clearTimer (cxn->imap_readBlockedTimerId) ;
4156 clearTimer (cxn->imap_writeBlockedTimerId) ;
4157 clearTimer (cxn->lmtp_readBlockedTimerId) ;
4158 clearTimer (cxn->lmtp_writeBlockedTimerId) ;
4160 clearTimer (cxn->imap_sleepTimerId);
4161 cxn->imap_sleepTimerId = 0;
4162 clearTimer (cxn->lmtp_sleepTimerId);
4163 cxn->lmtp_sleepTimerId = 0;
4165 clearTimer (cxn->dosomethingTimerId);
4167 free (cxn->imap_respBuffer);
4168 free (cxn->lmtp_respBuffer);
4174 /* exit program if that was the last connexion for the last host */
4175 /* XXX what about if there are ever multiple listeners?
4176 XXX this will be executed if all hosts on only one of the
4177 XXX listeners have gone */
4178 time_t now = theTime () ;
4179 char dateString [30] ;
4181 strlcpy (dateString,ctime (&now),sizeof (dateString)) ;
4182 dateString [24] = '\0' ;
4184 notice ("ME finishing at %s", dateString) ;
4191 /******************** PUBLIC FUNCTIONS ****************************/
4196 * Create a new Connection.
4198 * HOST is the host object we're owned by.
4199 * IDENT is an identifier to be added to syslog entries so we can tell
4200 * what's happening on different connections to the same peer.
4201 * IPNAME is the name (or ip address) of the remote)
4202 * MAXTOUT is the maximum amount of time to wait for a response before
4203 * considering the remote host dead.
4204 * PORTNUM is the portnum to contact on the remote end.
4205 * RESPTIMEOUT is the amount of time to wait for a response from a remote
4206 * before considering the connection dead.
4207 * CLOSEPERIOD is the number of seconds after connecting that the
4208 * connections should be closed down and reinitialized (due to problems
4209 * with old NNTP servers that hold history files open. Value of 0 means
4213 Connection newConnection (Host host,
4216 unsigned int artTout UNUSED,
4217 unsigned int portNum UNUSED,
4218 unsigned int respTimeout,
4219 unsigned int closePeriod UNUSED,
4220 double lowPassLow UNUSED,
4221 double lowPassHigh UNUSED,
4222 double lowPassFilter UNUSED)
4225 /* check arguments */
4227 /* allocate connection structure */
4228 cxn = xcalloc (1, sizeof(connection_t)) ;
4230 cxn->ident = ident ;
4231 cxn->ServerName = xstrdup (ipname) ;
4232 cxn->myHost = host ;
4234 /* setup mailfrom user */
4235 if (gethostname(hostname, MAXHOSTNAMELEN)!=0)
4237 d_printf(0,"%s gethostname failed\n",ipname);
4242 mailfrom_name = concat("news@", hostname, (char *) 0);
4244 cxn->next = gCxnList ;
4249 Initialize(cxn, respTimeout);
4255 /* Causes the Connection to build the network connection. */
4256 bool cxnConnect (Connection cxn)
4258 /* make the lmtp connection */
4259 if (lmtp_Connect(cxn) != RET_OK) return false;
4261 if (imap_Connect(cxn) != RET_OK) return false;
4267 static void QuitIfIdle(Connection cxn)
4269 if ((cxn->lmtp_state == LMTP_AUTHED_IDLE) &&
4270 (QueueItems(&(cxn->lmtp_todeliver_q))<=0)) {
4271 lmtp_IssueQuit(cxn);
4273 if ((cxn->imap_state == IMAP_IDLE_AUTHED) &&
4274 (QueueItems(&(cxn->imap_controlMsg_q))<=0)) {
4279 static void DeleteIfDisconnected(Connection cxn)
4281 /* we want to shut everything down. if both connections disconnected now we can */
4282 if ((cxn->issue_quit >= 1) &&
4283 (cxn->lmtp_state == LMTP_DISCONNECTED) &&
4284 (cxn->imap_state == IMAP_DISCONNECTED))
4287 switch (cxn->issue_quit)
4290 if (cxn->lmtp_state == LMTP_DISCONNECTED)
4292 cxn->lmtp_state = LMTP_WAITING;
4293 cxn->imap_state = IMAP_WAITING;
4294 cxn->issue_quit = 0;
4295 hostCxnWaiting (cxn->myHost,cxn) ; /* tell our Host we're waiting */
4299 if (cxn->lmtp_state == LMTP_DISCONNECTED)
4301 cxn->issue_quit = 0;
4303 if (imap_Connect(cxn)!=RET_OK) prepareReopenCbk(cxn,0);
4304 if (lmtp_Connect(cxn)!=RET_OK) prepareReopenCbk(cxn,1);
4308 if (cxn->lmtp_state == LMTP_DISCONNECTED)
4310 hostCxnDead (cxn->myHost,cxn) ;
4319 /* puts the connection into the wait state (i.e. waits for an article
4320 before initiating a connect). Can only be called right after
4321 newConnection returns, or while the Connection is in the (internal)
4323 void cxnWait (Connection cxn)
4325 cxn->issue_quit = 1;
4330 /* The Connection will disconnect as if cxnDisconnect were called and then
4331 it automatically reconnects to the remote. */
4332 void cxnFlush (Connection cxn)
4334 cxn->issue_quit = 2;
4341 /* The Connection sends remaining articles, then issues a QUIT and then
4343 void cxnClose (Connection cxn)
4345 d_printf(0,"%s:%d Closing cxn\n",hostPeerName (cxn->myHost), cxn->ident);
4346 cxn->issue_quit = 3;
4350 DeleteIfDisconnected(cxn);
4353 /* The Connection drops all queueed articles, then issues a QUIT and then
4355 void cxnTerminate (Connection cxn)
4357 d_printf(0,"%s:%d Terminate\n",hostPeerName (cxn->myHost), cxn->ident);
4359 cxn->issue_quit = 3;
4361 /* give any articles back to host in both queues */
4362 DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
4363 DeferAllArticles(cxn, &(cxn->imap_controlMsg_q));
4368 /* Blow away the connection gracelessly and immedately clean up */
4369 void cxnNuke (Connection cxn)
4371 d_printf(0,"%s:%d Nuking connection\n",cxn->ServerName, cxn->ident);
4373 cxn->issue_quit = 4;
4375 /* give any articles back to host in both queues */
4376 DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
4377 DeferAllArticles(cxn, &(cxn->imap_controlMsg_q));
4379 imap_Disconnect(cxn);
4380 lmtp_Disconnect(cxn);
4382 hostCxnDead (cxn->myHost,cxn);
4388 * true - must queue article. Don't try sending
4389 * false - queue of article may fail. Try sending
4391 * Always adds to lmtp queue even if control message
4395 static bool ProcessArticle(Connection cxn, Article art, bool must)
4399 /* Don't accept any articles when we're closing down the connection */
4400 if (cxn->issue_quit > 1) {
4404 /* if it's a regular message let's add it to the queue */
4405 result = AddToQueue(&(cxn->lmtp_todeliver_q), art, DELIVER,1,must);
4407 if (result == RET_EXCEEDS_SIZE) {
4411 if (result != RET_OK)
4413 d_printf(0,"%s:%d Error adding to delivery queue\n",
4414 hostPeerName (cxn->myHost), cxn->ident);
4418 if (must == true) return true;
4420 switch (cxn->lmtp_state)
4423 case LMTP_DISCONNECTED:
4424 if (cxn->lmtp_sleepTimerId == 0)
4425 if (lmtp_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,1);
4428 case LMTP_AUTHED_IDLE:
4429 lmtp_sendmessage(cxn,art);
4432 /* currently doing something */
4439 /* Tells the Connection to take the article and handle its
4440 transmission. If it can't (due to queue size or whatever), then the
4441 function returns false. The connection assumes ownership of the
4442 article if it accepts it (returns true). */
4443 bool cxnTakeArticle (Connection cxn, Article art)
4445 /* if we're closing down always refuse */
4446 if (cxn->issue_quit == 1) return false;
4448 return ProcessArticle (cxn,art,false);
4451 /* Tell the Connection to take the article (if it can) for later
4452 processing. Assumes ownership of it if it takes it. */
4453 bool cxnQueueArticle (Connection cxn, Article art)
4455 return ProcessArticle (cxn,art,true);
4458 /* generate a syslog message for the connections activity. Called by Host. */
4459 void cxnLogStats (Connection cxn, bool final)
4461 const char *peerName ;
4462 time_t now = theTime() ;
4463 int total, good, bad;
4465 ASSERT (cxn != NULL) ;
4467 peerName = hostPeerName (cxn->myHost) ;
4469 total = cxn->lmtp_succeeded + cxn->lmtp_failed;
4470 total += cxn->cancel_succeeded + cxn->cancel_failed;
4471 total += cxn->create_succeeded + cxn->create_failed;
4472 total += cxn->remove_succeeded + cxn->remove_failed;
4474 good = cxn->lmtp_succeeded;
4475 good += cxn->cancel_succeeded;
4476 good += cxn->create_succeeded;
4477 good += cxn->remove_succeeded;
4479 bad = cxn->lmtp_failed;
4480 bad += cxn->cancel_failed;
4481 bad += cxn->create_failed;
4482 bad += cxn->remove_failed;
4483 notice ("%s:%d %s seconds %ld accepted %d refused %d rejected %d",
4484 peerName, cxn->ident, (final ? "final" : "checkpoint"),
4485 (long) (now - cxn->timeCon), total, 0, bad);
4489 cxn->lmtp_succeeded = 0;
4490 cxn->lmtp_failed = 0;
4491 cxn->cancel_succeeded = 0;
4492 cxn->cancel_failed = 0;
4493 cxn->create_succeeded = 0;
4494 cxn->create_failed = 0;
4495 cxn->remove_succeeded = 0;
4496 cxn->remove_failed = 0;
4498 if (cxn->timeCon > 0)
4499 cxn->timeCon = theTime() ;
4504 /* return the number of articles the connection can be given. This lets
4505 the host shovel in as many as possible. May be zero. */
4506 size_t cxnQueueSpace (Connection cxn)
4511 lmtpsize = QueueSpace(&(cxn->lmtp_todeliver_q));
4512 imapsize = QueueSpace(&(cxn->imap_controlMsg_q));
4514 if (lmtpsize >=1) lmtpsize--;
4515 if (imapsize >=1) imapsize--;
4517 d_printf(1,"%s:%d Q Space lmtp size = %d state = %d\n",
4518 hostPeerName (cxn->myHost), cxn->ident,
4519 lmtpsize,cxn->lmtp_state);
4520 d_printf(1,"%s:%d Q Space imap size = %d state = %d\n",
4521 hostPeerName (cxn->myHost), cxn->ident,
4522 imapsize,cxn->imap_state);
4524 /* return the smaller of our 2 queues */
4525 if (lmtpsize < imapsize)
4531 /* adjust the mode no-CHECK filter values */
4532 void cxnSetCheckThresholds (Connection cxn,
4533 double lowFilter UNUSED,
4534 double highFilter UNUSED,
4535 double lowPassFilter UNUSED)
4537 d_printf(1,"%s:%d Threshold change. This means nothing to me\n",
4538 hostPeerName (cxn->myHost), cxn->ident);
4541 /* print some debugging info. */
4542 void gPrintCxnInfo (FILE *fp, unsigned int indentAmt)
4544 char indent [INDENT_BUFFER_SIZE] ;
4548 for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
4552 fprintf (fp,"%sGlobal Connection list : (count %d) {\n",
4554 for (cxn = gCxnList ; cxn != NULL ; cxn = cxn->next)
4555 printCxnInfo (cxn,fp,indentAmt + INDENT_INCR) ;
4556 fprintf (fp,"%s}\n",indent) ;
4559 void printCxnInfo (Connection cxn, FILE *fp, unsigned int indentAmt)
4561 char indent [INDENT_BUFFER_SIZE] ;
4563 article_queue_t *artH ;
4565 for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
4569 fprintf (fp,"%sConnection : %p {\n",indent, (void *) cxn) ;
4570 fprintf (fp,"%s host : %p\n",indent, (void *) cxn->myHost) ;
4571 fprintf (fp,"%s endpoint (imap): %p\n",indent, (void *) cxn->imap_endpoint) ;
4572 fprintf (fp,"%s endpoint (lmtp): %p\n",indent, (void *) cxn->lmtp_endpoint) ;
4573 fprintf (fp,"%s state (imap) : %s\n",indent, imap_stateToString (cxn->imap_state)) ;
4574 fprintf (fp,"%s state (lmtp) : %s\n",indent, lmtp_stateToString (cxn->lmtp_state)) ;
4575 fprintf (fp,"%s ident : %d\n",indent,cxn->ident) ;
4576 fprintf (fp,"%s ip-name (imap): %s\n", indent, cxn->ServerName) ;
4577 fprintf (fp,"%s ip-name (lmtp): %s\n", indent, cxn->ServerName) ;
4578 fprintf (fp,"%s port-number (imap) : %d\n",indent,cxn->imap_port) ;
4579 fprintf (fp,"%s port-number (lmtp) : %d\n",indent,cxn->lmtp_port) ;
4581 fprintf (fp,"%s Issuing Quit : %d\n",indent, cxn->issue_quit) ;
4583 fprintf (fp,"%s time-connected (imap) : %ld\n",indent,(long) cxn->imap_timeCon) ;
4584 fprintf (fp,"%s time-connected (lmtp) : %ld\n",indent,(long) cxn->lmtp_timeCon) ;
4585 fprintf (fp,"%s articles from INN : %d\n",indent,
4586 cxn->lmtp_succeeded+
4588 cxn->cancel_succeeded+
4590 cxn->create_succeeded+
4592 cxn->remove_succeeded+
4594 QueueSpace(&(cxn->lmtp_todeliver_q))+
4595 QueueSpace(&(cxn->imap_controlMsg_q))
4597 fprintf(fp,"%s LMTP STATS: yes: %d no: %d\n",indent,
4598 cxn->lmtp_succeeded, cxn->lmtp_failed);
4599 fprintf(fp,"%s control: yes: %d no: %d\n",indent,
4600 cxn->cancel_succeeded, cxn->cancel_failed);
4601 fprintf(fp,"%s create: yes: %d no: %d\n",indent,
4602 cxn->create_succeeded, cxn->create_failed);
4603 fprintf(fp,"%s remove: yes: %d no: %d\n",indent,
4604 cxn->remove_succeeded, cxn->remove_failed);
4606 fprintf (fp,"%s response-timeout : %d\n",indent,cxn->imap_readTimeout) ;
4607 fprintf (fp,"%s response-callback : %d\n",indent,cxn->imap_readBlockedTimerId) ;
4609 fprintf (fp,"%s write-timeout : %d\n",indent,cxn->imap_writeTimeout) ;
4610 fprintf (fp,"%s write-callback : %d\n",indent,cxn->imap_writeBlockedTimerId) ;
4612 fprintf (fp,"%s reopen wait : %d\n",indent,cxn->imap_sleepTimeout) ;
4613 fprintf (fp,"%s reopen id : %d\n",indent,cxn->imap_sleepTimerId) ;
4615 fprintf (fp,"%s IMAP queue {\n",indent) ;
4616 for (artH = cxn->imap_controlMsg_q.head; artH != NULL ; artH = artH->next)
4617 printArticleInfo (artH->data.article,fp,indentAmt + INDENT_INCR) ;
4618 fprintf (fp,"%s }\n",indent) ;
4620 fprintf (fp,"%s LMTP queue {\n",indent) ;
4621 for (artH = cxn->lmtp_todeliver_q.head ; artH != NULL ; artH = artH->next)
4622 printArticleInfo (artH->data.control->article,fp,indentAmt + INDENT_INCR) ;
4623 fprintf (fp,"%s }\n",indent) ;
4625 fprintf (fp,"%s}\n",indent) ;
4628 /* config file load callback */
4629 int cxnConfigLoadCbk (void *data UNUSED)
4633 FILE *fp = (FILE *) data ;
4635 if (getInteger (topScope,"max-reconnect-time",&iv,NO_INHERIT))
4640 logOrPrint (LOG_ERR,fp,
4641 "ME config: value of %s (%ld) in %s cannot be less"
4642 " than 1. Using %ld", "max-reconnect-time",
4643 iv,"global scope",(long) MAX_RECON_PER);
4644 iv = MAX_RECON_PER ;
4648 iv = MAX_RECON_PER ;
4649 max_reconnect_period = (unsigned int) iv ;
4651 if (getInteger (topScope,"initial-reconnect-time",&iv,NO_INHERIT))
4656 logOrPrint (LOG_ERR,fp,
4657 "ME config: value of %s (%ld) in %s cannot be less"
4658 " than 1. Using %ld", "initial-reconnect-time",
4659 iv,"global scope",(long)INIT_RECON_PER);
4660 iv = INIT_RECON_PER ;
4664 iv = INIT_RECON_PER ;
4665 init_reconnect_period = (unsigned int) iv ;
4670 /* check connection state is in cxnWaitingS, cxnConnectingS or cxnIdleS */
4671 bool cxnCheckstate (Connection cxn)
4673 d_printf(5, "%s:%d Being asked to check state\n",
4674 hostPeerName (cxn->myHost), cxn->ident);
4676 /* return false if either connection is doing something */
4677 if (cxn->imap_state > IMAP_IDLE_AUTHED) return false;
4678 if (cxn->lmtp_state > LMTP_AUTHED_IDLE) return false;