1 /*
2 **  Feed articles to an IMAP server via LMTP and IMAP.
3 **
4 **  Written by Tim Martin.
5 **
6 **  Instead of feeding articles via nntp to another host this feeds the
7 **  messages via lmtp to a host and the control messages (cancel's etc..) it
8 **  performs via IMAP.  This means it has 2 active connections at any given
9 **  time and 2 queues.
10 **
11 **  When an article comes in it is immediatly placed in the lmtp queue. When
12 **  an article is picked off the lmtp queue for processing first check if it's
13 **  a control message.  If so, place it in the IMAP queue.  If not, attempt to
14 **  deliver via LMTP.
15 **
16 **  This attempts to follow the exact same api as connection.c.
17 **
18 **  TODO:
19 **
20 **  feed to smtp
21 **  security layers?  <--punt on for now
22 **  authname/password per connection object
23 **  untagged IMAP messages
24 */
25 
26 #include "portable/system.h"
27 
28 #include "portable/socket.h"
29 #include <ctype.h>
30 #include <errno.h>
31 #include <netdb.h>
32 #include <syslog.h>
33 #include <time.h>
34 
35 #include "inn/libinn.h"
36 #include "inn/messages.h"
37 
38 #include "article.h"
39 #include "buffer.h"
40 #include "configfile.h"
41 #include "connection.h"
42 #include "endpoint.h"
43 #include "host.h"
44 #include "innfeed.h"
45 
46 #ifdef HAVE_SASL
47 #    include <sasl/sasl.h>
48 #    include <sasl/saslplug.h>
49 #    include <sasl/saslutil.h>
50 
51 /* For Cyrus SASL versions < 2.1.25 (in hexadecimal notation below). */
52 #    if !defined(SASL_VERSION_FULL) || SASL_VERSION_FULL < 0x020119
53 typedef int (*sasl_callback_ft)(void);
54 #    endif
55 #endif
56 
57 /* There's a useless "break" when SASL support is not enabled, reported
58  * by Clang but GCC warns if it does not see it, so let's just keep it
59  * for the time being and silent this warning. */
60 #if !defined(HAVE_SASL) && (defined(__llvm__) || defined(__clang__))
61 #    pragma GCC diagnostic ignored "-Wunreachable-code-break"
62 #endif
63 
64 #ifndef MAXHOSTNAMELEN
65 #    define MAXHOSTNAMELEN 1024
66 #endif
67 
68 #define IMAP_PORT 143
69 
70 #ifdef SMTPMODE
71 #    define LMTP_PORT 25
72 #else
73 #    define LMTP_PORT 2003
74 #endif
75 
76 #define IMAP_TAGLENGTH 6
77 
78 #define QUEUE_MAX_SIZE 250
79 
80 #define DOSOMETHING_TIMEOUT 60
81 
82 
83 static char hostname[MAXHOSTNAMELEN];
84 static char *mailfrom_name = NULL; /* default to no return path */
85 
86 #ifdef HAVE_SASL
87 static int initialized_sasl =
88     0; /* weather sasl_client_init() has been called */
89 #endif
90 
91 /* states the imap connection may be in */
92 typedef enum
93 {
94 
95     IMAP_DISCONNECTED = 1,
96     IMAP_WAITING,
97 
98     IMAP_CONNECTED_NOTAUTH,
99 
100     IMAP_READING_INTRO,
101 
102     IMAP_WRITING_CAPABILITY,
103     IMAP_READING_CAPABILITY,
104 
105     IMAP_WRITING_STARTAUTH,
106     IMAP_READING_STEPAUTH,
107     IMAP_WRITING_STEPAUTH,
108 
109     IMAP_IDLE_AUTHED,
110     IMAP_WRITING_NOOP,
111     IMAP_READING_NOOP,
112 
113     IMAP_WRITING_CREATE,
114     IMAP_READING_CREATE,
115 
116     IMAP_WRITING_DELETE,
117     IMAP_READING_DELETE,
118 
119     IMAP_WRITING_SELECT,
120     IMAP_READING_SELECT,
121 
122     IMAP_WRITING_SEARCH,
123     IMAP_READING_SEARCH,
124 
125     IMAP_WRITING_STORE,
126     IMAP_READING_STORE,
127 
128     IMAP_WRITING_CLOSE,
129     IMAP_READING_CLOSE,
130 
131     IMAP_WRITING_QUIT,
132     IMAP_READING_QUIT
133 
134 } imap_state_t;
135 
136 typedef enum
137 {
138     LMTP_DISCONNECTED = 1,
139     LMTP_WAITING,
140 
141     LMTP_CONNECTED_NOTAUTH,
142 
143     LMTP_READING_INTRO,
144 
145     LMTP_WRITING_LHLO,
146     LMTP_READING_LHLO,
147 
148     LMTP_WRITING_STARTAUTH,
149     LMTP_READING_STEPAUTH,
150     LMTP_WRITING_STEPAUTH,
151 
152     LMTP_AUTHED_IDLE,
153     LMTP_WRITING_NOOP,
154     LMTP_READING_NOOP,
155 
156     LMTP_READING_RSET,
157     LMTP_READING_MAILFROM,
158     LMTP_READING_RCPTTO,
159     LMTP_READING_DATA,
160     LMTP_READING_CONTENTS,
161 
162     LMTP_WRITING_UPTODATA,
163     LMTP_WRITING_CONTENTS,
164 
165     LMTP_WRITING_QUIT,
166     LMTP_READING_QUIT
167 
168 } lmtp_state_t;
169 
170 typedef struct imap_capabilities_s {
171 
172     int imap4;         /* does server support imap4bis? */
173     int logindisabled; /* does the server allow the login command? */
174 
175     char *saslmechs; /* supported SASL mechanisms */
176 
177 } imap_capabilities_t;
178 
179 typedef struct lmtp_capabilities_s {
180 
181     int Eightbitmime;
182     int EnhancedStatusCodes;
183     int pipelining;
184 
185     char *saslmechs;
186 
187 } lmtp_capabilities_t;
188 
189 typedef enum
190 {
191     STAT_CONT = 0,
192     STAT_NO = 1,
193     STAT_OK = 2,
194     STAT_FAIL = 3
195 } imt_stat;
196 
197 /* Message types */
198 typedef enum
199 {
200     DELIVER,
201     CREATE_FOLDER,
202     CANCEL_MSG,
203     DELETE_FOLDER
204 } control_type_t;
205 
206 typedef struct control_item_s {
207 
208     Article article;
209     char *folder;
210     char *msgid;       /* only for cancel's */
211     unsigned long uid; /* only for cancel's */
212 
213 } control_item_t;
214 
215 typedef struct article_queue_s {
216 
217     control_type_t type;
218 
219     time_t arrived;
220     time_t nextsend; /* time we should next try to send article */
221 
222     int trys;
223 
224     int counts_toward_size;
225 
226     union {
227         Article article;
228         control_item_t *control;
229         void *generic;
230     } data;
231 
232     struct article_queue_s *next;
233 
234 } article_queue_t;
235 
236 typedef struct Q_s {
237 
238     article_queue_t *head;
239 
240     article_queue_t *tail;
241 
242     int size;
243 
244 } Q_t;
245 
246 typedef struct connection_s {
247 
248     /* common stuff */
249     char *ServerName;
250 
251     char *lmtp_respBuffer; /* buffer all responses are read into */
252     Buffer lmtp_rBuffer;   /* buffer all responses are read into */
253 
254     Host myHost; /* the host who owns the connection */
255 
256     time_t timeCon; /* the time the connect happened
257                        (last auth succeeded) */
258 
259     int issue_quit; /* Three states:
260                      *   0 - don't do anything
261                      *   1 - after issue quit enter wait state
262                      *   2 - after issue quit reconnect
263                      *   3 - after issue quit delete connection
264                      *   4 - nuke cxn
265                      */
266 
267     /* Statistics */
268     int lmtp_succeeded;
269     int lmtp_failed;
270 
271     int cancel_succeeded;
272     int cancel_failed;
273 
274     int create_succeeded;
275     int create_failed;
276 
277     int remove_succeeded;
278     int remove_failed;
279 
280 
281     /* LMTP stuff */
282     int lmtp_port;
283     lmtp_state_t lmtp_state;
284 #ifdef HAVE_SASL
285     sasl_conn_t *saslconn_lmtp;
286 #endif /* HAVE_SASL */
287     int sockfd_lmtp;
288 
289     time_t lmtp_timeCon;
290 
291     EndPoint lmtp_endpoint;
292     unsigned int ident; /* an identifier for syslogging. */
293 
294     lmtp_capabilities_t *lmtp_capabilities;
295 
296     int lmtp_disconnects;
297     char *lmtp_tofree_str;
298 
299     article_queue_t *current_article;
300     Buffer *current_bufs;
301     int current_rcpts_issued;
302     int current_rcpts_okayed;
303 
304     /* Timer for the max amount of time to wait for a response from the
305        remote */
306     unsigned int lmtp_readTimeout;
307     TimeoutId lmtp_readBlockedTimerId;
308 
309     /* Timer for the max amount of time to wait for a any amount of data
310        to be written to the remote */
311     unsigned int lmtp_writeTimeout;
312     TimeoutId lmtp_writeBlockedTimerId;
313 
314     /* Timer for the number of seconds to sleep before attempting a
315        reconnect. */
316     unsigned int lmtp_sleepTimeout;
317     TimeoutId lmtp_sleepTimerId;
318 
319     /* Timer for max amount between queueing some articles and trying to send
320      * them */
321     unsigned int dosomethingTimeout;
322     TimeoutId dosomethingTimerId;
323 
324     Q_t lmtp_todeliver_q;
325 
326 
327     /* IMAP stuff */
328     int imap_port;
329 #ifdef HAVE_SASL
330     sasl_conn_t *imap_saslconn;
331 #endif /* HAVE_SASL */
332 
333     char *imap_respBuffer;
334     Buffer imap_rBuffer;
335     EndPoint imap_endpoint;
336 
337     imap_capabilities_t *imap_capabilities;
338 
339     int imap_sockfd;
340 
341     time_t imap_timeCon;
342 
343     imap_state_t imap_state;
344     int imap_disconnects;
345     char *imap_tofree_str;
346 
347     char imap_currentTag[IMAP_TAGLENGTH + 1];
348     int imap_tag_num;
349 
350     /* Timer for the max amount of time to wait for a response from the
351        remote */
352     unsigned int imap_readTimeout;
353     TimeoutId imap_readBlockedTimerId;
354 
355     /* Timer for the max amount of time to wait for a any amount of data
356        to be written to the remote */
357     unsigned int imap_writeTimeout;
358     TimeoutId imap_writeBlockedTimerId;
359 
360     /* Timer for the number of seconds to sleep before attempting a
361        reconnect. */
362     unsigned int imap_sleepTimeout;
363     TimeoutId imap_sleepTimerId;
364 
365     Q_t imap_controlMsg_q;
366 
367     article_queue_t *current_control;
368 
369     struct connection_s *next;
370 
371 } connection_t;
372 
373 static Connection gCxnList = NULL;
374 static unsigned int gCxnCount = 0;
375 unsigned int max_reconnect_period = MAX_RECON_PER;
376 unsigned int init_reconnect_period = INIT_RECON_PER;
377 
378 typedef enum
379 {
380     RET_OK = 0,
381     RET_FAIL = 1,
382     RET_QUEUE_EMPTY,
383     RET_EXCEEDS_SIZE,
384     RET_NO_FULLLINE,
385     RET_NO,
386     RET_ARTICLE_BAD
387 } conn_ret;
388 
389 
390 /********** Private Function Declarations *************/
391 
392 static void lmtp_readCB(EndPoint e, IoStatus i, Buffer *b, void *d);
393 static void imap_readCB(EndPoint e, IoStatus i, Buffer *b, void *d);
394 static void imap_writeCB(EndPoint e, IoStatus i, Buffer *b, void *d);
395 static void lmtp_writeCB(EndPoint e, IoStatus i, Buffer *b, void *d);
396 
397 static conn_ret lmtp_Connect(connection_t *cxn);
398 static conn_ret imap_Connect(connection_t *cxn);
399 
400 static void prepareReopenCbk(Connection cxn, int type);
401 
402 static void lmtp_readTimeoutCbk(TimeoutId id, void *data);
403 static void imap_readTimeoutCbk(TimeoutId id, void *data);
404 
405 static void dosomethingTimeoutCbk(TimeoutId id, void *data);
406 
407 static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen);
408 static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen);
409 
410 static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback,
411                             EndPoint endp, Buffer *array);
412 static void lmtp_sendmessage(connection_t *cxn, Article justadded);
413 static void imap_ProcessQueue(connection_t *cxn);
414 
415 static conn_ret FindHeader(Buffer *bufs, const char *header, char **start,
416                            char **end);
417 static conn_ret PopFromQueue(Q_t *q, article_queue_t **item);
418 
419 enum failure_type
420 {
421     MSG_SUCCESS = 0,
422     MSG_FAIL_DELIVER = 1,
423     MSG_GIVE_BACK = 2,
424     MSG_MISSING = 3
425 };
426 
427 static void QueueForgetAbout(connection_t *cxn, article_queue_t *item,
428                              enum failure_type failed);
429 
430 static void delConnection(Connection cxn);
431 static void DeleteIfDisconnected(Connection cxn);
432 static void DeferAllArticles(connection_t *cxn, Q_t *q);
433 
434 static void lmtp_Disconnect(connection_t *cxn);
435 static void imap_Disconnect(connection_t *cxn);
436 static conn_ret imap_listenintro(connection_t *cxn);
437 
438 static void imap_writeTimeoutCbk(TimeoutId id, void *data);
439 static void lmtp_writeTimeoutCbk(TimeoutId id, void *data);
440 
441 /******************** PRIVATE FUNCTIONS ***************************/
442 
443 static const char *
imap_stateToString(int state)444 imap_stateToString(int state)
445 {
446     switch (state) {
447     case IMAP_DISCONNECTED:
448         return "disconnected";
449     case IMAP_WAITING:
450         return "waiting";
451     case IMAP_CONNECTED_NOTAUTH:
452         return "connected (unauthenticated)";
453     case IMAP_READING_INTRO:
454         return "reading intro";
455     case IMAP_WRITING_CAPABILITY:
456         return "writing CAPABILITY";
457     case IMAP_READING_CAPABILITY:
458         return "reading CAPABILITY";
459     case IMAP_WRITING_STARTAUTH:
460         return "writing AUTHENTICATE";
461     case IMAP_READING_STEPAUTH:
462         return "reading stepauth";
463     case IMAP_WRITING_STEPAUTH:
464         return "writing stepauth";
465     case IMAP_IDLE_AUTHED:
466         return "idle (authenticated)";
467     case IMAP_WRITING_NOOP:
468         return "writing NOOP";
469     case IMAP_READING_NOOP:
470         return "reading NOOP response";
471     case IMAP_WRITING_CREATE:
472         return "writing CREATE";
473     case IMAP_READING_CREATE:
474         return "reading CREATE response";
475     case IMAP_WRITING_DELETE:
476         return "writing DELETE command";
477     case IMAP_READING_DELETE:
478         return "reading DELETE response";
479     case IMAP_WRITING_SELECT:
480         return "writing SELECT";
481     case IMAP_READING_SELECT:
482         return "reading SELECT response";
483     case IMAP_WRITING_SEARCH:
484         return "writing SEARCH";
485     case IMAP_READING_SEARCH:
486         return "reading SEARCH response";
487     case IMAP_WRITING_STORE:
488         return "writing STORE";
489     case IMAP_READING_STORE:
490         return "reading STORE response";
491     case IMAP_WRITING_CLOSE:
492         return "writing CLOSE";
493     case IMAP_READING_CLOSE:
494         return "reading CLOSE response";
495     case IMAP_WRITING_QUIT:
496         return "writing LOGOUT";
497     case IMAP_READING_QUIT:
498         return "reading LOGOUT response";
499     default:
500         return "Unknown state";
501     }
502 }
503 
504 static const char *
lmtp_stateToString(int state)505 lmtp_stateToString(int state)
506 {
507     switch (state) {
508     case LMTP_DISCONNECTED:
509         return "disconnected";
510     case LMTP_WAITING:
511         return "waiting";
512     case LMTP_CONNECTED_NOTAUTH:
513         return "connected (unauthenticated)";
514     case LMTP_READING_INTRO:
515         return "reading intro";
516     case LMTP_WRITING_LHLO:
517         return "writing LHLO";
518     case LMTP_READING_LHLO:
519         return "reading LHLO response";
520     case LMTP_WRITING_STARTAUTH:
521         return "writing AUTH";
522     case LMTP_READING_STEPAUTH:
523         return "reading stepauth";
524     case LMTP_WRITING_STEPAUTH:
525         return "writing stepauth";
526     case LMTP_AUTHED_IDLE:
527         return "idle (authenticated)";
528     case LMTP_WRITING_NOOP:
529         return "writing NOOP";
530     case LMTP_READING_NOOP:
531         return "reading NOOP response";
532     case LMTP_READING_RSET:
533         return "reading RSET response";
534     case LMTP_READING_MAILFROM:
535         return "reading MAIL FROM response";
536     case LMTP_READING_RCPTTO:
537         return "reading RCPT TO response";
538     case LMTP_READING_DATA:
539         return "reading DATA response";
540     case LMTP_READING_CONTENTS:
541         return "reading contents response";
542     case LMTP_WRITING_UPTODATA:
543         return "writing RSET, MAIL FROM, RCPT TO, DATA commands";
544     case LMTP_WRITING_CONTENTS:
545         return "writing contents of message";
546     case LMTP_WRITING_QUIT:
547         return "writing QUIT";
548     case LMTP_READING_QUIT:
549         return "reading QUIT";
550     default:
551         return "unknown state";
552     }
553 }
554 
555 /******************************* Queue functions
556  * ***********************************/
557 
558 /*
559  * Add a message to a generic queue
560  *
561  *  q       - the queue adding to
562  *  item    - the data to add to the queue
563  *  type    - the type of item it is (i.e. cancel,lmtp,etc..)
564  *  addsmsg - weather this should be counted toward the queue size
565  *            this is for control msg's that create multiple queue items.
566  *            For example a cancel message canceling a message in multiple
567  *            newsgroups will create >1 queue item but we only want it to count
568  *            once towards the queue
569  *  must    - wheather we must take it even though it may put us over our max
570  * size
571  */
572 
573 static conn_ret
AddToQueue(Q_t * q,void * item,control_type_t type,int addsmsg,bool must)574 AddToQueue(Q_t *q, void *item, control_type_t type, int addsmsg, bool must)
575 {
576     article_queue_t *newentry;
577 
578     if (must == false) {
579         if (q->size >= QUEUE_MAX_SIZE) {
580             return RET_EXCEEDS_SIZE;
581         }
582     } else {
583         if (q->size >= QUEUE_MAX_SIZE * 10) {
584             d_printf(0, "Queue has grown way too much. Dropping article\n");
585             return RET_FAIL;
586         }
587     }
588 
589     /* add to the end of our queue */
590     newentry = xmalloc(sizeof(article_queue_t));
591 
592     newentry->type = type;
593 
594     /* send as soon as possible */
595     newentry->nextsend = newentry->arrived = time(NULL);
596 
597     newentry->trys = 0;
598 
599     newentry->data.generic = item;
600     newentry->next = NULL;
601     newentry->counts_toward_size = addsmsg;
602 
603     /* add to end of queue */
604     if (q->tail == NULL) {
605         q->head = newentry;
606         q->tail = newentry;
607     } else {
608 
609         q->tail->next = newentry;
610         q->tail = newentry;
611     }
612 
613     q->size += addsmsg;
614 
615     return RET_OK;
616 }
617 
618 /*
619  * Pop an item from the queue
620  *
621  * q    - the queue to pop from
622  * item - where the item shall be placed upon sucess
623  *
624  */
625 
626 static conn_ret
PopFromQueue(Q_t * q,article_queue_t ** item)627 PopFromQueue(Q_t *q, article_queue_t **item)
628 {
629     /* if queue empty return error */
630     if (q->head == NULL) {
631         return RET_QUEUE_EMPTY;
632     }
633 
634     /* set what we return */
635     *item = q->head;
636 
637     q->head = q->head->next;
638     if (q->head == NULL)
639         q->tail = NULL;
640 
641     q->size -= (*item)->counts_toward_size;
642 
643     return RET_OK;
644 }
645 
646 /*
647  * ReQueue an item. Will either put it back in the queue for another try
648  * or forget about it
649  *
650  *  cxn     - our connection object (needed so forget about things)
651  *  q       - the queue to requeue to
652  *  entry   - the item to put back
653  */
654 
655 static void
ReQueue(connection_t * cxn,Q_t * q,article_queue_t * entry)656 ReQueue(connection_t *cxn, Q_t *q, article_queue_t *entry)
657 {
658     /* look at the time it's been here */
659     entry->nextsend =
660         time(NULL) + (entry->trys * 30); /* xxx better formula? */
661 
662     entry->trys++;
663 
664     /* give up after 5 tries xxx configurable??? */
665     if (entry->trys >= 5) {
666         QueueForgetAbout(cxn, entry, MSG_FAIL_DELIVER);
667         return;
668     }
669 
670 
671     /* ok let's add back to the end of the queue */
672     entry->next = NULL;
673 
674     /* add to end of queue */
675     if (q->tail == NULL) {
676         q->head = entry;
677         q->tail = entry;
678     } else {
679         q->tail->next = entry;
680         q->tail = entry;
681     }
682 
683     q->size += entry->counts_toward_size;
684 }
685 
686 
687 /*
688  * Forget about an item. Tells host object if we succeeded/failed/etc with the
689  * message
690  *
691  * cxn    - connection object
692  * item   - item
693  * failed - type of failure (see below)
694  *
695  * failed:
696  *   0 - succeeded delivering message
697  *   1 - failed delivering message
698  *   2 - Try to give back to host
699  *   3 - Article missing (i.e. can't find on disk)
700  */
701 static void
QueueForgetAbout(connection_t * cxn,article_queue_t * item,enum failure_type failed)702 QueueForgetAbout(connection_t *cxn, article_queue_t *item,
703                  enum failure_type failed)
704 {
705     Article art = NULL;
706 
707     switch (item->type) {
708     case DELIVER:
709         if (failed > 0)
710             cxn->lmtp_failed++;
711         art = item->data.article;
712         break;
713 
714     case CANCEL_MSG:
715         if (failed > 0)
716             cxn->cancel_failed++;
717         free(item->data.control->msgid);
718         free(item->data.control->folder);
719 
720         if (item->counts_toward_size == 1)
721             art = item->data.control->article;
722 
723         free(item->data.control);
724         break;
725 
726     case CREATE_FOLDER:
727         if (failed > 0)
728             cxn->create_failed++;
729         free(item->data.control->folder);
730 
731         art = item->data.control->article;
732 
733         free(item->data.control);
734         break;
735 
736     case DELETE_FOLDER:
737         if (failed > 0)
738             cxn->remove_failed++;
739         free(item->data.control->folder);
740 
741         art = item->data.control->article;
742 
743         free(item->data.control);
744         break;
745 
746     default:
747         d_printf(0,
748                  "%s:%d QueueForgetAbout(): "
749                  "Unknown type to forget about\n",
750                  hostPeerName(cxn->myHost), cxn->ident);
751         break;
752     }
753 
754     if (art != NULL) {
755         switch (failed) {
756         case MSG_SUCCESS:
757             hostArticleAccepted(cxn->myHost, cxn, art);
758             break;
759 
760         case MSG_FAIL_DELIVER:
761             hostArticleRejected(cxn->myHost, cxn, art);
762             break;
763 
764         case MSG_GIVE_BACK:
765             hostTakeBackArticle(cxn->myHost, cxn, art);
766             break;
767 
768         case MSG_MISSING:
769             hostArticleIsMissing(cxn->myHost, cxn, art);
770             break;
771         default:
772             d_printf(0, "%s:%d QueueForgetAbout(): failure type unknown\n",
773                      hostPeerName(cxn->myHost), cxn->ident);
774             break;
775         }
776     }
777 
778     free(item);
779 }
780 
781 /*
782  * How much space is available in the queue
783  */
784 
785 static int
QueueSpace(Q_t * q)786 QueueSpace(Q_t *q)
787 {
788     int ret = QUEUE_MAX_SIZE - q->size;
789     if (ret < 0)
790         ret = 0;
791     return ret;
792 }
793 
794 /*
795  * How many items are in the queue
796  */
797 
798 static int
QueueItems(Q_t * q)799 QueueItems(Q_t *q)
800 {
801     return q->size;
802 }
803 
804 
805 /***************************** END Queue functions
806  * ***********************************/
807 
808 /***************************** Generic Parse Functions
809  * *******************************/
810 
811 /* returns the end of the header */
812 
813 static char *
GetUntil(char * str)814 GetUntil(char *str)
815 {
816     while (((*str) != '\0') && ((*str) != '\r') && ((*str) != '\n')) {
817         str++;
818     }
819 
820     return str;
821 }
822 
823 static char *
GotoNextLine(char * str)824 GotoNextLine(char *str)
825 {
826     while (((*str) != '\0') && ((*str) != '\r') && ((*str) != '\n')) {
827         str++;
828     }
829 
830     if (*str == '\r')
831         str++;
832     if (*str == '\n')
833         str++;
834 
835     return str;
836 }
837 
838 /*
839  * Finds the given header field in the message
840  *  Returns NULL if not found
841  */
842 static conn_ret
FindHeader(Buffer * bufs,const char * header,char ** start,char ** end)843 FindHeader(Buffer *bufs, const char *header, char **start, char **end)
844 {
845     Buffer b;
846     int size;
847     char *str_base;
848     char *str;
849     int headerlen = strlen(header);
850 
851     if (bufs == NULL) {
852         if (start)
853             *start = NULL;
854         return RET_ARTICLE_BAD;
855     }
856 
857     b = bufs[0];
858     size = bufferSize(b);
859     str_base = bufferBase(b);
860     str = str_base;
861 
862     while ((str - str_base) < size - headerlen) {
863         if (*str == header[0]) {
864             if ((strncasecmp(header, str, headerlen) == 0)
865                 && (*(str + headerlen) == ':')) {
866 
867                 if (start) {
868                     *start = str + headerlen + 1;
869 
870                     /* get rid of leading whitespace */
871                     while (isspace((unsigned char) **start))
872                         (*start)++;
873                 }
874 
875                 if (end)
876                     *end = GetUntil(str + headerlen + 1);
877 
878                 return RET_OK;
879             }
880         } else if (*str == '\n') {
881             /* end of headers */
882             return RET_NO;
883         }
884         str = GotoNextLine(str);
885     }
886 
887     return RET_NO;
888 }
889 
890 static conn_ret
GetLine(char * buf,char * ret,int retmaxsize)891 GetLine(char *buf, char *ret, int retmaxsize)
892 {
893     char *str_base;
894     char *str;
895 
896     int size = strlen(buf);
897     str_base = buf;
898     str = str_base;
899 
900     while ((*str) != '\0') {
901         if ((*str) == '\n') {
902             if (str - str_base > retmaxsize) {
903                 d_printf(0, "Max size exceeded! %s\n", str_base);
904                 return RET_FAIL;
905             }
906 
907             /* fill in the return string */
908             memcpy(ret, str_base, str - str_base);
909             ret[str - str_base - 1] = '\0';
910 
911             memcpy(str_base, str_base + (str - str_base) + 1,
912                    size - (str - str_base));
913             str_base[size - (str - str_base)] = '\0';
914 
915             return RET_OK;
916         }
917 
918         str++;
919     }
920 
921     /* couldn't find a full line */
922     return RET_NO_FULLLINE;
923 }
924 
925 
926 /************************** END Generic Parse Functions
927  * *******************************/
928 
929 /************************ Writing to Network functions *****************/
930 
931 static conn_ret
WriteToWire(connection_t * cxn,EndpRWCB callback,EndPoint endp,Buffer * array)932 WriteToWire(connection_t *cxn, EndpRWCB callback, EndPoint endp, Buffer *array)
933 {
934 
935     if (array == NULL)
936         return RET_FAIL;
937 
938     prepareWrite(endp, array, NULL, callback, cxn);
939 
940     return RET_OK;
941 }
942 
943 static conn_ret
WriteToWire_str(connection_t * cxn,EndpRWCB callback,EndPoint endp,char * str,int slen)944 WriteToWire_str(connection_t *cxn, EndpRWCB callback, EndPoint endp, char *str,
945                 int slen)
946 {
947     conn_ret result;
948     Buffer buff;
949     Buffer *writeArr;
950 
951     if (slen == -1)
952         slen = strlen(str);
953 
954     buff = newBufferByCharP(str, slen + 1, slen);
955     ASSERT(buff != NULL);
956 
957     writeArr = makeBufferArray(buff, NULL);
958 
959     result = WriteToWire(cxn, callback, endp, writeArr);
960 
961     return result;
962 }
963 
964 static conn_ret
WriteToWire_imapstr(connection_t * cxn,char * str,int slen)965 WriteToWire_imapstr(connection_t *cxn, char *str, int slen)
966 {
967     /* prepare the timeouts */
968     clearTimer(cxn->imap_readBlockedTimerId);
969 
970     /* set up the write timer. */
971     clearTimer(cxn->imap_writeBlockedTimerId);
972 
973     if (cxn->imap_writeTimeout > 0)
974         cxn->imap_writeBlockedTimerId =
975             prepareSleep(imap_writeTimeoutCbk, cxn->imap_writeTimeout, cxn);
976     cxn->imap_tofree_str = str;
977     return WriteToWire_str(cxn, imap_writeCB, cxn->imap_endpoint, str, slen);
978 }
979 
980 static conn_ret
WriteToWire_lmtpstr(connection_t * cxn,char * str,int slen)981 WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen)
982 {
983     /* prepare the timeouts */
984     clearTimer(cxn->lmtp_readBlockedTimerId);
985 
986     /* set up the write timer. */
987     clearTimer(cxn->lmtp_writeBlockedTimerId);
988 
989     if (cxn->lmtp_writeTimeout > 0)
990         cxn->lmtp_writeBlockedTimerId =
991             prepareSleep(lmtp_writeTimeoutCbk, cxn->lmtp_writeTimeout, cxn);
992 
993     cxn->lmtp_tofree_str = str;
994     return WriteToWire_str(cxn, lmtp_writeCB, cxn->lmtp_endpoint, str, slen);
995 }
996 
997 static conn_ret
WriteArticle(connection_t * cxn,Buffer * array)998 WriteArticle(connection_t *cxn, Buffer *array)
999 {
1000     conn_ret result;
1001 
1002     /* Just call WriteToWire since it's easy. */
1003     result = WriteToWire(cxn, lmtp_writeCB, cxn->lmtp_endpoint, array);
1004 
1005     if (result != RET_OK) {
1006         return result;
1007     }
1008 
1009     cxn->lmtp_state = LMTP_WRITING_CONTENTS;
1010 
1011     return RET_OK;
1012 }
1013 
1014 /************************ END Writing to Network functions *****************/
1015 
1016 
1017 /*
1018  * Adds a cancel item to the control queue
1019  * Cancel item to delete message with <msgid> in <folder>
1020  *
1021  * cxn       - connection object
1022  * folder    - pointer to start of folder string (this is a pointer into the
1023  * actual message buffer) folderlen - length of folder string msgid     -
1024  * pointer to start of msgid string (this is a pointer into the actual message
1025  * buffer) msgidlen  - length of msgid string art       - the article for this
1026  * control message (NULL if this cancel object lacks one) must      - if must
1027  * be accepted into queue
1028  */
1029 
1030 static conn_ret
addCancelItem(connection_t * cxn,char * folder,int folderlen,char * msgid,int msgidlen,Article art,int must)1031 addCancelItem(connection_t *cxn, char *folder, int folderlen, char *msgid,
1032               int msgidlen, Article art, int must)
1033 {
1034     control_item_t *item;
1035     conn_ret result;
1036     int i;
1037 
1038     ASSERT(folder);
1039     ASSERT(msgid);
1040     ASSERT(cxn);
1041 
1042     /* sanity check folder, msgid */
1043     for (i = 0; i < folderlen; i++)
1044         ASSERT(!isspace((unsigned char) folder[i]));
1045     for (i = 0; i < msgidlen; i++)
1046         ASSERT(!isspace((unsigned char) msgid[i]));
1047 
1048     /* create the object */
1049     item = xcalloc(1, sizeof(control_item_t));
1050 
1051     item->folder = xcalloc(folderlen + 1, 1);
1052     memcpy(item->folder, folder, folderlen);
1053     item->folder[folderlen] = '\0';
1054 
1055     item->msgid = xcalloc(msgidlen + 1, 1);
1056     memcpy(item->msgid, msgid, msgidlen);
1057     item->msgid[msgidlen] = '\0';
1058 
1059     item->article = art;
1060 
1061     /* try to add to the queue (counts if art isn't null) */
1062     result = AddToQueue(&(cxn->imap_controlMsg_q), item, CANCEL_MSG,
1063                         (art != NULL), must);
1064     if (result != RET_OK) {
1065         d_printf(1,
1066                  "%s:%d addCancelItem(): "
1067                  "I thought we had in space in [imap] queue "
1068                  "but apparently not\n",
1069                  hostPeerName(cxn->myHost), cxn->ident);
1070 
1071         /* cleanup */
1072         free(item->folder);
1073         free(item->msgid);
1074         free(item);
1075 
1076         return result;
1077     }
1078 
1079     return RET_OK;
1080 }
1081 
1082 static conn_ret
AddControlMsg(connection_t * cxn,Article art,Buffer * bufs,char * control_header,char * control_header_end,bool must)1083 AddControlMsg(connection_t *cxn, Article art, Buffer *bufs,
1084               char *control_header, char *control_header_end, bool must)
1085 {
1086     char *rcpt_list = NULL, *rcpt_list_end;
1087     control_item_t *item;
1088     conn_ret res = RET_OK;
1089     int t;
1090 
1091     /* make sure contents ok; this also should load it into memory */
1092     if (!artContentsOk(art)) {
1093         d_printf(0,
1094                  "%s:%d AddControlMsg(): "
1095                  "artContentsOk() said article was bad\n",
1096                  hostPeerName(cxn->myHost), cxn->ident);
1097         hostArticleIsMissing(cxn->myHost, cxn, art);
1098         return RET_FAIL;
1099     }
1100 
1101     /* now let's look at the control to see what it is */
1102     if (!strncasecmp(control_header, "newgroup", 8)) {
1103         control_header += 8;
1104         t = CREATE_FOLDER;
1105     } else if (!strncasecmp(control_header, "rmgroup", 7)) {
1106         /* jump past "rmgroup" */
1107         control_header += 7;
1108         t = DELETE_FOLDER;
1109     } else if (!strncasecmp(control_header, "cancel", 6)) {
1110         t = CANCEL_MSG;
1111         control_header += 6;
1112     } else {
1113         /* unrecognized type */
1114         char tmp[100];
1115         char *endstr;
1116         size_t clen;
1117 
1118         endstr = strchr(control_header, '\n');
1119         clen = endstr - control_header;
1120 
1121         if (clen > sizeof(tmp) - 1)
1122             clen = sizeof(tmp) - 1;
1123 
1124         memcpy(tmp, control_header, clen);
1125         tmp[clen] = '\0';
1126 
1127         d_printf(0, "%s:%d Don't understand Control header field [%s]\n",
1128                  hostPeerName(cxn->myHost), cxn->ident, tmp);
1129         return RET_FAIL;
1130     }
1131 
1132     switch (t) {
1133     case CREATE_FOLDER:
1134     case DELETE_FOLDER: {
1135         int folderlen;
1136 
1137         /* go past all white space */
1138         while ((*control_header == ' ')
1139                && (control_header != control_header_end)) {
1140             control_header++;
1141         }
1142 
1143         /* trim trailing whitespace */
1144         while (control_header_end[-1] == ' ') {
1145             control_header_end--;
1146         }
1147 
1148         if (control_header >= control_header_end) {
1149             d_printf(0,
1150                      "%s:%d addControlMsg(): "
1151                      "newgroup/rmgroup Control header field has no group "
1152                      "specified\n",
1153                      hostPeerName(cxn->myHost), cxn->ident);
1154             return RET_FAIL;
1155         }
1156 
1157         folderlen = control_header_end - control_header;
1158 
1159         item = xcalloc(1, sizeof(control_item_t));
1160 
1161         item->folder = xcalloc(folderlen + 1, 1);
1162         memcpy(item->folder, control_header, folderlen);
1163         item->folder[folderlen] = '\0';
1164 
1165         item->article = art;
1166 
1167         if (AddToQueue(&(cxn->imap_controlMsg_q), item, t, 1, must)
1168             != RET_OK) {
1169             d_printf(1,
1170                      "%s:%d addCancelItem(): "
1171                      "I thought we had in space in [imap] queue"
1172                      " but apparently not\n",
1173                      hostPeerName(cxn->myHost), cxn->ident);
1174             free(item->folder);
1175             free(item);
1176             return RET_FAIL;
1177         }
1178 
1179         break;
1180     }
1181 
1182     case CANCEL_MSG: {
1183         char *str, *laststart;
1184 
1185         while (((*control_header) == ' ')
1186                && (control_header != control_header_end)) {
1187             control_header++;
1188         }
1189 
1190         if (control_header == control_header_end) {
1191             d_printf(0,
1192                      "%s:%d Control header field contains cancel "
1193                      "with no msgid specified\n",
1194                      hostPeerName(cxn->myHost), cxn->ident);
1195             return RET_FAIL;
1196         }
1197 
1198         if (FindHeader(bufs, "Newsgroups", &rcpt_list, &rcpt_list_end)
1199             != RET_OK) {
1200             d_printf(0,
1201                      "%s:%d Cancel message contains no Newsgroups header "
1202                      "field\n",
1203                      hostPeerName(cxn->myHost), cxn->ident);
1204             return RET_FAIL;
1205         }
1206 
1207         str = rcpt_list;
1208         laststart = rcpt_list;
1209 
1210         while (str != rcpt_list_end) {
1211             if (*str == ',') {
1212                 /* eliminate leading whitespace */
1213                 while (((*laststart) == ' ') || ((*laststart) == '\t')) {
1214                     laststart++;
1215                 }
1216 
1217                 res = addCancelItem(
1218                     cxn, laststart, str - laststart, control_header,
1219                     control_header_end - control_header, NULL, must);
1220                 if (res != RET_OK)
1221                     return res;
1222 
1223                 laststart = str + 1;
1224             }
1225 
1226             str++;
1227         }
1228 
1229         if (laststart < str) {
1230 
1231             res =
1232                 addCancelItem(cxn, laststart, str - laststart, control_header,
1233                               control_header_end - control_header, art, must);
1234             if (res != RET_OK)
1235                 return res;
1236         }
1237         break;
1238     }
1239     default:
1240         /* huh?!? */
1241         d_printf(0, "%s:%d internal error in addControlMsg()\n",
1242                  hostPeerName(cxn->myHost), cxn->ident);
1243     }
1244     return RET_FAIL;
1245 }
1246 
1247 /*
1248  * Show msg handling statistics
1249  */
1250 
1251 static void
show_stats(connection_t * cxn)1252 show_stats(connection_t *cxn)
1253 {
1254     d_printf(0, "%s:%d\n", hostPeerName(cxn->myHost), cxn->ident);
1255     d_printf(0, "  imap queue = %d lmtp queue = %d\n",
1256              QueueItems(&(cxn->imap_controlMsg_q)),
1257              QueueItems(&(cxn->lmtp_todeliver_q)));
1258     d_printf(0, "  imap state = %s\n", imap_stateToString(cxn->imap_state));
1259     d_printf(0, "  lmtp state = %s\n", lmtp_stateToString(cxn->lmtp_state));
1260     d_printf(0, "  delivered:  yes: %d no: %d\n", cxn->lmtp_succeeded,
1261              cxn->lmtp_failed);
1262     d_printf(0, "  cancel:     yes: %d no: %d\n", cxn->cancel_succeeded,
1263              cxn->cancel_failed);
1264     d_printf(0, "  create:     yes: %d no: %d\n", cxn->create_succeeded,
1265              cxn->create_failed);
1266     d_printf(0, "  remove:     yes: %d no: %d\n", cxn->remove_succeeded,
1267              cxn->remove_failed);
1268 }
1269 
1270 /**************************** SASL helper functions
1271  * ******************************/
1272 
1273 #ifdef HAVE_SASL
1274 /* callback to get userid or authid */
1275 static int
getsimple(void * context UNUSED,int id,const char ** result,unsigned * len)1276 getsimple(void *context UNUSED, int id, const char **result, unsigned *len)
1277 {
1278     char *authid;
1279 
1280     if (!result)
1281         return SASL_BADPARAM;
1282 
1283 
1284     switch (id) {
1285     case SASL_CB_GETREALM:
1286         *result = deliver_realm;
1287         if (len)
1288             *len = deliver_realm ? strlen(deliver_realm) : 0;
1289         break;
1290     case SASL_CB_USER:
1291         *result = deliver_username;
1292         if (len)
1293             *len = deliver_username ? strlen(deliver_username) : 0;
1294         break;
1295     case SASL_CB_AUTHNAME:
1296         authid = deliver_authname;
1297         *result = authid;
1298         if (len)
1299             *len = authid ? strlen(authid) : 0;
1300         break;
1301     case SASL_CB_LANGUAGE:
1302         *result = NULL;
1303         if (len)
1304             *len = 0;
1305         break;
1306     default:
1307         return SASL_BADPARAM;
1308     }
1309     return SASL_OK;
1310 }
1311 
1312 /* callback to get password */
1313 static int
getsecret(sasl_conn_t * conn,void * context UNUSED,int id,sasl_secret_t ** psecret)1314 getsecret(sasl_conn_t *conn, void *context UNUSED, int id,
1315           sasl_secret_t **psecret)
1316 {
1317     size_t passlen;
1318 
1319     if (!conn || !psecret || id != SASL_CB_PASS)
1320         return SASL_BADPARAM;
1321 
1322     if (deliver_password == NULL) {
1323         d_printf(0, "SASL requested a password but I don't have one\n");
1324         return SASL_FAIL;
1325     }
1326 
1327     passlen = strlen(deliver_password);
1328     *psecret = xmalloc(sizeof(sasl_secret_t) + passlen + 1);
1329     if (!*psecret)
1330         return SASL_FAIL;
1331 
1332     strlcpy((char *) (*psecret)->data, deliver_password, passlen + 1);
1333     (*psecret)->len = passlen;
1334 
1335     return SASL_OK;
1336 }
1337 
1338 #    if __GNUC__ > 7
1339 #        pragma GCC diagnostic ignored "-Wcast-function-type"
1340 #    endif
1341 
1342 /* callbacks we support */
1343 static sasl_callback_t saslcallbacks[] = {
1344     {SASL_CB_GETREALM, (sasl_callback_ft) &getsimple, NULL},
1345     {SASL_CB_USER, (sasl_callback_ft) &getsimple, NULL},
1346     {SASL_CB_AUTHNAME, (sasl_callback_ft) &getsimple, NULL},
1347     {SASL_CB_PASS, (sasl_callback_ft) &getsecret, NULL},
1348     {SASL_CB_LIST_END, NULL, NULL}};
1349 
1350 #    if __GNUC__ > 7
1351 #        pragma GCC diagnostic warning "-Wcast-function-type"
1352 #    endif
1353 
1354 static sasl_security_properties_t *
make_secprops(int min,int max)1355 make_secprops(int min, int max)
1356 {
1357     sasl_security_properties_t *ret =
1358         xmalloc(sizeof(sasl_security_properties_t));
1359 
1360     ret->maxbufsize = 1024;
1361     ret->min_ssf = min;
1362     ret->max_ssf = max;
1363 
1364     ret->security_flags = 0;
1365     ret->property_names = NULL;
1366     ret->property_values = NULL;
1367 
1368     return ret;
1369 }
1370 
1371 #    ifndef NI_WITHSCOPEID
1372 #        define NI_WITHSCOPEID 0
1373 #    endif
1374 #    ifndef NI_MAXHOST
1375 #        define NI_MAXHOST 1025
1376 #    endif
1377 #    ifndef NI_MAXSERV
1378 #        define NI_MAXSERV 32
1379 #    endif
1380 
1381 static int
iptostring(const struct sockaddr * addr,socklen_t addrlen,char * out,unsigned outlen)1382 iptostring(const struct sockaddr *addr, socklen_t addrlen, char *out,
1383            unsigned outlen)
1384 {
1385     char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
1386 
1387     if (!addr || !out)
1388         return SASL_BADPARAM;
1389 
1390     getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
1391                 NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);
1392 
1393     if (outlen < strlen(hbuf) + strlen(pbuf) + 2)
1394         return SASL_BUFOVER;
1395 
1396     snprintf(out, outlen, "%s;%s", hbuf, pbuf);
1397 
1398     return SASL_OK;
1399 }
1400 
1401 static conn_ret
SetSASLProperties(sasl_conn_t * conn,int sock,int minssf,int maxssf)1402 SetSASLProperties(sasl_conn_t *conn, int sock, int minssf, int maxssf)
1403 {
1404     sasl_security_properties_t *secprops = NULL;
1405     socklen_t addrsize = sizeof(struct sockaddr_in);
1406     char localip[NI_MAXHOST + NI_MAXSERV + 1];
1407     char remoteip[NI_MAXHOST + NI_MAXSERV + 1];
1408     struct sockaddr_in saddr_l;
1409     struct sockaddr_in saddr_r;
1410 
1411     /* create a security structure and give it to sasl */
1412     secprops = make_secprops(minssf, maxssf);
1413     if (secprops != NULL) {
1414         sasl_setprop(conn, SASL_SEC_PROPS, secprops);
1415         free(secprops);
1416     }
1417 
1418     if (getpeername(sock, (struct sockaddr *) &saddr_r, &addrsize) != 0)
1419         return RET_FAIL;
1420 
1421     if (iptostring((struct sockaddr *) &saddr_r, sizeof(struct sockaddr_in),
1422                    remoteip, sizeof(remoteip)))
1423         return RET_FAIL;
1424 
1425     if (sasl_setprop(conn, SASL_IPREMOTEPORT, remoteip) != SASL_OK)
1426         return RET_FAIL;
1427 
1428     addrsize = sizeof(struct sockaddr_in);
1429     if (getsockname(sock, (struct sockaddr *) &saddr_l, &addrsize) != 0)
1430         return RET_FAIL;
1431 
1432     if (iptostring((struct sockaddr *) &saddr_l, sizeof(struct sockaddr_in),
1433                    localip, sizeof(localip)))
1434         return RET_FAIL;
1435 
1436     if (sasl_setprop(conn, SASL_IPLOCALPORT, localip) != SASL_OK)
1437         return RET_FAIL;
1438 
1439     return RET_OK;
1440 }
1441 #endif /* HAVE_SASL */
1442 
1443 /************************** END SASL helper functions
1444  * ******************************/
1445 
1446 /************************* Startup functions
1447  * **********************************/
1448 
1449 static conn_ret
Initialize(connection_t * cxn,int respTimeout)1450 Initialize(connection_t *cxn, int respTimeout)
1451 {
1452 #ifdef HAVE_SASL
1453     conn_ret saslresult;
1454 #endif /* HAVE_SASL */
1455 
1456     d_printf(1, "%s:%d initializing....\n", hostPeerName(cxn->myHost),
1457              cxn->ident);
1458 
1459 #ifdef HAVE_SASL
1460     /* only call sasl_client_init() once */
1461     if (initialized_sasl == 0) {
1462         /* Initialize SASL */
1463         saslresult = sasl_client_init(saslcallbacks);
1464 
1465         if (saslresult != SASL_OK) {
1466             d_printf(0,
1467                      "%s:%d Error initializing SASL (sasl_client_init) (%s)\n",
1468                      hostPeerName(cxn->myHost), cxn->ident,
1469                      sasl_errstring(saslresult, NULL, NULL));
1470             return RET_FAIL;
1471         } else {
1472             initialized_sasl = 1;
1473         }
1474     }
1475 #endif /* HAVE_SASL */
1476 
1477     cxn->lmtp_rBuffer = newBuffer(4096);
1478     if (cxn->lmtp_rBuffer == NULL) {
1479         d_printf(0, "%s:%d Failure allocating buffer for lmtp_rBuffer\n",
1480                  hostPeerName(cxn->myHost), cxn->ident);
1481         return RET_FAIL;
1482     }
1483     bufferAddNullByte(cxn->lmtp_rBuffer);
1484 
1485 
1486     cxn->imap_rBuffer = newBuffer(4096);
1487     if (cxn->imap_rBuffer == NULL) {
1488         d_printf(0, "%s:%d Failure allocating buffer for imap_rBuffer \n",
1489                  hostPeerName(cxn->myHost), cxn->ident);
1490         return RET_FAIL;
1491     }
1492     bufferAddNullByte(cxn->imap_rBuffer);
1493 
1494     /* Initialize timeouts */
1495     cxn->lmtp_writeTimeout = respTimeout;
1496     cxn->lmtp_readTimeout = respTimeout;
1497     cxn->imap_writeTimeout = respTimeout;
1498     cxn->imap_readTimeout = respTimeout;
1499     cxn->lmtp_sleepTimerId = 0;
1500     cxn->lmtp_sleepTimeout = init_reconnect_period;
1501     cxn->imap_sleepTimerId = 0;
1502     cxn->imap_sleepTimeout = init_reconnect_period;
1503 
1504     cxn->dosomethingTimeout = DOSOMETHING_TIMEOUT;
1505 
1506     /* set up the write timer. */
1507     clearTimer(cxn->dosomethingTimerId);
1508 
1509     if (cxn->dosomethingTimeout > 0)
1510         cxn->dosomethingTimerId =
1511             prepareSleep(dosomethingTimeoutCbk, cxn->dosomethingTimeout, cxn);
1512 
1513 
1514     return RET_OK;
1515 }
1516 
1517 
1518 /* initialize the network */
1519 static conn_ret
init_net(char * serverFQDN,int port,int * sock)1520 init_net(char *serverFQDN, int port, int *sock)
1521 {
1522     struct sockaddr_in addr;
1523     struct hostent *hp;
1524 
1525     if ((hp = gethostbyname(serverFQDN)) == NULL) {
1526         d_printf(0, "gethostbyname(): %s\n", strerror(errno));
1527         return RET_FAIL;
1528     }
1529 
1530     if (((*sock) = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1531         d_printf(0, "socket(): %s\n", strerror(errno));
1532         return RET_FAIL;
1533     }
1534 
1535     memset(&addr, 0, sizeof addr);
1536     addr.sin_family = AF_INET;
1537     memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
1538     addr.sin_port = htons(port);
1539 
1540     if (connect((*sock), (struct sockaddr *) &addr, sizeof(addr)) < 0) {
1541         d_printf(0, "connect(): %s\n", strerror(errno));
1542         return RET_FAIL;
1543     }
1544 
1545     return RET_OK;
1546 }
1547 
1548 
1549 static conn_ret
SetupLMTPConnection(connection_t * cxn,char * serverName,int port)1550 SetupLMTPConnection(connection_t *cxn, char *serverName, int port)
1551 {
1552 #ifdef HAVE_SASL
1553     int saslresult;
1554 #endif /* HAVE_SASL */
1555     conn_ret result;
1556 
1557     cxn->lmtp_port = port;
1558 
1559     if (serverName == NULL) {
1560         d_printf(0, "%s:%d serverName is null\n", hostPeerName(cxn->myHost),
1561                  cxn->ident);
1562         return RET_FAIL;
1563     }
1564 
1565 #ifdef HAVE_SASL
1566     /* Free the SASL connection if we already had one */
1567     if (cxn->saslconn_lmtp != NULL) {
1568         sasl_dispose(&cxn->saslconn_lmtp);
1569     }
1570 
1571     /* Start SASL */
1572     saslresult = sasl_client_new("lmtp", serverName, NULL, NULL, NULL, 0,
1573                                  &cxn->saslconn_lmtp);
1574 
1575     if (saslresult != SASL_OK) {
1576 
1577         d_printf(0, "%s:%d:LMTP Error creating a new SASL connection (%s)\n",
1578                  hostPeerName(cxn->myHost), cxn->ident,
1579                  sasl_errstring(saslresult, NULL, NULL));
1580         return RET_FAIL;
1581     }
1582 #endif /* HAVE_SASL */
1583 
1584     /* Connect the Socket */
1585     result = init_net(serverName, LMTP_PORT, /*port,*/
1586                       &(cxn->sockfd_lmtp));
1587 
1588     if (result != RET_OK) {
1589         d_printf(0, "%s:%d unable to connect to lmtp host\n",
1590                  hostPeerName(cxn->myHost), cxn->ident);
1591         return RET_FAIL;
1592     }
1593 
1594     if (cxn->lmtp_respBuffer)
1595         free(cxn->lmtp_respBuffer);
1596     cxn->lmtp_respBuffer = xmalloc(4096);
1597     cxn->lmtp_respBuffer[0] = '\0';
1598 
1599     /* Free if we had an existing one */
1600     if (cxn->lmtp_endpoint != NULL) {
1601         delEndPoint(cxn->lmtp_endpoint);
1602         cxn->lmtp_endpoint = NULL;
1603     }
1604 
1605     cxn->lmtp_endpoint = newEndPoint(cxn->sockfd_lmtp);
1606     if (cxn->lmtp_endpoint == NULL) {
1607         d_printf(0, "%s:%d:LMTP failure creating endpoint\n",
1608                  hostPeerName(cxn->myHost), cxn->ident);
1609         return RET_FAIL;
1610     }
1611 
1612 #ifdef HAVE_SASL
1613     /* Set the SASL properties */
1614     result = SetSASLProperties(cxn->saslconn_lmtp, cxn->sockfd_lmtp, 0, 0);
1615 
1616     if (result != RET_OK) {
1617         d_printf(0, "%s:%d:LMTP error setting SASL properties\n",
1618                  hostPeerName(cxn->myHost), cxn->ident);
1619         return RET_FAIL;
1620     }
1621 #endif /* HAVE_SASL */
1622 
1623 
1624     return RET_OK;
1625 }
1626 
1627 static conn_ret
SetupIMAPConnection(connection_t * cxn,char * serverName,int port)1628 SetupIMAPConnection(connection_t *cxn, char *serverName, int port)
1629 {
1630 #ifdef HAVE_SASL
1631     int saslresult;
1632 #endif /* HAVE_SASL */
1633     conn_ret result;
1634 
1635     cxn->imap_port = port;
1636 
1637     if (serverName == NULL) {
1638         d_printf(0, "%s:%d:IMAP Servername is null", hostPeerName(cxn->myHost),
1639                  cxn->ident);
1640         return RET_FAIL;
1641     }
1642 
1643 #ifdef HAVE_SASL
1644     /* Free the SASL connection if we already had one */
1645     if (cxn->imap_saslconn != NULL) {
1646         sasl_dispose(&cxn->imap_saslconn);
1647     }
1648 
1649     /* Start SASL */
1650     saslresult = sasl_client_new("imap", serverName, NULL, NULL, NULL, 0,
1651                                  &cxn->imap_saslconn);
1652 
1653     if (saslresult != SASL_OK) {
1654         d_printf(0, "%s:%d:IMAP Error creating a new SASL connection (%s)",
1655                  hostPeerName(cxn->myHost), cxn->ident,
1656                  sasl_errstring(saslresult, NULL, NULL));
1657         return RET_FAIL;
1658     }
1659 #endif /* HAVE_SASL */
1660 
1661     /* Connect the Socket */
1662     result = init_net(serverName, port, &(cxn->imap_sockfd));
1663 
1664     if (result != RET_OK) {
1665         d_printf(0, "%s:%d:IMAP Unable to start network connection for IMAP",
1666                  hostPeerName(cxn->myHost), cxn->ident);
1667         return RET_FAIL;
1668     }
1669 
1670     if (cxn->imap_respBuffer)
1671         free(cxn->imap_respBuffer);
1672     cxn->imap_respBuffer = xmalloc(4096);
1673     cxn->imap_respBuffer[0] = '\0';
1674 
1675     /* Free if we had an existing one */
1676     if (cxn->imap_endpoint != NULL) {
1677         delEndPoint(cxn->imap_endpoint);
1678         cxn->imap_endpoint = NULL;
1679     }
1680 
1681     cxn->imap_endpoint = newEndPoint(cxn->imap_sockfd);
1682     if (cxn->imap_endpoint == NULL) {
1683         d_printf(0, "%s:%d:IMAP Failure creating imap endpoint\n",
1684                  hostPeerName(cxn->myHost), cxn->ident);
1685         return RET_FAIL;
1686     }
1687 
1688 #ifdef HAVE_SASL
1689     /* Set the SASL properties */
1690     result = SetSASLProperties(cxn->imap_saslconn, cxn->imap_sockfd, 0, 0);
1691     if (result != RET_OK) {
1692         d_printf(0, "%s:%d:IMAP Error setting sasl properties",
1693                  hostPeerName(cxn->myHost), cxn->ident);
1694         return result;
1695     }
1696 #endif /* HAVE_SASL */
1697 
1698 
1699     return RET_OK;
1700 }
1701 
1702 /************************* END Startup functions
1703  * **********************************/
1704 
1705 /* Return the response code for this line
1706    -1 if it doesn't seem to have one
1707 */
1708 static int
ask_code(char * str)1709 ask_code(char *str)
1710 {
1711     int ret = 0;
1712 
1713     if (str == NULL)
1714         return -1;
1715 
1716     if (strlen(str) < 3)
1717         return -1;
1718 
1719     /* check to make sure 0-2 are digits */
1720     if ((!isdigit((unsigned char) str[0]))
1721         || (!isdigit((unsigned char) str[1]))
1722         || (!isdigit((unsigned char) str[2]))) {
1723         d_printf(0, "Parse error: response does not begin with a code [%s]\n",
1724                  str);
1725         return -1;
1726     }
1727 
1728 
1729     ret = ((str[0] - '0') * 100) + ((str[1] - '0') * 10) + (str[2] - '0');
1730 
1731     return ret;
1732 }
1733 
1734 /* is this a continuation or not?
1735    220-fdfsd is        (1)
1736    220 fdsfs is not    (0)
1737  */
1738 
1739 static int
ask_keepgoing(char * str)1740 ask_keepgoing(char *str)
1741 {
1742     if (str == NULL)
1743         return 0;
1744     if (strlen(str) < 4)
1745         return 0;
1746 
1747     if (str[3] == '-')
1748         return 1;
1749 
1750     return 0;
1751 }
1752 
1753 
1754 static conn_ret
lmtp_listenintro(connection_t * cxn)1755 lmtp_listenintro(connection_t *cxn)
1756 {
1757     Buffer *readBuffers;
1758 
1759     /* set up to receive */
1760     readBuffers = makeBufferArray(bufferTakeRef(cxn->lmtp_rBuffer), NULL);
1761     prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
1762 
1763     cxn->lmtp_state = LMTP_READING_INTRO;
1764 
1765     return RET_OK;
1766 }
1767 
1768 
1769 /************************** IMAP functions ***********************/
1770 
1771 static conn_ret
imap_Connect(connection_t * cxn)1772 imap_Connect(connection_t *cxn)
1773 {
1774     conn_ret result;
1775 
1776     ASSERT(cxn->imap_sleepTimerId == 0);
1777 
1778     /* make the IMAP connection */ SetupIMAPConnection(cxn, cxn->ServerName,
1779                                                        IMAP_PORT);
1780 
1781     /* Listen to the intro and start the authenticating process */
1782     result = imap_listenintro(cxn);
1783 
1784     return result;
1785 }
1786 
1787 /*
1788  * This is called when the data write timeout for the remote
1789  * goes off. We tear down the connection and notify our host.
1790  */
1791 static void
imap_writeTimeoutCbk(TimeoutId id UNUSED,void * data)1792 imap_writeTimeoutCbk(TimeoutId id UNUSED, void *data)
1793 {
1794     connection_t *cxn = (Connection) data;
1795     const char *peerName;
1796 
1797     peerName = hostPeerName(cxn->myHost);
1798 
1799     syslog(LOG_WARNING, "timeout for %s", peerName);
1800     d_printf(0, "%s: shutting down non-responsive IMAP connection (%s)\n",
1801              hostPeerName(cxn->myHost), imap_stateToString(cxn->imap_state));
1802 
1803     cxnLogStats(cxn, true);
1804 
1805     imap_Disconnect(cxn);
1806 }
1807 
1808 /*
1809  * This is called when the timeout for the response from the remote
1810  * goes off. We tear down the connection and notify our host.
1811  */
1812 static void
imap_readTimeoutCbk(TimeoutId id,void * data)1813 imap_readTimeoutCbk(TimeoutId id, void *data)
1814 {
1815     Connection cxn = (Connection) data;
1816     const char *peerName;
1817 
1818     ASSERT(id == cxn->imap_readBlockedTimerId);
1819 
1820     peerName = hostPeerName(cxn->myHost);
1821 
1822     warn("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident);
1823     d_printf(0, "%s:%d shutting down non-responsive IMAP connection (%s)\n",
1824              hostPeerName(cxn->myHost), cxn->ident,
1825              imap_stateToString(cxn->imap_state));
1826 
1827     cxnLogStats(cxn, true);
1828 
1829     if (cxn->imap_state == IMAP_DISCONNECTED) {
1830         imap_Disconnect(cxn);
1831         lmtp_Disconnect(cxn);
1832         delConnection(cxn);
1833     } else {
1834         imap_Disconnect(cxn);
1835     }
1836 }
1837 
1838 /*
1839  * Called by the EndPoint class when the timer goes off
1840  */
1841 static void
imap_reopenTimeoutCbk(TimeoutId id,void * data)1842 imap_reopenTimeoutCbk(TimeoutId id, void *data)
1843 {
1844     Connection cxn = (Connection) data;
1845 
1846     ASSERT(id == cxn->imap_sleepTimerId);
1847 
1848     cxn->imap_sleepTimerId = 0;
1849 
1850     d_printf(1,
1851              "%s:%d:IMAP Reopen timer rang. Try to make new connection now\n",
1852              hostPeerName(cxn->myHost), cxn->ident);
1853 
1854     if (cxn->imap_state != IMAP_DISCONNECTED) {
1855         warn("%s:%d cxnsleep connection in bad state: %s",
1856              hostPeerName(cxn->myHost), cxn->ident,
1857              imap_stateToString(cxn->imap_state));
1858     } else {
1859         if (imap_Connect(cxn) != RET_OK)
1860             prepareReopenCbk(cxn, 0);
1861     }
1862 }
1863 
1864 static void
imap_Disconnect(connection_t * cxn)1865 imap_Disconnect(connection_t *cxn)
1866 {
1867     clearTimer(cxn->imap_sleepTimerId);
1868     cxn->imap_sleepTimerId = 0;
1869     clearTimer(cxn->imap_readBlockedTimerId);
1870     clearTimer(cxn->imap_writeBlockedTimerId);
1871 
1872     DeferAllArticles(
1873         cxn, &(cxn->imap_controlMsg_q)); /* give any articles back to Host */
1874 
1875     cxn->imap_state = IMAP_DISCONNECTED;
1876 
1877     cxn->imap_disconnects++;
1878 
1879     cxn->imap_respBuffer[0] = '\0';
1880 
1881     if (cxn->issue_quit == 0)
1882         prepareReopenCbk(cxn, 0);
1883 
1884     DeleteIfDisconnected(cxn);
1885 }
1886 
1887 /************************** END IMAP functions ***********************/
1888 
1889 /************************ LMTP functions **************************/
1890 
1891 /*
1892  * Create a network lmtp connection
1893  * and start listening for the intro string
1894  *
1895  */
1896 
1897 static conn_ret
lmtp_Connect(connection_t * cxn)1898 lmtp_Connect(connection_t *cxn)
1899 {
1900     conn_ret result;
1901 
1902     ASSERT(cxn->lmtp_sleepTimerId == 0);
1903 
1904     /* make the LMTP connection */
1905     result = SetupLMTPConnection(cxn, cxn->ServerName, LMTP_PORT);
1906 
1907     if (result != RET_OK)
1908         return result;
1909 
1910     /* Listen to the intro */
1911     result = lmtp_listenintro(cxn);
1912 
1913     return result;
1914 }
1915 
1916 
1917 static void
lmtp_Disconnect(connection_t * cxn)1918 lmtp_Disconnect(connection_t *cxn)
1919 {
1920     clearTimer(cxn->lmtp_sleepTimerId);
1921     cxn->lmtp_sleepTimerId = 0;
1922     clearTimer(cxn->lmtp_readBlockedTimerId);
1923     clearTimer(cxn->lmtp_writeBlockedTimerId);
1924 
1925     /* give any articles back to Host */
1926     DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
1927 
1928     cxn->lmtp_state = LMTP_DISCONNECTED;
1929 
1930     cxn->lmtp_disconnects++;
1931 
1932     cxn->lmtp_respBuffer[0] = '\0';
1933 
1934     if (cxn->issue_quit == 0)
1935         prepareReopenCbk(cxn, 1);
1936 
1937     DeleteIfDisconnected(cxn);
1938 }
1939 
1940 
1941 /*
1942  * Called by the EndPoint class when the timer goes off
1943  */
1944 static void
lmtp_reopenTimeoutCbk(TimeoutId id,void * data)1945 lmtp_reopenTimeoutCbk(TimeoutId id, void *data)
1946 {
1947     Connection cxn = (Connection) data;
1948 
1949     ASSERT(id == cxn->lmtp_sleepTimerId);
1950 
1951     cxn->lmtp_sleepTimerId = 0;
1952 
1953     d_printf(1,
1954              "%s:%d:LMTP Reopen timer rang. Try to make new connection now\n",
1955              hostPeerName(cxn->myHost), cxn->ident);
1956 
1957     if (cxn->lmtp_state != LMTP_DISCONNECTED) {
1958         warn("%s:%d cxnsleep connection in bad state: %s",
1959              hostPeerName(cxn->myHost), cxn->ident,
1960              lmtp_stateToString(cxn->lmtp_state));
1961     } else {
1962         if (lmtp_Connect(cxn) != RET_OK)
1963             prepareReopenCbk(cxn, 1);
1964     }
1965 }
1966 
1967 /*
1968  * Set up the callback used when the Connection is sleeping (i.e. will try
1969  * to reopen the connection).
1970  *
1971  * type (0 = imap, 1 = lmtp)
1972  */
1973 static void
prepareReopenCbk(Connection cxn,int type)1974 prepareReopenCbk(Connection cxn, int type)
1975 {
1976     /* xxx check state */
1977 
1978 
1979     if (type == 0) {
1980 
1981         cxn->imap_sleepTimerId =
1982             prepareSleep(imap_reopenTimeoutCbk, cxn->imap_sleepTimeout, cxn);
1983         d_printf(1,
1984                  "%s:%d IMAP connection error\n"
1985                  "  will try to reconnect in %d seconds\n",
1986                  hostPeerName(cxn->myHost), cxn->ident,
1987                  cxn->imap_sleepTimeout);
1988     } else {
1989         cxn->lmtp_sleepTimerId =
1990             prepareSleep(lmtp_reopenTimeoutCbk, cxn->lmtp_sleepTimeout, cxn);
1991         d_printf(1,
1992                  "%s:%d:LMTP connection error\n"
1993                  "will try to reconnect in %d seconds\n",
1994                  hostPeerName(cxn->myHost), cxn->ident,
1995                  cxn->lmtp_sleepTimeout);
1996     }
1997 
1998     /* bump the sleep timer amount each time to wait longer and longer. Gets
1999        reset in resetConnection() */
2000     if (type == 0) {
2001         cxn->imap_sleepTimeout *= 2;
2002         if (cxn->imap_sleepTimeout > max_reconnect_period)
2003             cxn->imap_sleepTimeout = max_reconnect_period;
2004     } else {
2005         cxn->lmtp_sleepTimeout *= 2;
2006         if (cxn->lmtp_sleepTimeout > max_reconnect_period)
2007             cxn->lmtp_sleepTimeout = max_reconnect_period;
2008     }
2009 }
2010 
2011 /*
2012  * This is called when the timeout for the response from the remote
2013  * goes off. We tear down the connection and notify our host.
2014  */
2015 static void
lmtp_readTimeoutCbk(TimeoutId id,void * data)2016 lmtp_readTimeoutCbk(TimeoutId id, void *data)
2017 {
2018     Connection cxn = (Connection) data;
2019     const char *peerName;
2020 
2021     ASSERT(id == cxn->lmtp_readBlockedTimerId);
2022 
2023     peerName = hostPeerName(cxn->myHost);
2024 
2025     warn("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident);
2026     d_printf(0, "%s:%d shutting down non-responsive LMTP connection (%s)\n",
2027              hostPeerName(cxn->myHost), cxn->ident,
2028              lmtp_stateToString(cxn->lmtp_state));
2029 
2030     cxnLogStats(cxn, true);
2031 
2032     if (cxn->lmtp_state == LMTP_DISCONNECTED) {
2033         imap_Disconnect(cxn);
2034         lmtp_Disconnect(cxn);
2035         delConnection(cxn);
2036     } else {
2037         lmtp_Disconnect(cxn);
2038     }
2039 }
2040 
2041 
2042 /*
2043  * This is called when the data write timeout for the remote
2044  * goes off. We tear down the connection and notify our host.
2045  */
2046 static void
lmtp_writeTimeoutCbk(TimeoutId id UNUSED,void * data)2047 lmtp_writeTimeoutCbk(TimeoutId id UNUSED, void *data)
2048 {
2049     connection_t *cxn = (Connection) data;
2050     const char *peerName;
2051 
2052     peerName = hostPeerName(cxn->myHost);
2053 
2054     syslog(LOG_WARNING, "timeout for %s", peerName);
2055     d_printf(0, "%s:%d shutting down non-responsive LMTP connection (%s)\n",
2056              hostPeerName(cxn->myHost), cxn->ident,
2057              lmtp_stateToString(cxn->lmtp_state));
2058 
2059     cxnLogStats(cxn, true);
2060 
2061     lmtp_Disconnect(cxn);
2062 }
2063 
2064 /************************ END LMTP functions **************************/
2065 
2066 /************************** LMTP write functions ********************/
2067 
2068 static conn_ret
lmtp_noop(connection_t * cxn)2069 lmtp_noop(connection_t *cxn)
2070 {
2071     conn_ret result;
2072     char *p;
2073 
2074     p = xstrdup("NOOP\r\n");
2075     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2076     if (result != RET_OK)
2077         return result;
2078 
2079     cxn->lmtp_state = LMTP_WRITING_NOOP;
2080 
2081     return RET_OK;
2082 }
2083 
2084 static conn_ret
lmtp_IssueQuit(connection_t * cxn)2085 lmtp_IssueQuit(connection_t *cxn)
2086 {
2087     conn_ret result;
2088     char *p;
2089 
2090     p = xstrdup("QUIT\r\n");
2091     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2092     if (result != RET_OK)
2093         return result;
2094 
2095     cxn->lmtp_state = LMTP_WRITING_QUIT;
2096 
2097     return RET_OK;
2098 }
2099 
2100 static conn_ret
lmtp_getcapabilities(connection_t * cxn)2101 lmtp_getcapabilities(connection_t *cxn)
2102 {
2103     conn_ret result;
2104     char *p;
2105 
2106     if (cxn->lmtp_capabilities != NULL) {
2107         if (cxn->lmtp_capabilities->saslmechs) {
2108             free(cxn->lmtp_capabilities->saslmechs);
2109         }
2110         free(cxn->lmtp_capabilities);
2111         cxn->lmtp_capabilities = NULL;
2112     }
2113 
2114     cxn->lmtp_capabilities = xcalloc(1, sizeof(lmtp_capabilities_t));
2115     cxn->lmtp_capabilities->saslmechs = NULL;
2116 
2117 #ifdef SMTPMODE
2118     p = concat("EHLO ", hostname, "\r\n", (char *) 0);
2119 #else
2120     p = concat("LHLO ", hostname, "\r\n", (char *) 0);
2121 #endif /* SMTPMODE */
2122 
2123     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2124     if (result != RET_OK)
2125         return result;
2126 
2127     cxn->lmtp_state = LMTP_WRITING_LHLO;
2128 
2129     return RET_OK;
2130 }
2131 
2132 #ifdef HAVE_SASL
2133 static conn_ret
lmtp_authenticate(connection_t * cxn)2134 lmtp_authenticate(connection_t *cxn)
2135 {
2136     int saslresult;
2137 
2138     const char *mechusing;
2139     const char *out;
2140     unsigned int outlen;
2141     char *inbase64;
2142     int inbase64len;
2143     conn_ret result;
2144     char *p;
2145 
2146     sasl_interact_t *client_interact = NULL;
2147 
2148     saslresult = sasl_client_start(
2149         cxn->saslconn_lmtp, cxn->lmtp_capabilities->saslmechs,
2150         &client_interact, &out, &outlen, &mechusing);
2151 
2152     if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) {
2153 
2154         d_printf(0, "%s:%d:LMTP Error calling sasl_client_start (%s)\n",
2155                  hostPeerName(cxn->myHost), cxn->ident,
2156                  sasl_errstring(saslresult, NULL, NULL));
2157         return RET_FAIL;
2158     }
2159 
2160     d_printf(
2161         1,
2162         "%s:%d:LMTP Decided to try to authenticate with SASL mechanism=%s\n",
2163         hostPeerName(cxn->myHost), cxn->ident, mechusing);
2164 
2165     if (!out) {
2166         /* no initial client response */
2167         p = concat("AUTH ", mechusing, "\r\n", (char *) 0);
2168     } else if (!outlen) {
2169         /* empty initial client response */
2170         p = concat("AUTH ", mechusing, " =\r\n", (char *) 0);
2171     } else {
2172         /* Initial client response - convert to base64.
2173          * 2n+7 bytes are enough to contain the result of the base64
2174          * encoding of a string whose length is n bytes.
2175          * In sasl_encode64() calls, the fourth argument is the length
2176          * of the third including the null terminator (thus 2n+8 bytes). */
2177         inbase64 = xmalloc(outlen * 2 + 8);
2178 
2179         saslresult = sasl_encode64(out, outlen, inbase64, outlen * 2 + 8,
2180                                    (unsigned *) &inbase64len);
2181         if (saslresult != SASL_OK)
2182             return RET_FAIL;
2183         p = concat("AUTH ", mechusing, " ", inbase64, "\r\n", (char *) 0);
2184         free(inbase64);
2185     }
2186 
2187     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2188     if (result != RET_OK) {
2189         d_printf(0, "%s:%d:LMTP WriteToWire() failure during AUTH\n",
2190                  hostPeerName(cxn->myHost), cxn->ident);
2191         /* Disconnection is handled in the calling function. */
2192         return result;
2193     }
2194 
2195     cxn->lmtp_state = LMTP_WRITING_STARTAUTH;
2196 
2197     return RET_OK;
2198 }
2199 
2200 static imt_stat
lmtp_getauthline(char * str,char ** line,int * linelen)2201 lmtp_getauthline(char *str, char **line, int *linelen)
2202 {
2203     int saslresult;
2204     int response_code = -1;
2205 
2206     response_code = ask_code(str);
2207 
2208     if (response_code == 334) {
2209 
2210         /* continue */
2211 
2212     } else if (response_code == 235) {
2213 
2214         /* woohoo! authentication complete */
2215         return STAT_OK;
2216 
2217     } else {
2218         /* failure of some sort */
2219         d_printf(0, "?:?:LMTP Authentication failure (%d) [%s]\n",
2220                  response_code, str);
2221         return STAT_NO;
2222     }
2223 
2224     str += 4; /* jump past the "334 " */
2225 
2226     *line = xmalloc(strlen(str) + 30);
2227     if ((*line) == NULL) {
2228         return STAT_NO;
2229     }
2230 
2231     /* decode this line */
2232     saslresult = sasl_decode64(str, strlen(str), *line, strlen(str) + 1,
2233                                (unsigned *) linelen);
2234     if (saslresult != SASL_OK) {
2235         d_printf(0, "?:?:LMTP base64 decoding error\n");
2236         return STAT_NO;
2237     }
2238 
2239     return STAT_CONT;
2240 }
2241 #endif /* HAVE_SASL */
2242 
2243 static void
lmtp_writeCB(EndPoint e UNUSED,IoStatus i UNUSED,Buffer * b,void * d)2244 lmtp_writeCB(EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b, void *d)
2245 {
2246     connection_t *cxn = (connection_t *) d;
2247     Buffer *readBuffers;
2248 
2249     clearTimer(cxn->lmtp_writeBlockedTimerId);
2250 
2251     /* Free the string that was written */
2252     freeBufferArray(b);
2253     if (cxn->lmtp_tofree_str != NULL) {
2254         free(cxn->lmtp_tofree_str);
2255         cxn->lmtp_tofree_str = NULL;
2256     }
2257 
2258     /* set up to receive */
2259     readBuffers = makeBufferArray(bufferTakeRef(cxn->lmtp_rBuffer), NULL);
2260     prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
2261 
2262     /* set up the response timer. */
2263     clearTimer(cxn->lmtp_readBlockedTimerId);
2264 
2265     if (cxn->lmtp_readTimeout > 0)
2266         cxn->lmtp_readBlockedTimerId =
2267             prepareSleep(lmtp_readTimeoutCbk, cxn->lmtp_readTimeout, cxn);
2268 
2269 
2270     switch (cxn->lmtp_state) {
2271 
2272     case LMTP_WRITING_LHLO:
2273         cxn->lmtp_state = LMTP_READING_LHLO;
2274         break;
2275 
2276     case LMTP_WRITING_STARTAUTH:
2277     case LMTP_WRITING_STEPAUTH:
2278 
2279         cxn->lmtp_state = LMTP_READING_STEPAUTH;
2280 
2281         break;
2282 
2283     case LMTP_WRITING_UPTODATA:
2284         /* expect result to rset */
2285         cxn->lmtp_state = LMTP_READING_RSET;
2286         break;
2287 
2288     case LMTP_WRITING_CONTENTS:
2289         /* so we sent the whole DATA command
2290            let's see what the server responded */
2291 
2292         cxn->lmtp_state = LMTP_READING_CONTENTS;
2293 
2294         break;
2295 
2296     case LMTP_WRITING_NOOP:
2297         cxn->lmtp_state = LMTP_READING_NOOP;
2298         break;
2299 
2300     case LMTP_WRITING_QUIT:
2301         cxn->lmtp_state = LMTP_READING_QUIT;
2302         break;
2303 
2304     default:
2305 
2306         d_printf(0, "%s:%d:LMTP Unknown state. Internal error\n",
2307                  hostPeerName(cxn->myHost), cxn->ident);
2308 
2309         break;
2310     }
2311 }
2312 
2313 /************************** END LMTP write functions ********************/
2314 
2315 /************************** IMAP sending functions ************************/
2316 
2317 
2318 static void
imap_writeCB(EndPoint e UNUSED,IoStatus i UNUSED,Buffer * b,void * d)2319 imap_writeCB(EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b, void *d)
2320 {
2321     connection_t *cxn = (connection_t *) d;
2322     Buffer *readBuffers;
2323 
2324     clearTimer(cxn->imap_writeBlockedTimerId);
2325 
2326     /* free the string we just wrote out */
2327     freeBufferArray(b);
2328     if (cxn->imap_tofree_str != NULL) {
2329         free(cxn->imap_tofree_str);
2330         cxn->imap_tofree_str = NULL;
2331     }
2332 
2333     /* set up to receive */
2334     readBuffers = makeBufferArray(bufferTakeRef(cxn->imap_rBuffer), NULL);
2335     prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
2336 
2337     /* set up the response timer. */
2338     clearTimer(cxn->imap_readBlockedTimerId);
2339 
2340     if (cxn->imap_readTimeout > 0)
2341         cxn->imap_readBlockedTimerId =
2342             prepareSleep(imap_readTimeoutCbk, cxn->imap_readTimeout, cxn);
2343 
2344     switch (cxn->imap_state) {
2345     case IMAP_WRITING_CAPABILITY:
2346         cxn->imap_state = IMAP_READING_CAPABILITY;
2347         break;
2348 
2349     case IMAP_WRITING_STEPAUTH:
2350     case IMAP_WRITING_STARTAUTH:
2351         cxn->imap_state = IMAP_READING_STEPAUTH;
2352         break;
2353 
2354     case IMAP_WRITING_CREATE:
2355         cxn->imap_state = IMAP_READING_CREATE;
2356         break;
2357 
2358     case IMAP_WRITING_DELETE:
2359         cxn->imap_state = IMAP_READING_DELETE;
2360         break;
2361 
2362     case IMAP_WRITING_SELECT:
2363         cxn->imap_state = IMAP_READING_SELECT;
2364         break;
2365 
2366     case IMAP_WRITING_SEARCH:
2367         cxn->imap_state = IMAP_READING_SEARCH;
2368         break;
2369 
2370     case IMAP_WRITING_STORE:
2371         cxn->imap_state = IMAP_READING_STORE;
2372         break;
2373 
2374     case IMAP_WRITING_CLOSE:
2375         cxn->imap_state = IMAP_READING_CLOSE;
2376         break;
2377 
2378     case IMAP_WRITING_NOOP:
2379         cxn->imap_state = IMAP_READING_NOOP;
2380         break;
2381 
2382     case IMAP_WRITING_QUIT:
2383         cxn->imap_state = IMAP_READING_QUIT;
2384         break;
2385 
2386     default:
2387         d_printf(0, "%s:%d:IMAP invalid connection state\n",
2388                  hostPeerName(cxn->myHost), cxn->ident);
2389         imap_Disconnect(cxn);
2390         break;
2391     }
2392 }
2393 
2394 /*
2395  * Tag is already allocated
2396  */
2397 
2398 static void
imap_GetTag(connection_t * cxn)2399 imap_GetTag(connection_t *cxn)
2400 {
2401     snprintf(cxn->imap_currentTag, IMAP_TAGLENGTH + 1, "%06d",
2402              cxn->imap_tag_num);
2403     cxn->imap_tag_num++;
2404     if (cxn->imap_tag_num >= 999999) {
2405         cxn->imap_tag_num = 0;
2406     }
2407 }
2408 
2409 #ifdef HAVE_SASL
2410 static conn_ret
imap_sendAuthStep(connection_t * cxn,char * str)2411 imap_sendAuthStep(connection_t *cxn, char *str)
2412 {
2413     conn_ret result;
2414     int saslresult;
2415     char in[4096];
2416     unsigned int inlen;
2417     const char *out;
2418     unsigned int outlen;
2419     char *inbase64;
2420     unsigned int inbase64len;
2421 
2422     /* base64 decode it */
2423 
2424     saslresult = sasl_decode64(str, strlen(str), in, strlen(str) + 1, &inlen);
2425     if (saslresult != SASL_OK) {
2426         d_printf(0, "%s:%d:IMAP base64 decoding error\n",
2427                  hostPeerName(cxn->myHost), cxn->ident);
2428         return RET_FAIL;
2429     }
2430 
2431     saslresult =
2432         sasl_client_step(cxn->imap_saslconn, in, inlen, NULL, &out, &outlen);
2433 
2434     /* check if sasl succeeded */
2435     if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
2436 
2437         d_printf(0, "%s:%d:IMAP sasl_client_step failed with %s\n",
2438                  hostPeerName(cxn->myHost), cxn->ident,
2439                  sasl_errstring(saslresult, NULL, NULL));
2440         cxn->imap_state = IMAP_CONNECTED_NOTAUTH;
2441         return RET_FAIL;
2442     }
2443     /* Convert to base64.
2444      * 2n+7 bytes are enough to contain the result of the base64
2445      * encoding of a string whose length is n bytes.
2446      * In sasl_encode64() calls, the fourth argument is the length
2447      * of the third including the null terminator (thus 2n+8 bytes).
2448      * And CRLF takes the last two bytes (thus 2n+10 bytes). */
2449     inbase64 = xmalloc(outlen * 2 + 10);
2450 
2451     saslresult = sasl_encode64(out, outlen, inbase64, outlen * 2 + 8,
2452                                (unsigned *) &inbase64len);
2453 
2454     if (saslresult != SASL_OK)
2455         return RET_FAIL;
2456 
2457     /* Append endline. */
2458     strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10 - inbase64len);
2459     inbase64len += 2;
2460 
2461     /* Send to server. */
2462     result = WriteToWire_imapstr(cxn, inbase64, inbase64len);
2463     if (result != RET_OK)
2464         return result;
2465 
2466     cxn->imap_state = IMAP_WRITING_STEPAUTH;
2467 
2468     return RET_OK;
2469 }
2470 #endif /* HAVE_SASL */
2471 
2472 static conn_ret
imap_sendAuthenticate(connection_t * cxn)2473 imap_sendAuthenticate(connection_t *cxn)
2474 {
2475     conn_ret result;
2476     char *p;
2477 
2478 #ifdef HAVE_SASL
2479     const char *mechusing;
2480     int saslresult = SASL_NOMECH;
2481 
2482     sasl_interact_t *client_interact = NULL;
2483 
2484     if (cxn->imap_capabilities->saslmechs) {
2485         saslresult = sasl_client_start(
2486             cxn->imap_saslconn, cxn->imap_capabilities->saslmechs,
2487             &client_interact, NULL, NULL, &mechusing);
2488     }
2489 
2490     /* If no mechs try "login" */
2491     if (saslresult == SASL_NOMECH) {
2492 
2493 #else /* HAVE_SASL */
2494 
2495     { /* always do login */
2496 
2497 #endif /* HAVE_SASL */
2498         d_printf(1, "%s:%d:IMAP No mechanism found. Trying login method\n",
2499                  hostPeerName(cxn->myHost), cxn->ident);
2500 
2501         if (cxn->imap_capabilities->logindisabled == 1) {
2502             d_printf(0,
2503                      "%s:%d:IMAP Login command w/o security layer not allowed "
2504                      "on this server\n",
2505                      hostPeerName(cxn->myHost), cxn->ident);
2506             return RET_FAIL;
2507         }
2508 
2509         if (deliver_authname == NULL) {
2510             d_printf(
2511                 0,
2512                 "%s:%d:IMAP Unable to log in because can't find a authname\n",
2513                 hostPeerName(cxn->myHost), cxn->ident);
2514             return RET_FAIL;
2515         }
2516 
2517         if (deliver_password == NULL) {
2518             d_printf(
2519                 0,
2520                 "%s:%d:IMAP Unable to log in because can't find a password\n",
2521                 hostPeerName(cxn->myHost), cxn->ident);
2522             return RET_FAIL;
2523         }
2524 
2525         imap_GetTag(cxn);
2526 
2527         p = concat(cxn->imap_currentTag, " LOGIN ", deliver_authname, " \"",
2528                    deliver_password, "\"\r\n", (char *) 0);
2529 
2530         result = WriteToWire_imapstr(cxn, p, strlen(p));
2531         if (result != RET_OK)
2532             return result;
2533 
2534         cxn->imap_state = IMAP_WRITING_STARTAUTH;
2535 
2536         return RET_OK;
2537     }
2538 
2539 #ifdef HAVE_SASL
2540     if ((saslresult != SASL_OK) && (saslresult != SASL_CONTINUE)) {
2541 
2542         d_printf(
2543             0,
2544             "%s:%d:IMAP Error calling sasl_client_start (%s) mechusing = %s\n",
2545             hostPeerName(cxn->myHost), cxn->ident,
2546             sasl_errstring(saslresult, NULL, NULL), mechusing);
2547         return RET_FAIL;
2548     }
2549 
2550     d_printf(1,
2551              "%s:%d:IMAP Trying to authenticate to imap with %s mechanism\n",
2552              hostPeerName(cxn->myHost), cxn->ident, mechusing);
2553 
2554     imap_GetTag(cxn);
2555 
2556     p = concat(cxn->imap_currentTag, " AUTHENTICATE ", mechusing, "\r\n",
2557                (char *) 0);
2558     result = WriteToWire_imapstr(cxn, p, strlen(p));
2559     if (result != RET_OK)
2560         return result;
2561 
2562     cxn->imap_state = IMAP_WRITING_STARTAUTH;
2563 
2564     return RET_OK;
2565 #endif /* HAVE_SASL */
2566 }
2567 
2568 static conn_ret
2569 imap_CreateGroup(connection_t *cxn, char *bboard)
2570 {
2571     conn_ret result;
2572     char *tosend;
2573 
2574     d_printf(1, "%s:%d:IMAP Ok creating group [%s]\n",
2575              hostPeerName(cxn->myHost), cxn->ident, bboard);
2576 
2577     imap_GetTag(cxn);
2578 
2579     tosend =
2580         concat(cxn->imap_currentTag, " CREATE ", bboard, "\r\n", (char *) 0);
2581 
2582     result = WriteToWire_imapstr(cxn, tosend, -1);
2583     if (result != RET_OK)
2584         return result;
2585 
2586     cxn->imap_state = IMAP_WRITING_CREATE;
2587 
2588     return RET_OK;
2589 }
2590 
2591 static conn_ret
2592 imap_DeleteGroup(connection_t *cxn, char *bboard)
2593 {
2594     conn_ret result;
2595     char *tosend;
2596 
2597     d_printf(1, "%s:%d:IMAP Ok removing bboard [%s]\n",
2598              hostPeerName(cxn->myHost), cxn->ident, bboard);
2599 
2600     imap_GetTag(cxn);
2601 
2602     tosend =
2603         concat(cxn->imap_currentTag, " DELETE ", bboard, "\r\n", (char *) 0);
2604     result = WriteToWire_imapstr(cxn, tosend, -1);
2605     if (result != RET_OK)
2606         return result;
2607 
2608     cxn->imap_state = IMAP_WRITING_DELETE;
2609 
2610     return RET_OK;
2611 }
2612 
2613 static conn_ret
2614 imap_CancelMsg(connection_t *cxn, char *newsgroup)
2615 {
2616     conn_ret result;
2617     char *tosend;
2618 
2619     ASSERT(newsgroup);
2620 
2621     imap_GetTag(cxn);
2622 
2623     /* select mbox */
2624     tosend = concat(cxn->imap_currentTag, " SELECT ", newsgroup, "\r\n",
2625                     (char *) 0);
2626     result = WriteToWire_imapstr(cxn, tosend, -1);
2627     if (result != RET_OK)
2628         return result;
2629 
2630     cxn->imap_state = IMAP_WRITING_SELECT;
2631 
2632     hostArticleOffered(cxn->myHost, cxn);
2633 
2634     return RET_OK;
2635 }
2636 
2637 static conn_ret
2638 imap_sendSearch(connection_t *cxn, char *msgid)
2639 {
2640     conn_ret result;
2641     char *tosend;
2642 
2643     ASSERT(msgid);
2644 
2645     imap_GetTag(cxn);
2646 
2647     /* preform search */
2648     tosend =
2649         concat(cxn->imap_currentTag, " UID SEARCH header \"Message-ID\" \"",
2650                msgid, "\"\r\n", (char *) 0);
2651     result = WriteToWire_imapstr(cxn, tosend, -1);
2652     if (result != RET_OK)
2653         return result;
2654 
2655     cxn->imap_state = IMAP_WRITING_SEARCH;
2656 
2657     return RET_OK;
2658 }
2659 
2660 static conn_ret
2661 imap_sendKill(connection_t *cxn, unsigned uid)
2662 {
2663     conn_ret result;
2664     char *tosend = NULL;
2665 
2666     imap_GetTag(cxn);
2667 
2668     xasprintf(&tosend, "%s UID STORE %d +FLAGS.SILENT (\\Deleted)\r\n",
2669               cxn->imap_currentTag, uid);
2670 
2671     result = WriteToWire_imapstr(cxn, tosend, -1);
2672     if (result != RET_OK)
2673         return result;
2674 
2675     cxn->imap_state = IMAP_WRITING_STORE;
2676 
2677     return RET_OK;
2678 }
2679 
2680 static conn_ret
2681 imap_sendSimple(connection_t *cxn, const char *atom, int st)
2682 {
2683     char *tosend;
2684     conn_ret result;
2685 
2686     imap_GetTag(cxn);
2687     tosend = concat(cxn->imap_currentTag, " ", atom, "\r\n", (char *) 0);
2688 
2689     result = WriteToWire_imapstr(cxn, tosend, -1);
2690     if (result != RET_OK)
2691         return result;
2692 
2693     cxn->imap_state = st;
2694 
2695     return RET_OK;
2696 }
2697 
2698 static conn_ret
2699 imap_sendClose(connection_t *cxn)
2700 {
2701     return imap_sendSimple(cxn, "CLOSE", IMAP_WRITING_CLOSE);
2702 }
2703 
2704 static conn_ret
2705 imap_sendQuit(connection_t *cxn)
2706 {
2707     return imap_sendSimple(cxn, "LOGOUT", IMAP_WRITING_QUIT);
2708 }
2709 
2710 static conn_ret
2711 imap_noop(connection_t *cxn)
2712 {
2713     return imap_sendSimple(cxn, "NOOP", IMAP_WRITING_NOOP);
2714 }
2715 
2716 
2717 static conn_ret
2718 imap_sendCapability(connection_t *cxn)
2719 {
2720     return imap_sendSimple(cxn, "CAPABILITY", IMAP_WRITING_CAPABILITY);
2721 }
2722 
2723 /************************** END IMAP sending functions
2724  * ************************/
2725 
2726 /************************** IMAP reading functions ***************************/
2727 
2728 static conn_ret
2729 imap_listenintro(connection_t *cxn)
2730 {
2731     Buffer *readBuffers;
2732 
2733     /* set up to receive */
2734     readBuffers = makeBufferArray(bufferTakeRef(cxn->imap_rBuffer), NULL);
2735     prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
2736 
2737     cxn->imap_state = IMAP_READING_INTRO;
2738 
2739     return RET_OK;
2740 }
2741 
2742 static conn_ret
2743 imap_ParseCapability(char *string, imap_capabilities_t **caps)
2744 {
2745     char *str = string;
2746     char *start = str;
2747     size_t mechlen;
2748 
2749     /* allocate the caps structure if it doesn't already exist */
2750     if ((*caps) == NULL)
2751         (*caps) = xcalloc(1, sizeof(imap_capabilities_t));
2752 
2753     while ((*str) != '\0') {
2754 
2755         while (((*str) != '\0') && ((*str) != ' ')) {
2756             str++;
2757         }
2758 
2759         if ((*str) != '\0') {
2760             *str = '\0';
2761             str++;
2762         }
2763 
2764         if (strcasecmp(start, "IMAP4") == 0) {
2765             (*caps)->imap4 = 1;
2766         } else if (strcasecmp(start, "LOGINDISABLED") == 0) {
2767             (*caps)->logindisabled = 1;
2768         } else if (strncasecmp(start, "AUTH=", 5) == 0) {
2769             if ((*caps)->saslmechs == NULL) {
2770                 (*caps)->saslmechs = xstrdup(start + 5);
2771             } else {
2772                 mechlen = strlen((*caps)->saslmechs) + 1;
2773                 mechlen += strlen(start + 5) + 1;
2774                 (*caps)->saslmechs = xrealloc((*caps)->saslmechs, mechlen);
2775                 strlcat((*caps)->saslmechs, " ", mechlen);
2776                 strlcat((*caps)->saslmechs, start + 5, mechlen);
2777             }
2778         }
2779 
2780         start = str;
2781     }
2782 
2783     if ((*caps)->saslmechs) {
2784         d_printf(1, "?:?:IMAP parsed capabilities: saslmechs = %s\n",
2785                  (*caps)->saslmechs);
2786     }
2787 
2788     return RET_OK;
2789 }
2790 
2791 
2792 static void
2793 imap_readCB(EndPoint e, IoStatus i, Buffer *b, void *d)
2794 {
2795     connection_t *cxn = (connection_t *) d;
2796     Buffer *readBuffers;
2797 
2798     int okno;
2799     char *str;
2800     char strbuf[4096];
2801     conn_ret ret;
2802     char *p;
2803 
2804     p = bufferBase(b[0]);
2805 
2806     /* Add what we got to our internal read buffer */
2807     bufferAddNullByte(b[0]);
2808 
2809     if (i != IoDone) {
2810         errno = endPointErrno(e);
2811 
2812         syslog(LOG_ERR, "%s:%d IMAP i/o failed: %m", hostPeerName(cxn->myHost),
2813                cxn->ident);
2814         freeBufferArray(b);
2815         imap_Disconnect(cxn);
2816         return;
2817     }
2818 
2819     if (strchr(p, '\n') == NULL) {
2820         /* partial read. expand buffer and retry */
2821 
2822         if (expandBuffer(b[0], BUFFER_EXPAND_AMOUNT) == false) {
2823             d_printf(0, "%s:%d:IMAP expanding buffer returned false\n",
2824                      hostPeerName(cxn->myHost), cxn->ident);
2825 
2826             imap_Disconnect(cxn);
2827             return;
2828         }
2829         readBuffers = makeBufferArray(bufferTakeRef(b[0]), NULL);
2830 
2831         if (!prepareRead(e, readBuffers, imap_readCB, cxn, 1)) {
2832             imap_Disconnect(cxn);
2833         }
2834 
2835         freeBufferArray(b);
2836         return;
2837     }
2838 
2839     clearTimer(cxn->imap_readBlockedTimerId);
2840 
2841     /* we got something. add to our buffer and free b */
2842 
2843     strcat(cxn->imap_respBuffer, p);
2844 
2845     bufferSetDataSize(b[0], 0);
2846 
2847     freeBufferArray(b);
2848 
2849 
2850     /* goto here to take another step */
2851 reset:
2852 
2853     /* see if we have a full line */
2854     ret = GetLine(cxn->imap_respBuffer, strbuf, sizeof(strbuf));
2855     str = strbuf;
2856 
2857     /* if we don't have a full line */
2858     if (ret == RET_NO_FULLLINE) {
2859 
2860         readBuffers = makeBufferArray(bufferTakeRef(cxn->imap_rBuffer), NULL);
2861 
2862         if (!prepareRead(e, readBuffers, imap_readCB, cxn, 1)) {
2863             imap_Disconnect(cxn);
2864         }
2865         return;
2866 
2867     } else if (ret != RET_OK) {
2868         return;
2869     }
2870 
2871     /* if untagged */
2872     if ((str[0] == '*') && (str[1] == ' ')) {
2873         str += 2;
2874 
2875         /* now figure out what kind of untagged it is */
2876         if (strncasecmp(str, "CAPABILITY ", 11) == 0) {
2877             str += 11;
2878 
2879             imap_ParseCapability(str, &(cxn->imap_capabilities));
2880 
2881         } else if (strncasecmp(str, "SEARCH", 6) == 0) {
2882 
2883             str += 6;
2884 
2885             if ((*str) == ' ') {
2886                 str++;
2887 
2888                 cxn->current_control->data.control->uid = atoi(str);
2889 
2890                 d_printf(1, "%s:%d:IMAP i think the UID = %ld\n",
2891                          hostPeerName(cxn->myHost), cxn->ident,
2892                          cxn->current_control->data.control->uid);
2893             } else {
2894                 /* it's probably a blank uid (i.e. message doesn't exist) */
2895                 cxn->current_control->data.control->uid = (unsigned long) -1;
2896             }
2897 
2898         } else if (strncasecmp(str, "OK ", 3) == 0) {
2899 
2900             if (cxn->imap_state == IMAP_READING_INTRO) {
2901                 imap_sendCapability(cxn); /* xxx errors */
2902                 return;
2903 
2904             } else {
2905             }
2906 
2907         } else {
2908             /* untagged command not understood */
2909         }
2910 
2911         /* always might be more to look at */
2912         goto reset;
2913 
2914     } else if ((str[0] == '+') && (str[1] == ' ')) {
2915 
2916         str += 2;
2917 
2918         if (cxn->imap_state == IMAP_READING_STEPAUTH) {
2919 #ifdef HAVE_SASL
2920             if (imap_sendAuthStep(cxn, str) != RET_OK) {
2921                 imap_Disconnect(cxn);
2922             }
2923 #else
2924             d_printf(
2925                 0,
2926                 "%s:%d:IMAP got a '+ ...' without SASL. Something's wrong\n",
2927                 hostPeerName(cxn->myHost), cxn->ident);
2928             imap_Disconnect(cxn);
2929 #endif /* HAVE_SASL */
2930 
2931             return;
2932         } else {
2933             d_printf(0,
2934                      "%s:%d:IMAP got a '+ ...' in state where not expected\n",
2935                      hostPeerName(cxn->myHost), cxn->ident);
2936             imap_Disconnect(cxn);
2937             return;
2938         }
2939 
2940     } else if (strncasecmp(str, cxn->imap_currentTag, IMAP_TAGLENGTH) == 0) {
2941         /* matches our tag */
2942         str += IMAP_TAGLENGTH;
2943 
2944         if (str[0] != ' ') {
2945             d_printf(0,
2946                      "%s:%d:IMAP Parse error: tag with no space afterward\n",
2947                      hostPeerName(cxn->myHost), cxn->ident);
2948             imap_Disconnect(cxn);
2949             return;
2950         }
2951         str++;
2952 
2953         /* should be OK/NO */
2954         if (strncasecmp(str, "OK", 2) == 0) {
2955             okno = 1;
2956         } else {
2957             okno = 0;
2958         }
2959 
2960         switch (cxn->imap_state) {
2961         case IMAP_READING_CAPABILITY:
2962 
2963             if (okno == 1) {
2964                 if (imap_sendAuthenticate(cxn) != RET_OK) {
2965                     d_printf(0, "%s:%d:IMAP sendauthenticate failed\n",
2966                              hostPeerName(cxn->myHost), cxn->ident);
2967                     imap_Disconnect(cxn);
2968                 }
2969                 return;
2970             } else {
2971                 d_printf(0, "%s:%d:IMAP CAPABILITY gave a NO response\n",
2972                          hostPeerName(cxn->myHost), cxn->ident);
2973                 imap_Disconnect(cxn);
2974             }
2975             return;
2976             /* NOTREACHED */
2977 
2978         case IMAP_READING_STEPAUTH:
2979 
2980             if (okno == 1) {
2981 
2982                 cxn->imap_sleepTimeout = init_reconnect_period;
2983 
2984                 cxn->imap_timeCon = theTime();
2985                 cxn->timeCon = theTime();
2986 
2987                 d_printf(0, "%s:%d IMAP authentication succeeded\n",
2988                          hostPeerName(cxn->myHost), cxn->ident);
2989 
2990                 cxn->imap_disconnects = 0;
2991 
2992                 cxn->imap_state = IMAP_IDLE_AUTHED;
2993 
2994                 /* try to send a message if we have one */
2995                 imap_ProcessQueue(cxn);
2996             } else {
2997                 d_printf(0, "%s:%d:IMAP Authentication failed with [%s]\n",
2998                          hostPeerName(cxn->myHost), cxn->ident, str);
2999                 imap_Disconnect(cxn);
3000             }
3001 
3002             return;
3003             /* NOTREACHED */
3004 
3005         case IMAP_READING_CREATE:
3006 
3007             if (okno == 1) {
3008 
3009                 d_printf(1, "%s:%d:IMAP Create of bboard successful\n",
3010                          hostPeerName(cxn->myHost), cxn->ident);
3011                 cxn->create_succeeded++;
3012 
3013                 /* we can delete article now */
3014                 QueueForgetAbout(cxn, cxn->current_control, MSG_SUCCESS);
3015             } else {
3016                 d_printf(1, "%s:%d:IMAP Create failed with [%s] for %s\n",
3017                          hostPeerName(cxn->myHost), cxn->ident, str,
3018                          cxn->current_control->data.control->folder);
3019 
3020                 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3021             }
3022 
3023             imap_ProcessQueue(cxn);
3024 
3025             break;
3026             /* NOTREACHED */
3027 
3028         case IMAP_READING_DELETE:
3029 
3030             if (okno == 1) {
3031                 d_printf(1, "%s:%d:IMAP Delete successful\n",
3032                          hostPeerName(cxn->myHost), cxn->ident);
3033                 cxn->remove_succeeded++;
3034 
3035                 /* we can delete article now */
3036                 QueueForgetAbout(cxn, cxn->current_control, MSG_SUCCESS);
3037             } else {
3038                 d_printf(1,
3039                          "%s:%d:IMAP Delete mailbox failed with [%s] for %s\n",
3040                          hostPeerName(cxn->myHost), cxn->ident, str,
3041                          cxn->current_control->data.control->folder);
3042 
3043                 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3044             }
3045 
3046             imap_ProcessQueue(cxn);
3047             return;
3048             /* NOTREACHED */
3049 
3050         case IMAP_READING_SELECT:
3051 
3052             if (okno == 1) {
3053 
3054                 imap_sendSearch(cxn,
3055                                 cxn->current_control->data.control->msgid);
3056                 return;
3057 
3058             } else {
3059                 d_printf(1, "%s:%d:IMAP Select failed with [%s] for %s\n",
3060                          hostPeerName(cxn->myHost), cxn->ident, str,
3061                          cxn->current_control->data.control->folder);
3062 
3063                 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3064 
3065                 cxn->imap_state = IMAP_IDLE_AUTHED;
3066 
3067                 imap_ProcessQueue(cxn);
3068                 return;
3069             }
3070             /* NOTREACHED */
3071 
3072         case IMAP_READING_SEARCH:
3073             /* if no message let's forget about it */
3074             if (cxn->current_control->data.control->uid
3075                 == (unsigned long) -1) {
3076                 d_printf(2, "%s:%d:IMAP Search didn't find the message\n",
3077                          hostPeerName(cxn->myHost), cxn->ident);
3078                 QueueForgetAbout(cxn, cxn->current_control, MSG_FAIL_DELIVER);
3079                 if (imap_sendClose(cxn) != RET_OK)
3080                     imap_Disconnect(cxn);
3081                 return;
3082             }
3083 
3084             if (okno == 1) {
3085                 /* we got a uid. let's delete it */
3086                 if (imap_sendKill(cxn, cxn->current_control->data.control->uid)
3087                     != RET_OK)
3088                     imap_Disconnect(cxn);
3089                 return;
3090             } else {
3091                 d_printf(0, "%s:%d IMAP Received NO response to SEARCH\n",
3092                          hostPeerName(cxn->myHost), cxn->ident);
3093                 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3094 
3095                 if (imap_sendClose(cxn) != RET_OK)
3096                     imap_Disconnect(cxn);
3097                 return;
3098             }
3099             /* NOTREACHED */
3100 
3101         case IMAP_READING_STORE:
3102 
3103             if (okno == 1) {
3104 
3105                 d_printf(1, "%s:%d:IMAP Processed a Cancel fully\n",
3106                          hostPeerName(cxn->myHost), cxn->ident);
3107 
3108                 /* we can delete article now */
3109                 QueueForgetAbout(cxn, cxn->current_control, MSG_SUCCESS);
3110 
3111                 cxn->cancel_succeeded++;
3112 
3113                 if (imap_sendClose(cxn) != RET_OK)
3114                     imap_Disconnect(cxn);
3115                 return;
3116 
3117             } else {
3118 
3119                 d_printf(1, "%s:%d:IMAP Store failed\n",
3120                          hostPeerName(cxn->myHost), cxn->ident);
3121                 ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3122 
3123                 if (imap_sendClose(cxn) != RET_OK)
3124                     imap_Disconnect(cxn);
3125                 return;
3126             }
3127             /* NOTREACHED */
3128 
3129         case IMAP_READING_NOOP:
3130             cxn->imap_state = IMAP_IDLE_AUTHED;
3131             return;
3132             /* NOTREACHED */
3133 
3134         case IMAP_READING_CLOSE:
3135             if (!okno) {
3136                 /* we can't do anything about it */
3137                 d_printf(1, "%s:%d:IMAP Close failed\n",
3138                          hostPeerName(cxn->myHost), cxn->ident);
3139             }
3140 
3141             cxn->imap_state = IMAP_IDLE_AUTHED;
3142 
3143             imap_ProcessQueue(cxn);
3144             return;
3145             /* NOTREACHED */
3146 
3147         case IMAP_READING_QUIT:
3148 
3149             /* we don't care if the server said OK or NO just
3150                that it said something */
3151 
3152             d_printf(1, "%s:%d:IMAP Read quit response\n",
3153                      hostPeerName(cxn->myHost), cxn->ident);
3154 
3155             cxn->imap_state = IMAP_DISCONNECTED;
3156 
3157             DeleteIfDisconnected(cxn);
3158             break;
3159 
3160         default:
3161             d_printf(0, "%s:%d:IMAP I don't understand state %d [%s]\n",
3162                      hostPeerName(cxn->myHost), cxn->ident, cxn->imap_state,
3163                      str);
3164             imap_Disconnect(cxn);
3165         }
3166 
3167     } else {
3168         d_printf(0,
3169                  "%s:%d:IMAP tag (%s) doesn't match what we gave (%s). What's "
3170                  "up with that??\n",
3171                  hostPeerName(cxn->myHost), cxn->ident, str,
3172                  cxn->imap_currentTag);
3173         imap_Disconnect(cxn);
3174     }
3175 }
3176 
3177 /************************** END IMAP reading functions
3178  * ***************************/
3179 
3180 /*************************** LMTP reading functions
3181  * ****************************/
3182 
3183 static void
3184 lmtp_readCB(EndPoint e, IoStatus i, Buffer *b, void *d)
3185 {
3186     connection_t *cxn = (connection_t *) d;
3187     char str[4096];
3188     Buffer *readBuffers;
3189     conn_ret result;
3190     int response_code;
3191     conn_ret ret;
3192 #ifdef HAVE_SASL
3193     int inlen;
3194     char *in;
3195     size_t outlen;
3196     const char *out;
3197     char *inbase64;
3198     int inbase64len;
3199     imt_stat status;
3200     int saslresult;
3201     sasl_interact_t *client_interact = NULL;
3202 #endif /* HAVE_SASL */
3203 
3204     char *p = bufferBase(b[0]);
3205 
3206     bufferAddNullByte(b[0]);
3207 
3208     if (i != IoDone) {
3209         errno = endPointErrno(e);
3210         syslog(LOG_ERR, "%s:%d LMTP i/o failed: %m", hostPeerName(cxn->myHost),
3211                cxn->ident);
3212 
3213         freeBufferArray(b);
3214         lmtp_Disconnect(cxn);
3215         return;
3216     }
3217 
3218     if (strchr(p, '\n') == NULL) {
3219         /* partial read. expand buffer and retry */
3220 
3221         d_printf(0, "%s:%d:LMTP Partial. retry\n", hostPeerName(cxn->myHost),
3222                  cxn->ident);
3223         expandBuffer(b[0], BUFFER_EXPAND_AMOUNT);
3224         readBuffers = makeBufferArray(bufferTakeRef(b[0]), NULL);
3225 
3226         if (!prepareRead(e, readBuffers, lmtp_readCB, cxn, 1)) {
3227             lmtp_Disconnect(cxn);
3228         }
3229 
3230         freeBufferArray(b);
3231         return;
3232     }
3233 
3234     clearTimer(cxn->lmtp_readBlockedTimerId);
3235 
3236     /* Add what we got to our internal read buffer */
3237     strcat(cxn->lmtp_respBuffer, p);
3238 
3239     bufferSetDataSize(b[0], 0);
3240 
3241     freeBufferArray(b);
3242 
3243 reset:
3244     /* see if we have a full line */
3245     ret = GetLine(cxn->lmtp_respBuffer, str, sizeof(str));
3246 
3247     /* get a line */
3248     if (ret != RET_OK) {
3249         if (ret != RET_NO_FULLLINE) {
3250             /* was a more serious error */
3251             d_printf(0, "%s:%d:LMTP Internal error getting line from server\n",
3252                      hostPeerName(cxn->myHost), cxn->ident);
3253             lmtp_Disconnect(cxn);
3254             return;
3255         }
3256 
3257         /* set up to receive some more */
3258         readBuffers = makeBufferArray(bufferTakeRef(cxn->lmtp_rBuffer), NULL);
3259         prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
3260         return;
3261     }
3262 
3263     switch (cxn->lmtp_state) {
3264 
3265     case LMTP_READING_INTRO:
3266 
3267         if (ask_code(str) != 220) {
3268             d_printf(0,
3269                      "%s:%d:LMTP Initial server msg does not start with 220 "
3270                      "(began with %d)\n",
3271                      hostPeerName(cxn->myHost), cxn->ident, ask_code(str));
3272             lmtp_Disconnect(cxn);
3273             return;
3274         }
3275 
3276         /* the initial intro could have many lines via
3277            continuations. see if we need to read more */
3278         if (ask_keepgoing(str) == 1) {
3279             goto reset;
3280         }
3281 
3282         result = lmtp_getcapabilities(cxn);
3283 
3284         if (result != RET_OK) {
3285             d_printf(0, "%s:%d:LMTP lmtp_getcapabilities() failure\n",
3286                      hostPeerName(cxn->myHost), cxn->ident);
3287             lmtp_Disconnect(cxn);
3288             return;
3289         }
3290 
3291         break;
3292 
3293     case LMTP_READING_LHLO:
3294         /* receive the response(s) */
3295         response_code = ask_code(str);
3296 
3297         if (response_code != 250) /* was none */
3298         {
3299             d_printf(0, "%s:%d:LMTP Response code unexpected (%d)\n",
3300                      hostPeerName(cxn->myHost), cxn->ident, response_code);
3301             lmtp_Disconnect(cxn);
3302             return;
3303         }
3304 
3305         /* look for one we know about; ignore all others */
3306         if (strncasecmp(str + 4, "8BITMIME", strlen("8BITMIME")) == 0) {
3307             cxn->lmtp_capabilities->Eightbitmime = 1;
3308         } else if (strncasecmp(str + 4, "ENHANCEDSTATUSCODES",
3309                                strlen("ENHANCEDSTATUSCODES"))
3310                    == 0) {
3311             cxn->lmtp_capabilities->EnhancedStatusCodes = 1;
3312         } else if (strncasecmp(str + 4, "AUTH", 4) == 0) {
3313             cxn->lmtp_capabilities->saslmechs = xstrdup(str + 4 + 5);
3314         } else if (strncasecmp(str + 4, "PIPELINING", strlen("PIPELINING"))
3315                    == 0) {
3316             cxn->lmtp_capabilities->pipelining = 1;
3317         } else {
3318             /* don't care; ignore */
3319         }
3320 
3321         /* see if this is the last line of the capability */
3322         if (ask_keepgoing(str) == 1) {
3323             goto reset;
3324         } else {
3325             /* we require a few capabilities */
3326             if (!cxn->lmtp_capabilities->pipelining) {
3327                 d_printf(0, "%s:%d:LMTP We require PIPELINING\n",
3328                          hostPeerName(cxn->myHost), cxn->ident);
3329 
3330                 lmtp_Disconnect(cxn);
3331                 return;
3332             }
3333 #ifdef HAVE_SASL
3334             if (cxn->lmtp_capabilities->saslmechs) {
3335                 /* start the authentication */
3336                 result = lmtp_authenticate(cxn);
3337 
3338                 if (result != RET_OK) {
3339                     d_printf(0, "%s:%d:LMTP lmtp_authenticate() error\n",
3340                              hostPeerName(cxn->myHost), cxn->ident);
3341                     lmtp_Disconnect(cxn);
3342                     return;
3343                 }
3344 #else
3345             if (0) {
3346                 /* noop */
3347 #endif
3348             } else {
3349                 /* either we can't authenticate or the remote server
3350                    doesn't support it */
3351                 cxn->lmtp_state = LMTP_AUTHED_IDLE;
3352                 d_printf(1,
3353                          "%s:%d:LMTP Even though we can't authenticate"
3354                          " we're going to try to feed anyway\n",
3355                          hostPeerName(cxn->myHost), cxn->ident);
3356                 /* We just assume we don't need to authenticate
3357                    (great assumption huh?) */
3358                 hostRemoteStreams(cxn->myHost, cxn, true);
3359 
3360                 cxn->lmtp_timeCon = theTime();
3361                 cxn->timeCon = theTime();
3362 
3363                 /* try to send a message if we have one */
3364                 lmtp_sendmessage(cxn, NULL);
3365                 return;
3366             }
3367         }
3368         break;
3369 
3370 #ifdef HAVE_SASL
3371     case LMTP_READING_STEPAUTH:
3372         inlen = 0;
3373         status = lmtp_getauthline(str, &in, &inlen);
3374 
3375         switch (status) {
3376 
3377         case STAT_CONT:
3378 
3379             saslresult =
3380                 sasl_client_step(cxn->saslconn_lmtp, in, inlen,
3381                                  &client_interact, &out, (unsigned *) &outlen);
3382 
3383             free(in);
3384 
3385             /* check if sasl succeeded */
3386             if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
3387                 d_printf(0, "%s:%d:LMTP sasl_client_step(): %s\n",
3388                          hostPeerName(cxn->myHost), cxn->ident,
3389                          sasl_errstring(saslresult, NULL, NULL));
3390 
3391                 lmtp_Disconnect(cxn);
3392                 return;
3393             }
3394 
3395             /* Convert to base64.
3396              * 2n+7 bytes are enough to contain the result of the base64
3397              * encoding of a string whose length is n bytes.
3398              * In sasl_encode64() calls, the fourth argument is the length
3399              * of the third including the null terminator (thus 2n+8 bytes).
3400              * And CRLF takes the last two bytes (thus 2n+10 bytes). */
3401             inbase64 = xmalloc(outlen * 2 + 10);
3402 
3403             saslresult = sasl_encode64(out, outlen, inbase64, outlen * 2 + 8,
3404                                        (unsigned *) &inbase64len);
3405 
3406             if (saslresult != SASL_OK) {
3407                 d_printf(0, "%s:%d:LMTP sasl_encode64(): %s\n",
3408                          hostPeerName(cxn->myHost), cxn->ident,
3409                          sasl_errstring(saslresult, NULL, NULL));
3410 
3411                 lmtp_Disconnect(cxn);
3412                 return;
3413             }
3414 
3415             /* Add an endline. */
3416             strlcpy(inbase64 + inbase64len, "\r\n",
3417                     outlen * 2 + 10 - inbase64len);
3418             inbase64len += 2;
3419 
3420             /* Send to server. */
3421             result = WriteToWire_lmtpstr(cxn, inbase64, inbase64len);
3422             if (result != RET_OK) {
3423                 d_printf(0, "%s:%d:LMTP WriteToWire() failure\n",
3424                          hostPeerName(cxn->myHost), cxn->ident);
3425                 lmtp_Disconnect(cxn);
3426                 return;
3427             }
3428 
3429             cxn->lmtp_state = LMTP_WRITING_STEPAUTH;
3430             break;
3431 
3432         case STAT_OK:
3433             cxn->lmtp_sleepTimeout = init_reconnect_period;
3434 
3435             d_printf(0, "%s:%d LMTP authentication succeeded\n",
3436                      hostPeerName(cxn->myHost), cxn->ident);
3437 
3438             cxn->lmtp_disconnects = 0;
3439 
3440             hostRemoteStreams(cxn->myHost, cxn, true);
3441 
3442             cxn->lmtp_timeCon = theTime();
3443             cxn->timeCon = theTime();
3444 
3445             cxn->lmtp_state = LMTP_AUTHED_IDLE;
3446 
3447 
3448             /* try to send a message if we have one */
3449             lmtp_sendmessage(cxn, NULL);
3450             return;
3451 
3452         default:
3453             d_printf(0, "%s:%d:LMTP failed authentication\n",
3454                      hostPeerName(cxn->myHost), cxn->ident);
3455             lmtp_Disconnect(cxn);
3456             return;
3457         }
3458         break;
3459 #endif /* HAVE_SASL */
3460 
3461     case LMTP_READING_RSET:
3462         if (ask_keepgoing(str)) {
3463             goto reset;
3464         }
3465         if (ask_code(str) != 250) {
3466             d_printf(0, "%s:%d:LMTP RSET failed with (%d)\n",
3467                      hostPeerName(cxn->myHost), cxn->ident, ask_code(str));
3468             lmtp_Disconnect(cxn);
3469             return;
3470         }
3471 
3472         /* we pipelined so next we receive the mail from response */
3473         cxn->lmtp_state = LMTP_READING_MAILFROM;
3474         goto reset;
3475 
3476     case LMTP_READING_MAILFROM:
3477         if (ask_keepgoing(str)) {
3478             goto reset;
3479         }
3480         if (ask_code(str) != 250) {
3481             d_printf(0, "%s:%d:LMTP MAILFROM failed with (%d)\n",
3482                      hostPeerName(cxn->myHost), cxn->ident, ask_code(str));
3483             lmtp_Disconnect(cxn);
3484             return;
3485         }
3486 
3487         /* we pipelined so next we receive the rcpt's */
3488         cxn->lmtp_state = LMTP_READING_RCPTTO;
3489         goto reset;
3490         /* NOTREACHED */
3491 
3492     case LMTP_READING_RCPTTO:
3493         if (ask_keepgoing(str)) {
3494             goto reset;
3495         }
3496         if (ask_code(str) != 250) {
3497             d_printf(1, "%s:%d:LMTP RCPT TO failed with (%d) %s\n",
3498                      hostPeerName(cxn->myHost), cxn->ident, ask_code(str),
3499                      str);
3500 
3501             /* if got a 5xx don't try to send anymore */
3502             cxn->current_article->trys = 100;
3503 
3504             cxn->current_rcpts_issued--;
3505         } else {
3506             cxn->current_rcpts_okayed++;
3507         }
3508 
3509         /* if issued equals number okayed then we're done */
3510         if (cxn->current_rcpts_okayed == cxn->current_rcpts_issued) {
3511             cxn->lmtp_state = LMTP_READING_DATA;
3512         } else {
3513             /* stay in same state */
3514         }
3515         goto reset;
3516         /* NOTREACHED */
3517 
3518     case LMTP_READING_DATA:
3519         if (ask_keepgoing(str)) {
3520             goto reset;
3521         }
3522         if (cxn->current_rcpts_issued == 0) {
3523             if (cxn->current_article->trys < 100) {
3524                 d_printf(1,
3525                          "%s:%d:LMTP None of the rcpts "
3526                          "were accepted for this message. Re-queueing\n",
3527                          hostPeerName(cxn->myHost), cxn->ident);
3528             }
3529 
3530             ReQueue(cxn, &(cxn->lmtp_todeliver_q), cxn->current_article);
3531 
3532             cxn->lmtp_state = LMTP_AUTHED_IDLE;
3533             lmtp_sendmessage(cxn, NULL);
3534         } else {
3535             if (WriteArticle(cxn, cxn->current_bufs) != RET_OK) {
3536                 d_printf(0, "%s:%d:LMTP Error writing article\n",
3537                          hostPeerName(cxn->myHost), cxn->ident);
3538                 lmtp_Disconnect(cxn);
3539                 return;
3540             }
3541 
3542             cxn->lmtp_state = LMTP_WRITING_CONTENTS;
3543         }
3544 
3545         break;
3546 
3547     case LMTP_READING_CONTENTS:
3548         if (ask_keepgoing(str)) {
3549             goto reset;
3550         }
3551 
3552         /* need 1 response from server for every rcpt */
3553         cxn->current_rcpts_issued--;
3554 
3555         if (ask_code(str) != 250) {
3556             d_printf(1, "%s:%d:LMTP DATA failed with %d (%s)\n",
3557                      hostPeerName(cxn->myHost), cxn->ident, ask_code(str),
3558                      str);
3559             cxn->current_rcpts_okayed--;
3560         }
3561 
3562         if (cxn->current_rcpts_issued > 0) {
3563             goto reset;
3564         }
3565 
3566         /*
3567          * current_rcpts_okayed is number that succeeded
3568          *
3569          */
3570         if (cxn->current_rcpts_okayed == 0) {
3571             cxn->lmtp_state = LMTP_AUTHED_IDLE;
3572         } else {
3573             cxn->lmtp_state = LMTP_AUTHED_IDLE;
3574             cxn->lmtp_succeeded++;
3575             d_printf(1, "%s:%d:LMTP Woohoo! message accepted\n",
3576                      hostPeerName(cxn->myHost), cxn->ident);
3577         }
3578 
3579         /* we can delete article now */
3580         QueueForgetAbout(cxn, cxn->current_article, MSG_SUCCESS);
3581 
3582         /* try to send another if we have one and we're still idle
3583          * forgetting the msg might have made us unidle
3584          */
3585         if (cxn->lmtp_state == LMTP_AUTHED_IDLE) {
3586             lmtp_sendmessage(cxn, NULL);
3587         }
3588 
3589         break;
3590 
3591     case LMTP_READING_NOOP:
3592         if (ask_keepgoing(str)) {
3593             goto reset;
3594         }
3595         cxn->lmtp_state = LMTP_AUTHED_IDLE;
3596         break;
3597 
3598     case LMTP_READING_QUIT:
3599         d_printf(1, "%s:%d:LMTP read quit\n", hostPeerName(cxn->myHost),
3600                  cxn->ident);
3601 
3602         cxn->lmtp_state = LMTP_DISCONNECTED;
3603 
3604         DeleteIfDisconnected(cxn);
3605         break;
3606 
3607     default:
3608 
3609         d_printf(0, "%s:%d:LMTP Bad state in lmtp_readCB %d\n",
3610                  hostPeerName(cxn->myHost), cxn->ident, cxn->lmtp_state);
3611         lmtp_Disconnect(cxn);
3612         return;
3613     }
3614 }
3615 
3616 /*
3617  * Add a rcpt to:<foo> to the string
3618  *
3619  */
3620 
3621 static void
3622 addrcpt(char *newrcpt, int newrcptlen, char **out, int *outalloc)
3623 {
3624     int size = strlen(*out);
3625     int fsize = size;
3626     int newsize = size + 9 + strlen(deliver_rcpt_to) + newrcptlen + 3;
3627     int rc;
3628     char c;
3629 
3630     /* see if we need to grow the string */
3631     if (newsize > *outalloc) {
3632         (*outalloc) = newsize;
3633         (*out) = xrealloc(*out, *outalloc);
3634     }
3635 
3636     strlcpy((*out) + size, "RCPT TO:<", newsize - size);
3637     size += 9;
3638 
3639     c = newrcpt[newrcptlen];
3640     newrcpt[newrcptlen] = '\0';
3641 #if __GNUC__ > 4 || defined(__llvm__) || defined(__clang__)
3642 #    pragma GCC diagnostic ignored "-Wformat-nonliteral"
3643 #endif
3644     rc = snprintf((*out) + size, newsize - size, deliver_rcpt_to, newrcpt);
3645 #if __GNUC__ > 4 || defined(__llvm__) || defined(__clang__)
3646 #    pragma GCC diagnostic warning "-Wformat-nonliteral"
3647 #endif
3648     if (rc < 0) {
3649         /* Do nothing. */
3650     } else if (rc >= newsize - size) {
3651         size = newsize;
3652     } else {
3653         size += rc;
3654     }
3655     newrcpt[newrcptlen] = c;
3656 
3657     strlcpy((*out) + size, ">\r\n", newsize - size);
3658 
3659     /* has embedded '\n' */
3660     d_printf(2, "Attempting to send to: %s", (*out) + fsize);
3661 }
3662 
3663 /*
3664  * Takes the Newsgroups header field body and makes it into a list of
3665  * RCPT TO:'s we can send over the wire
3666  *
3667  *  in     - newsgroups header start
3668  *  in_end - end of newsgroups header
3669  *  num    - number of rcpt's we created
3670  */
3671 
3672 static char *
3673 ConvertRcptList(char *in, char *in_end, int *num)
3674 {
3675     int retalloc = 400;
3676     char *ret = xmalloc(retalloc);
3677     char *str = in;
3678     char *laststart = in;
3679 
3680     (*num) = 0;
3681 
3682     /* start it off empty */
3683     strlcpy(ret, "", retalloc);
3684 
3685     while (str != in_end) {
3686         if ((*str) == ',') {
3687             /* eliminate leading whitespace */
3688             while (((*laststart) == ' ') || ((*laststart) == '\t')) {
3689                 laststart++;
3690             }
3691 
3692 #ifndef SMTPMODE
3693             addrcpt(laststart, str - laststart, &ret, &retalloc);
3694             (*num)++;
3695 #endif /* SMTPMODE */
3696             laststart = str + 1;
3697         }
3698 
3699         str++;
3700     }
3701 
3702     if (laststart < str) {
3703         addrcpt(laststart, str - laststart, &ret, &retalloc);
3704         (*num)++;
3705     }
3706 
3707     return ret;
3708 }
3709 
3710 static void
3711 addto(char *newrcpt, int newrcptlen, const char *sep, char **out,
3712       int *outalloc)
3713 {
3714     int size = strlen(*out);
3715     int newsize =
3716         size + strlen(sep) + 1 + strlen(deliver_to_header) + newrcptlen + 1;
3717     int rc;
3718     char c;
3719 
3720     /* see if we need to grow the string */
3721     if (newsize > *outalloc) {
3722         (*outalloc) = newsize;
3723         (*out) = xrealloc(*out, *outalloc);
3724     }
3725 
3726     rc = snprintf((*out) + size, newsize - size, "%s<", sep);
3727     if (rc < 0) {
3728         /* Do nothing. */
3729     } else if (rc >= newsize - size) {
3730         size = newsize;
3731     } else {
3732         size += rc;
3733     }
3734 
3735     c = newrcpt[newrcptlen];
3736     newrcpt[newrcptlen] = '\0';
3737 #if __GNUC__ > 4 || defined(__llvm__) || defined(__clang__)
3738 #    pragma GCC diagnostic ignored "-Wformat-nonliteral"
3739 #endif
3740     rc = snprintf((*out) + size, newsize - size, deliver_to_header, newrcpt);
3741 #if __GNUC__ > 4 || defined(__llvm__) || defined(__clang__)
3742 #    pragma GCC diagnostic warning "-Wformat-nonliteral"
3743 #endif
3744     if (rc < 0) {
3745         /* Do nothing. */
3746     } else if (rc >= newsize - size) {
3747         size = newsize;
3748     } else {
3749         size += rc;
3750     }
3751     newrcpt[newrcptlen] = c;
3752 
3753     strlcpy((*out) + size, ">", newsize - size);
3754 }
3755 
3756 /*
3757  * Takes the Newsgroups header field body and makes it into a To header field
3758  * body.
3759  *
3760  *  in     - newsgroups header start
3761  *  in_end - end of newsgroups header
3762  */
3763 
3764 static char *
3765 BuildToHeader(char *in, char *in_end)
3766 {
3767     int retalloc = 400;
3768     char *ret = xmalloc(retalloc);
3769     char *str = in;
3770     char *laststart = in;
3771     const char *sep = "";
3772 
3773     /* start it off with the header field name */
3774     strlcpy(ret, "To: ", retalloc);
3775 
3776     while (str != in_end) {
3777         if ((*str) == ',') {
3778             /* eliminate leading whitespace */
3779             while (((*laststart) == ' ') || ((*laststart) == '\t')) {
3780                 laststart++;
3781             }
3782 
3783             addto(laststart, str - laststart, sep, &ret, &retalloc);
3784             laststart = str + 1;
3785 
3786             /* separate multiple addresses with a comma */
3787             sep = ", ";
3788         }
3789 
3790         str++;
3791     }
3792 
3793     if (laststart < str) {
3794         addto(laststart, str - laststart, sep, &ret, &retalloc);
3795     }
3796 
3797     /* terminate the header field body */
3798     strlcat(ret, "\n\r", retalloc);
3799     return ret;
3800 }
3801 
3802 /*************************** END LMTP reading functions
3803  * ****************************/
3804 
3805 
3806 /*
3807  * Process the control message queue. If we run out ask the host for more.
3808  *
3809  *  cxn       - connection object
3810  */
3811 
3812 static void
3813 imap_ProcessQueue(connection_t *cxn)
3814 {
3815     article_queue_t *item;
3816     conn_ret result;
3817 
3818 retry:
3819 
3820     /* pull an article off the queue */
3821     result = PopFromQueue(&(cxn->imap_controlMsg_q), &item);
3822 
3823     if (result == RET_QUEUE_EMPTY) {
3824         if (cxn->issue_quit) {
3825             imap_sendQuit(cxn);
3826             return;
3827         }
3828 
3829         cxn->imap_state = IMAP_IDLE_AUTHED;
3830 
3831         /* now we wait for articles from our Host, or we have some
3832            articles already. On infrequently used connections, the
3833            network link is torn down and rebuilt as needed. So we may
3834            be rebuilding the connection here in which case we have an
3835            article to send. */
3836 
3837         /* make sure imap has _lots_ of space too */
3838         if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0)
3839             && (QueueItems(&(cxn->imap_controlMsg_q)) == 0)) {
3840             if (hostGimmeArticle(cxn->myHost, cxn) == true)
3841                 goto retry;
3842         }
3843 
3844         return;
3845     }
3846 
3847     cxn->current_control = item;
3848 
3849     switch (item->type) {
3850     case CREATE_FOLDER:
3851         imap_CreateGroup(cxn, item->data.control->folder);
3852         break;
3853 
3854     case CANCEL_MSG:
3855         imap_CancelMsg(cxn, item->data.control->folder);
3856         break;
3857 
3858     case DELETE_FOLDER:
3859         imap_DeleteGroup(cxn, item->data.control->folder);
3860         break;
3861     default:
3862         break;
3863     }
3864 
3865     return;
3866 }
3867 
3868 
3869 /*
3870  *
3871  * Pulls a message off the queue and trys to start sending it. If the
3872  * message is a control message put it in the control queue and grab
3873  * another message. If the message doesn't exist on disk or something
3874  * is wrong with it tell the host and try again. If we run out of
3875  * messages to get tell the host we want more
3876  *
3877  * cxn       - connection object
3878  * justadded - the article that was just added to the queue
3879  */
3880 
3881 static void
3882 lmtp_sendmessage(connection_t *cxn, Article justadded)
3883 {
3884     bool res;
3885     conn_ret result;
3886     char *p;
3887     Buffer *bufs;
3888     char *control_header = NULL;
3889     char *control_header_end = NULL;
3890 
3891     article_queue_t *item;
3892     char *rcpt_list, *rcpt_list_end;
3893 
3894     /* retry point */
3895 retry:
3896 
3897     /* pull an article off the queue */
3898     result = PopFromQueue(&(cxn->lmtp_todeliver_q), &item);
3899 
3900     if (result == RET_QUEUE_EMPTY) {
3901         if (cxn->issue_quit) {
3902             lmtp_IssueQuit(cxn);
3903             return;
3904         }
3905         /* now we wait for articles from our Host, or we have some
3906            articles already. On infrequently used connections, the
3907            network link is torn down and rebuilt as needed. So we may
3908            be rebuilding the connection here in which case we have an
3909            article to send. */
3910 
3911         /* make sure imap has space too */
3912         d_printf(1, "%s:%d stalled waiting for articles\n",
3913                  hostPeerName(cxn->myHost), cxn->ident);
3914         if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0)
3915             && (QueueItems(&(cxn->imap_controlMsg_q)) == 0)) {
3916             if (hostGimmeArticle(cxn->myHost, cxn) == true)
3917                 goto retry;
3918         }
3919 
3920         return;
3921     }
3922 
3923     /* make sure contents ok; this also should load it into memory */
3924     res = artContentsOk(item->data.article);
3925     if (res == false) {
3926         if (justadded == item->data.article) {
3927             ReQueue(cxn, &(cxn->lmtp_todeliver_q), item);
3928             return;
3929         } else {
3930             /* tell to reject taking this message */
3931             QueueForgetAbout(cxn, item, MSG_MISSING);
3932         }
3933 
3934         goto retry;
3935     }
3936 
3937     /* Check if it's a control message */
3938     bufs = artGetNntpBuffers(item->data.article);
3939     if (bufs == NULL) {
3940         /* tell to reject taking this message */
3941         QueueForgetAbout(cxn, item, MSG_MISSING);
3942         goto retry;
3943     }
3944 
3945     result = FindHeader(bufs, "Control", &control_header, &control_header_end);
3946     if (result == RET_OK) {
3947         result = AddControlMsg(cxn, item->data.article, bufs, control_header,
3948                                control_header_end, 1);
3949         if (result != RET_OK) {
3950             d_printf(1, "%s:%d Error adding to [imap] control queue\n",
3951                      hostPeerName(cxn->myHost), cxn->ident);
3952             ReQueue(cxn, &(cxn->lmtp_todeliver_q), item);
3953             return;
3954         }
3955 
3956         switch (cxn->imap_state) {
3957         case IMAP_IDLE_AUTHED:
3958             /* we're idle. let's process the queue */
3959             imap_ProcessQueue(cxn);
3960             break;
3961         case IMAP_DISCONNECTED:
3962         case IMAP_WAITING:
3963             /* Let's connect. Once we're connected we can
3964                worry about the message */
3965             if (cxn->imap_sleepTimerId == 0) {
3966                 if (imap_Connect(cxn) != RET_OK)
3967                     prepareReopenCbk(cxn, 0);
3968             }
3969             break;
3970         default:
3971             /* we're doing something right now */
3972             break;
3973         }
3974 
3975         /* all we did was add a control message.
3976            we still want to get an lmtp message */
3977         goto retry;
3978     }
3979 
3980     if (cxn->current_bufs != NULL) {
3981         /* freeBufferArray(cxn->current_bufs); */
3982         cxn->current_bufs = NULL;
3983     }
3984     cxn->current_bufs = bufs;
3985     cxn->current_article = item;
3986 
3987     /* we make use of pipelining here
3988        send:
3989          rset
3990          mail from
3991          rcpt to
3992          data
3993     */
3994 
3995     /* find out who it's going to */
3996     result = FindHeader(cxn->current_bufs, "Newsgroups", &rcpt_list,
3997                         &rcpt_list_end);
3998 
3999     if ((result != RET_OK) || (rcpt_list == NULL)) {
4000         d_printf(1, "%s:%d Didn't find Newsgroups header field\n",
4001                  hostPeerName(cxn->myHost), cxn->ident);
4002         QueueForgetAbout(cxn, cxn->current_article, MSG_FAIL_DELIVER);
4003         goto retry;
4004     }
4005 
4006     /* free's original rcpt_list */
4007     rcpt_list =
4008         ConvertRcptList(rcpt_list, rcpt_list_end, &cxn->current_rcpts_issued);
4009     cxn->current_rcpts_okayed = 0;
4010 
4011     if (mailfrom_name == NULL)
4012         mailfrom_name = xstrdup("");
4013     p = concat("RSET\r\n"
4014                "MAIL FROM:<",
4015                mailfrom_name, ">\r\n", rcpt_list, "DATA\r\n", (char *) 0);
4016 
4017     cxn->lmtp_state = LMTP_WRITING_UPTODATA;
4018     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
4019     if (result != RET_OK) {
4020         d_printf(0, "%s:%d failed trying to write\n",
4021                  hostPeerName(cxn->myHost), cxn->ident);
4022         lmtp_Disconnect(cxn);
4023         return;
4024     }
4025 
4026     /* prepend To header field to article */
4027     if (deliver_to_header) {
4028         char *to_list, *to_list_end;
4029         int i, len;
4030 
4031         result = FindHeader(cxn->current_bufs, "Followup-To", &to_list,
4032                             &to_list_end);
4033 
4034         if ((result != RET_OK) || (to_list == NULL)) {
4035             FindHeader(cxn->current_bufs, "Newsgroups", &to_list,
4036                        &to_list_end);
4037         }
4038 
4039         /* free's original to_list */
4040         to_list = BuildToHeader(to_list, to_list_end);
4041 
4042         len = bufferArrayLen(cxn->current_bufs);
4043         cxn->current_bufs =
4044             xrealloc(cxn->current_bufs, sizeof(Buffer) * (len + 2));
4045         cxn->current_bufs[len + 1] = NULL;
4046 
4047         for (i = len; i > 0; i--) {
4048             cxn->current_bufs[i] = cxn->current_bufs[i - 1];
4049         }
4050 
4051         cxn->current_bufs[0] =
4052             newBufferByCharP(to_list, strlen(to_list + 1), strlen(to_list));
4053     }
4054 
4055     hostArticleOffered(cxn->myHost, cxn);
4056 }
4057 
4058 /*
4059  * Called by the EndPoint class when the timer goes off
4060  */
4061 static void
4062 dosomethingTimeoutCbk(TimeoutId id, void *data)
4063 {
4064     Connection cxn = (Connection) data;
4065 
4066     ASSERT(id == cxn->dosomethingTimerId);
4067 
4068     show_stats(cxn);
4069 
4070     /* we're disconnected but there are things to send */
4071     if ((cxn->lmtp_state == LMTP_DISCONNECTED) && (cxn->lmtp_sleepTimerId == 0)
4072         && QueueItems(&(cxn->lmtp_todeliver_q)) > 0) {
4073         if (lmtp_Connect(cxn) != RET_OK)
4074             prepareReopenCbk(cxn, 1);
4075     }
4076 
4077     if ((cxn->imap_state == IMAP_DISCONNECTED) && (cxn->imap_sleepTimerId == 0)
4078         && (QueueItems(&(cxn->imap_controlMsg_q)) > 0)) {
4079         if (imap_Connect(cxn) != RET_OK)
4080             prepareReopenCbk(cxn, 0);
4081     }
4082 
4083 
4084     /* if we're idle and there are items to send let's send them */
4085     if ((cxn->lmtp_state == LMTP_AUTHED_IDLE)
4086         && QueueItems(&(cxn->lmtp_todeliver_q)) > 0) {
4087         lmtp_sendmessage(cxn, NULL);
4088     } else if (cxn->lmtp_state == LMTP_AUTHED_IDLE) {
4089         lmtp_noop(cxn);
4090     }
4091 
4092     if ((cxn->imap_state == IMAP_IDLE_AUTHED)
4093         && (QueueItems(&(cxn->imap_controlMsg_q)) > 0)) {
4094         imap_ProcessQueue(cxn);
4095     } else if (cxn->imap_state == IMAP_IDLE_AUTHED) {
4096         imap_noop(cxn);
4097     }
4098 
4099     /* set up the timer. */
4100     clearTimer(cxn->dosomethingTimerId);
4101 
4102     cxn->dosomethingTimerId =
4103         prepareSleep(dosomethingTimeoutCbk, cxn->dosomethingTimeout, cxn);
4104 }
4105 
4106 /* Give all articles in the queue back to the host. We're probably
4107  * going to exit soon.
4108  * */
4109 
4110 static void
4111 DeferAllArticles(connection_t *cxn, Q_t *q)
4112 {
4113     article_queue_t *cur;
4114     conn_ret ret;
4115 
4116     while (1) {
4117         ret = PopFromQueue(q, &cur);
4118         if (ret == RET_QUEUE_EMPTY)
4119             return;
4120 
4121         if (ret == RET_OK) {
4122             QueueForgetAbout(cxn, cur, MSG_GIVE_BACK);
4123         } else {
4124             d_printf(0,
4125                      "%s:%d Error emptying queue (deffering all articles)\n",
4126                      hostPeerName(cxn->myHost), cxn->ident);
4127             return;
4128         }
4129     }
4130 }
4131 
4132 /*
4133  * Does the actual deletion of a connection and all its private data.
4134  */
4135 static void
4136 delConnection(Connection cxn)
4137 {
4138     bool shutDown;
4139     Connection c, q;
4140 
4141     if (cxn == NULL)
4142         return;
4143 
4144     d_printf(1, "Deleting connection: %s:%d\n", hostPeerName(cxn->myHost),
4145              cxn->ident);
4146 
4147     for (c = gCxnList, q = NULL; c != NULL; q = c, c = c->next)
4148         if (c == cxn) {
4149             if (gCxnList == c)
4150                 gCxnList = gCxnList->next;
4151             else
4152                 q->next = c->next;
4153             break;
4154         }
4155 
4156     ASSERT(c != NULL);
4157 
4158     if (cxn->lmtp_endpoint != NULL)
4159         delEndPoint(cxn->lmtp_endpoint);
4160     if (cxn->imap_endpoint != NULL)
4161         delEndPoint(cxn->imap_endpoint);
4162 
4163     delBuffer(cxn->imap_rBuffer);
4164     delBuffer(cxn->lmtp_rBuffer);
4165 
4166     /* tell the Host we're outta here. */
4167     shutDown = hostCxnGone(cxn->myHost, cxn);
4168 
4169     cxn->ident = 0;
4170     cxn->timeCon = 0;
4171 
4172     free(cxn->ServerName);
4173 
4174     clearTimer(cxn->imap_readBlockedTimerId);
4175     clearTimer(cxn->imap_writeBlockedTimerId);
4176     clearTimer(cxn->lmtp_readBlockedTimerId);
4177     clearTimer(cxn->lmtp_writeBlockedTimerId);
4178 
4179     clearTimer(cxn->imap_sleepTimerId);
4180     cxn->imap_sleepTimerId = 0;
4181     clearTimer(cxn->lmtp_sleepTimerId);
4182     cxn->lmtp_sleepTimerId = 0;
4183 
4184     clearTimer(cxn->dosomethingTimerId);
4185 
4186     free(cxn->imap_respBuffer);
4187     free(cxn->lmtp_respBuffer);
4188 
4189     free(cxn);
4190 
4191     if (shutDown) {
4192         /* exit program if that was the last connexion for the last host */
4193         /* XXX what about if there are ever multiple listeners?
4194            XXX    this will be executed if all hosts on only one of the
4195            XXX    listeners have gone */
4196         time_t now = theTime();
4197         char dateString[30];
4198 
4199         timeToString(now, dateString, sizeof(dateString));
4200         notice("ME finishing at %s", dateString);
4201 
4202         exit(0);
4203     }
4204 }
4205 
4206 
4207 /******************** PUBLIC FUNCTIONS ****************************/
4208 
4209 
4210 /*
4211  * Create a new Connection.
4212  *
4213  * HOST is the host object we're owned by.
4214  * IDENT is an identifier to be added to syslog entries so we can tell
4215  *    what's happening on different connections to the same peer.
4216  * IPNAME is the name (or ip address) of the remote)
4217  * MAXTOUT is the maximum amount of time to wait for a response before
4218  *    considering the remote host dead.
4219  * PORTNUM is the portnum to contact on the remote end.
4220  * RESPTIMEOUT is the amount of time to wait for a response from a remote
4221  *    before considering the connection dead.
4222  * CLOSEPERIOD is the number of seconds after connecting that the
4223  *     connections should be closed down and reinitialized (due to problems
4224  *     with old NNTP servers that hold history files open. Value of 0 means
4225  *     no close down.
4226  */
4227 
4228 Connection
4229 newConnection(Host host, unsigned int ident, const char *ipname,
4230               unsigned int artTout UNUSED, unsigned int portNum UNUSED,
4231               unsigned int respTimeout, unsigned int closePeriod UNUSED,
4232               double lowPassLow UNUSED, double lowPassHigh UNUSED,
4233               double lowPassFilter UNUSED)
4234 {
4235     Connection cxn;
4236     /* check arguments */
4237 
4238     /* allocate connection structure */
4239     cxn = xcalloc(1, sizeof(connection_t));
4240 
4241     cxn->ident = ident;
4242     cxn->ServerName = xstrdup(ipname);
4243     cxn->myHost = host;
4244 
4245     /* setup mailfrom user */
4246     if (gethostname(hostname, MAXHOSTNAMELEN) != 0) {
4247         d_printf(0, "%s gethostname failed\n", ipname);
4248         return NULL;
4249     }
4250 
4251 
4252     mailfrom_name = concat("news@", hostname, (char *) 0);
4253 
4254     cxn->next = gCxnList;
4255     gCxnList = cxn;
4256     gCxnCount++;
4257 
4258     /* init stuff */
4259     Initialize(cxn, respTimeout);
4260 
4261     return cxn;
4262 }
4263 
4264 
4265 /* Causes the Connection to build the network connection. */
4266 bool
4267 cxnConnect(Connection cxn)
4268 {
4269     /* make the lmtp connection */
4270     if (lmtp_Connect(cxn) != RET_OK)
4271         return false;
4272 
4273     if (imap_Connect(cxn) != RET_OK)
4274         return false;
4275 
4276     return true;
4277 }
4278 
4279 
4280 static void
4281 QuitIfIdle(Connection cxn)
4282 {
4283     if ((cxn->lmtp_state == LMTP_AUTHED_IDLE)
4284         && (QueueItems(&(cxn->lmtp_todeliver_q)) <= 0)) {
4285         lmtp_IssueQuit(cxn);
4286     }
4287     if ((cxn->imap_state == IMAP_IDLE_AUTHED)
4288         && (QueueItems(&(cxn->imap_controlMsg_q)) <= 0)) {
4289         imap_sendQuit(cxn);
4290     }
4291 }
4292 
4293 static void
4294 DeleteIfDisconnected(Connection cxn)
4295 {
4296     /* we want to shut everything down. if both connections disconnected now we
4297      * can */
4298     if ((cxn->issue_quit >= 1) && (cxn->lmtp_state == LMTP_DISCONNECTED)
4299         && (cxn->imap_state == IMAP_DISCONNECTED)) {
4300 
4301         switch (cxn->issue_quit) {
4302         case 1:
4303             if (cxn->lmtp_state == LMTP_DISCONNECTED) {
4304                 cxn->lmtp_state = LMTP_WAITING;
4305                 cxn->imap_state = IMAP_WAITING;
4306                 cxn->issue_quit = 0;
4307                 hostCxnWaiting(cxn->myHost,
4308                                cxn); /* tell our Host we're waiting */
4309             }
4310             break;
4311         case 2:
4312             if (cxn->lmtp_state == LMTP_DISCONNECTED) {
4313                 cxn->issue_quit = 0;
4314 
4315                 if (imap_Connect(cxn) != RET_OK)
4316                     prepareReopenCbk(cxn, 0);
4317                 if (lmtp_Connect(cxn) != RET_OK)
4318                     prepareReopenCbk(cxn, 1);
4319             }
4320             break;
4321         case 3:
4322             if (cxn->lmtp_state == LMTP_DISCONNECTED) {
4323                 hostCxnDead(cxn->myHost, cxn);
4324                 delConnection(cxn);
4325             }
4326             break;
4327         }
4328     }
4329 }
4330 
4331 /* puts the connection into the wait state (i.e. waits for an article
4332    before initiating a connect). Can only be called right after
4333    newConnection returns, or while the Connection is in the (internal)
4334    Sleeping state. */
4335 void
4336 cxnWait(Connection cxn)
4337 {
4338     cxn->issue_quit = 1;
4339 
4340     QuitIfIdle(cxn);
4341 }
4342 
4343 /* The Connection will disconnect as if cxnDisconnect were called and then
4344    it automatically reconnects to the remote. */
4345 void
4346 cxnFlush(Connection cxn)
4347 {
4348     cxn->issue_quit = 2;
4349 
4350     QuitIfIdle(cxn);
4351 }
4352 
4353 
4354 /* The Connection sends remaining articles, then issues a QUIT and then
4355    deletes itself */
4356 void
4357 cxnClose(Connection cxn)
4358 {
4359     d_printf(0, "%s:%d Closing cxn\n", hostPeerName(cxn->myHost), cxn->ident);
4360     cxn->issue_quit = 3;
4361 
4362     QuitIfIdle(cxn);
4363 
4364     DeleteIfDisconnected(cxn);
4365 }
4366 
4367 /* The Connection drops all queueed articles, then issues a QUIT and then
4368    deletes itself */
4369 void
4370 cxnTerminate(Connection cxn)
4371 {
4372     d_printf(0, "%s:%d Terminate\n", hostPeerName(cxn->myHost), cxn->ident);
4373 
4374     cxn->issue_quit = 3;
4375 
4376     /* give any articles back to host in both queues */
4377     DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
4378     DeferAllArticles(cxn, &(cxn->imap_controlMsg_q));
4379 
4380     QuitIfIdle(cxn);
4381 }
4382 
4383 /* Blow away the connection gracelessly and immedately clean up */
4384 void
4385 cxnNuke(Connection cxn)
4386 {
4387     d_printf(0, "%s:%d Nuking connection\n", cxn->ServerName, cxn->ident);
4388 
4389     cxn->issue_quit = 4;
4390 
4391     /* give any articles back to host in both queues */
4392     DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
4393     DeferAllArticles(cxn, &(cxn->imap_controlMsg_q));
4394 
4395     imap_Disconnect(cxn);
4396     lmtp_Disconnect(cxn);
4397 
4398     hostCxnDead(cxn->myHost, cxn);
4399     delConnection(cxn);
4400 }
4401 
4402 /*
4403  * must
4404  *   true  - must queue article. Don't try sending
4405  *   false - queue of article may fail. Try sending
4406  *
4407  * Always adds to lmtp queue even if control message
4408  *
4409  */
4410 
4411 static bool
4412 ProcessArticle(Connection cxn, Article art, bool must)
4413 {
4414     conn_ret result;
4415 
4416     /* Don't accept any articles when we're closing down the connection */
4417     if (cxn->issue_quit > 1) {
4418         return false;
4419     }
4420 
4421     /* if it's a regular message let's add it to the queue */
4422     result = AddToQueue(&(cxn->lmtp_todeliver_q), art, DELIVER, 1, must);
4423 
4424     if (result == RET_EXCEEDS_SIZE) {
4425         return false;
4426     }
4427 
4428     if (result != RET_OK) {
4429         d_printf(0, "%s:%d Error adding to delivery queue\n",
4430                  hostPeerName(cxn->myHost), cxn->ident);
4431         return must;
4432     }
4433 
4434     if (must)
4435         return true;
4436 
4437     switch (cxn->lmtp_state) {
4438     case LMTP_WAITING:
4439     case LMTP_DISCONNECTED:
4440         if (cxn->lmtp_sleepTimerId == 0)
4441             if (lmtp_Connect(cxn) != RET_OK)
4442                 prepareReopenCbk(cxn, 1);
4443         break;
4444 
4445     case LMTP_AUTHED_IDLE:
4446         lmtp_sendmessage(cxn, art);
4447         break;
4448     default:
4449         /* currently doing something */
4450         break;
4451     }
4452 
4453     return true;
4454 }
4455 
4456 /* Tells the Connection to take the article and handle its
4457    transmission. If it can't (due to queue size or whatever), then the
4458    function returns false. The connection assumes ownership of the
4459    article if it accepts it (returns true). */
4460 bool
4461 cxnTakeArticle(Connection cxn, Article art)
4462 {
4463     /* if we're closing down always refuse */
4464     if (cxn->issue_quit == 1)
4465         return false;
4466 
4467     return ProcessArticle(cxn, art, false);
4468 }
4469 
4470 /* Tell the Connection to take the article (if it can) for later
4471    processing. Assumes ownership of it if it takes it. */
4472 bool
4473 cxnQueueArticle(Connection cxn, Article art)
4474 {
4475     return ProcessArticle(cxn, art, true);
4476 }
4477 
4478 /* generate a syslog message for the connections activity. Called by Host. */
4479 void
4480 cxnLogStats(Connection cxn, bool final)
4481 {
4482     const char *peerName;
4483     time_t now = theTime();
4484     int total, bad;
4485 
4486     ASSERT(cxn != NULL);
4487 
4488     peerName = hostPeerName(cxn->myHost);
4489 
4490     total = cxn->lmtp_succeeded + cxn->lmtp_failed;
4491     total += cxn->cancel_succeeded + cxn->cancel_failed;
4492     total += cxn->create_succeeded + cxn->create_failed;
4493     total += cxn->remove_succeeded + cxn->remove_failed;
4494 
4495     bad = cxn->lmtp_failed;
4496     bad += cxn->cancel_failed;
4497     bad += cxn->create_failed;
4498     bad += cxn->remove_failed;
4499 
4500     notice("%s:%d %s seconds %ld accepted %d refused %d rejected %d", peerName,
4501            cxn->ident, (final ? "final" : "checkpoint"),
4502            (long) (now - cxn->timeCon), total, 0, bad);
4503     show_stats(cxn);
4504 
4505     if (final) {
4506         cxn->lmtp_succeeded = 0;
4507         cxn->lmtp_failed = 0;
4508         cxn->cancel_succeeded = 0;
4509         cxn->cancel_failed = 0;
4510         cxn->create_succeeded = 0;
4511         cxn->create_failed = 0;
4512         cxn->remove_succeeded = 0;
4513         cxn->remove_failed = 0;
4514 
4515         if (cxn->timeCon > 0)
4516             cxn->timeCon = theTime();
4517     }
4518 }
4519 
4520 /* return the number of articles the connection can be given. This lets
4521    the host shovel in as many as possible. May be zero. */
4522 size_t
4523 cxnQueueSpace(Connection cxn)
4524 {
4525     int lmtpsize;
4526     int imapsize;
4527 
4528     lmtpsize = QueueSpace(&(cxn->lmtp_todeliver_q));
4529     imapsize = QueueSpace(&(cxn->imap_controlMsg_q));
4530 
4531     if (lmtpsize >= 1)
4532         lmtpsize--;
4533     if (imapsize >= 1)
4534         imapsize--;
4535 
4536     d_printf(1, "%s:%d Q Space lmtp size = %d state = %d\n",
4537              hostPeerName(cxn->myHost), cxn->ident, lmtpsize, cxn->lmtp_state);
4538     d_printf(1, "%s:%d Q Space imap size = %d state = %d\n",
4539              hostPeerName(cxn->myHost), cxn->ident, imapsize, cxn->imap_state);
4540 
4541     /* return the smaller of our 2 queues */
4542     if (lmtpsize < imapsize)
4543         return lmtpsize;
4544     else
4545         return imapsize;
4546 }
4547 
4548 /* adjust the mode no-CHECK filter values */
4549 void
4550 cxnSetCheckThresholds(Connection cxn, double lowFilter UNUSED,
4551                       double highFilter UNUSED, double lowPassFilter UNUSED)
4552 {
4553     d_printf(1, "%s:%d Threshold change. This means nothing to me\n",
4554              hostPeerName(cxn->myHost), cxn->ident);
4555 }
4556 
4557 /* print some debugging info. */
4558 void
4559 gPrintCxnInfo(FILE *fp, unsigned int indentAmt)
4560 {
4561     char indent[INDENT_BUFFER_SIZE];
4562     unsigned int i;
4563     Connection cxn;
4564 
4565     for (i = 0; i < MIN(INDENT_BUFFER_SIZE - 1, indentAmt); i++)
4566         indent[i] = ' ';
4567     indent[i] = '\0';
4568 
4569     fprintf(fp, "%sGlobal Connection list : (count %u) {\n", indent,
4570             gCxnCount);
4571     for (cxn = gCxnList; cxn != NULL; cxn = cxn->next)
4572         printCxnInfo(cxn, fp, indentAmt + INDENT_INCR);
4573     fprintf(fp, "%s}\n", indent);
4574 }
4575 
4576 void
4577 printCxnInfo(Connection cxn, FILE *fp, unsigned int indentAmt)
4578 {
4579     char indent[INDENT_BUFFER_SIZE];
4580     unsigned int i;
4581     article_queue_t *artH;
4582 
4583     for (i = 0; i < MIN(INDENT_BUFFER_SIZE - 1, indentAmt); i++)
4584         indent[i] = ' ';
4585     indent[i] = '\0';
4586 
4587     fprintf(fp, "%sConnection : %p {\n", indent, (void *) cxn);
4588     fprintf(fp, "%s    host : %p\n", indent, (void *) cxn->myHost);
4589     fprintf(fp, "%s    endpoint (imap): %p\n", indent,
4590             (void *) cxn->imap_endpoint);
4591     fprintf(fp, "%s    endpoint (lmtp): %p\n", indent,
4592             (void *) cxn->lmtp_endpoint);
4593     fprintf(fp, "%s    state (imap) : %s\n", indent,
4594             imap_stateToString(cxn->imap_state));
4595     fprintf(fp, "%s    state (lmtp) : %s\n", indent,
4596             lmtp_stateToString(cxn->lmtp_state));
4597     fprintf(fp, "%s    ident : %u\n", indent, cxn->ident);
4598     fprintf(fp, "%s    ip-name (imap): %s\n", indent, cxn->ServerName);
4599     fprintf(fp, "%s    ip-name (lmtp): %s\n", indent, cxn->ServerName);
4600     fprintf(fp, "%s    port-number (imap) : %d\n", indent, cxn->imap_port);
4601     fprintf(fp, "%s    port-number (lmtp) : %d\n", indent, cxn->lmtp_port);
4602 
4603     fprintf(fp, "%s    Issuing Quit : %d\n", indent, cxn->issue_quit);
4604 
4605     fprintf(fp, "%s    time-connected (imap) : %ld\n", indent,
4606             (long) cxn->imap_timeCon);
4607     fprintf(fp, "%s    time-connected (lmtp) : %ld\n", indent,
4608             (long) cxn->lmtp_timeCon);
4609     fprintf(fp, "%s    articles from INN : %d\n", indent,
4610             cxn->lmtp_succeeded + cxn->lmtp_failed + cxn->cancel_succeeded
4611                 + cxn->cancel_failed + cxn->create_succeeded
4612                 + cxn->create_failed + cxn->remove_succeeded
4613                 + cxn->remove_failed + QueueSpace(&(cxn->lmtp_todeliver_q))
4614                 + QueueSpace(&(cxn->imap_controlMsg_q)));
4615     fprintf(fp, "%s    LMTP STATS: yes: %d no: %d\n", indent,
4616             cxn->lmtp_succeeded, cxn->lmtp_failed);
4617     fprintf(fp, "%s    control:    yes: %d no: %d\n", indent,
4618             cxn->cancel_succeeded, cxn->cancel_failed);
4619     fprintf(fp, "%s    create:     yes: %d no: %d\n", indent,
4620             cxn->create_succeeded, cxn->create_failed);
4621     fprintf(fp, "%s    remove:     yes: %d no: %d\n", indent,
4622             cxn->remove_succeeded, cxn->remove_failed);
4623 
4624     fprintf(fp, "%s    response-timeout : %u\n", indent,
4625             cxn->imap_readTimeout);
4626     fprintf(fp, "%s    response-callback : %d\n", indent,
4627             cxn->imap_readBlockedTimerId);
4628 
4629     fprintf(fp, "%s    write-timeout : %u\n", indent, cxn->imap_writeTimeout);
4630     fprintf(fp, "%s    write-callback : %d\n", indent,
4631             cxn->imap_writeBlockedTimerId);
4632 
4633     fprintf(fp, "%s    reopen wait : %u\n", indent, cxn->imap_sleepTimeout);
4634     fprintf(fp, "%s    reopen id : %d\n", indent, cxn->imap_sleepTimerId);
4635 
4636     fprintf(fp, "%s    IMAP queue {\n", indent);
4637     for (artH = cxn->imap_controlMsg_q.head; artH != NULL; artH = artH->next)
4638         printArticleInfo(artH->data.article, fp, indentAmt + INDENT_INCR);
4639     fprintf(fp, "%s    }\n", indent);
4640 
4641     fprintf(fp, "%s    LMTP queue {\n", indent);
4642     for (artH = cxn->lmtp_todeliver_q.head; artH != NULL; artH = artH->next)
4643         printArticleInfo(artH->data.control->article, fp,
4644                          indentAmt + INDENT_INCR);
4645     fprintf(fp, "%s    }\n", indent);
4646 
4647     fprintf(fp, "%s}\n", indent);
4648 }
4649 
4650 /* config file load callback */
4651 int
4652 cxnConfigLoadCbk(void *data UNUSED)
4653 {
4654     long iv;
4655     int rval = 1;
4656     FILE *fp = (FILE *) data;
4657 
4658     if (getInteger(topScope, "max-reconnect-time", &iv, NO_INHERIT)) {
4659         if (iv < 1) {
4660             rval = 0;
4661             logOrPrint(LOG_ERR, fp,
4662                        "ME config: value of %s (%ld) in %s cannot be less"
4663                        " than 1. Using %ld",
4664                        "max-reconnect-time", iv, "global scope",
4665                        (long) MAX_RECON_PER);
4666             iv = MAX_RECON_PER;
4667         }
4668     } else
4669         iv = MAX_RECON_PER;
4670     max_reconnect_period = (unsigned int) iv;
4671 
4672     if (getInteger(topScope, "initial-reconnect-time", &iv, NO_INHERIT)) {
4673         if (iv < 1) {
4674             rval = 0;
4675             logOrPrint(LOG_ERR, fp,
4676                        "ME config: value of %s (%ld) in %s cannot be less"
4677                        " than 1. Using %ld",
4678                        "initial-reconnect-time", iv, "global scope",
4679                        (long) INIT_RECON_PER);
4680             iv = INIT_RECON_PER;
4681         }
4682     } else
4683         iv = INIT_RECON_PER;
4684     init_reconnect_period = (unsigned int) iv;
4685 
4686     return rval;
4687 }
4688 
4689 /* check connection state is in cxnWaitingS, cxnConnectingS or cxnIdleS */
4690 bool
4691 cxnCheckstate(Connection cxn)
4692 {
4693     d_printf(5, "%s:%d Being asked to check state\n",
4694              hostPeerName(cxn->myHost), cxn->ident);
4695 
4696     /* return false if either connection is doing something */
4697     if (cxn->imap_state > IMAP_IDLE_AUTHED)
4698         return false;
4699     if (cxn->lmtp_state > LMTP_AUTHED_IDLE)
4700         return false;
4701 
4702     return true;
4703 }
4704