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