chiark / gitweb /
Commit 2.4.5-5 as unpacked
[inn-innduct.git] / innfeed / imap_connection.c
1 /*  $Id: imap_connection.c 7103 2004-12-23 22:36:27Z rra $
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 "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 #endif
51
52 #ifndef MAXHOSTNAMELEN
53 #define MAXHOSTNAMELEN 1024
54 #endif
55
56 #define IMAP_PORT 143
57
58 #ifdef SMTPMODE
59 # define LMTP_PORT 25
60 #else
61 # define LMTP_PORT 2003
62 #endif
63
64 #define IMAP_TAGLENGTH 6
65
66 #define QUEUE_MAX_SIZE 250
67
68 #define DOSOMETHING_TIMEOUT 60
69
70
71
72 /* external */
73 extern char *deliver_username;
74 extern char *deliver_authname;
75 extern char *deliver_password;
76 extern char *deliver_realm;
77 extern char *deliver_rcpt_to;
78 extern char *deliver_to_header;
79
80
81 char hostname[MAXHOSTNAMELEN];
82 char *mailfrom_name = NULL; /* default to no return path */
83
84 #ifdef HAVE_SASL
85 static int initialized_sasl = 0; /* weather sasl_client_init() has been called */
86 #endif
87
88 /* states the imap connection may be in */
89 typedef enum {
90
91     IMAP_DISCONNECTED = 1,
92     IMAP_WAITING,
93     
94     IMAP_CONNECTED_NOTAUTH,
95
96     IMAP_READING_INTRO,
97
98     IMAP_WRITING_CAPABILITY,
99     IMAP_READING_CAPABILITY,
100
101     IMAP_WRITING_STARTAUTH,
102     IMAP_READING_STEPAUTH,
103     IMAP_WRITING_STEPAUTH,
104
105     IMAP_IDLE_AUTHED,
106     IMAP_WRITING_NOOP,
107     IMAP_READING_NOOP,
108
109     IMAP_WRITING_CREATE,
110     IMAP_READING_CREATE,
111
112     IMAP_WRITING_DELETE,
113     IMAP_READING_DELETE,
114
115     IMAP_WRITING_SELECT,
116     IMAP_READING_SELECT,
117
118     IMAP_WRITING_SEARCH,
119     IMAP_READING_SEARCH,
120
121     IMAP_WRITING_STORE,
122     IMAP_READING_STORE,
123     
124     IMAP_WRITING_CLOSE,
125     IMAP_READING_CLOSE,
126
127     IMAP_WRITING_QUIT,
128     IMAP_READING_QUIT
129    
130 } imap_state_t;
131
132 typedef enum {
133     LMTP_DISCONNECTED = 1,
134     LMTP_WAITING,
135
136     LMTP_CONNECTED_NOTAUTH,
137
138     LMTP_READING_INTRO,
139
140     LMTP_WRITING_LHLO,
141     LMTP_READING_LHLO,
142
143     LMTP_WRITING_STARTAUTH,
144     LMTP_READING_STEPAUTH,
145     LMTP_WRITING_STEPAUTH,
146
147     LMTP_AUTHED_IDLE,
148     LMTP_WRITING_NOOP,
149     LMTP_READING_NOOP,
150    
151     LMTP_READING_RSET,
152     LMTP_READING_MAILFROM,
153     LMTP_READING_RCPTTO,
154     LMTP_READING_DATA,
155     LMTP_READING_CONTENTS,
156
157     LMTP_WRITING_UPTODATA,
158     LMTP_WRITING_CONTENTS,
159
160     LMTP_WRITING_QUIT,
161     LMTP_READING_QUIT
162
163 } lmtp_state_t;
164
165 typedef struct imap_capabilities_s {
166
167     int imap4;         /* does server support imap4bis? */
168     int logindisabled; /* does the server allow the login command? */
169
170     char *saslmechs;   /* supported SASL mechanisms */
171
172 } imap_capabilities_t;
173
174 typedef struct lmtp_capabilities_s {
175
176     int Eightbitmime;
177     int EnhancedStatusCodes;
178     int pipelining;
179
180     char *saslmechs;
181
182 } lmtp_capabilities_t;
183
184 typedef enum {
185     STAT_CONT = 0,
186     STAT_NO = 1,
187     STAT_OK = 2,
188     STAT_FAIL = 3
189 } imt_stat;
190
191 /* Message types */
192 typedef enum {
193     DELIVER,
194     CREATE_FOLDER,
195     CANCEL_MSG,
196     DELETE_FOLDER
197 } control_type_t;
198
199 typedef struct control_item_s {
200
201     Article article;
202     char *folder;
203     char *msgid;                 /* only for cancel's */
204     unsigned long  uid;          /* only for cancel's */
205
206 } control_item_t;
207
208 typedef struct article_queue_s {
209
210     control_type_t type;
211
212     time_t arrived;
213     time_t nextsend; /* time we should next try to send article */
214
215     int trys;
216
217     int counts_toward_size;
218
219     union {
220         Article article;
221         control_item_t *control;
222         void *generic;
223     } data;
224
225     struct article_queue_s *next;
226
227 } article_queue_t;
228
229 typedef struct Q_s {
230
231     article_queue_t *head;
232
233     article_queue_t *tail;
234
235     int size;
236
237 } Q_t;
238
239 typedef struct connection_s {
240
241     /* common stuff */
242     char *ServerName;
243
244     char *lmtp_respBuffer;         /* buffer all responses are read into */
245     Buffer lmtp_rBuffer;           /* buffer all responses are read into */
246
247     Host myHost ;                   /* the host who owns the connection */
248     
249     time_t timeCon ;                /* the time the connect happened 
250                                        (last auth succeeded) */
251
252     int issue_quit;                 /* Three states:
253                                      *   0 - don't do anything
254                                      *   1 - after issue quit enter wait state
255                                      *   2 - after issue quit reconnect
256                                      *   3 - after issue quit delete connection
257                                      *   4 - nuke cxn
258                                      */
259
260     /* Statistics */    
261     int lmtp_succeeded;
262     int lmtp_failed;
263
264     int cancel_succeeded;
265     int cancel_failed;
266     
267     int create_succeeded;
268     int create_failed;
269     
270     int remove_succeeded;
271     int remove_failed;
272
273     
274     /* LMTP stuff */
275     int lmtp_port;
276     lmtp_state_t lmtp_state;
277 #ifdef HAVE_SASL
278     sasl_conn_t *saslconn_lmtp;
279 #endif /* HAVE_SASL */
280     int sockfd_lmtp;
281
282     time_t lmtp_timeCon ;
283
284     EndPoint lmtp_endpoint;
285     unsigned int ident ;               /* an identifier for syslogging. */
286
287     lmtp_capabilities_t *lmtp_capabilities;
288
289     int lmtp_disconnects;
290     char *lmtp_tofree_str;
291
292     article_queue_t *current_article;
293     Buffer *current_bufs;
294     int     current_rcpts_issued;
295     int     current_rcpts_okayed;
296
297     /* Timer for the max amount of time to wait for a response from the
298        remote */
299     unsigned int lmtp_readTimeout ;
300     TimeoutId lmtp_readBlockedTimerId ;
301
302     /* Timer for the max amount of time to wait for a any amount of data
303        to be written to the remote */
304     unsigned int lmtp_writeTimeout ;
305     TimeoutId lmtp_writeBlockedTimerId ;
306
307     /* Timer for the number of seconds to sleep before attempting a
308        reconnect. */
309     unsigned int lmtp_sleepTimeout ;
310     TimeoutId lmtp_sleepTimerId ;
311
312     /* Timer for max amount between queueing some articles and trying to send them */
313     unsigned int dosomethingTimeout ;
314     TimeoutId dosomethingTimerId ;
315
316     Q_t lmtp_todeliver_q;
317
318
319
320     /* IMAP stuff */
321     int imap_port;
322 #ifdef HAVE_SASL
323     sasl_conn_t *imap_saslconn;
324 #endif /* HAVE_SASL */
325
326     char *imap_respBuffer;
327     Buffer imap_rBuffer;
328     EndPoint imap_endpoint;
329
330     imap_capabilities_t *imap_capabilities;
331     
332     int imap_sockfd;
333
334     time_t imap_timeCon ;
335
336     imap_state_t imap_state;
337     int imap_disconnects;
338     char *imap_tofree_str;
339
340     char imap_currentTag[IMAP_TAGLENGTH];
341     int  imap_tag_num;
342
343     /* Timer for the max amount of time to wait for a response from the
344        remote */
345     unsigned int imap_readTimeout ;
346     TimeoutId imap_readBlockedTimerId ;
347
348     /* Timer for the max amount of time to wait for a any amount of data
349        to be written to the remote */
350     unsigned int imap_writeTimeout ;
351     TimeoutId imap_writeBlockedTimerId ;
352
353     /* Timer for the number of seconds to sleep before attempting a
354        reconnect. */
355     unsigned int imap_sleepTimeout ;
356     TimeoutId imap_sleepTimerId ;
357
358     Q_t imap_controlMsg_q;
359
360     article_queue_t *current_control;
361
362     struct connection_s *next;
363
364 } connection_t;
365
366 static Connection gCxnList = NULL ;
367 static unsigned int gCxnCount= 0 ;
368 static unsigned int max_reconnect_period = MAX_RECON_PER ;
369 static unsigned int init_reconnect_period = INIT_RECON_PER;
370
371 typedef enum {
372     RET_OK = 0,
373     RET_FAIL = 1,
374     RET_QUEUE_EMPTY,
375     RET_EXCEEDS_SIZE,
376     RET_NO_FULLLINE,
377     RET_NO,
378     RET_ARTICLE_BAD
379 } conn_ret;
380
381
382 /********** Private Function Declarations *************/
383
384 static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d);
385 static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d);
386 static void imap_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d);
387 static void lmtp_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d);
388
389 static conn_ret lmtp_Connect(connection_t *cxn);
390 static conn_ret imap_Connect(connection_t *cxn);
391
392 static void prepareReopenCbk (Connection cxn, int type);
393
394 static void lmtp_readTimeoutCbk (TimeoutId id, void *data);
395 static void imap_readTimeoutCbk (TimeoutId id, void *data);
396
397 static void dosomethingTimeoutCbk (TimeoutId id, void *data);
398
399 static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen);
400 static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen);
401
402 static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback, 
403                             EndPoint endp, Buffer *array);
404 static void lmtp_sendmessage(connection_t *cxn, Article justadded);
405 static void imap_ProcessQueue(connection_t *cxn);
406
407 static conn_ret FindHeader(Buffer *bufs, const char *header, char **start, char **end);
408 static conn_ret PopFromQueue(Q_t *q, article_queue_t **item);
409
410 enum failure_type {
411     MSG_SUCCESS = 0,
412     MSG_FAIL_DELIVER = 1,
413     MSG_GIVE_BACK = 2,
414     MSG_MISSING = 3
415 };
416
417 static void QueueForgetAbout(connection_t *cxn, article_queue_t *item, 
418                              enum failure_type failed);
419
420 static void delConnection (Connection cxn);
421 static void DeleteIfDisconnected(Connection cxn);
422 static void DeferAllArticles(connection_t *cxn, Q_t *q);
423
424 static void lmtp_Disconnect(connection_t *cxn);
425 static void imap_Disconnect(connection_t *cxn);
426 static conn_ret imap_listenintro(connection_t *cxn);
427
428 static void imap_writeTimeoutCbk (TimeoutId id, void *data);
429 static void lmtp_writeTimeoutCbk (TimeoutId id, void *data);
430
431 /******************** PRIVATE FUNCTIONS ***************************/
432
433 static const char *imap_stateToString(int state)
434 {
435     switch (state)
436         {
437         case IMAP_DISCONNECTED: return "disconnected";   
438         case IMAP_WAITING: return "waiting";
439         case IMAP_CONNECTED_NOTAUTH: return "connected (unauthenticated)";
440         case IMAP_READING_INTRO: return "reading intro";
441         case IMAP_WRITING_CAPABILITY: return "writing CAPABILITY";
442         case IMAP_READING_CAPABILITY: return "reading CAPABILITY";
443         case IMAP_WRITING_STARTAUTH: return "writing AUTHENTICATE";
444         case IMAP_READING_STEPAUTH: return "reading stepauth";
445         case IMAP_WRITING_STEPAUTH: return "writing stepauth";
446         case IMAP_IDLE_AUTHED: return "idle (authenticated)";
447         case IMAP_WRITING_NOOP: return "writing NOOP";
448         case IMAP_READING_NOOP: return "reading NOOP response";
449         case IMAP_WRITING_CREATE: return "writing CREATE";
450         case IMAP_READING_CREATE: return "reading CREATE response";
451         case IMAP_WRITING_DELETE: return "writing DELETE command";
452         case IMAP_READING_DELETE: return "reading DELETE response";            
453         case IMAP_WRITING_SELECT: return "writing SELECT";
454         case IMAP_READING_SELECT: return "reading SELECT response";
455         case IMAP_WRITING_SEARCH: return "writing SEARCH";
456         case IMAP_READING_SEARCH: return "reading SEARCH response";
457         case IMAP_WRITING_STORE: return "writing STORE";
458         case IMAP_READING_STORE: return "reading STORE response";           
459         case IMAP_WRITING_CLOSE: return "writing CLOSE";
460         case IMAP_READING_CLOSE: return "reading CLOSE response";
461         case IMAP_WRITING_QUIT: return "writing LOGOUT";
462         case IMAP_READING_QUIT: return "reading LOGOUT response";
463         default: return "Unknown state";
464         }
465 }
466
467 static const char *lmtp_stateToString(int state)
468 {
469     switch(state)
470         {
471         case LMTP_DISCONNECTED: return "disconnected";
472         case LMTP_WAITING: return "waiting";
473         case LMTP_CONNECTED_NOTAUTH: return "connected (unauthenticated)";
474         case LMTP_READING_INTRO: return "reading intro";
475         case LMTP_WRITING_LHLO: return "writing LHLO";
476         case LMTP_READING_LHLO: return "reading LHLO response";
477         case LMTP_WRITING_STARTAUTH: return "writing AUTH";
478         case LMTP_READING_STEPAUTH: return "reading stepauth";
479         case LMTP_WRITING_STEPAUTH: return "writing stepauth";
480         case LMTP_AUTHED_IDLE: return "idle (authenticated)";
481         case LMTP_WRITING_NOOP: return "writing NOOP";
482         case LMTP_READING_NOOP: return "reading NOOP response";
483         case LMTP_READING_RSET: return "reading RSET response";
484         case LMTP_READING_MAILFROM: return "reading MAIL FROM response";
485         case LMTP_READING_RCPTTO: return "reading RCPT TO response";
486         case LMTP_READING_DATA: return "reading DATA response";
487         case LMTP_READING_CONTENTS: return "reading contents response";
488         case LMTP_WRITING_UPTODATA: 
489             return "writing RSET, MAIL FROM, RCPT TO, DATA commands";
490         case LMTP_WRITING_CONTENTS: return "writing contents of message";
491         case LMTP_WRITING_QUIT: return "writing QUIT";
492         case LMTP_READING_QUIT: return "reading QUIT";
493         default: return "unknown state";
494         }
495 }
496
497 /******************************* Queue functions ***********************************/
498
499 /*
500  * Add a message to a generic queue
501  *
502  *  q       - the queue adding to
503  *  item    - the data to add to the queue
504  *  type    - the type of item it is (i.e. cancel,lmtp,etc..)
505  *  addsmsg - weather this should be counted toward the queue size
506  *            this is for control msg's that create multiple queue items.
507  *            For example a cancel message canceling a message in multiple
508  *            newsgroups will create >1 queue item but we only want it to count
509  *            once towards the queue
510  *  must    - wheather we must take it even though it may put us over our max size
511  */
512
513 static conn_ret AddToQueue(Q_t *q, void *item, 
514                            control_type_t type, int addsmsg, bool must)
515 {
516     article_queue_t *newentry;
517
518     if (must == false)
519     {
520         if (q->size >= QUEUE_MAX_SIZE)
521         {
522             return RET_EXCEEDS_SIZE;
523         }
524     } else {
525         if (q->size >= QUEUE_MAX_SIZE * 10)
526         {
527             d_printf(0, "Queue has grown way too much. Dropping article\n");
528             return RET_FAIL;
529         }
530     }
531
532     /* add to the end of our queue */
533     newentry = xmalloc(sizeof(article_queue_t));
534
535     newentry->type = type;
536
537     /* send as soon as possible */
538     newentry->nextsend = newentry->arrived = time(NULL);
539
540     newentry->trys = 0;
541
542     newentry->data.generic = item;
543     newentry->next = NULL;
544     newentry->counts_toward_size = addsmsg;
545
546     /* add to end of queue */
547     if (q->tail == NULL)
548     {
549         q->head = newentry;
550         q->tail = newentry;
551     } else {
552
553         q->tail->next = newentry;
554         q->tail = newentry;
555     }
556
557     q->size+=addsmsg;
558
559     return RET_OK;
560 }
561
562 /*
563  * Pop an item from the queue
564  *
565  * q    - the queue to pop from
566  * item - where the item shall be placed upon sucess
567  *
568  */
569
570 static conn_ret PopFromQueue(Q_t *q, article_queue_t **item)
571 {
572     /* if queue empty return error */
573     if ( q->head == NULL)
574     {
575         return RET_QUEUE_EMPTY;
576     }
577     
578     /* set what we return */
579     *item = q->head;
580
581     q->head = q->head->next;
582     if (q->head == NULL) q->tail = NULL;
583
584     q->size-=(*item)->counts_toward_size;
585     
586     return RET_OK;
587 }
588
589 /*
590  * ReQueue an item. Will either put it back in the queue for another try
591  * or forget about it
592  *
593  *  cxn     - our connection object (needed so forget about things)
594  *  q       - the queue to requeue to 
595  *  entry   - the item to put back
596  */
597
598 static void ReQueue(connection_t *cxn, Q_t *q, article_queue_t *entry)
599 {
600     /* look at the time it's been here */
601     entry->nextsend = time(NULL) + (entry->trys *30); /* xxx better formula? */
602
603     entry->trys++;
604     
605     /* give up after 5 tries xxx configurable??? */
606     if (entry->trys >= 5)
607     {
608         QueueForgetAbout(cxn, entry, MSG_FAIL_DELIVER);
609         return;
610     }
611
612
613     /* ok let's add back to the end of the queue */
614     entry->next = NULL;
615
616     /* add to end of queue */
617     if (q->tail == NULL)
618     {
619         q->head = entry;
620         q->tail = entry;
621     } else {
622         q->tail->next = entry;
623         q->tail = entry;
624     }
625
626     q->size+=entry->counts_toward_size;
627 }
628
629
630
631 /*
632  * Forget about an item. Tells host object if we succeeded/failed/etc with the message
633  *
634  * cxn    - connection object
635  * item   - item
636  * failed - type of failure (see below)
637  *
638  * failed:
639  *   0 - succeeded delivering message
640  *   1 - failed delivering message
641  *   2 - Try to give back to host
642  *   3 - Article missing (i.e. can't find on disk)
643  */
644 static void QueueForgetAbout(connection_t *cxn, article_queue_t *item, 
645                              enum failure_type failed)
646 {
647     Article art = NULL;
648     
649     switch (item->type)
650         {
651         case DELIVER:
652             if (failed>0)
653                 cxn->lmtp_failed++;
654             art = item->data.article;
655             break;
656
657         case CANCEL_MSG:
658             if (failed>0)
659                 cxn->cancel_failed++;
660             free(item->data.control->msgid);
661             free(item->data.control->folder);
662
663             if (item->counts_toward_size == 1)
664                 art = item->data.control->article;
665
666             free(item->data.control );
667             break;
668
669         case CREATE_FOLDER: 
670             if (failed>0)
671                 cxn->create_failed++;
672             free(item->data.control->folder);
673
674             art = item->data.control->article;
675
676             free(item->data.control );
677             break;
678  
679         case DELETE_FOLDER:
680             if (failed>0)
681                 cxn->remove_failed++;
682             free(item->data.control->folder);
683
684             art = item->data.control->article;
685
686             free(item->data.control );
687             break;
688
689         default:
690             d_printf(0, "%s:%d QueueForgetAbout(): "
691                      "Unknown type to forget about\n",
692                      hostPeerName (cxn->myHost), cxn->ident);
693             break;
694         }
695
696     if (art!=NULL) {
697         switch (failed) {
698         case MSG_SUCCESS:
699             hostArticleAccepted (cxn->myHost, cxn, art);
700             break;
701
702         case MSG_FAIL_DELIVER:
703             hostArticleRejected (cxn->myHost, cxn, art);
704             break;
705
706         case MSG_GIVE_BACK:
707             hostTakeBackArticle (cxn->myHost, cxn, art);
708             break;
709
710         case MSG_MISSING:
711             hostArticleIsMissing(cxn->myHost, cxn, art);
712             break;
713         default:
714             d_printf(0,"%s:%d QueueForgetAbout(): failure type unknown\n",
715                      hostPeerName (cxn->myHost), cxn->ident);
716             break;
717         }
718     }
719
720     free(item);
721 }
722
723 /*
724  * How much space is available in the queue
725  */
726
727 static int QueueSpace(Q_t *q)
728 {
729     int ret = QUEUE_MAX_SIZE - q->size;
730     if (ret < 0) ret = 0;
731     return ret;
732 }
733
734 /*
735  * How many items are in the queue
736  */
737
738 static int QueueItems(Q_t *q)
739 {
740     return q->size;
741 }
742
743
744 /***************************** END Queue functions ***********************************/
745
746 /***************************** Generic Parse Functions *******************************/
747
748 /* returns the end of the header */
749
750 static char *GetUntil(char *str)
751 {
752     while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n'))
753     {
754         str++;
755     }
756
757     return str;
758 }
759
760 static char *GotoNextLine(char *str)
761 {
762     while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n'))
763     {
764         str++;
765     }
766
767     if (*str == '\r') str++;
768     if (*str == '\n') str++;
769
770     return str;
771 }
772
773 /*
774  * Finds the given header in the message
775  *  Returns NULL if not found
776  */
777 static conn_ret FindHeader(Buffer *bufs, const char *header, char **start,
778         char **end)
779 {
780     Buffer b;
781     int size;
782     char *str_base;
783     char *str;
784     int headerlen = strlen(header);
785     
786     if (bufs==NULL)
787     {
788         if (start)
789             *start=NULL; 
790         return RET_ARTICLE_BAD; 
791     }
792     
793     b = bufs[0];
794     size = bufferSize(b);
795     str_base = bufferBase(b);
796     str = str_base;
797     
798     while ((str - str_base) < size - headerlen)
799     {
800         if (*str == header[0])
801         {
802             if ((strncasecmp(header, str, headerlen)==0) && ( *(str + headerlen)==':'))
803             {
804
805                 if (start)
806                 {
807                     *start = str+headerlen+1;
808
809                     /* get rid of leading whitespace */
810                     while ( isspace((int) **start))                     
811                         (*start)++;
812                 }
813                 
814                 if (end)
815                     *end = GetUntil(str+headerlen+1);
816                 
817                 return RET_OK;
818             }
819         } else if (*str == '\n') { 
820             /* end of headers */
821             return RET_NO;
822         }
823         str = GotoNextLine(str);
824     }
825
826     return RET_NO;
827 }
828
829 static conn_ret GetLine(char *buf, char *ret, int retmaxsize)
830 {
831     char *str_base;
832     char *str;
833
834     int size = strlen(buf);
835     str_base = buf;
836     str = str_base;
837
838     while ( (*str) != '\0')
839     {
840         if ((*str) == '\n')
841         {
842             if (str-str_base > retmaxsize)
843             {
844                 d_printf(0, "Max size exceeded! %s\n",str_base);
845                 return RET_FAIL;
846             }
847
848             /* fill in the return string */
849             memcpy(ret, str_base, str-str_base);
850             ret[ str - str_base -1] = '\0';
851
852             memcpy( str_base, str_base + (str-str_base)+1, size - (str-str_base));
853             str_base[size - (str-str_base)]='\0';
854
855             return RET_OK;
856         }
857
858         str++;
859     }
860
861     /* couldn't find a full line */
862     return RET_NO_FULLLINE;
863 }
864
865
866
867 /************************** END Generic Parse Functions *******************************/
868
869 /************************ Writing to Network functions *****************/
870
871 static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback, 
872                             EndPoint endp, Buffer *array)
873 {
874
875     if (array == NULL) return RET_FAIL;
876
877     prepareWrite (endp,
878                   array,
879                   NULL,
880                   callback,
881                   cxn);
882
883     return RET_OK;
884 }
885
886 static conn_ret WriteToWire_str(connection_t *cxn, EndpRWCB callback,
887                                 EndPoint endp, char *str, int slen)
888 {
889     conn_ret result;
890     Buffer buff;
891     Buffer *writeArr;
892
893     if (slen==-1) slen = strlen(str);
894
895     buff = newBufferByCharP(str, slen+1, slen);
896     ASSERT (buff != NULL);   
897
898     writeArr = makeBufferArray (buff, NULL) ;
899
900     result = WriteToWire(cxn, callback, endp, writeArr);
901
902     return result;
903 }
904
905 static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen)
906 {
907     /* prepare the timeouts */
908     clearTimer (cxn->imap_readBlockedTimerId) ;
909     
910     /* set up the write timer. */
911     clearTimer (cxn->imap_writeBlockedTimerId) ;
912
913     if (cxn->imap_writeTimeout > 0)
914         cxn->imap_writeBlockedTimerId = prepareSleep (imap_writeTimeoutCbk, cxn->imap_writeTimeout,
915                                                  cxn);
916     cxn->imap_tofree_str = str;
917     return WriteToWire_str(cxn, imap_writeCB, cxn->imap_endpoint, str, slen);
918 }
919
920 static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen)
921 {
922     /* prepare the timeouts */
923     clearTimer (cxn->lmtp_readBlockedTimerId) ;
924     
925     /* set up the write timer. */
926     clearTimer (cxn->lmtp_writeBlockedTimerId) ;
927
928     if (cxn->lmtp_writeTimeout > 0)
929         cxn->lmtp_writeBlockedTimerId = prepareSleep (lmtp_writeTimeoutCbk, cxn->lmtp_writeTimeout,
930                                                  cxn) ;
931
932
933
934     cxn->lmtp_tofree_str = str;
935     return WriteToWire_str(cxn, lmtp_writeCB, cxn->lmtp_endpoint, str, slen);
936 }
937
938 static conn_ret WriteArticle(connection_t *cxn, Buffer *array)
939 {
940     int array_len = bufferArrayLen (array);
941     int lup=0;
942     int result;
943
944     for (lup=0;lup<array_len;lup++)
945     {
946         int current_size;
947         Buffer current_buf;
948         char *current_start;
949
950         current_buf = array[lup];
951                 
952         current_size = bufferDataSize( current_buf );
953         
954         current_start = bufferBase( current_buf );
955
956     }
957
958     /* just call writetowire since it's easy */
959     result = WriteToWire(cxn, lmtp_writeCB, cxn->lmtp_endpoint, array);
960
961     if (result!=RET_OK)
962     {
963         return result;
964     }
965
966     cxn->lmtp_state = LMTP_WRITING_CONTENTS;
967
968     return RET_OK;
969 }
970
971 /************************ END Writing to Network functions *****************/
972
973
974
975 /*
976  * Adds a cancel item to the control queue
977  * Cancel item to delete message with <msgid> in <folder>
978  *
979  * cxn       - connection object
980  * folder    - pointer to start of folder string (this is a pointer into the actual message buffer)
981  * folderlen - length of folder string 
982  * msgid     - pointer to start of msgid string (this is a pointer into the actual message buffer)
983  * msgidlen  - length of msgid string 
984  * art       - the article for this control message (NULL if this cancel object lacks one)
985  * must      - if must be accepted into queue
986  */
987
988 static conn_ret addCancelItem(connection_t *cxn, 
989                               char *folder, int folderlen,
990                               char *msgid, int msgidlen,
991                               Article art, int must)
992 {
993     control_item_t *item;
994     conn_ret result;
995     int i;
996
997     ASSERT(folder); ASSERT(msgid); ASSERT(cxn);
998
999     /* sanity check folder, msgid */
1000     for (i = 0; i < folderlen; i++) ASSERT(!isspace((int) folder[i]));
1001     for (i = 0; i < msgidlen; i++) ASSERT(!isspace((int) msgid[i]));
1002
1003     /* create the object */
1004     item = xcalloc (1, sizeof(control_item_t));
1005
1006     item->folder = xcalloc(folderlen+1, 1);
1007     memcpy(item->folder, folder, folderlen);
1008     item->folder[folderlen] = '\0';
1009     
1010     item->msgid = xcalloc (msgidlen+1, 1);
1011     memcpy(item->msgid, msgid, msgidlen);
1012     item->msgid[msgidlen] = '\0';
1013
1014     item->article = art;
1015     
1016     /* try to add to the queue (counts if art isn't null) */
1017     result = AddToQueue(&(cxn->imap_controlMsg_q), item, CANCEL_MSG, (art != NULL), must);
1018     if (result != RET_OK) {
1019         d_printf(1,"%s:%d addCancelItem(): "
1020                  "I thought we had in space in [imap] queue "
1021                  "but apparently not\n",
1022                  hostPeerName (cxn->myHost), cxn->ident);
1023
1024         /* cleanup */
1025         free(item->folder);
1026         free(item->msgid);
1027         free(item);
1028
1029         return result;
1030     }
1031                 
1032     return RET_OK;
1033 }
1034
1035 static conn_ret AddControlMsg(connection_t *cxn, 
1036                               Article art, 
1037                               Buffer *bufs, 
1038                               char *control_header, 
1039                               char *control_header_end, 
1040                               bool must)
1041 {
1042     char *rcpt_list = NULL, *rcpt_list_end;
1043     control_item_t *item;
1044     conn_ret res = RET_OK;
1045     int t;
1046
1047     /* make sure contents ok; this also should load it into memory */
1048     if (!artContentsOk (art)) {
1049         d_printf(0, "%s:%d AddControlMsg(): "
1050                  "artContentsOk() said article was bad\n",
1051                  hostPeerName (cxn->myHost), cxn->ident);
1052         hostArticleIsMissing (cxn->myHost, cxn, art);
1053         return RET_FAIL;
1054     }
1055
1056     res = RET_OK;
1057     /* now let's look at the control to see what it is */
1058     if (!strncasecmp(control_header,"newgroup",8)) {
1059         control_header += 8;
1060         t = CREATE_FOLDER;
1061     } else if (!strncasecmp(control_header,"rmgroup",7)) {
1062         /* jump past "rmgroup" */
1063         control_header += 7;
1064         t = DELETE_FOLDER;
1065     } else if (!strncasecmp(control_header,"cancel",6)) {
1066         t = CANCEL_MSG;
1067         control_header += 6;
1068     } else {
1069         /* unrecognized type */
1070         char tmp[100];
1071         char *endstr;
1072         size_t clen;
1073
1074         endstr = strchr(control_header,'\n');
1075         clen = endstr - control_header;
1076
1077         if (clen > sizeof(tmp)-1) clen = sizeof(tmp)-1;
1078
1079         memcpy(tmp,control_header, clen);
1080         tmp[clen]='\0';
1081         
1082         d_printf(0,"%s:%d Don't understand control header [%s]\n",
1083                  hostPeerName (cxn->myHost), cxn->ident,tmp);
1084         return RET_FAIL;
1085     }
1086
1087     switch (t) {
1088     case CREATE_FOLDER:
1089     case DELETE_FOLDER:
1090     {
1091         int folderlen;
1092
1093         /* go past all white space */
1094         while ((*control_header == ' ') && 
1095                (control_header != control_header_end)) {
1096             control_header++;
1097         }
1098
1099         /* trim trailing whitespace */
1100         while (control_header_end[-1] == ' ') {
1101             control_header_end--;
1102         }
1103
1104         if (control_header >= control_header_end) {
1105             d_printf(0,"%s:%d addControlMsg(): "
1106                      "newgroup/rmgroup header has no group specified\n",
1107                      hostPeerName (cxn->myHost), cxn->ident);
1108             return RET_FAIL;
1109         }
1110
1111         folderlen = control_header_end - control_header;
1112
1113         item = xcalloc(1, sizeof(control_item_t));
1114
1115         item->folder = xcalloc(folderlen + 1, 1);
1116         memcpy(item->folder, control_header, folderlen);
1117         item->folder[folderlen] = '\0';
1118
1119         item->article = art;
1120
1121         if (AddToQueue(&(cxn->imap_controlMsg_q), item, t, 1, must) != RET_OK) {
1122             d_printf(1,"%s:%d addCancelItem(): "
1123                      "I thought we had in space in [imap] queue"
1124                      " but apparently not\n",
1125                      hostPeerName (cxn->myHost), cxn->ident);
1126             free(item->folder);
1127             free(item);
1128             return RET_FAIL;
1129         }
1130
1131         break;
1132     }
1133
1134     case CANCEL_MSG:
1135     {
1136         char *str, *laststart;
1137
1138         while (((*control_header) == ' ') && 
1139                (control_header != control_header_end))
1140         {
1141             control_header++;
1142         }
1143
1144         if (control_header == control_header_end)
1145         {
1146             d_printf(0, "%s:%d Control header contains cancel "
1147                         "with no msgid specified\n",
1148                      hostPeerName (cxn->myHost), cxn->ident);
1149             return RET_FAIL;
1150         }
1151
1152         if (FindHeader(bufs, "Newsgroups", &rcpt_list, &rcpt_list_end)!=RET_OK)
1153         {
1154             d_printf(0,"%s:%d Cancel msg contains no newsgroups header\n",
1155                      hostPeerName (cxn->myHost), cxn->ident);
1156             return RET_FAIL;
1157         }
1158
1159         str = rcpt_list;
1160         laststart = rcpt_list;
1161
1162         while (str != rcpt_list_end)
1163         {
1164             if (*str == ',') {
1165                 /* eliminate leading whitespace */
1166                 while (((*laststart) ==' ') || ((*laststart)=='\t'))
1167                 {
1168                     laststart++;
1169                 }
1170
1171                 res = addCancelItem(cxn, laststart, 
1172                                     str - laststart, 
1173                                     control_header, 
1174                                     control_header_end - control_header,
1175                                     NULL, must);
1176                 if (res!=RET_OK) return res;
1177                 
1178                 laststart = str+1;
1179             }
1180
1181             str++;
1182         }
1183         
1184         if (laststart<str)
1185         {
1186
1187             res = addCancelItem(cxn, laststart, str - laststart, 
1188                                 control_header,
1189                                 control_header_end - control_header, 
1190                                 art, must);
1191             if (res!=RET_OK) return res;
1192         }
1193         break;
1194     }
1195     default:
1196         /* huh?!? */
1197         d_printf(0, "%s:%d internal error in addControlMsg()\n",
1198                      hostPeerName (cxn->myHost), cxn->ident);
1199     }
1200     return RET_FAIL;
1201 }
1202
1203 /*
1204  * Show msg handling statistics
1205  */
1206
1207 static void show_stats(connection_t *cxn)
1208 {   
1209     d_printf(0, "%s:%d\n",hostPeerName (cxn->myHost), cxn->ident);
1210     d_printf(0, "  imap queue = %d lmtp queue = %d\n",
1211              QueueItems(&(cxn->imap_controlMsg_q)),
1212              QueueItems(&(cxn->lmtp_todeliver_q)));
1213     d_printf(0,"  imap state = %s\n", imap_stateToString(cxn->imap_state));
1214     d_printf(0,"  lmtp state = %s\n", lmtp_stateToString(cxn->lmtp_state));
1215     d_printf(0,"  delivered:  yes: %d no: %d\n",
1216              cxn->lmtp_succeeded, 
1217              cxn->lmtp_failed);
1218     d_printf(0,"  cancel:     yes: %d no: %d\n",
1219              cxn->cancel_succeeded, cxn->cancel_failed);
1220     d_printf(0,"  create:     yes: %d no: %d\n",
1221              cxn->create_succeeded, cxn->create_failed);
1222     d_printf(0,"  remove:     yes: %d no: %d\n",
1223              cxn->remove_succeeded, cxn->remove_failed);
1224 }
1225
1226 /**************************** SASL helper functions ******************************/
1227
1228 #ifdef HAVE_SASL
1229 /* callback to get userid or authid */
1230 static int getsimple(void *context __attribute__((unused)),
1231                      int id,
1232                      const char **result,
1233                      unsigned *len)
1234 {
1235   char *username;
1236   char *authid;
1237
1238   if (! result)
1239     return SASL_BADPARAM;
1240
1241
1242   switch (id) {
1243   case SASL_CB_GETREALM:
1244       *result = deliver_realm;
1245       if (len)
1246           *len = deliver_realm ? strlen(deliver_realm) : 0;
1247       break;
1248
1249   case SASL_CB_USER:
1250       *result = deliver_username;
1251       if (len)
1252           *len = deliver_username ? strlen(deliver_username) : 0;
1253     break;
1254   case SASL_CB_AUTHNAME:
1255     authid=deliver_authname;
1256     *result = authid;
1257     if (len)
1258       *len = authid ? strlen(authid) : 0;
1259       break;
1260   case SASL_CB_LANGUAGE:
1261     *result = NULL;
1262     if (len)
1263       *len = 0;
1264     break;
1265   default:
1266     return SASL_BADPARAM;
1267   }
1268   return SASL_OK;
1269 }
1270
1271 /* callback to get password */
1272 static int
1273 getsecret(sasl_conn_t *conn,
1274           void *context __attribute__((unused)),
1275           int id,
1276           sasl_secret_t **psecret)
1277 {
1278   size_t passlen;
1279
1280   if (! conn || ! psecret || id != SASL_CB_PASS)
1281     return SASL_BADPARAM;
1282
1283   if (deliver_password==NULL)
1284   {
1285       d_printf(0,"SASL requested a password but I don't have one\n");
1286       return SASL_FAIL;
1287   }
1288
1289   passlen = strlen(deliver_password);
1290   *psecret = xmalloc(sizeof(sasl_secret_t) + passlen + 1);
1291   if (! *psecret)
1292     return SASL_FAIL;
1293
1294   strlcpy((*psecret)->data, deliver_password, passlen + 1);
1295   (*psecret)->len = passlen;
1296
1297   return SASL_OK;
1298 }
1299
1300
1301 /* callbacks we support */
1302 static sasl_callback_t saslcallbacks[] = {
1303   {
1304     SASL_CB_GETREALM, &getsimple, NULL
1305   }, {
1306     SASL_CB_USER, &getsimple, NULL
1307   }, {
1308     SASL_CB_AUTHNAME, &getsimple, NULL
1309   }, {
1310     SASL_CB_PASS, &getsecret, NULL    
1311   }, {
1312     SASL_CB_LIST_END, NULL, NULL
1313   }
1314 };
1315
1316 static sasl_security_properties_t *make_secprops(int min,int max)
1317 {
1318   sasl_security_properties_t *ret=
1319     xmalloc(sizeof(sasl_security_properties_t));
1320
1321   ret->maxbufsize=1024;
1322   ret->min_ssf=min;
1323   ret->max_ssf=max;
1324
1325   ret->security_flags=0;
1326   ret->property_names=NULL;
1327   ret->property_values=NULL;
1328
1329   return ret;
1330 }
1331
1332 #ifndef NI_WITHSCOPEID
1333 #define NI_WITHSCOPEID  0
1334 #endif
1335 #ifndef NI_MAXHOST
1336 #define NI_MAXHOST      1025
1337 #endif
1338 #ifndef NI_MAXSERV
1339 #define NI_MAXSERV      32
1340 #endif
1341
1342 static int iptostring(const struct sockaddr *addr, socklen_t addrlen,
1343                      char *out, unsigned outlen) {
1344     char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
1345     
1346     if(!addr || !out) return SASL_BADPARAM;
1347
1348     getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
1349                 NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);
1350
1351     if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
1352         return SASL_BUFOVER;
1353
1354     snprintf(out, outlen, "%s;%s", hbuf, pbuf);
1355
1356     return SASL_OK;
1357 }
1358
1359 static conn_ret SetSASLProperties(sasl_conn_t *conn, int sock, int minssf, int maxssf)
1360 {
1361   int saslresult;
1362   sasl_security_properties_t *secprops=NULL;
1363   int addrsize=sizeof(struct sockaddr_in);
1364   char localip[60], remoteip[60];
1365   struct sockaddr_in saddr_l;
1366   struct sockaddr_in saddr_r;
1367
1368   /* create a security structure and give it to sasl */
1369   secprops = make_secprops(minssf, maxssf);
1370   if (secprops != NULL)
1371   {
1372     sasl_setprop(conn, SASL_SEC_PROPS, secprops);
1373     free(secprops);
1374   }
1375
1376   if (getpeername(sock,(struct sockaddr *)&saddr_r,&addrsize)!=0)
1377     return RET_FAIL;
1378
1379   if (iptostring((struct sockaddr *)&saddr_r, sizeof(struct sockaddr_in),
1380                 remoteip, sizeof(remoteip)))
1381     return RET_FAIL;
1382
1383   if (sasl_setprop(conn, SASL_IPREMOTEPORT, remoteip)!=SASL_OK)
1384     return RET_FAIL;
1385   
1386   addrsize=sizeof(struct sockaddr_in);
1387   if (getsockname(sock,(struct sockaddr *) &saddr_l,&addrsize)!=0)
1388     return RET_FAIL;
1389
1390   if (iptostring((struct sockaddr *)&saddr_l, sizeof(struct sockaddr_in),
1391                  localip, sizeof(localip)))
1392     return RET_FAIL;
1393     
1394   if (sasl_setprop(conn, SASL_IPLOCALPORT, localip)!=SASL_OK)
1395     return RET_FAIL;
1396   
1397   return RET_OK;
1398 }
1399 #endif /* HAVE_SASL */
1400
1401 /************************** END SASL helper functions ******************************/
1402
1403 /************************* Startup functions **********************************/
1404
1405 static conn_ret Initialize(connection_t *cxn, int respTimeout)
1406 {
1407 #ifdef HAVE_SASL
1408     conn_ret saslresult;
1409 #endif /* HAVE_SASL */
1410
1411     d_printf(1,"%s:%d initializing....\n",
1412              hostPeerName (cxn->myHost), cxn->ident);
1413
1414 #ifdef HAVE_SASL
1415     /* only call sasl_client_init() once */
1416     if (initialized_sasl == 0)
1417     {
1418         /* Initialize SASL */
1419         saslresult=sasl_client_init(saslcallbacks);
1420         
1421         if (saslresult!=SASL_OK)
1422         {
1423             d_printf(0,
1424                      "%s:%d Error initializing SASL (sasl_client_init) (%s)\n",
1425                      hostPeerName (cxn->myHost), cxn->ident,
1426                      sasl_errstring(saslresult, NULL, NULL));
1427             return RET_FAIL;
1428         } else {
1429             initialized_sasl = 1;
1430         }
1431     }
1432 #endif /* HAVE_SASL */
1433
1434     cxn->lmtp_rBuffer = newBuffer(4096);
1435     if (cxn->lmtp_rBuffer == NULL)
1436     {
1437         d_printf(0, "%s:%d Failure allocating buffer for lmtp_rBuffer\n",
1438                  hostPeerName (cxn->myHost), cxn->ident);
1439         return RET_FAIL;
1440     }
1441     bufferAddNullByte(cxn->lmtp_rBuffer);
1442
1443
1444     cxn->imap_rBuffer = newBuffer(4096);
1445     if (cxn->imap_rBuffer == NULL)
1446     {
1447         d_printf(0, "%s:%d Failure allocating buffer for imap_rBuffer \n",
1448                  hostPeerName (cxn->myHost), cxn->ident);
1449         return RET_FAIL;
1450     }
1451     bufferAddNullByte(cxn->imap_rBuffer);
1452
1453     /* Initialize timeouts */
1454     cxn->lmtp_writeTimeout = respTimeout;
1455     cxn->lmtp_readTimeout = respTimeout;
1456     cxn->imap_writeTimeout = respTimeout;
1457     cxn->imap_readTimeout = respTimeout;
1458     cxn->lmtp_sleepTimerId = 0 ;
1459     cxn->lmtp_sleepTimeout = init_reconnect_period ;
1460     cxn->imap_sleepTimerId = 0 ;
1461     cxn->imap_sleepTimeout = init_reconnect_period ;
1462
1463     cxn->dosomethingTimeout = DOSOMETHING_TIMEOUT;
1464
1465     /* set up the write timer. */
1466     clearTimer (cxn->dosomethingTimerId) ;
1467
1468     if (cxn->dosomethingTimeout > 0)
1469         cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk, 
1470                                                 cxn->dosomethingTimeout, cxn);
1471
1472
1473
1474     return RET_OK;
1475 }
1476
1477
1478 /* initialize the network */
1479 static conn_ret init_net(char *serverFQDN, 
1480                          int port,
1481                          int *sock)
1482 {
1483   struct sockaddr_in addr;
1484   struct hostent *hp;
1485
1486   if ((hp = gethostbyname(serverFQDN)) == NULL) {
1487       d_printf(0, "gethostbyname(): %s\n", strerror(errno));
1488     return RET_FAIL;
1489   }
1490
1491   if (( (*sock) = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1492       d_printf(0, "socket(): %s\n", strerror(errno));
1493       return RET_FAIL;  
1494   }
1495
1496   addr.sin_family = AF_INET;
1497   memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
1498   addr.sin_port = htons(port);
1499
1500   if (connect( (*sock), (struct sockaddr *) &addr, sizeof (addr)) < 0) {
1501       d_printf(0,"connect(): %s\n",
1502                strerror(errno));
1503       return RET_FAIL;
1504   }
1505
1506   return RET_OK;
1507 }
1508
1509
1510
1511 static conn_ret SetupLMTPConnection(connection_t *cxn,
1512                                     char *serverName,
1513                                     int port)
1514 {
1515 #ifdef HAVE_SASL
1516     int saslresult;
1517 #endif /* HAVE_SASL */
1518     conn_ret result;
1519
1520     cxn->lmtp_port = port;
1521
1522     if (serverName==NULL)
1523     {
1524         d_printf(0, "%s:%d serverName is null\n",
1525                  hostPeerName (cxn->myHost), cxn->ident);
1526         return RET_FAIL;        
1527     }
1528
1529 #ifdef HAVE_SASL
1530     /* Free the SASL connection if we already had one */
1531     if (cxn->saslconn_lmtp!=NULL)
1532     {
1533         sasl_dispose(&cxn->saslconn_lmtp);
1534     }
1535
1536     /* Start SASL */
1537     saslresult=sasl_client_new("lmtp",
1538                                serverName,
1539                                NULL,
1540                                NULL,
1541                                NULL,
1542                                0,
1543                                &cxn->saslconn_lmtp);
1544
1545     if (saslresult != SASL_OK)
1546     {
1547
1548         d_printf(0, "%s:%d:LMTP Error creating a new SASL connection (%s)\n",
1549                  hostPeerName (cxn->myHost), cxn->ident,
1550                  sasl_errstring(saslresult,NULL,NULL));
1551         return RET_FAIL;
1552     }
1553 #endif /* HAVE_SASL */
1554
1555     /* Connect the Socket */
1556     result = init_net(serverName,
1557                       LMTP_PORT, /*port,*/
1558                       &(cxn->sockfd_lmtp));
1559    
1560     if (result != RET_OK)
1561     {
1562         d_printf(0, "%s:%d unable to connect to lmtp host\n",
1563                  hostPeerName (cxn->myHost), cxn->ident);
1564         return RET_FAIL;
1565     }
1566
1567     if (cxn->lmtp_respBuffer) free(cxn->lmtp_respBuffer);
1568     cxn->lmtp_respBuffer = xmalloc (4096);
1569     cxn->lmtp_respBuffer[0]='\0';
1570
1571     /* Free if we had an existing one */
1572     if (cxn->lmtp_endpoint != NULL)
1573     {
1574         delEndPoint(cxn->lmtp_endpoint);
1575         cxn->lmtp_endpoint = NULL;
1576     }
1577
1578     cxn->lmtp_endpoint = newEndPoint(cxn->sockfd_lmtp);
1579     if (cxn->lmtp_endpoint == NULL)
1580     {
1581         d_printf(0, "%s:%d:LMTP failure creating endpoint\n",
1582                  hostPeerName (cxn->myHost), cxn->ident);
1583         return RET_FAIL;
1584     }
1585
1586 #ifdef HAVE_SASL
1587     /* Set the SASL properties */
1588     result = SetSASLProperties(cxn->saslconn_lmtp, cxn->sockfd_lmtp, 
1589                                0, 0);
1590     
1591     if (result != RET_OK)
1592     {
1593         d_printf(0,"%s:%d:LMTP error setting SASL properties\n",
1594                  hostPeerName (cxn->myHost), cxn->ident);
1595         return RET_FAIL;
1596     }
1597 #endif /* HAVE_SASL */
1598
1599
1600     return RET_OK;
1601 }
1602
1603 static conn_ret SetupIMAPConnection(connection_t *cxn,
1604                                     char *serverName,
1605                                     int port)
1606 {
1607 #ifdef HAVE_SASL
1608     int saslresult;
1609 #endif /* HAVE_SASL */
1610     conn_ret result;
1611
1612     cxn->imap_port = port;
1613
1614     if (serverName==NULL)
1615     {
1616         d_printf(0,"%s:%d:IMAP Servername is null",
1617                  hostPeerName (cxn->myHost), cxn->ident);
1618         return RET_FAIL;        
1619     }
1620
1621 #ifdef HAVE_SASL
1622     /* Free the SASL connection if we already had one */
1623     if (cxn->imap_saslconn!=NULL)
1624     {
1625         sasl_dispose(&cxn->imap_saslconn);
1626     }
1627
1628     /* Start SASL */
1629     saslresult=sasl_client_new("imap",
1630                                serverName,
1631                                NULL,
1632                                NULL,
1633                                NULL,
1634                                0,
1635                                &cxn->imap_saslconn);
1636
1637     if (saslresult != SASL_OK)
1638     {
1639         d_printf(0,"%s:%d:IMAP Error creating a new SASL connection (%s)",
1640                  hostPeerName (cxn->myHost), cxn->ident,
1641                  sasl_errstring(saslresult,NULL,NULL));
1642         return RET_FAIL;
1643     }
1644 #endif /* HAVE_SASL */
1645
1646     /* Connect the Socket */
1647     result = init_net(serverName,
1648                       port,
1649                       &(cxn->imap_sockfd));
1650    
1651     if (result != RET_OK)
1652     {
1653         d_printf(0,"%s:%d:IMAP Unable to start network connection for IMAP",
1654                  hostPeerName (cxn->myHost), cxn->ident);
1655         return RET_FAIL;
1656     }
1657
1658     if (cxn->imap_respBuffer) free(cxn->imap_respBuffer);
1659     cxn->imap_respBuffer = xmalloc (4096);
1660     cxn->imap_respBuffer[0]='\0';
1661
1662     /* Free if we had an existing one */
1663     if (cxn->imap_endpoint != NULL)
1664     {
1665         delEndPoint(cxn->imap_endpoint);
1666         cxn->imap_endpoint = NULL;
1667     }
1668
1669     cxn->imap_endpoint = newEndPoint(cxn->imap_sockfd);
1670     if (cxn->imap_endpoint == NULL)
1671     {
1672         d_printf(0,"%s:%d:IMAP Failure creating imap endpoint\n",
1673                  hostPeerName (cxn->myHost), cxn->ident);
1674         return RET_FAIL;
1675     }
1676
1677 #ifdef HAVE_SASL
1678     /* Set the SASL properties */
1679     result = SetSASLProperties(cxn->imap_saslconn, cxn->imap_sockfd, 
1680                                0, 0);    
1681     if (result != RET_OK)
1682     {
1683         d_printf(0,"%s:%d:IMAP Error setting sasl properties",
1684                  hostPeerName (cxn->myHost), cxn->ident);
1685         return result;
1686     }
1687 #endif /* HAVE_SASL */
1688
1689
1690     return RET_OK;
1691 }
1692
1693 /************************* END Startup functions **********************************/
1694
1695 /* Return the response code for this line
1696    -1 if it doesn't seem to have one
1697 */
1698 static int ask_code(char *str)
1699 {
1700     int ret = 0;
1701     
1702     if (str==NULL) return -1;
1703
1704     if (strlen(str) < 3) return -1;
1705
1706     /* check to make sure 0-2 are digits */
1707     if ((isdigit((int) str[0])==0) ||
1708         (isdigit((int) str[1])==0) ||
1709         (isdigit((int) str[2])==0))
1710     {
1711         d_printf(0,
1712                  "Parse error: response does not begin with a code [%s]\n",
1713                  str);
1714         return -1;
1715     }
1716
1717
1718     ret = ((str[0]-'0')*100)+
1719           ((str[1]-'0')*10)+
1720           (str[2]-'0');
1721     
1722     return ret;
1723 }
1724
1725 /* is this a continuation or not?
1726    220-fdfsd is        (1)
1727    220 fdsfs is not    (0)
1728  */
1729
1730 static int ask_keepgoing(char *str)
1731 {
1732     if (str==NULL) return 0;
1733     if (strlen(str) < 4) return 0;
1734
1735     if (str[3]=='-') return 1;
1736
1737     return 0;
1738 }
1739
1740
1741 static conn_ret lmtp_listenintro(connection_t *cxn)
1742 {
1743     Buffer *readBuffers;
1744
1745     /* set up to receive */
1746     readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
1747     prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);    
1748
1749     cxn->lmtp_state = LMTP_READING_INTRO;
1750
1751     return RET_OK;
1752 }
1753
1754
1755
1756 /************************** IMAP functions ***********************/
1757
1758 static conn_ret imap_Connect(connection_t *cxn)
1759 {
1760     conn_ret result;
1761
1762     ASSERT(cxn->imap_sleepTimerId == 0);
1763
1764     /* make the IMAP connection */
1765     result = SetupIMAPConnection(cxn,
1766                                  cxn->ServerName,
1767                                  IMAP_PORT); 
1768
1769     /* Listen to the intro and start the authenticating process */
1770     result = imap_listenintro(cxn);
1771
1772     return result;
1773 }
1774
1775 /*
1776  * This is called when the data write timeout for the remote
1777  * goes off. We tear down the connection and notify our host.
1778  */
1779 static void imap_writeTimeoutCbk (TimeoutId id UNUSED, void *data)
1780 {
1781   connection_t *cxn = (Connection) data ;
1782   const char *peerName ;
1783
1784   peerName = hostPeerName (cxn->myHost) ;
1785
1786   syslog (LOG_WARNING, "timeout for %s", peerName);
1787   d_printf(0, "%s: shutting down non-responsive IMAP connection (%s)\n",
1788            hostPeerName (cxn->myHost), 
1789            imap_stateToString(cxn->imap_state));
1790
1791   cxnLogStats (cxn,true) ;
1792
1793   imap_Disconnect(cxn);
1794 }
1795
1796 /*
1797  * This is called when the timeout for the reponse from the remote
1798  * goes off. We tear down the connection and notify our host.
1799  */
1800 static void imap_readTimeoutCbk (TimeoutId id, void *data)
1801 {
1802   Connection cxn = (Connection) data ;
1803   const char *peerName ;
1804
1805   ASSERT (id == cxn->imap_readBlockedTimerId) ;
1806
1807   peerName = hostPeerName (cxn->myHost) ;
1808
1809   warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ;
1810   d_printf(0, "%s:%d shutting down non-responsive IMAP connection (%s)\n",
1811            hostPeerName (cxn->myHost), cxn->ident,
1812            imap_stateToString(cxn->imap_state));
1813
1814   cxnLogStats (cxn,true);
1815
1816   if (cxn->imap_state == IMAP_DISCONNECTED)
1817     {
1818       imap_Disconnect(cxn);
1819       lmtp_Disconnect(cxn);
1820       delConnection (cxn) ;
1821     }
1822   else {
1823       imap_Disconnect(cxn);
1824   }      
1825 }
1826
1827 /*
1828  * Called by the EndPoint class when the timer goes off
1829  */
1830 static void imap_reopenTimeoutCbk (TimeoutId id, void *data)
1831 {
1832   Connection cxn = (Connection) data ;
1833
1834   ASSERT (id == cxn->imap_sleepTimerId) ;
1835
1836   cxn->imap_sleepTimerId = 0 ;
1837
1838   d_printf(1,"%s:%d:IMAP Reopen timer rang. Try to make new connection now\n",
1839            hostPeerName (cxn->myHost), cxn->ident) ;
1840   
1841   if (cxn->imap_state != IMAP_DISCONNECTED)
1842     {
1843       warn ("%s:%d cxnsleep connection in bad state: %s",
1844             hostPeerName (cxn->myHost), cxn->ident,
1845             imap_stateToString (cxn->imap_state)) ;
1846     }
1847   else {
1848       if (imap_Connect(cxn) != RET_OK)
1849           prepareReopenCbk(cxn, 0);
1850   }
1851 }
1852
1853 static void imap_Disconnect(connection_t *cxn)
1854 {
1855     clearTimer (cxn->imap_sleepTimerId) ;
1856     cxn->imap_sleepTimerId = 0;
1857     clearTimer (cxn->imap_readBlockedTimerId) ;
1858     clearTimer (cxn->imap_writeBlockedTimerId) ;
1859
1860     DeferAllArticles(cxn, &(cxn->imap_controlMsg_q)) ;      /* give any articles back to Host */
1861
1862     cxn->imap_state = IMAP_DISCONNECTED;
1863
1864     cxn->imap_disconnects++;
1865
1866     cxn->imap_respBuffer[0]='\0';
1867
1868     if (cxn->issue_quit == 0)
1869         prepareReopenCbk(cxn,0);
1870
1871     DeleteIfDisconnected(cxn);
1872 }
1873
1874 /************************** END IMAP functions ***********************/
1875
1876 /************************ LMTP functions **************************/
1877
1878 /*
1879  * Create a network lmtp connection
1880  * and start listening for the intro string
1881  *
1882  */
1883
1884 static conn_ret lmtp_Connect(connection_t *cxn)
1885 {
1886     conn_ret result;
1887
1888     ASSERT(cxn->lmtp_sleepTimerId == 0);
1889
1890     /* make the LMTP connection */
1891     result = SetupLMTPConnection(cxn,
1892                                  cxn->ServerName,
1893                                  LMTP_PORT);
1894
1895     if (result!=RET_OK) return result;
1896
1897     /* Listen to the intro */
1898     result = lmtp_listenintro(cxn);
1899
1900     return result;
1901 }
1902
1903
1904
1905 static void lmtp_Disconnect(connection_t *cxn)
1906 {
1907     clearTimer (cxn->lmtp_sleepTimerId) ;
1908     cxn->lmtp_sleepTimerId = 0;
1909     clearTimer (cxn->lmtp_readBlockedTimerId) ;
1910     clearTimer (cxn->lmtp_writeBlockedTimerId) ;
1911
1912     /* give any articles back to Host */
1913     DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q)) ;      
1914
1915     cxn->lmtp_state = LMTP_DISCONNECTED;
1916
1917     cxn->lmtp_disconnects++;
1918
1919     cxn->lmtp_respBuffer[0]='\0';
1920
1921     if (cxn->issue_quit == 0)
1922         prepareReopenCbk(cxn,1);
1923
1924     DeleteIfDisconnected(cxn);
1925 }
1926
1927
1928
1929 /*
1930  * Called by the EndPoint class when the timer goes off
1931  */
1932 static void lmtp_reopenTimeoutCbk (TimeoutId id, void *data)
1933 {
1934   Connection cxn = (Connection) data ;
1935
1936   ASSERT (id == cxn->lmtp_sleepTimerId) ;
1937
1938   cxn->lmtp_sleepTimerId = 0 ;
1939
1940   d_printf(1,"%s:%d:LMTP Reopen timer rang. Try to make new connection now\n",
1941            hostPeerName (cxn->myHost), cxn->ident) ;
1942         
1943   if (cxn->lmtp_state != LMTP_DISCONNECTED)
1944     {
1945       warn ("%s:%d cxnsleep connection in bad state: %s",
1946             hostPeerName (cxn->myHost), cxn->ident,
1947             lmtp_stateToString (cxn->lmtp_state)) ;
1948     }
1949   else {
1950       if (lmtp_Connect(cxn) != RET_OK)
1951           prepareReopenCbk(cxn, 1);
1952   }
1953 }
1954
1955 /*
1956  * Set up the callback used when the Connection is sleeping (i.e. will try
1957  * to reopen the connection).
1958  *
1959  * type (0 = imap, 1 = lmtp)
1960  */
1961 static void prepareReopenCbk (Connection cxn, int type)
1962 {
1963     /* xxx check state */
1964
1965
1966
1967     if (type == 0) {
1968
1969         cxn->imap_sleepTimerId = prepareSleep (imap_reopenTimeoutCbk, 
1970                                                cxn->imap_sleepTimeout, cxn) ;
1971         d_printf (1,"%s:%d IMAP connection error\n"
1972                   "  will try to reconnect in %d seconds\n",
1973                   hostPeerName (cxn->myHost), cxn->ident, 
1974                   cxn->imap_sleepTimeout) ;
1975     } else {
1976         cxn->lmtp_sleepTimerId = prepareSleep (lmtp_reopenTimeoutCbk, 
1977                                                cxn->lmtp_sleepTimeout, cxn) ;
1978         d_printf (1,"%s:%d:LMTP connection error\n"
1979                   "will try to reconnect in %d seconds\n",
1980                   hostPeerName (cxn->myHost), cxn->ident, 
1981                   cxn->lmtp_sleepTimeout) ;
1982     }
1983
1984     /* bump the sleep timer amount each time to wait longer and longer. Gets
1985        reset in resetConnection() */
1986     if (type == 0) {
1987         cxn->imap_sleepTimeout *= 2 ;
1988         if (cxn->imap_sleepTimeout > max_reconnect_period)
1989             cxn->imap_sleepTimeout = max_reconnect_period ;
1990     } else {
1991         cxn->lmtp_sleepTimeout *= 2 ;
1992         if (cxn->lmtp_sleepTimeout > max_reconnect_period)
1993             cxn->lmtp_sleepTimeout = max_reconnect_period ;
1994     }
1995 }
1996
1997 /*
1998  * This is called when the timeout for the reponse from the remote
1999  * goes off. We tear down the connection and notify our host.
2000  */
2001 static void lmtp_readTimeoutCbk (TimeoutId id, void *data)
2002 {
2003   Connection cxn = (Connection) data ;
2004   const char *peerName ;
2005
2006   ASSERT (id == cxn->lmtp_readBlockedTimerId) ;
2007
2008   peerName = hostPeerName (cxn->myHost) ;
2009
2010   warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ;
2011   d_printf(0,"%s:%d shutting down non-responsive LMTP connection (%s)\n",
2012            hostPeerName (cxn->myHost), cxn->ident,
2013            lmtp_stateToString(cxn->lmtp_state));
2014
2015   cxnLogStats (cxn,true) ;
2016
2017   if (cxn->lmtp_state == LMTP_DISCONNECTED) {
2018       imap_Disconnect(cxn);
2019       lmtp_Disconnect(cxn);
2020       delConnection (cxn) ;
2021   } else {
2022       lmtp_Disconnect(cxn);
2023   }      
2024 }
2025
2026
2027
2028 /*
2029  * This is called when the data write timeout for the remote
2030  * goes off. We tear down the connection and notify our host.
2031  */
2032 static void lmtp_writeTimeoutCbk (TimeoutId id UNUSED, void *data)
2033 {
2034     connection_t *cxn = (Connection) data ;
2035     const char *peerName ;
2036
2037     peerName = hostPeerName (cxn->myHost) ;
2038
2039     syslog (LOG_WARNING, "timeout for %s", peerName);
2040     d_printf(0, "%s:%d shutting down non-responsive LMTP connection (%s)\n",
2041              hostPeerName (cxn->myHost), cxn->ident,
2042              lmtp_stateToString(cxn->lmtp_state)) ;
2043
2044     cxnLogStats (cxn,true);
2045
2046     lmtp_Disconnect(cxn);
2047 }
2048
2049 /************************ END LMTP functions **************************/
2050
2051 /************************** LMTP write functions ********************/
2052
2053 static conn_ret lmtp_noop(connection_t *cxn)
2054 {
2055     int result;
2056     char *p;
2057
2058     p = xstrdup("NOOP\r\n");
2059     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2060     if (result!=RET_OK) return result;
2061
2062     cxn->lmtp_state = LMTP_WRITING_NOOP;
2063
2064     return RET_OK;
2065 }
2066
2067 static conn_ret lmtp_IssueQuit(connection_t *cxn)
2068 {
2069     int result;
2070     char *p;
2071
2072     p = xstrdup("QUIT\r\n");
2073     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2074     if (result!=RET_OK) return result;
2075
2076     cxn->lmtp_state = LMTP_WRITING_QUIT;
2077
2078     return RET_OK;
2079 }
2080
2081 static conn_ret lmtp_getcapabilities(connection_t *cxn)
2082 {
2083     int result;
2084     char *p;
2085
2086     if (cxn->lmtp_capabilities != NULL)
2087     {
2088         if (cxn->lmtp_capabilities->saslmechs) {
2089             free( cxn->lmtp_capabilities->saslmechs);
2090         }
2091         free( cxn->lmtp_capabilities );
2092         cxn->lmtp_capabilities = NULL;
2093     }
2094
2095     cxn->lmtp_capabilities = xcalloc (1, sizeof(lmtp_capabilities_t));
2096     cxn->lmtp_capabilities->saslmechs = NULL;
2097
2098 #ifdef SMTPMODE
2099     p = concat("EHLO ", hostname, "\r\n", (char *) 0);
2100 #else
2101     p = concat("LHLO ", hostname, "\r\n", (char *) 0);
2102 #endif /* SMTPMODE */
2103
2104     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2105     if (result!=RET_OK) return result;
2106
2107     cxn->lmtp_state = LMTP_WRITING_LHLO;
2108
2109     return RET_OK;    
2110 }
2111
2112 #ifdef HAVE_SASL
2113 static conn_ret lmtp_authenticate(connection_t *cxn)
2114 {
2115     int saslresult;
2116     
2117     const char *mechusing;
2118     const char *out;
2119     unsigned int outlen;
2120     char *inbase64;
2121     int inbase64len;
2122     int status;
2123     int result;
2124
2125     char *p;
2126
2127     sasl_interact_t *client_interact=NULL;
2128
2129
2130     saslresult=sasl_client_start(cxn->saslconn_lmtp, 
2131                                  cxn->lmtp_capabilities->saslmechs,
2132                                  &client_interact,
2133                                  &out, &outlen,
2134                                  &mechusing);
2135
2136
2137
2138     if ((saslresult != SASL_OK) && 
2139         (saslresult != SASL_CONTINUE)) {
2140
2141         d_printf(0,"%s:%d:LMTP Error calling sasl_client_start (%s)\n",
2142                  hostPeerName (cxn->myHost), cxn->ident,
2143                  sasl_errstring(saslresult, NULL, NULL));
2144         return RET_FAIL;
2145     }
2146
2147     d_printf(1,"%s:%d:LMTP Decided to try to authenticate with SASL mechanism=%s\n",           
2148              hostPeerName (cxn->myHost), cxn->ident,mechusing);
2149
2150     if (!out)
2151     {
2152         /* no initial client response */
2153         p = concat("AUTH ", mechusing, "\r\n", (char *) 0);
2154     } else if (!outlen) {
2155         /* empty initial client response */
2156         p = concat("AUTH ", mechusing, " =\r\n", (char *) 0);
2157     } else {
2158         /* initial client response - convert to base64 */
2159         inbase64 = xmalloc(outlen*2+10);
2160
2161         saslresult = sasl_encode64(out, outlen,
2162                                    inbase64, outlen*2+10,
2163                                    (unsigned *) &inbase64len);
2164         if (saslresult != SASL_OK) return RET_FAIL;
2165         p = concat("AUTH ", mechusing, " ", inbase64, "\r\n", (char *) 0);
2166         free(inbase64);
2167     }
2168
2169     result = WriteToWire_lmtpstr(cxn, p, strlen(p));
2170
2171     cxn->lmtp_state = LMTP_WRITING_STARTAUTH;
2172
2173     return RET_OK;
2174 }
2175
2176 static imt_stat lmtp_getauthline(char *str, char **line, int *linelen)
2177 {
2178   char buf[4096];
2179   int saslresult;
2180   int response_code = -1;
2181   
2182   response_code = ask_code(str);
2183
2184   if (response_code == 334) {
2185       
2186       /* continue */
2187
2188   } else if (response_code == 235) {
2189
2190       /* woohoo! authentication complete */
2191       return STAT_OK;
2192
2193   } else {
2194       /* failure of some sort */
2195       d_printf(0,"?:?:LMTP Authentication failure (%d) [%s]\n",
2196                response_code,str);
2197       return STAT_NO;
2198   }
2199
2200   str += 4; /* jump past the "334 " */
2201
2202   *line = xmalloc(strlen(str)+30);
2203   if ((*line)==NULL) {
2204       return STAT_NO;
2205   }
2206
2207   /* decode this line */      
2208   saslresult = sasl_decode64(str, strlen(str), 
2209                              *line, strlen(str)+1, (unsigned *) linelen);
2210   if (saslresult != SASL_OK) {
2211       d_printf(0,"?:?:LMTP base64 decoding error\n");
2212       return STAT_NO;
2213   }
2214
2215   return STAT_CONT;
2216 }
2217 #endif /* HAVE_SASL */
2218
2219 static void lmtp_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b,
2220         void *d)
2221 {
2222     connection_t *cxn = (connection_t *) d;
2223     Buffer *readBuffers;
2224
2225     clearTimer (cxn->lmtp_writeBlockedTimerId) ;
2226
2227     /* Free the string that was written */
2228     freeBufferArray (b);
2229     if (cxn->lmtp_tofree_str!=NULL)
2230     {
2231         free(cxn->lmtp_tofree_str);
2232         cxn->lmtp_tofree_str=NULL;
2233     }
2234
2235     /* set up to receive */
2236     readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
2237     prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
2238
2239    /* set up the response timer. */
2240     clearTimer (cxn->lmtp_readBlockedTimerId) ;
2241
2242     if (cxn->lmtp_readTimeout > 0)
2243         cxn->lmtp_readBlockedTimerId = prepareSleep (lmtp_readTimeoutCbk, 
2244                                                      cxn->lmtp_readTimeout, cxn) ;
2245
2246
2247     switch (cxn->lmtp_state)
2248         {
2249             
2250         case LMTP_WRITING_LHLO:
2251             cxn->lmtp_state = LMTP_READING_LHLO;
2252             break;
2253
2254         case LMTP_WRITING_STARTAUTH:
2255         case LMTP_WRITING_STEPAUTH:
2256
2257             cxn->lmtp_state = LMTP_READING_STEPAUTH;
2258
2259             break;
2260
2261         case LMTP_WRITING_UPTODATA:
2262             /* expect result to rset */
2263             cxn->lmtp_state = LMTP_READING_RSET;
2264             break;
2265
2266         case LMTP_WRITING_CONTENTS:
2267             /* so we sent the whole DATA command
2268                let's see what the server responded */
2269
2270             cxn->lmtp_state = LMTP_READING_CONTENTS;
2271
2272             break;
2273
2274         case LMTP_WRITING_NOOP:
2275             cxn->lmtp_state = LMTP_READING_NOOP;
2276             break;
2277
2278         case LMTP_WRITING_QUIT:
2279             cxn->lmtp_state = LMTP_READING_QUIT;
2280             break;
2281
2282         default:
2283
2284             d_printf(0,"%s:%d:LMTP Unknown state. Internal error\n",
2285                      hostPeerName (cxn->myHost), cxn->ident) ;
2286
2287             break;
2288         }
2289 }
2290
2291 /************************** END LMTP write functions ********************/
2292
2293 /************************** IMAP sending functions ************************/
2294
2295
2296 static void imap_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b,
2297         void *d)
2298 {
2299     connection_t *cxn = (connection_t *) d;
2300     Buffer *readBuffers;
2301
2302     clearTimer (cxn->imap_writeBlockedTimerId) ;
2303
2304     /* free the string we just wrote out */
2305     freeBufferArray (b);
2306     if (cxn->imap_tofree_str!=NULL)
2307     {
2308         free(cxn->imap_tofree_str);
2309         cxn->imap_tofree_str=NULL;
2310     }
2311
2312     /* set up to receive */
2313     readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
2314     prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
2315
2316     /* set up the response timer. */
2317     clearTimer (cxn->imap_readBlockedTimerId) ;
2318
2319     if (cxn->imap_readTimeout > 0)
2320         cxn->imap_readBlockedTimerId = prepareSleep(imap_readTimeoutCbk, 
2321                                                     cxn->imap_readTimeout, 
2322                                                     cxn);
2323
2324     switch (cxn->imap_state) {
2325     case IMAP_WRITING_CAPABILITY:
2326         cxn->imap_state = IMAP_READING_CAPABILITY;
2327         break;
2328
2329     case IMAP_WRITING_STEPAUTH:
2330     case IMAP_WRITING_STARTAUTH:
2331         cxn->imap_state = IMAP_READING_STEPAUTH;
2332         break;
2333
2334     case IMAP_WRITING_CREATE:
2335         cxn->imap_state = IMAP_READING_CREATE;
2336         break;
2337
2338     case IMAP_WRITING_DELETE:
2339         cxn->imap_state = IMAP_READING_DELETE;
2340         break;
2341
2342     case IMAP_WRITING_SELECT:
2343         cxn->imap_state = IMAP_READING_SELECT;
2344         break;
2345
2346     case IMAP_WRITING_SEARCH:
2347         cxn->imap_state = IMAP_READING_SEARCH;
2348         break;
2349
2350     case IMAP_WRITING_STORE:
2351         cxn->imap_state = IMAP_READING_STORE;
2352         break;
2353
2354     case IMAP_WRITING_CLOSE:
2355         cxn->imap_state = IMAP_READING_CLOSE;
2356         break;
2357
2358     case IMAP_WRITING_NOOP:
2359         cxn->imap_state = IMAP_READING_NOOP;
2360         break;
2361
2362     case IMAP_WRITING_QUIT:
2363         cxn->imap_state = IMAP_READING_QUIT;
2364         break;
2365
2366     default:
2367         d_printf(0,"%s:%d:IMAP invalid connection state\n",
2368                  hostPeerName (cxn->myHost), cxn->ident) ;
2369         imap_Disconnect(cxn);
2370         break;
2371     }
2372 }
2373
2374 /*
2375  * Tag is already allocated
2376  */
2377
2378 static void imap_GetTag(connection_t *cxn)
2379 {
2380     sprintf(cxn->imap_currentTag,"%06d",cxn->imap_tag_num);
2381     cxn->imap_tag_num++;
2382     if (cxn->imap_tag_num >= 999999)
2383     {
2384         cxn->imap_tag_num = 0;
2385     }
2386 }
2387
2388 #ifdef HAVE_SASL
2389 static conn_ret imap_sendAuthStep(connection_t *cxn, char *str)
2390 {
2391     conn_ret result;
2392     int saslresult;
2393     char in[4096];
2394     unsigned int inlen;
2395     const char *out;
2396     unsigned int outlen;
2397     char *inbase64;
2398     unsigned int inbase64len;
2399
2400     /* base64 decode it */
2401
2402     saslresult = sasl_decode64(str, strlen(str), 
2403                                in, strlen(str)+1, &inlen);
2404     if (saslresult != SASL_OK) {
2405         d_printf(0,"%s:%d:IMAP base64 decoding error\n",
2406                  hostPeerName (cxn->myHost), cxn->ident) ;
2407         return RET_FAIL;
2408     }
2409
2410     saslresult=sasl_client_step(cxn->imap_saslconn,
2411                                 in,
2412                                 inlen,
2413                                 NULL,
2414                                 &out,
2415                                 &outlen);
2416
2417     /* check if sasl succeeded */
2418     if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
2419
2420         d_printf(0,"%s:%d:IMAP sasl_client_step failed with %s\n",
2421                  hostPeerName (cxn->myHost), cxn->ident,
2422                  sasl_errstring(saslresult,NULL,NULL));
2423         cxn->imap_state = IMAP_CONNECTED_NOTAUTH;
2424         return RET_FAIL;
2425     }
2426
2427     inbase64 = xmalloc(outlen * 2 + 10);
2428
2429     /* convert to base64 */
2430     saslresult = sasl_encode64(out, outlen,
2431                                inbase64, outlen*2, (unsigned *) &inbase64len);
2432
2433     if (saslresult != SASL_OK) return RET_FAIL;
2434
2435     /* append endline */
2436     strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10 - inbase64len);
2437     inbase64len+=2;
2438     
2439     /* send to server */
2440     result = WriteToWire_imapstr(cxn,inbase64, inbase64len);
2441     
2442     cxn->imap_state = IMAP_WRITING_STEPAUTH;
2443
2444     return result;
2445 }
2446 #endif /* HAVE_SASL */
2447
2448 static conn_ret imap_sendAuthenticate(connection_t *cxn)
2449 {
2450     int result;
2451
2452     char *p;
2453
2454 #ifdef HAVE_SASL
2455     const char *mechusing;
2456     char *inbase64;
2457     int inbase64len;
2458     int saslresult=SASL_NOMECH;
2459
2460     sasl_interact_t *client_interact=NULL;
2461
2462     if (cxn->imap_capabilities->saslmechs) {
2463         saslresult=sasl_client_start(cxn->imap_saslconn, 
2464                                      cxn->imap_capabilities->saslmechs,
2465                                      &client_interact,
2466                                      NULL, NULL,
2467                                      &mechusing);
2468     }
2469
2470
2471
2472     /* If no mechs try "login" */
2473     if (saslresult == SASL_NOMECH)
2474     {
2475
2476 #else /* HAVE_SASL */
2477
2478      { /* always do login */
2479
2480 #endif /* HAVE_SASL */
2481         d_printf(1,"%s:%d:IMAP No mechanism found. Trying login method\n",
2482                  hostPeerName (cxn->myHost), cxn->ident) ;
2483
2484         if (cxn->imap_capabilities->logindisabled==1)
2485         {
2486             d_printf(0,"%s:%d:IMAP Login command w/o security layer not allowed on this server\n",
2487                      hostPeerName (cxn->myHost), cxn->ident);
2488             return RET_FAIL;
2489         }
2490
2491         if (deliver_authname==NULL)
2492         {
2493             d_printf(0,"%s:%d:IMAP Unable to log in because can't find a authname\n",
2494                      hostPeerName (cxn->myHost), cxn->ident) ;
2495             return RET_FAIL;
2496         }
2497
2498         if (deliver_password==NULL)
2499         {
2500             d_printf(0,"%s:%d:IMAP Unable to log in because can't find a password\n",
2501                      hostPeerName (cxn->myHost), cxn->ident) ;
2502             return RET_FAIL;
2503         }
2504
2505         imap_GetTag(cxn);
2506
2507         p = concat(cxn->imap_currentTag, " LOGIN ", deliver_authname, " \"",
2508                    deliver_password, "\"\r\n", (char *) 0);
2509         
2510         result = WriteToWire_imapstr(cxn, p, strlen(p));
2511         
2512         cxn->imap_state = IMAP_WRITING_STARTAUTH;
2513         
2514         return RET_OK;       
2515     }
2516
2517 #ifdef HAVE_SASL
2518     if ((saslresult != SASL_OK) && 
2519         (saslresult != SASL_CONTINUE)) {
2520
2521         d_printf(0,"%s:%d:IMAP Error calling sasl_client_start (%s) mechusing = %s\n",
2522                  hostPeerName (cxn->myHost), cxn->ident,
2523                  sasl_errstring(saslresult, NULL, NULL), mechusing);
2524         return RET_FAIL;
2525     }
2526
2527     d_printf(1,"%s:%d:IMAP Trying to authenticate to imap with %s mechanism\n",
2528              hostPeerName (cxn->myHost), cxn->ident,
2529              mechusing);
2530
2531     imap_GetTag(cxn);
2532
2533     p = concat(cxn->imap_currentTag, " AUTHENTICATE ", mechusing, "\r\n",
2534                (char *) 0);
2535     result = WriteToWire_imapstr(cxn, p, strlen(p));
2536
2537     cxn->imap_state = IMAP_WRITING_STARTAUTH;
2538
2539     return RET_OK;
2540 #endif /* HAVE_SASL */
2541 }
2542
2543 static conn_ret imap_CreateGroup(connection_t *cxn, char *bboard)
2544 {
2545     conn_ret result;
2546     char *tosend;
2547
2548     d_printf(1,"%s:%d:IMAP Ok creating group [%s]\n",
2549              hostPeerName (cxn->myHost), cxn->ident,
2550              bboard);
2551
2552     imap_GetTag(cxn);
2553
2554     tosend = concat(cxn->imap_currentTag, " CREATE ", bboard, "\r\n",
2555                     (char *) 0);
2556
2557     result = WriteToWire_imapstr(cxn, tosend, -1);
2558     if (result!=RET_OK) return result;
2559     
2560     cxn->imap_state = IMAP_WRITING_CREATE;
2561     
2562     return RET_OK;
2563 }
2564
2565 static conn_ret imap_DeleteGroup(connection_t *cxn, char *bboard)
2566 {
2567     conn_ret result;
2568     char *tosend;
2569
2570     d_printf(1,"%s:%d:IMAP Ok removing bboard [%s]\n",
2571                  hostPeerName (cxn->myHost), cxn->ident, bboard);
2572
2573     imap_GetTag(cxn);
2574
2575     tosend = concat(cxn->imap_currentTag, " DELETE ", bboard, "\r\n",
2576                     (char *) 0);
2577     result = WriteToWire_imapstr(cxn, tosend, -1);
2578     if (result!=RET_OK) return result;
2579
2580     cxn->imap_state = IMAP_WRITING_DELETE;
2581     
2582     return RET_OK;
2583 }
2584
2585 static conn_ret imap_CancelMsg(connection_t *cxn, char *newsgroup)
2586 {
2587     conn_ret result;
2588     char *tosend;
2589
2590     ASSERT(newsgroup);
2591
2592     imap_GetTag(cxn);
2593
2594     /* select mbox */
2595     tosend = concat(cxn->imap_currentTag, " SELECT ", newsgroup, "\r\n",
2596                     (char *) 0);
2597     result = WriteToWire_imapstr(cxn, tosend, -1);
2598     if (result != RET_OK) return result;
2599
2600     cxn->imap_state = IMAP_WRITING_SELECT;
2601
2602     hostArticleOffered (cxn->myHost, cxn);
2603
2604     return RET_OK;
2605 }
2606
2607 static conn_ret imap_sendSearch(connection_t *cxn, char *msgid)
2608 {
2609     conn_ret result;
2610     char *tosend;
2611
2612     ASSERT(msgid);
2613
2614     imap_GetTag(cxn);
2615
2616     /* preform search */
2617     tosend = concat(cxn->imap_currentTag,
2618                     " UID SEARCH header \"Message-ID\" \"", msgid, "\"\r\n",
2619                     (char *) 0);
2620     result = WriteToWire_imapstr(cxn, tosend, -1);
2621     if (result != RET_OK) return result;
2622
2623     cxn->imap_state = IMAP_WRITING_SEARCH;
2624
2625     return RET_OK;
2626 }
2627
2628 static conn_ret imap_sendKill(connection_t *cxn, unsigned uid)
2629 {
2630     conn_ret result;
2631     char *tosend;
2632     size_t length;
2633
2634     imap_GetTag(cxn);
2635
2636     length = 7 + 50 + 20;
2637     tosend = xmalloc(length);
2638     snprintf(tosend,length,"%s UID STORE %d +FLAGS.SILENT (\\Deleted)\r\n",
2639              cxn->imap_currentTag, uid);
2640
2641     result = WriteToWire_imapstr(cxn, tosend, -1);
2642     if (result != RET_OK) return result;
2643
2644     cxn->imap_state = IMAP_WRITING_STORE;
2645
2646     return RET_OK;
2647 }
2648
2649 static conn_ret imap_sendSimple(connection_t *cxn, const char *atom, int st)
2650 {
2651     char *tosend;
2652     conn_ret result;
2653
2654     imap_GetTag(cxn);
2655     tosend = concat(cxn->imap_currentTag, " ", atom, "\r\n", (char *) 0);
2656
2657     result = WriteToWire_imapstr(cxn, tosend, -1);
2658     if (result != RET_OK) return result;
2659
2660     cxn->imap_state = st;
2661
2662     return RET_OK;
2663 }
2664
2665 static conn_ret imap_sendClose(connection_t *cxn)
2666 {
2667     return imap_sendSimple(cxn, "CLOSE", IMAP_WRITING_CLOSE);
2668 }
2669
2670 static conn_ret imap_sendQuit(connection_t *cxn)
2671 {
2672     return imap_sendSimple(cxn, "LOGOUT", IMAP_WRITING_QUIT);
2673 }
2674
2675 static conn_ret imap_noop(connection_t *cxn)
2676 {
2677     return imap_sendSimple(cxn, "NOOP", IMAP_WRITING_NOOP);
2678 }
2679         
2680
2681 static conn_ret imap_sendCapability(connection_t *cxn)
2682 {
2683     return imap_sendSimple(cxn, "CAPABILITY", IMAP_WRITING_CAPABILITY);
2684 }
2685
2686 /************************** END IMAP sending functions ************************/
2687
2688 /************************** IMAP reading functions ***************************/
2689
2690 static conn_ret imap_listenintro(connection_t *cxn)
2691 {
2692     Buffer *readBuffers;
2693
2694     /* set up to receive */
2695     readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
2696     prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);    
2697
2698     cxn->imap_state = IMAP_READING_INTRO;
2699
2700     return RET_OK;
2701 }
2702
2703 static conn_ret imap_ParseCapability(char *string, imap_capabilities_t **caps)
2704 {
2705     char *str = string;
2706     char *start = str;
2707     size_t mechlen;
2708
2709     /* allocate the caps structure if it doesn't already exist */
2710     if ( (*caps) == NULL)
2711         (*caps) = xcalloc(1, sizeof(imap_capabilities_t));
2712
2713     while ( (*str) != '\0')
2714     {
2715
2716         while (((*str) != '\0') && ((*str)!=' '))
2717         {
2718             str++;
2719         }
2720         
2721         if ( (*str) != '\0')
2722         {
2723             *str = '\0';
2724             str++;
2725         }
2726
2727         if ( strcasecmp(start,"IMAP4")==0)
2728         {
2729             (*caps)->imap4 = 1;
2730         } else if (strcasecmp(start,"LOGINDISABLED")==0) {
2731             (*caps)->logindisabled = 1;
2732         } else if ( strncmp(start, "AUTH=", 5)==0) {
2733             
2734             if ( (*caps)->saslmechs == NULL)
2735             {
2736                 (*caps)->saslmechs = xstrdup (start + 5);
2737             } else {
2738                 mechlen = strlen((*caps)->saslmechs) + 1;
2739                 mechlen += strlen(start + 5) + 1;
2740                 (*caps)->saslmechs = xrealloc((*caps)->saslmechs, mechlen);
2741                 strlcat((*caps)->saslmechs, " ", mechlen);
2742                 strlcat((*caps)->saslmechs, start + 5, mechlen);
2743             }
2744         }
2745         
2746         start = str;
2747           
2748     }
2749
2750     if ((*caps)->saslmechs) {
2751         d_printf(1,"?:?:IMAP parsed capabilities: saslmechs = %s\n",
2752                  (*caps)->saslmechs);
2753     }
2754
2755     return RET_OK;
2756 }
2757
2758
2759 static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d)
2760 {
2761     connection_t *cxn = (connection_t *) d;
2762     Buffer *readBuffers ;
2763
2764     int okno;
2765     char *str;
2766     char strbuf[4096];
2767     char *linestart;
2768     conn_ret ret;
2769     char *p;
2770
2771     p = bufferBase(b[0]);
2772
2773     /* Add what we got to our internal read buffer */
2774     bufferAddNullByte (b[0]) ;
2775
2776     if (i != IoDone) {
2777         errno = endPointErrno(e);
2778
2779         syslog(LOG_ERR, "%s:%d IMAP i/o failed: %m",
2780                hostPeerName (cxn->myHost), cxn->ident);
2781         freeBufferArray (b);
2782         imap_Disconnect(cxn);
2783         return;
2784     }
2785
2786     if (strchr (p, '\n') == NULL) {
2787         /* partial read. expand buffer and retry */
2788         
2789         if (expandBuffer (b[0], BUFFER_EXPAND_AMOUNT)==false) {
2790             d_printf(0,"%s:%d:IMAP expanding buffer returned false\n",
2791                      hostPeerName (cxn->myHost), cxn->ident);
2792             
2793             imap_Disconnect(cxn);
2794             return;
2795         }
2796         readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ;
2797         
2798         if (!prepareRead (e, readBuffers, imap_readCB, cxn, 1)) {
2799             imap_Disconnect(cxn);
2800         }
2801         
2802         freeBufferArray (b);
2803         return;
2804     }
2805
2806     clearTimer (cxn->imap_readBlockedTimerId) ;
2807
2808     /* we got something. add to our buffer and free b */
2809
2810     strcat(cxn->imap_respBuffer, p);
2811
2812     bufferSetDataSize( b[0], 0);
2813
2814     freeBufferArray (b);
2815
2816
2817
2818     /* goto here to take another step */
2819  reset:
2820
2821     /* see if we have a full line */
2822     ret = GetLine( cxn->imap_respBuffer , strbuf, sizeof(strbuf));
2823     str = strbuf;
2824     linestart = str;
2825
2826     /* if we don't have a full line */
2827     if ( ret == RET_NO_FULLLINE)
2828         {
2829
2830             readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
2831
2832             if ( !prepareRead (e, readBuffers, imap_readCB, cxn, 1) )
2833                 {
2834                     imap_Disconnect(cxn);
2835                 }
2836             return;
2837
2838         } else if (ret!=RET_OK)
2839             {
2840                 return;
2841             }
2842
2843     /* if untagged */
2844     if ((str[0]=='*') && (str[1]==' '))
2845     {
2846         str+=2;
2847         
2848         /* now figure out what kind of untagged it is */
2849         if (strncasecmp(str,"CAPABILITY ",11)==0)
2850         {
2851             str+=11;
2852             
2853             imap_ParseCapability(str,&(cxn->imap_capabilities));
2854             
2855         } else if (strncasecmp(str,"SEARCH",6)==0) {
2856
2857             str+=6;
2858             
2859             if ( (*str) == ' ')
2860             {
2861                 str++;
2862                 
2863                 cxn->current_control->data.control->uid = atoi(str);
2864                 
2865                 d_printf(1,"%s:%d:IMAP i think the UID = %ld\n",
2866                          hostPeerName (cxn->myHost), cxn->ident,
2867                          cxn->current_control->data.control->uid);
2868             } else {
2869                 /* it's probably a blank uid (i.e. message doesn't exist) */
2870                 cxn->current_control->data.control->uid = (unsigned long)-1;
2871             }
2872
2873             
2874         } else if (strncasecmp(str,"OK ",3)==0) {
2875             
2876             if (cxn->imap_state==IMAP_READING_INTRO)
2877             {
2878                 imap_sendCapability(cxn); /* xxx errors */
2879                 return;
2880                 
2881             } else {
2882                 
2883             }
2884             
2885             
2886         } else {
2887             /* untagged command not understood */
2888         }
2889
2890         /* always might be more to look at */
2891         goto reset;
2892
2893     } else if ((str[0]=='+') && (str[1]==' ')) {
2894
2895         str+=2;
2896
2897         if (cxn->imap_state == IMAP_READING_STEPAUTH)
2898         {
2899 #ifdef HAVE_SASL
2900             if (imap_sendAuthStep(cxn, str)!=RET_OK)
2901             {
2902                 imap_Disconnect(cxn);
2903             }
2904 #else
2905             d_printf(0,"%s:%d:IMAP got a '+ ...' without SASL. Something's wrong\n",
2906                      hostPeerName (cxn->myHost), cxn->ident);
2907             imap_Disconnect(cxn);
2908 #endif /* HAVE_SASL */
2909
2910             return;
2911         } else {
2912             d_printf(0,"%s:%d:IMAP got a '+ ...' in state where not expected\n",
2913                      hostPeerName (cxn->myHost), cxn->ident);
2914             imap_Disconnect(cxn);
2915             return;
2916         }
2917         
2918
2919         
2920     } else if (strncmp(str, cxn->imap_currentTag, IMAP_TAGLENGTH)==0) { 
2921         /* matches our tag */
2922         str += IMAP_TAGLENGTH;
2923         
2924         if (str[0]!=' ')
2925         {
2926             d_printf(0,"%s:%d:IMAP Parse error: tag with no space afterward\n",
2927                      hostPeerName (cxn->myHost), cxn->ident);
2928             imap_Disconnect(cxn);
2929             return;         
2930         }
2931         str++;
2932
2933         /* should be OK/NO */
2934         if (strncmp(str,"OK",2)==0)
2935         {
2936             okno = 1;
2937         } else {
2938             okno = 0;
2939         }
2940         
2941         switch(cxn->imap_state)
2942             {
2943             case IMAP_READING_CAPABILITY:
2944
2945                 if (okno==1) {
2946                     if (imap_sendAuthenticate(cxn)!=RET_OK)
2947                     {
2948                         d_printf(0,"%s:%d:IMAP sendauthenticate failed\n",
2949                                  hostPeerName (cxn->myHost), cxn->ident);
2950                         imap_Disconnect(cxn);
2951                     }
2952                     return;
2953                 } else {
2954                     d_printf(0,"%s:%d:IMAP CAPABILITY gave a NO response\n",
2955                              hostPeerName (cxn->myHost), cxn->ident);
2956                     imap_Disconnect(cxn);
2957                 }
2958                 return;
2959                 
2960                 break;
2961             case IMAP_READING_STEPAUTH:
2962                 
2963                 if (okno == 1) {
2964
2965                     cxn->imap_sleepTimeout = init_reconnect_period ;
2966
2967                     cxn->imap_timeCon = theTime () ;
2968                     cxn->timeCon = theTime () ;
2969                     
2970                     d_printf(0,"%s:%d IMAP authentication succeeded\n",
2971                              hostPeerName (cxn->myHost), cxn->ident);
2972
2973                     cxn->imap_disconnects=0;
2974                     
2975                     cxn->imap_state = IMAP_IDLE_AUTHED;
2976
2977                     /* try to send a message if we have one */
2978                     
2979                     imap_ProcessQueue(cxn);
2980                 } else {
2981                     d_printf(0,"%s:%d:IMAP Authentication failed with [%s]\n",               
2982                              hostPeerName (cxn->myHost), cxn->ident,str);
2983                     imap_Disconnect(cxn);
2984                 }
2985
2986                 return;
2987
2988                 break;
2989                 
2990             case IMAP_READING_CREATE:
2991
2992                     if (okno==1) {
2993                         
2994                         d_printf(1,"%s:%d:IMAP Create of bboard successful\n",
2995                                  hostPeerName (cxn->myHost), cxn->ident);
2996                                  
2997                         cxn->create_succeeded++;
2998
2999                         /* we can delete article now */
3000                         QueueForgetAbout(cxn, cxn->current_control, 
3001                                          MSG_SUCCESS);
3002                     } else {
3003                         d_printf(1,"%s:%d:IMAP Create failed with [%s] for %s\n",
3004                                  hostPeerName (cxn->myHost), cxn->ident,str,
3005                                  cxn->current_control->data.control->folder);
3006
3007                         ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3008                     }
3009
3010                     imap_ProcessQueue(cxn);
3011                         
3012                     break;
3013                     
3014             case IMAP_READING_DELETE:
3015
3016                     if (okno==1) {
3017                         d_printf(1,"%s:%d:IMAP Delete successful\n",
3018                                  hostPeerName (cxn->myHost), cxn->ident);
3019                         cxn->remove_succeeded++;
3020
3021                         /* we can delete article now */
3022                         QueueForgetAbout(cxn, cxn->current_control, 
3023                                          MSG_SUCCESS);
3024                     } else {
3025                         d_printf(1,"%s:%d:IMAP Delete mailbox failed with [%s] for %s\n",
3026                                  hostPeerName (cxn->myHost), cxn->ident,str,
3027                                  cxn->current_control->data.control->folder);
3028
3029                         ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3030                         
3031                     }
3032
3033                     imap_ProcessQueue(cxn);
3034                     return;
3035                         
3036                     break;
3037
3038             case IMAP_READING_SELECT:
3039
3040                     if (okno==1) {
3041
3042                         imap_sendSearch(cxn, cxn->current_control->data.control->msgid);
3043                         return;
3044
3045                     } else {
3046                         d_printf(1,"%s:%d:IMAP Select failed with [%s] for %s\n",
3047                                  hostPeerName (cxn->myHost), cxn->ident,str,
3048                                  cxn->current_control->data.control->folder);
3049
3050                         ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3051                         
3052                         cxn->imap_state = IMAP_IDLE_AUTHED;
3053
3054                         imap_ProcessQueue(cxn);
3055                         return;
3056                     }
3057
3058                     break;
3059
3060             case IMAP_READING_SEARCH:
3061                 /* if no message let's forget about it */
3062                 if (cxn->current_control->data.control->uid
3063                         == (unsigned long)-1) {
3064                     d_printf(2, "%s:%d:IMAP Search didn't find the message\n",
3065                              hostPeerName (cxn->myHost), cxn->ident);
3066                     QueueForgetAbout(cxn, cxn->current_control, 
3067                                      MSG_FAIL_DELIVER);
3068                     if (imap_sendClose(cxn) != RET_OK)
3069                         imap_Disconnect(cxn);
3070                     return;
3071                 }
3072
3073                 if (okno==1) {
3074                     /* we got a uid. let's delete it */
3075                     if (imap_sendKill(cxn, 
3076                                       cxn->current_control->data.control->uid)
3077                             != RET_OK)
3078                         imap_Disconnect(cxn);
3079                     return;
3080                 } else {
3081                     d_printf(0, "%s:%d IMAP Received NO response to SEARCH\n",
3082                              hostPeerName (cxn->myHost), cxn->ident);
3083                     ReQueue(cxn, &(cxn->imap_controlMsg_q), 
3084                             cxn->current_control);
3085                     
3086                     if (imap_sendClose(cxn) != RET_OK)
3087                         imap_Disconnect(cxn);
3088                     return;
3089                 }
3090                 break;
3091
3092             case IMAP_READING_STORE:
3093
3094                     if (okno==1) {
3095
3096                         d_printf(1,"%s:%d:IMAP Processed a Cancel fully\n",
3097                                  hostPeerName (cxn->myHost), cxn->ident);
3098
3099                         /* we can delete article now */
3100                         QueueForgetAbout(cxn, cxn->current_control,
3101                                          MSG_SUCCESS);
3102
3103                         cxn->cancel_succeeded++;
3104
3105                         if (imap_sendClose(cxn) != RET_OK)
3106                             imap_Disconnect(cxn);
3107                         return;
3108
3109                     } else {
3110
3111                         d_printf(1,"%s:%d:IMAP Store failed\n",
3112                                  hostPeerName (cxn->myHost), cxn->ident);
3113                         ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
3114
3115                         if (imap_sendClose(cxn) != RET_OK)
3116                             imap_Disconnect(cxn);
3117                         return;
3118                     }
3119
3120                     break;
3121
3122             case IMAP_READING_NOOP:
3123                 cxn->imap_state = IMAP_IDLE_AUTHED;
3124                 return;
3125                 break;
3126
3127             case IMAP_READING_CLOSE:
3128                 if (!okno) {
3129                     /* we can't do anything about it */
3130                     d_printf(1,"%s:%d:IMAP Close failed\n",
3131                              hostPeerName (cxn->myHost), cxn->ident);
3132                 }
3133
3134                 cxn->imap_state = IMAP_IDLE_AUTHED;
3135                 
3136                 imap_ProcessQueue(cxn);             
3137                 return;
3138                 break;
3139
3140             case IMAP_READING_QUIT:
3141                 
3142                 /* we don't care if the server said OK or NO just
3143                    that it said something */
3144
3145                 d_printf(1,"%s:%d:IMAP Read quit response\n",
3146                          hostPeerName (cxn->myHost), cxn->ident);
3147                 
3148                 cxn->imap_state = IMAP_DISCONNECTED;
3149
3150                 DeleteIfDisconnected(cxn);
3151                 break;
3152                     
3153
3154             default:
3155                 d_printf(0,"%s:%d:IMAP I don't understand state %d [%s]\n",
3156                          hostPeerName (cxn->myHost), cxn->ident,
3157                          cxn->imap_state,str);
3158                 imap_Disconnect(cxn);
3159                 break;
3160             }
3161             
3162
3163     } else {
3164         d_printf(0,"%s:%d:IMAP tag (%s) doesn't match what we gave (%s). What's up with that??\n",
3165                  hostPeerName (cxn->myHost), cxn->ident, str, cxn->imap_currentTag);
3166         imap_Disconnect(cxn);
3167     }
3168     
3169 }
3170
3171 /************************** END IMAP reading functions ***************************/
3172
3173 /*************************** LMTP reading functions ****************************/
3174
3175 static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d)
3176 {
3177     connection_t *cxn = (connection_t *) d;
3178     char str[4096];
3179     Buffer *readBuffers;
3180     int result;
3181     int response_code;
3182     conn_ret ret;
3183 #ifdef HAVE_SASL
3184     int inlen;
3185     char *in;
3186     int outlen;
3187     const char *out;
3188     char *inbase64;
3189     int inbase64len;
3190     imt_stat status;
3191     int saslresult;
3192     sasl_interact_t *client_interact=NULL;
3193 #endif /* HAVE_SASL */
3194
3195     char *p = bufferBase(b[0]);
3196
3197     bufferAddNullByte (b[0]) ;
3198
3199     if (i != IoDone) {
3200         errno = endPointErrno(e);
3201         syslog(LOG_ERR, "%s:%d LMTP i/o failed: %m",
3202                hostPeerName (cxn->myHost), cxn->ident);
3203
3204         freeBufferArray (b);
3205         lmtp_Disconnect(cxn);
3206         return;
3207     }
3208
3209     if (strchr (p, '\n') == NULL)
3210     {
3211         /* partial read. expand buffer and retry */
3212
3213         d_printf(0,"%s:%d:LMTP Partial. retry\n",
3214                  hostPeerName (cxn->myHost),cxn->ident);
3215         expandBuffer (b[0], BUFFER_EXPAND_AMOUNT) ;
3216         readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ;
3217         
3218         if ( !prepareRead (e, readBuffers, lmtp_readCB, cxn, 1) )
3219         {
3220             lmtp_Disconnect(cxn);
3221         }
3222
3223         freeBufferArray (b);
3224         return;
3225     }
3226
3227     clearTimer (cxn->lmtp_readBlockedTimerId) ;
3228
3229     /* Add what we got to our internal read buffer */
3230     strcat(cxn->lmtp_respBuffer, p);
3231
3232     bufferSetDataSize( b[0], 0);
3233
3234     freeBufferArray (b);
3235
3236  reset:
3237     /* see if we have a full line */
3238     ret = GetLine( cxn->lmtp_respBuffer, str, sizeof(str));
3239
3240     /* get a line */
3241     if (ret!=RET_OK)
3242     {
3243         if (ret!=RET_NO_FULLLINE)
3244         {
3245             /* was a more serious error */
3246             d_printf(0,"%s:%d:LMTP Internal error getting line from server\n",
3247                      hostPeerName (cxn->myHost),cxn->ident);
3248             lmtp_Disconnect(cxn);
3249             return;         
3250         }
3251
3252         /* set up to receive some more */
3253         readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
3254         prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
3255         return;
3256     }
3257
3258     switch (cxn->lmtp_state)
3259         {
3260
3261         case LMTP_READING_INTRO:
3262             
3263             if (ask_code(str)!=220)
3264             {
3265                 d_printf(0,"%s:%d:LMTP Initial server msg does not start with 220 (began with %d)\n",
3266                          hostPeerName (cxn->myHost),cxn->ident,
3267                          ask_code(str));
3268                 lmtp_Disconnect(cxn);
3269                 return;
3270             }
3271
3272             /* the initial intro could have many lines via
3273                continuations. see if we need to read more */
3274             if (ask_keepgoing(str)==1)
3275             {
3276                 goto reset;
3277             }
3278
3279             result = lmtp_getcapabilities(cxn);
3280
3281             if (result != RET_OK)
3282             {
3283                 d_printf(0,"%s:%d:LMTP lmtp_getcapabilities() failure\n",
3284                          hostPeerName (cxn->myHost),cxn->ident);
3285                 lmtp_Disconnect(cxn);
3286                 return;
3287             }
3288
3289             break;
3290
3291         case LMTP_READING_LHLO:
3292             /* recieve the response(s) */
3293             response_code = ask_code(str);
3294             
3295             if (response_code != 250) /* was none */
3296             {
3297                 d_printf(0,"%s:%d:LMTP Response code unexpected (%d)\n",
3298                          hostPeerName (cxn->myHost),cxn->ident,
3299                          response_code);
3300                 lmtp_Disconnect(cxn);
3301                 return;
3302             }
3303
3304             /* look for one we know about; ignore all others */
3305             if (strncmp(str+4,"8BITMIME",strlen("8BITMIME"))==0)
3306             {
3307                 cxn->lmtp_capabilities->Eightbitmime = 1;
3308             } else if (strncmp(str+4, "ENHANCEDSTATUSCODES",
3309                                strlen("ENHANCEDSTATUSCODES"))==0) {
3310                 cxn->lmtp_capabilities->EnhancedStatusCodes = 1;
3311             } else if (strncmp(str+4, "AUTH",4)==0) {
3312                 cxn->lmtp_capabilities->saslmechs = xstrdup(str + 4 + 5);
3313             } else if (strncmp(str+4,"PIPELINING",strlen("PIPELINING"))==0) {
3314                 cxn->lmtp_capabilities->pipelining = 1;
3315             } else {
3316                 /* don't care; ignore */
3317             }
3318             
3319             /* see if this is the last line of the capability */
3320             if (ask_keepgoing(str)==1)
3321             {
3322                 goto reset;
3323             } else {
3324                 /* we require a few capabilities */
3325                 if (!cxn->lmtp_capabilities->pipelining) {
3326                     d_printf(0,"%s:%d:LMTP We require PIPELINING\n",
3327                              hostPeerName (cxn->myHost),cxn->ident);
3328                     
3329                     lmtp_Disconnect(cxn);
3330                     return;
3331                 }
3332 #ifdef HAVE_SASL
3333                 if (cxn->lmtp_capabilities->saslmechs) {
3334                     /* start the authentication */
3335                     result = lmtp_authenticate(cxn);
3336                     
3337                     if (result != RET_OK) {
3338                         d_printf(0,"%s:%d:LMTP lmtp_authenticate() error\n",
3339                              hostPeerName (cxn->myHost),cxn->ident);
3340                         lmtp_Disconnect(cxn);
3341                         return;
3342                     }
3343 #else
3344                 if (0) {
3345                     /* noop */
3346 #endif
3347                 } else {
3348                     /* either we can't authenticate or the remote server
3349                        doesn't support it */
3350                     cxn->lmtp_state = LMTP_AUTHED_IDLE;
3351                     d_printf(1,"%s:%d:LMTP Even though we can't authenticate"
3352                              " we're going to try to feed anyway\n",
3353                              hostPeerName (cxn->myHost),cxn->ident);
3354                     /* We just assume we don't need to authenticate 
3355                        (great assumption huh?) */
3356                     hostRemoteStreams (cxn->myHost, cxn, true) ;
3357
3358                     cxn->lmtp_timeCon = theTime () ;
3359                     cxn->timeCon = theTime () ;
3360                     
3361                     /* try to send a message if we have one */
3362                     lmtp_sendmessage(cxn,NULL);
3363                     return;
3364                 }
3365             }
3366             break;
3367
3368 #ifdef HAVE_SASL
3369         case LMTP_READING_STEPAUTH:
3370             inlen = 0;
3371             status = lmtp_getauthline(str, &in, &inlen);
3372
3373             switch (status)
3374                 {
3375
3376                 case STAT_CONT:
3377
3378                     saslresult=sasl_client_step(cxn->saslconn_lmtp,
3379                                                 in,
3380                                                 inlen,
3381                                                 &client_interact,
3382                                                 &out,
3383                                                 &outlen);
3384
3385                     free(in);
3386
3387                     /* check if sasl succeeded */
3388                     if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
3389                         d_printf(0,"%s:%d:LMTP sasl_client_step(): %s\n",
3390                                  hostPeerName (cxn->myHost),cxn->ident,
3391                                  sasl_errstring(saslresult,NULL,NULL));
3392
3393                         lmtp_Disconnect(cxn);
3394                         return;
3395                     }
3396
3397                     /* convert to base64 */
3398                     inbase64 = xmalloc(outlen*2+10);
3399
3400                     saslresult = sasl_encode64(out, outlen,
3401                                                inbase64, outlen*2+10, 
3402                                                (unsigned *) &inbase64len);
3403                     
3404                     if (saslresult != SASL_OK)
3405                     {
3406                         d_printf(0,"%s:%d:LMTP sasl_encode64(): %s\n",
3407                                  hostPeerName (cxn->myHost),cxn->ident,
3408                                  sasl_errstring(saslresult,NULL,NULL));
3409
3410                         lmtp_Disconnect(cxn);
3411                         return;
3412                     }
3413
3414                     /* add an endline */
3415                     strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10);
3416
3417                     /* send to server */
3418                     result = WriteToWire_lmtpstr(cxn,inbase64, inbase64len+2);
3419
3420                     if (result != RET_OK)
3421                     {
3422                         d_printf(0,"%s:%d:LMTP WriteToWire() failure\n",
3423                                  hostPeerName (cxn->myHost),cxn->ident);
3424                         lmtp_Disconnect(cxn);
3425                         return;
3426                     }
3427
3428                     cxn->lmtp_state = LMTP_WRITING_STEPAUTH;
3429                     break;
3430                     
3431                 case STAT_OK:
3432                     cxn->lmtp_sleepTimeout = init_reconnect_period ;
3433       
3434                     d_printf(0,"%s:%d LMTP authentication succeeded\n",
3435                              hostPeerName (cxn->myHost), cxn->ident);
3436
3437                     cxn->lmtp_disconnects=0;
3438
3439                     hostRemoteStreams (cxn->myHost, cxn, true) ;
3440
3441                     cxn->lmtp_timeCon = theTime () ;
3442                     cxn->timeCon = theTime () ;
3443
3444                     cxn->lmtp_state = LMTP_AUTHED_IDLE;
3445
3446
3447                     /* try to send a message if we have one */
3448                     lmtp_sendmessage(cxn,NULL);
3449                     return;
3450
3451                     break;
3452
3453                 default:
3454                     d_printf(0,"%s:%d:LMTP failed authentication\n",
3455                              hostPeerName (cxn->myHost),cxn->ident);
3456                     lmtp_Disconnect(cxn);
3457                     return;
3458                 }
3459             break;
3460 #endif /* HAVE_SASL */
3461                     
3462         case LMTP_READING_RSET:
3463             if (ask_keepgoing(str)) {
3464                 goto reset;
3465             }
3466             if (ask_code(str) != 250) {
3467                 d_printf(0,"%s:%d:LMTP RSET failed with (%d)\n",
3468                          hostPeerName (cxn->myHost),cxn->ident,
3469                          ask_code(str));
3470                 lmtp_Disconnect(cxn);
3471                 return;