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