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