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