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