+++ /dev/null
-/* $Id: imap_connection.c 7103 2004-12-23 22:36:27Z rra $
-**
-** Feed articles to an IMAP server via LMTP and IMAP.
-**
-** Written by Tim Martin.
-**
-** Instead of feeding articles via nntp to another host this feeds the
-** messages via lmtp to a host and the control messages (cancel's etc..) it
-** performs via IMAP. This means it has 2 active connections at any given
-** time and 2 queues.
-**
-** When an article comes in it is immediatly placed in the lmtp queue. When
-** an article is picked off the lmtp queue for processing first check if it's
-** a control message. If so, place it in the IMAP queue. If not, attempt to
-** deliver via LMTP.
-**
-** This attempts to follow the exact same api as connection.c.
-**
-** TODO:
-**
-** feed to smtp
-** security layers? <--punt on for now
-** authname/password per connection object
-** untagged IMAP messages
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include "portable/socket.h"
-#include <ctype.h>
-#include <errno.h>
-#include <netdb.h>
-#include <netdb.h>
-#include <time.h>
-#include <syslog.h>
-
-#include "inn/messages.h"
-#include "libinn.h"
-
-#include "buffer.h"
-#include "connection.h"
-#include "endpoint.h"
-#include "host.h"
-#include "innfeed.h"
-#include "article.h"
-#include "configfile.h"
-
-#ifdef HAVE_SASL
-# include <sasl/sasl.h>
-#endif
-
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 1024
-#endif
-
-#define IMAP_PORT 143
-
-#ifdef SMTPMODE
-# define LMTP_PORT 25
-#else
-# define LMTP_PORT 2003
-#endif
-
-#define IMAP_TAGLENGTH 6
-
-#define QUEUE_MAX_SIZE 250
-
-#define DOSOMETHING_TIMEOUT 60
-
-
-
-/* external */
-extern char *deliver_username;
-extern char *deliver_authname;
-extern char *deliver_password;
-extern char *deliver_realm;
-extern char *deliver_rcpt_to;
-extern char *deliver_to_header;
-
-
-char hostname[MAXHOSTNAMELEN];
-char *mailfrom_name = NULL; /* default to no return path */
-
-#ifdef HAVE_SASL
-static int initialized_sasl = 0; /* weather sasl_client_init() has been called */
-#endif
-
-/* states the imap connection may be in */
-typedef enum {
-
- IMAP_DISCONNECTED = 1,
- IMAP_WAITING,
-
- IMAP_CONNECTED_NOTAUTH,
-
- IMAP_READING_INTRO,
-
- IMAP_WRITING_CAPABILITY,
- IMAP_READING_CAPABILITY,
-
- IMAP_WRITING_STARTAUTH,
- IMAP_READING_STEPAUTH,
- IMAP_WRITING_STEPAUTH,
-
- IMAP_IDLE_AUTHED,
- IMAP_WRITING_NOOP,
- IMAP_READING_NOOP,
-
- IMAP_WRITING_CREATE,
- IMAP_READING_CREATE,
-
- IMAP_WRITING_DELETE,
- IMAP_READING_DELETE,
-
- IMAP_WRITING_SELECT,
- IMAP_READING_SELECT,
-
- IMAP_WRITING_SEARCH,
- IMAP_READING_SEARCH,
-
- IMAP_WRITING_STORE,
- IMAP_READING_STORE,
-
- IMAP_WRITING_CLOSE,
- IMAP_READING_CLOSE,
-
- IMAP_WRITING_QUIT,
- IMAP_READING_QUIT
-
-} imap_state_t;
-
-typedef enum {
- LMTP_DISCONNECTED = 1,
- LMTP_WAITING,
-
- LMTP_CONNECTED_NOTAUTH,
-
- LMTP_READING_INTRO,
-
- LMTP_WRITING_LHLO,
- LMTP_READING_LHLO,
-
- LMTP_WRITING_STARTAUTH,
- LMTP_READING_STEPAUTH,
- LMTP_WRITING_STEPAUTH,
-
- LMTP_AUTHED_IDLE,
- LMTP_WRITING_NOOP,
- LMTP_READING_NOOP,
-
- LMTP_READING_RSET,
- LMTP_READING_MAILFROM,
- LMTP_READING_RCPTTO,
- LMTP_READING_DATA,
- LMTP_READING_CONTENTS,
-
- LMTP_WRITING_UPTODATA,
- LMTP_WRITING_CONTENTS,
-
- LMTP_WRITING_QUIT,
- LMTP_READING_QUIT
-
-} lmtp_state_t;
-
-typedef struct imap_capabilities_s {
-
- int imap4; /* does server support imap4bis? */
- int logindisabled; /* does the server allow the login command? */
-
- char *saslmechs; /* supported SASL mechanisms */
-
-} imap_capabilities_t;
-
-typedef struct lmtp_capabilities_s {
-
- int Eightbitmime;
- int EnhancedStatusCodes;
- int pipelining;
-
- char *saslmechs;
-
-} lmtp_capabilities_t;
-
-typedef enum {
- STAT_CONT = 0,
- STAT_NO = 1,
- STAT_OK = 2,
- STAT_FAIL = 3
-} imt_stat;
-
-/* Message types */
-typedef enum {
- DELIVER,
- CREATE_FOLDER,
- CANCEL_MSG,
- DELETE_FOLDER
-} control_type_t;
-
-typedef struct control_item_s {
-
- Article article;
- char *folder;
- char *msgid; /* only for cancel's */
- unsigned long uid; /* only for cancel's */
-
-} control_item_t;
-
-typedef struct article_queue_s {
-
- control_type_t type;
-
- time_t arrived;
- time_t nextsend; /* time we should next try to send article */
-
- int trys;
-
- int counts_toward_size;
-
- union {
- Article article;
- control_item_t *control;
- void *generic;
- } data;
-
- struct article_queue_s *next;
-
-} article_queue_t;
-
-typedef struct Q_s {
-
- article_queue_t *head;
-
- article_queue_t *tail;
-
- int size;
-
-} Q_t;
-
-typedef struct connection_s {
-
- /* common stuff */
- char *ServerName;
-
- char *lmtp_respBuffer; /* buffer all responses are read into */
- Buffer lmtp_rBuffer; /* buffer all responses are read into */
-
- Host myHost ; /* the host who owns the connection */
-
- time_t timeCon ; /* the time the connect happened
- (last auth succeeded) */
-
- int issue_quit; /* Three states:
- * 0 - don't do anything
- * 1 - after issue quit enter wait state
- * 2 - after issue quit reconnect
- * 3 - after issue quit delete connection
- * 4 - nuke cxn
- */
-
- /* Statistics */
- int lmtp_succeeded;
- int lmtp_failed;
-
- int cancel_succeeded;
- int cancel_failed;
-
- int create_succeeded;
- int create_failed;
-
- int remove_succeeded;
- int remove_failed;
-
-
- /* LMTP stuff */
- int lmtp_port;
- lmtp_state_t lmtp_state;
-#ifdef HAVE_SASL
- sasl_conn_t *saslconn_lmtp;
-#endif /* HAVE_SASL */
- int sockfd_lmtp;
-
- time_t lmtp_timeCon ;
-
- EndPoint lmtp_endpoint;
- unsigned int ident ; /* an identifier for syslogging. */
-
- lmtp_capabilities_t *lmtp_capabilities;
-
- int lmtp_disconnects;
- char *lmtp_tofree_str;
-
- article_queue_t *current_article;
- Buffer *current_bufs;
- int current_rcpts_issued;
- int current_rcpts_okayed;
-
- /* Timer for the max amount of time to wait for a response from the
- remote */
- unsigned int lmtp_readTimeout ;
- TimeoutId lmtp_readBlockedTimerId ;
-
- /* Timer for the max amount of time to wait for a any amount of data
- to be written to the remote */
- unsigned int lmtp_writeTimeout ;
- TimeoutId lmtp_writeBlockedTimerId ;
-
- /* Timer for the number of seconds to sleep before attempting a
- reconnect. */
- unsigned int lmtp_sleepTimeout ;
- TimeoutId lmtp_sleepTimerId ;
-
- /* Timer for max amount between queueing some articles and trying to send them */
- unsigned int dosomethingTimeout ;
- TimeoutId dosomethingTimerId ;
-
- Q_t lmtp_todeliver_q;
-
-
-
- /* IMAP stuff */
- int imap_port;
-#ifdef HAVE_SASL
- sasl_conn_t *imap_saslconn;
-#endif /* HAVE_SASL */
-
- char *imap_respBuffer;
- Buffer imap_rBuffer;
- EndPoint imap_endpoint;
-
- imap_capabilities_t *imap_capabilities;
-
- int imap_sockfd;
-
- time_t imap_timeCon ;
-
- imap_state_t imap_state;
- int imap_disconnects;
- char *imap_tofree_str;
-
- char imap_currentTag[IMAP_TAGLENGTH];
- int imap_tag_num;
-
- /* Timer for the max amount of time to wait for a response from the
- remote */
- unsigned int imap_readTimeout ;
- TimeoutId imap_readBlockedTimerId ;
-
- /* Timer for the max amount of time to wait for a any amount of data
- to be written to the remote */
- unsigned int imap_writeTimeout ;
- TimeoutId imap_writeBlockedTimerId ;
-
- /* Timer for the number of seconds to sleep before attempting a
- reconnect. */
- unsigned int imap_sleepTimeout ;
- TimeoutId imap_sleepTimerId ;
-
- Q_t imap_controlMsg_q;
-
- article_queue_t *current_control;
-
- struct connection_s *next;
-
-} connection_t;
-
-static Connection gCxnList = NULL ;
-static unsigned int gCxnCount= 0 ;
-static unsigned int max_reconnect_period = MAX_RECON_PER ;
-static unsigned int init_reconnect_period = INIT_RECON_PER;
-
-typedef enum {
- RET_OK = 0,
- RET_FAIL = 1,
- RET_QUEUE_EMPTY,
- RET_EXCEEDS_SIZE,
- RET_NO_FULLLINE,
- RET_NO,
- RET_ARTICLE_BAD
-} conn_ret;
-
-
-/********** Private Function Declarations *************/
-
-static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d);
-static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d);
-static void imap_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d);
-static void lmtp_writeCB (EndPoint e, IoStatus i, Buffer *b, void *d);
-
-static conn_ret lmtp_Connect(connection_t *cxn);
-static conn_ret imap_Connect(connection_t *cxn);
-
-static void prepareReopenCbk (Connection cxn, int type);
-
-static void lmtp_readTimeoutCbk (TimeoutId id, void *data);
-static void imap_readTimeoutCbk (TimeoutId id, void *data);
-
-static void dosomethingTimeoutCbk (TimeoutId id, void *data);
-
-static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen);
-static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen);
-
-static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback,
- EndPoint endp, Buffer *array);
-static void lmtp_sendmessage(connection_t *cxn, Article justadded);
-static void imap_ProcessQueue(connection_t *cxn);
-
-static conn_ret FindHeader(Buffer *bufs, const char *header, char **start, char **end);
-static conn_ret PopFromQueue(Q_t *q, article_queue_t **item);
-
-enum failure_type {
- MSG_SUCCESS = 0,
- MSG_FAIL_DELIVER = 1,
- MSG_GIVE_BACK = 2,
- MSG_MISSING = 3
-};
-
-static void QueueForgetAbout(connection_t *cxn, article_queue_t *item,
- enum failure_type failed);
-
-static void delConnection (Connection cxn);
-static void DeleteIfDisconnected(Connection cxn);
-static void DeferAllArticles(connection_t *cxn, Q_t *q);
-
-static void lmtp_Disconnect(connection_t *cxn);
-static void imap_Disconnect(connection_t *cxn);
-static conn_ret imap_listenintro(connection_t *cxn);
-
-static void imap_writeTimeoutCbk (TimeoutId id, void *data);
-static void lmtp_writeTimeoutCbk (TimeoutId id, void *data);
-
-/******************** PRIVATE FUNCTIONS ***************************/
-
-static const char *imap_stateToString(int state)
-{
- switch (state)
- {
- case IMAP_DISCONNECTED: return "disconnected";
- case IMAP_WAITING: return "waiting";
- case IMAP_CONNECTED_NOTAUTH: return "connected (unauthenticated)";
- case IMAP_READING_INTRO: return "reading intro";
- case IMAP_WRITING_CAPABILITY: return "writing CAPABILITY";
- case IMAP_READING_CAPABILITY: return "reading CAPABILITY";
- case IMAP_WRITING_STARTAUTH: return "writing AUTHENTICATE";
- case IMAP_READING_STEPAUTH: return "reading stepauth";
- case IMAP_WRITING_STEPAUTH: return "writing stepauth";
- case IMAP_IDLE_AUTHED: return "idle (authenticated)";
- case IMAP_WRITING_NOOP: return "writing NOOP";
- case IMAP_READING_NOOP: return "reading NOOP response";
- case IMAP_WRITING_CREATE: return "writing CREATE";
- case IMAP_READING_CREATE: return "reading CREATE response";
- case IMAP_WRITING_DELETE: return "writing DELETE command";
- case IMAP_READING_DELETE: return "reading DELETE response";
- case IMAP_WRITING_SELECT: return "writing SELECT";
- case IMAP_READING_SELECT: return "reading SELECT response";
- case IMAP_WRITING_SEARCH: return "writing SEARCH";
- case IMAP_READING_SEARCH: return "reading SEARCH response";
- case IMAP_WRITING_STORE: return "writing STORE";
- case IMAP_READING_STORE: return "reading STORE response";
- case IMAP_WRITING_CLOSE: return "writing CLOSE";
- case IMAP_READING_CLOSE: return "reading CLOSE response";
- case IMAP_WRITING_QUIT: return "writing LOGOUT";
- case IMAP_READING_QUIT: return "reading LOGOUT response";
- default: return "Unknown state";
- }
-}
-
-static const char *lmtp_stateToString(int state)
-{
- switch(state)
- {
- case LMTP_DISCONNECTED: return "disconnected";
- case LMTP_WAITING: return "waiting";
- case LMTP_CONNECTED_NOTAUTH: return "connected (unauthenticated)";
- case LMTP_READING_INTRO: return "reading intro";
- case LMTP_WRITING_LHLO: return "writing LHLO";
- case LMTP_READING_LHLO: return "reading LHLO response";
- case LMTP_WRITING_STARTAUTH: return "writing AUTH";
- case LMTP_READING_STEPAUTH: return "reading stepauth";
- case LMTP_WRITING_STEPAUTH: return "writing stepauth";
- case LMTP_AUTHED_IDLE: return "idle (authenticated)";
- case LMTP_WRITING_NOOP: return "writing NOOP";
- case LMTP_READING_NOOP: return "reading NOOP response";
- case LMTP_READING_RSET: return "reading RSET response";
- case LMTP_READING_MAILFROM: return "reading MAIL FROM response";
- case LMTP_READING_RCPTTO: return "reading RCPT TO response";
- case LMTP_READING_DATA: return "reading DATA response";
- case LMTP_READING_CONTENTS: return "reading contents response";
- case LMTP_WRITING_UPTODATA:
- return "writing RSET, MAIL FROM, RCPT TO, DATA commands";
- case LMTP_WRITING_CONTENTS: return "writing contents of message";
- case LMTP_WRITING_QUIT: return "writing QUIT";
- case LMTP_READING_QUIT: return "reading QUIT";
- default: return "unknown state";
- }
-}
-
-/******************************* Queue functions ***********************************/
-
-/*
- * Add a message to a generic queue
- *
- * q - the queue adding to
- * item - the data to add to the queue
- * type - the type of item it is (i.e. cancel,lmtp,etc..)
- * addsmsg - weather this should be counted toward the queue size
- * this is for control msg's that create multiple queue items.
- * For example a cancel message canceling a message in multiple
- * newsgroups will create >1 queue item but we only want it to count
- * once towards the queue
- * must - wheather we must take it even though it may put us over our max size
- */
-
-static conn_ret AddToQueue(Q_t *q, void *item,
- control_type_t type, int addsmsg, bool must)
-{
- article_queue_t *newentry;
-
- if (must == false)
- {
- if (q->size >= QUEUE_MAX_SIZE)
- {
- return RET_EXCEEDS_SIZE;
- }
- } else {
- if (q->size >= QUEUE_MAX_SIZE * 10)
- {
- d_printf(0, "Queue has grown way too much. Dropping article\n");
- return RET_FAIL;
- }
- }
-
- /* add to the end of our queue */
- newentry = xmalloc(sizeof(article_queue_t));
-
- newentry->type = type;
-
- /* send as soon as possible */
- newentry->nextsend = newentry->arrived = time(NULL);
-
- newentry->trys = 0;
-
- newentry->data.generic = item;
- newentry->next = NULL;
- newentry->counts_toward_size = addsmsg;
-
- /* add to end of queue */
- if (q->tail == NULL)
- {
- q->head = newentry;
- q->tail = newentry;
- } else {
-
- q->tail->next = newentry;
- q->tail = newentry;
- }
-
- q->size+=addsmsg;
-
- return RET_OK;
-}
-
-/*
- * Pop an item from the queue
- *
- * q - the queue to pop from
- * item - where the item shall be placed upon sucess
- *
- */
-
-static conn_ret PopFromQueue(Q_t *q, article_queue_t **item)
-{
- /* if queue empty return error */
- if ( q->head == NULL)
- {
- return RET_QUEUE_EMPTY;
- }
-
- /* set what we return */
- *item = q->head;
-
- q->head = q->head->next;
- if (q->head == NULL) q->tail = NULL;
-
- q->size-=(*item)->counts_toward_size;
-
- return RET_OK;
-}
-
-/*
- * ReQueue an item. Will either put it back in the queue for another try
- * or forget about it
- *
- * cxn - our connection object (needed so forget about things)
- * q - the queue to requeue to
- * entry - the item to put back
- */
-
-static void ReQueue(connection_t *cxn, Q_t *q, article_queue_t *entry)
-{
- /* look at the time it's been here */
- entry->nextsend = time(NULL) + (entry->trys *30); /* xxx better formula? */
-
- entry->trys++;
-
- /* give up after 5 tries xxx configurable??? */
- if (entry->trys >= 5)
- {
- QueueForgetAbout(cxn, entry, MSG_FAIL_DELIVER);
- return;
- }
-
-
- /* ok let's add back to the end of the queue */
- entry->next = NULL;
-
- /* add to end of queue */
- if (q->tail == NULL)
- {
- q->head = entry;
- q->tail = entry;
- } else {
- q->tail->next = entry;
- q->tail = entry;
- }
-
- q->size+=entry->counts_toward_size;
-}
-
-
-
-/*
- * Forget about an item. Tells host object if we succeeded/failed/etc with the message
- *
- * cxn - connection object
- * item - item
- * failed - type of failure (see below)
- *
- * failed:
- * 0 - succeeded delivering message
- * 1 - failed delivering message
- * 2 - Try to give back to host
- * 3 - Article missing (i.e. can't find on disk)
- */
-static void QueueForgetAbout(connection_t *cxn, article_queue_t *item,
- enum failure_type failed)
-{
- Article art = NULL;
-
- switch (item->type)
- {
- case DELIVER:
- if (failed>0)
- cxn->lmtp_failed++;
- art = item->data.article;
- break;
-
- case CANCEL_MSG:
- if (failed>0)
- cxn->cancel_failed++;
- free(item->data.control->msgid);
- free(item->data.control->folder);
-
- if (item->counts_toward_size == 1)
- art = item->data.control->article;
-
- free(item->data.control );
- break;
-
- case CREATE_FOLDER:
- if (failed>0)
- cxn->create_failed++;
- free(item->data.control->folder);
-
- art = item->data.control->article;
-
- free(item->data.control );
- break;
-
- case DELETE_FOLDER:
- if (failed>0)
- cxn->remove_failed++;
- free(item->data.control->folder);
-
- art = item->data.control->article;
-
- free(item->data.control );
- break;
-
- default:
- d_printf(0, "%s:%d QueueForgetAbout(): "
- "Unknown type to forget about\n",
- hostPeerName (cxn->myHost), cxn->ident);
- break;
- }
-
- if (art!=NULL) {
- switch (failed) {
- case MSG_SUCCESS:
- hostArticleAccepted (cxn->myHost, cxn, art);
- break;
-
- case MSG_FAIL_DELIVER:
- hostArticleRejected (cxn->myHost, cxn, art);
- break;
-
- case MSG_GIVE_BACK:
- hostTakeBackArticle (cxn->myHost, cxn, art);
- break;
-
- case MSG_MISSING:
- hostArticleIsMissing(cxn->myHost, cxn, art);
- break;
- default:
- d_printf(0,"%s:%d QueueForgetAbout(): failure type unknown\n",
- hostPeerName (cxn->myHost), cxn->ident);
- break;
- }
- }
-
- free(item);
-}
-
-/*
- * How much space is available in the queue
- */
-
-static int QueueSpace(Q_t *q)
-{
- int ret = QUEUE_MAX_SIZE - q->size;
- if (ret < 0) ret = 0;
- return ret;
-}
-
-/*
- * How many items are in the queue
- */
-
-static int QueueItems(Q_t *q)
-{
- return q->size;
-}
-
-
-/***************************** END Queue functions ***********************************/
-
-/***************************** Generic Parse Functions *******************************/
-
-/* returns the end of the header */
-
-static char *GetUntil(char *str)
-{
- while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n'))
- {
- str++;
- }
-
- return str;
-}
-
-static char *GotoNextLine(char *str)
-{
- while (((*str) != '\0') && ( (*str) != '\r') && ( (*str) != '\n'))
- {
- str++;
- }
-
- if (*str == '\r') str++;
- if (*str == '\n') str++;
-
- return str;
-}
-
-/*
- * Finds the given header in the message
- * Returns NULL if not found
- */
-static conn_ret FindHeader(Buffer *bufs, const char *header, char **start,
- char **end)
-{
- Buffer b;
- int size;
- char *str_base;
- char *str;
- int headerlen = strlen(header);
-
- if (bufs==NULL)
- {
- if (start)
- *start=NULL;
- return RET_ARTICLE_BAD;
- }
-
- b = bufs[0];
- size = bufferSize(b);
- str_base = bufferBase(b);
- str = str_base;
-
- while ((str - str_base) < size - headerlen)
- {
- if (*str == header[0])
- {
- if ((strncasecmp(header, str, headerlen)==0) && ( *(str + headerlen)==':'))
- {
-
- if (start)
- {
- *start = str+headerlen+1;
-
- /* get rid of leading whitespace */
- while ( isspace((int) **start))
- (*start)++;
- }
-
- if (end)
- *end = GetUntil(str+headerlen+1);
-
- return RET_OK;
- }
- } else if (*str == '\n') {
- /* end of headers */
- return RET_NO;
- }
- str = GotoNextLine(str);
- }
-
- return RET_NO;
-}
-
-static conn_ret GetLine(char *buf, char *ret, int retmaxsize)
-{
- char *str_base;
- char *str;
-
- int size = strlen(buf);
- str_base = buf;
- str = str_base;
-
- while ( (*str) != '\0')
- {
- if ((*str) == '\n')
- {
- if (str-str_base > retmaxsize)
- {
- d_printf(0, "Max size exceeded! %s\n",str_base);
- return RET_FAIL;
- }
-
- /* fill in the return string */
- memcpy(ret, str_base, str-str_base);
- ret[ str - str_base -1] = '\0';
-
- memcpy( str_base, str_base + (str-str_base)+1, size - (str-str_base));
- str_base[size - (str-str_base)]='\0';
-
- return RET_OK;
- }
-
- str++;
- }
-
- /* couldn't find a full line */
- return RET_NO_FULLLINE;
-}
-
-
-
-/************************** END Generic Parse Functions *******************************/
-
-/************************ Writing to Network functions *****************/
-
-static conn_ret WriteToWire(connection_t *cxn, EndpRWCB callback,
- EndPoint endp, Buffer *array)
-{
-
- if (array == NULL) return RET_FAIL;
-
- prepareWrite (endp,
- array,
- NULL,
- callback,
- cxn);
-
- return RET_OK;
-}
-
-static conn_ret WriteToWire_str(connection_t *cxn, EndpRWCB callback,
- EndPoint endp, char *str, int slen)
-{
- conn_ret result;
- Buffer buff;
- Buffer *writeArr;
-
- if (slen==-1) slen = strlen(str);
-
- buff = newBufferByCharP(str, slen+1, slen);
- ASSERT (buff != NULL);
-
- writeArr = makeBufferArray (buff, NULL) ;
-
- result = WriteToWire(cxn, callback, endp, writeArr);
-
- return result;
-}
-
-static conn_ret WriteToWire_imapstr(connection_t *cxn, char *str, int slen)
-{
- /* prepare the timeouts */
- clearTimer (cxn->imap_readBlockedTimerId) ;
-
- /* set up the write timer. */
- clearTimer (cxn->imap_writeBlockedTimerId) ;
-
- if (cxn->imap_writeTimeout > 0)
- cxn->imap_writeBlockedTimerId = prepareSleep (imap_writeTimeoutCbk, cxn->imap_writeTimeout,
- cxn);
- cxn->imap_tofree_str = str;
- return WriteToWire_str(cxn, imap_writeCB, cxn->imap_endpoint, str, slen);
-}
-
-static conn_ret WriteToWire_lmtpstr(connection_t *cxn, char *str, int slen)
-{
- /* prepare the timeouts */
- clearTimer (cxn->lmtp_readBlockedTimerId) ;
-
- /* set up the write timer. */
- clearTimer (cxn->lmtp_writeBlockedTimerId) ;
-
- if (cxn->lmtp_writeTimeout > 0)
- cxn->lmtp_writeBlockedTimerId = prepareSleep (lmtp_writeTimeoutCbk, cxn->lmtp_writeTimeout,
- cxn) ;
-
-
-
- cxn->lmtp_tofree_str = str;
- return WriteToWire_str(cxn, lmtp_writeCB, cxn->lmtp_endpoint, str, slen);
-}
-
-static conn_ret WriteArticle(connection_t *cxn, Buffer *array)
-{
- int array_len = bufferArrayLen (array);
- int lup=0;
- int result;
-
- for (lup=0;lup<array_len;lup++)
- {
- int current_size;
- Buffer current_buf;
- char *current_start;
-
- current_buf = array[lup];
-
- current_size = bufferDataSize( current_buf );
-
- current_start = bufferBase( current_buf );
-
- }
-
- /* just call writetowire since it's easy */
- result = WriteToWire(cxn, lmtp_writeCB, cxn->lmtp_endpoint, array);
-
- if (result!=RET_OK)
- {
- return result;
- }
-
- cxn->lmtp_state = LMTP_WRITING_CONTENTS;
-
- return RET_OK;
-}
-
-/************************ END Writing to Network functions *****************/
-
-
-
-/*
- * Adds a cancel item to the control queue
- * Cancel item to delete message with <msgid> in <folder>
- *
- * cxn - connection object
- * folder - pointer to start of folder string (this is a pointer into the actual message buffer)
- * folderlen - length of folder string
- * msgid - pointer to start of msgid string (this is a pointer into the actual message buffer)
- * msgidlen - length of msgid string
- * art - the article for this control message (NULL if this cancel object lacks one)
- * must - if must be accepted into queue
- */
-
-static conn_ret addCancelItem(connection_t *cxn,
- char *folder, int folderlen,
- char *msgid, int msgidlen,
- Article art, int must)
-{
- control_item_t *item;
- conn_ret result;
- int i;
-
- ASSERT(folder); ASSERT(msgid); ASSERT(cxn);
-
- /* sanity check folder, msgid */
- for (i = 0; i < folderlen; i++) ASSERT(!isspace((int) folder[i]));
- for (i = 0; i < msgidlen; i++) ASSERT(!isspace((int) msgid[i]));
-
- /* create the object */
- item = xcalloc (1, sizeof(control_item_t));
-
- item->folder = xcalloc(folderlen+1, 1);
- memcpy(item->folder, folder, folderlen);
- item->folder[folderlen] = '\0';
-
- item->msgid = xcalloc (msgidlen+1, 1);
- memcpy(item->msgid, msgid, msgidlen);
- item->msgid[msgidlen] = '\0';
-
- item->article = art;
-
- /* try to add to the queue (counts if art isn't null) */
- result = AddToQueue(&(cxn->imap_controlMsg_q), item, CANCEL_MSG, (art != NULL), must);
- if (result != RET_OK) {
- d_printf(1,"%s:%d addCancelItem(): "
- "I thought we had in space in [imap] queue "
- "but apparently not\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- /* cleanup */
- free(item->folder);
- free(item->msgid);
- free(item);
-
- return result;
- }
-
- return RET_OK;
-}
-
-static conn_ret AddControlMsg(connection_t *cxn,
- Article art,
- Buffer *bufs,
- char *control_header,
- char *control_header_end,
- bool must)
-{
- char *rcpt_list = NULL, *rcpt_list_end;
- control_item_t *item;
- conn_ret res = RET_OK;
- int t;
-
- /* make sure contents ok; this also should load it into memory */
- if (!artContentsOk (art)) {
- d_printf(0, "%s:%d AddControlMsg(): "
- "artContentsOk() said article was bad\n",
- hostPeerName (cxn->myHost), cxn->ident);
- hostArticleIsMissing (cxn->myHost, cxn, art);
- return RET_FAIL;
- }
-
- res = RET_OK;
- /* now let's look at the control to see what it is */
- if (!strncasecmp(control_header,"newgroup",8)) {
- control_header += 8;
- t = CREATE_FOLDER;
- } else if (!strncasecmp(control_header,"rmgroup",7)) {
- /* jump past "rmgroup" */
- control_header += 7;
- t = DELETE_FOLDER;
- } else if (!strncasecmp(control_header,"cancel",6)) {
- t = CANCEL_MSG;
- control_header += 6;
- } else {
- /* unrecognized type */
- char tmp[100];
- char *endstr;
- size_t clen;
-
- endstr = strchr(control_header,'\n');
- clen = endstr - control_header;
-
- if (clen > sizeof(tmp)-1) clen = sizeof(tmp)-1;
-
- memcpy(tmp,control_header, clen);
- tmp[clen]='\0';
-
- d_printf(0,"%s:%d Don't understand control header [%s]\n",
- hostPeerName (cxn->myHost), cxn->ident,tmp);
- return RET_FAIL;
- }
-
- switch (t) {
- case CREATE_FOLDER:
- case DELETE_FOLDER:
- {
- int folderlen;
-
- /* go past all white space */
- while ((*control_header == ' ') &&
- (control_header != control_header_end)) {
- control_header++;
- }
-
- /* trim trailing whitespace */
- while (control_header_end[-1] == ' ') {
- control_header_end--;
- }
-
- if (control_header >= control_header_end) {
- d_printf(0,"%s:%d addControlMsg(): "
- "newgroup/rmgroup header has no group specified\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
- folderlen = control_header_end - control_header;
-
- item = xcalloc(1, sizeof(control_item_t));
-
- item->folder = xcalloc(folderlen + 1, 1);
- memcpy(item->folder, control_header, folderlen);
- item->folder[folderlen] = '\0';
-
- item->article = art;
-
- if (AddToQueue(&(cxn->imap_controlMsg_q), item, t, 1, must) != RET_OK) {
- d_printf(1,"%s:%d addCancelItem(): "
- "I thought we had in space in [imap] queue"
- " but apparently not\n",
- hostPeerName (cxn->myHost), cxn->ident);
- free(item->folder);
- free(item);
- return RET_FAIL;
- }
-
- break;
- }
-
- case CANCEL_MSG:
- {
- char *str, *laststart;
-
- while (((*control_header) == ' ') &&
- (control_header != control_header_end))
- {
- control_header++;
- }
-
- if (control_header == control_header_end)
- {
- d_printf(0, "%s:%d Control header contains cancel "
- "with no msgid specified\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
- if (FindHeader(bufs, "Newsgroups", &rcpt_list, &rcpt_list_end)!=RET_OK)
- {
- d_printf(0,"%s:%d Cancel msg contains no newsgroups header\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
- str = rcpt_list;
- laststart = rcpt_list;
-
- while (str != rcpt_list_end)
- {
- if (*str == ',') {
- /* eliminate leading whitespace */
- while (((*laststart) ==' ') || ((*laststart)=='\t'))
- {
- laststart++;
- }
-
- res = addCancelItem(cxn, laststart,
- str - laststart,
- control_header,
- control_header_end - control_header,
- NULL, must);
- if (res!=RET_OK) return res;
-
- laststart = str+1;
- }
-
- str++;
- }
-
- if (laststart<str)
- {
-
- res = addCancelItem(cxn, laststart, str - laststart,
- control_header,
- control_header_end - control_header,
- art, must);
- if (res!=RET_OK) return res;
- }
- break;
- }
- default:
- /* huh?!? */
- d_printf(0, "%s:%d internal error in addControlMsg()\n",
- hostPeerName (cxn->myHost), cxn->ident);
- }
- return RET_FAIL;
-}
-
-/*
- * Show msg handling statistics
- */
-
-static void show_stats(connection_t *cxn)
-{
- d_printf(0, "%s:%d\n",hostPeerName (cxn->myHost), cxn->ident);
- d_printf(0, " imap queue = %d lmtp queue = %d\n",
- QueueItems(&(cxn->imap_controlMsg_q)),
- QueueItems(&(cxn->lmtp_todeliver_q)));
- d_printf(0," imap state = %s\n", imap_stateToString(cxn->imap_state));
- d_printf(0," lmtp state = %s\n", lmtp_stateToString(cxn->lmtp_state));
- d_printf(0," delivered: yes: %d no: %d\n",
- cxn->lmtp_succeeded,
- cxn->lmtp_failed);
- d_printf(0," cancel: yes: %d no: %d\n",
- cxn->cancel_succeeded, cxn->cancel_failed);
- d_printf(0," create: yes: %d no: %d\n",
- cxn->create_succeeded, cxn->create_failed);
- d_printf(0," remove: yes: %d no: %d\n",
- cxn->remove_succeeded, cxn->remove_failed);
-}
-
-/**************************** SASL helper functions ******************************/
-
-#ifdef HAVE_SASL
-/* callback to get userid or authid */
-static int getsimple(void *context __attribute__((unused)),
- int id,
- const char **result,
- unsigned *len)
-{
- char *username;
- char *authid;
-
- if (! result)
- return SASL_BADPARAM;
-
-
- switch (id) {
- case SASL_CB_GETREALM:
- *result = deliver_realm;
- if (len)
- *len = deliver_realm ? strlen(deliver_realm) : 0;
- break;
-
- case SASL_CB_USER:
- *result = deliver_username;
- if (len)
- *len = deliver_username ? strlen(deliver_username) : 0;
- break;
- case SASL_CB_AUTHNAME:
- authid=deliver_authname;
- *result = authid;
- if (len)
- *len = authid ? strlen(authid) : 0;
- break;
- case SASL_CB_LANGUAGE:
- *result = NULL;
- if (len)
- *len = 0;
- break;
- default:
- return SASL_BADPARAM;
- }
- return SASL_OK;
-}
-
-/* callback to get password */
-static int
-getsecret(sasl_conn_t *conn,
- void *context __attribute__((unused)),
- int id,
- sasl_secret_t **psecret)
-{
- size_t passlen;
-
- if (! conn || ! psecret || id != SASL_CB_PASS)
- return SASL_BADPARAM;
-
- if (deliver_password==NULL)
- {
- d_printf(0,"SASL requested a password but I don't have one\n");
- return SASL_FAIL;
- }
-
- passlen = strlen(deliver_password);
- *psecret = xmalloc(sizeof(sasl_secret_t) + passlen + 1);
- if (! *psecret)
- return SASL_FAIL;
-
- strlcpy((*psecret)->data, deliver_password, passlen + 1);
- (*psecret)->len = passlen;
-
- return SASL_OK;
-}
-
-
-/* callbacks we support */
-static sasl_callback_t saslcallbacks[] = {
- {
- SASL_CB_GETREALM, &getsimple, NULL
- }, {
- SASL_CB_USER, &getsimple, NULL
- }, {
- SASL_CB_AUTHNAME, &getsimple, NULL
- }, {
- SASL_CB_PASS, &getsecret, NULL
- }, {
- SASL_CB_LIST_END, NULL, NULL
- }
-};
-
-static sasl_security_properties_t *make_secprops(int min,int max)
-{
- sasl_security_properties_t *ret=
- xmalloc(sizeof(sasl_security_properties_t));
-
- ret->maxbufsize=1024;
- ret->min_ssf=min;
- ret->max_ssf=max;
-
- ret->security_flags=0;
- ret->property_names=NULL;
- ret->property_values=NULL;
-
- return ret;
-}
-
-#ifndef NI_WITHSCOPEID
-#define NI_WITHSCOPEID 0
-#endif
-#ifndef NI_MAXHOST
-#define NI_MAXHOST 1025
-#endif
-#ifndef NI_MAXSERV
-#define NI_MAXSERV 32
-#endif
-
-static int iptostring(const struct sockaddr *addr, socklen_t addrlen,
- char *out, unsigned outlen) {
- char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
-
- if(!addr || !out) return SASL_BADPARAM;
-
- getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
- NI_NUMERICHOST | NI_WITHSCOPEID | NI_NUMERICSERV);
-
- if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
- return SASL_BUFOVER;
-
- snprintf(out, outlen, "%s;%s", hbuf, pbuf);
-
- return SASL_OK;
-}
-
-static conn_ret SetSASLProperties(sasl_conn_t *conn, int sock, int minssf, int maxssf)
-{
- int saslresult;
- sasl_security_properties_t *secprops=NULL;
- int addrsize=sizeof(struct sockaddr_in);
- char localip[60], remoteip[60];
- struct sockaddr_in saddr_l;
- struct sockaddr_in saddr_r;
-
- /* create a security structure and give it to sasl */
- secprops = make_secprops(minssf, maxssf);
- if (secprops != NULL)
- {
- sasl_setprop(conn, SASL_SEC_PROPS, secprops);
- free(secprops);
- }
-
- if (getpeername(sock,(struct sockaddr *)&saddr_r,&addrsize)!=0)
- return RET_FAIL;
-
- if (iptostring((struct sockaddr *)&saddr_r, sizeof(struct sockaddr_in),
- remoteip, sizeof(remoteip)))
- return RET_FAIL;
-
- if (sasl_setprop(conn, SASL_IPREMOTEPORT, remoteip)!=SASL_OK)
- return RET_FAIL;
-
- addrsize=sizeof(struct sockaddr_in);
- if (getsockname(sock,(struct sockaddr *) &saddr_l,&addrsize)!=0)
- return RET_FAIL;
-
- if (iptostring((struct sockaddr *)&saddr_l, sizeof(struct sockaddr_in),
- localip, sizeof(localip)))
- return RET_FAIL;
-
- if (sasl_setprop(conn, SASL_IPLOCALPORT, localip)!=SASL_OK)
- return RET_FAIL;
-
- return RET_OK;
-}
-#endif /* HAVE_SASL */
-
-/************************** END SASL helper functions ******************************/
-
-/************************* Startup functions **********************************/
-
-static conn_ret Initialize(connection_t *cxn, int respTimeout)
-{
-#ifdef HAVE_SASL
- conn_ret saslresult;
-#endif /* HAVE_SASL */
-
- d_printf(1,"%s:%d initializing....\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
-#ifdef HAVE_SASL
- /* only call sasl_client_init() once */
- if (initialized_sasl == 0)
- {
- /* Initialize SASL */
- saslresult=sasl_client_init(saslcallbacks);
-
- if (saslresult!=SASL_OK)
- {
- d_printf(0,
- "%s:%d Error initializing SASL (sasl_client_init) (%s)\n",
- hostPeerName (cxn->myHost), cxn->ident,
- sasl_errstring(saslresult, NULL, NULL));
- return RET_FAIL;
- } else {
- initialized_sasl = 1;
- }
- }
-#endif /* HAVE_SASL */
-
- cxn->lmtp_rBuffer = newBuffer(4096);
- if (cxn->lmtp_rBuffer == NULL)
- {
- d_printf(0, "%s:%d Failure allocating buffer for lmtp_rBuffer\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
- bufferAddNullByte(cxn->lmtp_rBuffer);
-
-
- cxn->imap_rBuffer = newBuffer(4096);
- if (cxn->imap_rBuffer == NULL)
- {
- d_printf(0, "%s:%d Failure allocating buffer for imap_rBuffer \n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
- bufferAddNullByte(cxn->imap_rBuffer);
-
- /* Initialize timeouts */
- cxn->lmtp_writeTimeout = respTimeout;
- cxn->lmtp_readTimeout = respTimeout;
- cxn->imap_writeTimeout = respTimeout;
- cxn->imap_readTimeout = respTimeout;
- cxn->lmtp_sleepTimerId = 0 ;
- cxn->lmtp_sleepTimeout = init_reconnect_period ;
- cxn->imap_sleepTimerId = 0 ;
- cxn->imap_sleepTimeout = init_reconnect_period ;
-
- cxn->dosomethingTimeout = DOSOMETHING_TIMEOUT;
-
- /* set up the write timer. */
- clearTimer (cxn->dosomethingTimerId) ;
-
- if (cxn->dosomethingTimeout > 0)
- cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk,
- cxn->dosomethingTimeout, cxn);
-
-
-
- return RET_OK;
-}
-
-
-/* initialize the network */
-static conn_ret init_net(char *serverFQDN,
- int port,
- int *sock)
-{
- struct sockaddr_in addr;
- struct hostent *hp;
-
- if ((hp = gethostbyname(serverFQDN)) == NULL) {
- d_printf(0, "gethostbyname(): %s\n", strerror(errno));
- return RET_FAIL;
- }
-
- if (( (*sock) = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- d_printf(0, "socket(): %s\n", strerror(errno));
- return RET_FAIL;
- }
-
- addr.sin_family = AF_INET;
- memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
- addr.sin_port = htons(port);
-
- if (connect( (*sock), (struct sockaddr *) &addr, sizeof (addr)) < 0) {
- d_printf(0,"connect(): %s\n",
- strerror(errno));
- return RET_FAIL;
- }
-
- return RET_OK;
-}
-
-
-
-static conn_ret SetupLMTPConnection(connection_t *cxn,
- char *serverName,
- int port)
-{
-#ifdef HAVE_SASL
- int saslresult;
-#endif /* HAVE_SASL */
- conn_ret result;
-
- cxn->lmtp_port = port;
-
- if (serverName==NULL)
- {
- d_printf(0, "%s:%d serverName is null\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
-#ifdef HAVE_SASL
- /* Free the SASL connection if we already had one */
- if (cxn->saslconn_lmtp!=NULL)
- {
- sasl_dispose(&cxn->saslconn_lmtp);
- }
-
- /* Start SASL */
- saslresult=sasl_client_new("lmtp",
- serverName,
- NULL,
- NULL,
- NULL,
- 0,
- &cxn->saslconn_lmtp);
-
- if (saslresult != SASL_OK)
- {
-
- d_printf(0, "%s:%d:LMTP Error creating a new SASL connection (%s)\n",
- hostPeerName (cxn->myHost), cxn->ident,
- sasl_errstring(saslresult,NULL,NULL));
- return RET_FAIL;
- }
-#endif /* HAVE_SASL */
-
- /* Connect the Socket */
- result = init_net(serverName,
- LMTP_PORT, /*port,*/
- &(cxn->sockfd_lmtp));
-
- if (result != RET_OK)
- {
- d_printf(0, "%s:%d unable to connect to lmtp host\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
- if (cxn->lmtp_respBuffer) free(cxn->lmtp_respBuffer);
- cxn->lmtp_respBuffer = xmalloc (4096);
- cxn->lmtp_respBuffer[0]='\0';
-
- /* Free if we had an existing one */
- if (cxn->lmtp_endpoint != NULL)
- {
- delEndPoint(cxn->lmtp_endpoint);
- cxn->lmtp_endpoint = NULL;
- }
-
- cxn->lmtp_endpoint = newEndPoint(cxn->sockfd_lmtp);
- if (cxn->lmtp_endpoint == NULL)
- {
- d_printf(0, "%s:%d:LMTP failure creating endpoint\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
-#ifdef HAVE_SASL
- /* Set the SASL properties */
- result = SetSASLProperties(cxn->saslconn_lmtp, cxn->sockfd_lmtp,
- 0, 0);
-
- if (result != RET_OK)
- {
- d_printf(0,"%s:%d:LMTP error setting SASL properties\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-#endif /* HAVE_SASL */
-
-
- return RET_OK;
-}
-
-static conn_ret SetupIMAPConnection(connection_t *cxn,
- char *serverName,
- int port)
-{
-#ifdef HAVE_SASL
- int saslresult;
-#endif /* HAVE_SASL */
- conn_ret result;
-
- cxn->imap_port = port;
-
- if (serverName==NULL)
- {
- d_printf(0,"%s:%d:IMAP Servername is null",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
-#ifdef HAVE_SASL
- /* Free the SASL connection if we already had one */
- if (cxn->imap_saslconn!=NULL)
- {
- sasl_dispose(&cxn->imap_saslconn);
- }
-
- /* Start SASL */
- saslresult=sasl_client_new("imap",
- serverName,
- NULL,
- NULL,
- NULL,
- 0,
- &cxn->imap_saslconn);
-
- if (saslresult != SASL_OK)
- {
- d_printf(0,"%s:%d:IMAP Error creating a new SASL connection (%s)",
- hostPeerName (cxn->myHost), cxn->ident,
- sasl_errstring(saslresult,NULL,NULL));
- return RET_FAIL;
- }
-#endif /* HAVE_SASL */
-
- /* Connect the Socket */
- result = init_net(serverName,
- port,
- &(cxn->imap_sockfd));
-
- if (result != RET_OK)
- {
- d_printf(0,"%s:%d:IMAP Unable to start network connection for IMAP",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
- if (cxn->imap_respBuffer) free(cxn->imap_respBuffer);
- cxn->imap_respBuffer = xmalloc (4096);
- cxn->imap_respBuffer[0]='\0';
-
- /* Free if we had an existing one */
- if (cxn->imap_endpoint != NULL)
- {
- delEndPoint(cxn->imap_endpoint);
- cxn->imap_endpoint = NULL;
- }
-
- cxn->imap_endpoint = newEndPoint(cxn->imap_sockfd);
- if (cxn->imap_endpoint == NULL)
- {
- d_printf(0,"%s:%d:IMAP Failure creating imap endpoint\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
-#ifdef HAVE_SASL
- /* Set the SASL properties */
- result = SetSASLProperties(cxn->imap_saslconn, cxn->imap_sockfd,
- 0, 0);
- if (result != RET_OK)
- {
- d_printf(0,"%s:%d:IMAP Error setting sasl properties",
- hostPeerName (cxn->myHost), cxn->ident);
- return result;
- }
-#endif /* HAVE_SASL */
-
-
- return RET_OK;
-}
-
-/************************* END Startup functions **********************************/
-
-/* Return the response code for this line
- -1 if it doesn't seem to have one
-*/
-static int ask_code(char *str)
-{
- int ret = 0;
-
- if (str==NULL) return -1;
-
- if (strlen(str) < 3) return -1;
-
- /* check to make sure 0-2 are digits */
- if ((isdigit((int) str[0])==0) ||
- (isdigit((int) str[1])==0) ||
- (isdigit((int) str[2])==0))
- {
- d_printf(0,
- "Parse error: response does not begin with a code [%s]\n",
- str);
- return -1;
- }
-
-
- ret = ((str[0]-'0')*100)+
- ((str[1]-'0')*10)+
- (str[2]-'0');
-
- return ret;
-}
-
-/* is this a continuation or not?
- 220-fdfsd is (1)
- 220 fdsfs is not (0)
- */
-
-static int ask_keepgoing(char *str)
-{
- if (str==NULL) return 0;
- if (strlen(str) < 4) return 0;
-
- if (str[3]=='-') return 1;
-
- return 0;
-}
-
-
-static conn_ret lmtp_listenintro(connection_t *cxn)
-{
- Buffer *readBuffers;
-
- /* set up to receive */
- readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
- prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
-
- cxn->lmtp_state = LMTP_READING_INTRO;
-
- return RET_OK;
-}
-
-
-
-/************************** IMAP functions ***********************/
-
-static conn_ret imap_Connect(connection_t *cxn)
-{
- conn_ret result;
-
- ASSERT(cxn->imap_sleepTimerId == 0);
-
- /* make the IMAP connection */
- result = SetupIMAPConnection(cxn,
- cxn->ServerName,
- IMAP_PORT);
-
- /* Listen to the intro and start the authenticating process */
- result = imap_listenintro(cxn);
-
- return result;
-}
-
-/*
- * This is called when the data write timeout for the remote
- * goes off. We tear down the connection and notify our host.
- */
-static void imap_writeTimeoutCbk (TimeoutId id UNUSED, void *data)
-{
- connection_t *cxn = (Connection) data ;
- const char *peerName ;
-
- peerName = hostPeerName (cxn->myHost) ;
-
- syslog (LOG_WARNING, "timeout for %s", peerName);
- d_printf(0, "%s: shutting down non-responsive IMAP connection (%s)\n",
- hostPeerName (cxn->myHost),
- imap_stateToString(cxn->imap_state));
-
- cxnLogStats (cxn,true) ;
-
- imap_Disconnect(cxn);
-}
-
-/*
- * This is called when the timeout for the reponse from the remote
- * goes off. We tear down the connection and notify our host.
- */
-static void imap_readTimeoutCbk (TimeoutId id, void *data)
-{
- Connection cxn = (Connection) data ;
- const char *peerName ;
-
- ASSERT (id == cxn->imap_readBlockedTimerId) ;
-
- peerName = hostPeerName (cxn->myHost) ;
-
- warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ;
- d_printf(0, "%s:%d shutting down non-responsive IMAP connection (%s)\n",
- hostPeerName (cxn->myHost), cxn->ident,
- imap_stateToString(cxn->imap_state));
-
- cxnLogStats (cxn,true);
-
- if (cxn->imap_state == IMAP_DISCONNECTED)
- {
- imap_Disconnect(cxn);
- lmtp_Disconnect(cxn);
- delConnection (cxn) ;
- }
- else {
- imap_Disconnect(cxn);
- }
-}
-
-/*
- * Called by the EndPoint class when the timer goes off
- */
-static void imap_reopenTimeoutCbk (TimeoutId id, void *data)
-{
- Connection cxn = (Connection) data ;
-
- ASSERT (id == cxn->imap_sleepTimerId) ;
-
- cxn->imap_sleepTimerId = 0 ;
-
- d_printf(1,"%s:%d:IMAP Reopen timer rang. Try to make new connection now\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
-
- if (cxn->imap_state != IMAP_DISCONNECTED)
- {
- warn ("%s:%d cxnsleep connection in bad state: %s",
- hostPeerName (cxn->myHost), cxn->ident,
- imap_stateToString (cxn->imap_state)) ;
- }
- else {
- if (imap_Connect(cxn) != RET_OK)
- prepareReopenCbk(cxn, 0);
- }
-}
-
-static void imap_Disconnect(connection_t *cxn)
-{
- clearTimer (cxn->imap_sleepTimerId) ;
- cxn->imap_sleepTimerId = 0;
- clearTimer (cxn->imap_readBlockedTimerId) ;
- clearTimer (cxn->imap_writeBlockedTimerId) ;
-
- DeferAllArticles(cxn, &(cxn->imap_controlMsg_q)) ; /* give any articles back to Host */
-
- cxn->imap_state = IMAP_DISCONNECTED;
-
- cxn->imap_disconnects++;
-
- cxn->imap_respBuffer[0]='\0';
-
- if (cxn->issue_quit == 0)
- prepareReopenCbk(cxn,0);
-
- DeleteIfDisconnected(cxn);
-}
-
-/************************** END IMAP functions ***********************/
-
-/************************ LMTP functions **************************/
-
-/*
- * Create a network lmtp connection
- * and start listening for the intro string
- *
- */
-
-static conn_ret lmtp_Connect(connection_t *cxn)
-{
- conn_ret result;
-
- ASSERT(cxn->lmtp_sleepTimerId == 0);
-
- /* make the LMTP connection */
- result = SetupLMTPConnection(cxn,
- cxn->ServerName,
- LMTP_PORT);
-
- if (result!=RET_OK) return result;
-
- /* Listen to the intro */
- result = lmtp_listenintro(cxn);
-
- return result;
-}
-
-
-
-static void lmtp_Disconnect(connection_t *cxn)
-{
- clearTimer (cxn->lmtp_sleepTimerId) ;
- cxn->lmtp_sleepTimerId = 0;
- clearTimer (cxn->lmtp_readBlockedTimerId) ;
- clearTimer (cxn->lmtp_writeBlockedTimerId) ;
-
- /* give any articles back to Host */
- DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q)) ;
-
- cxn->lmtp_state = LMTP_DISCONNECTED;
-
- cxn->lmtp_disconnects++;
-
- cxn->lmtp_respBuffer[0]='\0';
-
- if (cxn->issue_quit == 0)
- prepareReopenCbk(cxn,1);
-
- DeleteIfDisconnected(cxn);
-}
-
-
-
-/*
- * Called by the EndPoint class when the timer goes off
- */
-static void lmtp_reopenTimeoutCbk (TimeoutId id, void *data)
-{
- Connection cxn = (Connection) data ;
-
- ASSERT (id == cxn->lmtp_sleepTimerId) ;
-
- cxn->lmtp_sleepTimerId = 0 ;
-
- d_printf(1,"%s:%d:LMTP Reopen timer rang. Try to make new connection now\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
-
- if (cxn->lmtp_state != LMTP_DISCONNECTED)
- {
- warn ("%s:%d cxnsleep connection in bad state: %s",
- hostPeerName (cxn->myHost), cxn->ident,
- lmtp_stateToString (cxn->lmtp_state)) ;
- }
- else {
- if (lmtp_Connect(cxn) != RET_OK)
- prepareReopenCbk(cxn, 1);
- }
-}
-
-/*
- * Set up the callback used when the Connection is sleeping (i.e. will try
- * to reopen the connection).
- *
- * type (0 = imap, 1 = lmtp)
- */
-static void prepareReopenCbk (Connection cxn, int type)
-{
- /* xxx check state */
-
-
-
- if (type == 0) {
-
- cxn->imap_sleepTimerId = prepareSleep (imap_reopenTimeoutCbk,
- cxn->imap_sleepTimeout, cxn) ;
- d_printf (1,"%s:%d IMAP connection error\n"
- " will try to reconnect in %d seconds\n",
- hostPeerName (cxn->myHost), cxn->ident,
- cxn->imap_sleepTimeout) ;
- } else {
- cxn->lmtp_sleepTimerId = prepareSleep (lmtp_reopenTimeoutCbk,
- cxn->lmtp_sleepTimeout, cxn) ;
- d_printf (1,"%s:%d:LMTP connection error\n"
- "will try to reconnect in %d seconds\n",
- hostPeerName (cxn->myHost), cxn->ident,
- cxn->lmtp_sleepTimeout) ;
- }
-
- /* bump the sleep timer amount each time to wait longer and longer. Gets
- reset in resetConnection() */
- if (type == 0) {
- cxn->imap_sleepTimeout *= 2 ;
- if (cxn->imap_sleepTimeout > max_reconnect_period)
- cxn->imap_sleepTimeout = max_reconnect_period ;
- } else {
- cxn->lmtp_sleepTimeout *= 2 ;
- if (cxn->lmtp_sleepTimeout > max_reconnect_period)
- cxn->lmtp_sleepTimeout = max_reconnect_period ;
- }
-}
-
-/*
- * This is called when the timeout for the reponse from the remote
- * goes off. We tear down the connection and notify our host.
- */
-static void lmtp_readTimeoutCbk (TimeoutId id, void *data)
-{
- Connection cxn = (Connection) data ;
- const char *peerName ;
-
- ASSERT (id == cxn->lmtp_readBlockedTimerId) ;
-
- peerName = hostPeerName (cxn->myHost) ;
-
- warn ("%s:%d cxnsleep non-responsive connection", peerName, cxn->ident) ;
- d_printf(0,"%s:%d shutting down non-responsive LMTP connection (%s)\n",
- hostPeerName (cxn->myHost), cxn->ident,
- lmtp_stateToString(cxn->lmtp_state));
-
- cxnLogStats (cxn,true) ;
-
- if (cxn->lmtp_state == LMTP_DISCONNECTED) {
- imap_Disconnect(cxn);
- lmtp_Disconnect(cxn);
- delConnection (cxn) ;
- } else {
- lmtp_Disconnect(cxn);
- }
-}
-
-
-
-/*
- * This is called when the data write timeout for the remote
- * goes off. We tear down the connection and notify our host.
- */
-static void lmtp_writeTimeoutCbk (TimeoutId id UNUSED, void *data)
-{
- connection_t *cxn = (Connection) data ;
- const char *peerName ;
-
- peerName = hostPeerName (cxn->myHost) ;
-
- syslog (LOG_WARNING, "timeout for %s", peerName);
- d_printf(0, "%s:%d shutting down non-responsive LMTP connection (%s)\n",
- hostPeerName (cxn->myHost), cxn->ident,
- lmtp_stateToString(cxn->lmtp_state)) ;
-
- cxnLogStats (cxn,true);
-
- lmtp_Disconnect(cxn);
-}
-
-/************************ END LMTP functions **************************/
-
-/************************** LMTP write functions ********************/
-
-static conn_ret lmtp_noop(connection_t *cxn)
-{
- int result;
- char *p;
-
- p = xstrdup("NOOP\r\n");
- result = WriteToWire_lmtpstr(cxn, p, strlen(p));
- if (result!=RET_OK) return result;
-
- cxn->lmtp_state = LMTP_WRITING_NOOP;
-
- return RET_OK;
-}
-
-static conn_ret lmtp_IssueQuit(connection_t *cxn)
-{
- int result;
- char *p;
-
- p = xstrdup("QUIT\r\n");
- result = WriteToWire_lmtpstr(cxn, p, strlen(p));
- if (result!=RET_OK) return result;
-
- cxn->lmtp_state = LMTP_WRITING_QUIT;
-
- return RET_OK;
-}
-
-static conn_ret lmtp_getcapabilities(connection_t *cxn)
-{
- int result;
- char *p;
-
- if (cxn->lmtp_capabilities != NULL)
- {
- if (cxn->lmtp_capabilities->saslmechs) {
- free( cxn->lmtp_capabilities->saslmechs);
- }
- free( cxn->lmtp_capabilities );
- cxn->lmtp_capabilities = NULL;
- }
-
- cxn->lmtp_capabilities = xcalloc (1, sizeof(lmtp_capabilities_t));
- cxn->lmtp_capabilities->saslmechs = NULL;
-
-#ifdef SMTPMODE
- p = concat("EHLO ", hostname, "\r\n", (char *) 0);
-#else
- p = concat("LHLO ", hostname, "\r\n", (char *) 0);
-#endif /* SMTPMODE */
-
- result = WriteToWire_lmtpstr(cxn, p, strlen(p));
- if (result!=RET_OK) return result;
-
- cxn->lmtp_state = LMTP_WRITING_LHLO;
-
- return RET_OK;
-}
-
-#ifdef HAVE_SASL
-static conn_ret lmtp_authenticate(connection_t *cxn)
-{
- int saslresult;
-
- const char *mechusing;
- const char *out;
- unsigned int outlen;
- char *inbase64;
- int inbase64len;
- int status;
- int result;
-
- char *p;
-
- sasl_interact_t *client_interact=NULL;
-
-
- saslresult=sasl_client_start(cxn->saslconn_lmtp,
- cxn->lmtp_capabilities->saslmechs,
- &client_interact,
- &out, &outlen,
- &mechusing);
-
-
-
- if ((saslresult != SASL_OK) &&
- (saslresult != SASL_CONTINUE)) {
-
- d_printf(0,"%s:%d:LMTP Error calling sasl_client_start (%s)\n",
- hostPeerName (cxn->myHost), cxn->ident,
- sasl_errstring(saslresult, NULL, NULL));
- return RET_FAIL;
- }
-
- d_printf(1,"%s:%d:LMTP Decided to try to authenticate with SASL mechanism=%s\n",
- hostPeerName (cxn->myHost), cxn->ident,mechusing);
-
- if (!out)
- {
- /* no initial client response */
- p = concat("AUTH ", mechusing, "\r\n", (char *) 0);
- } else if (!outlen) {
- /* empty initial client response */
- p = concat("AUTH ", mechusing, " =\r\n", (char *) 0);
- } else {
- /* initial client response - convert to base64 */
- inbase64 = xmalloc(outlen*2+10);
-
- saslresult = sasl_encode64(out, outlen,
- inbase64, outlen*2+10,
- (unsigned *) &inbase64len);
- if (saslresult != SASL_OK) return RET_FAIL;
- p = concat("AUTH ", mechusing, " ", inbase64, "\r\n", (char *) 0);
- free(inbase64);
- }
-
- result = WriteToWire_lmtpstr(cxn, p, strlen(p));
-
- cxn->lmtp_state = LMTP_WRITING_STARTAUTH;
-
- return RET_OK;
-}
-
-static imt_stat lmtp_getauthline(char *str, char **line, int *linelen)
-{
- char buf[4096];
- int saslresult;
- int response_code = -1;
-
- response_code = ask_code(str);
-
- if (response_code == 334) {
-
- /* continue */
-
- } else if (response_code == 235) {
-
- /* woohoo! authentication complete */
- return STAT_OK;
-
- } else {
- /* failure of some sort */
- d_printf(0,"?:?:LMTP Authentication failure (%d) [%s]\n",
- response_code,str);
- return STAT_NO;
- }
-
- str += 4; /* jump past the "334 " */
-
- *line = xmalloc(strlen(str)+30);
- if ((*line)==NULL) {
- return STAT_NO;
- }
-
- /* decode this line */
- saslresult = sasl_decode64(str, strlen(str),
- *line, strlen(str)+1, (unsigned *) linelen);
- if (saslresult != SASL_OK) {
- d_printf(0,"?:?:LMTP base64 decoding error\n");
- return STAT_NO;
- }
-
- return STAT_CONT;
-}
-#endif /* HAVE_SASL */
-
-static void lmtp_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b,
- void *d)
-{
- connection_t *cxn = (connection_t *) d;
- Buffer *readBuffers;
-
- clearTimer (cxn->lmtp_writeBlockedTimerId) ;
-
- /* Free the string that was written */
- freeBufferArray (b);
- if (cxn->lmtp_tofree_str!=NULL)
- {
- free(cxn->lmtp_tofree_str);
- cxn->lmtp_tofree_str=NULL;
- }
-
- /* set up to receive */
- readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
- prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
-
- /* set up the response timer. */
- clearTimer (cxn->lmtp_readBlockedTimerId) ;
-
- if (cxn->lmtp_readTimeout > 0)
- cxn->lmtp_readBlockedTimerId = prepareSleep (lmtp_readTimeoutCbk,
- cxn->lmtp_readTimeout, cxn) ;
-
-
- switch (cxn->lmtp_state)
- {
-
- case LMTP_WRITING_LHLO:
- cxn->lmtp_state = LMTP_READING_LHLO;
- break;
-
- case LMTP_WRITING_STARTAUTH:
- case LMTP_WRITING_STEPAUTH:
-
- cxn->lmtp_state = LMTP_READING_STEPAUTH;
-
- break;
-
- case LMTP_WRITING_UPTODATA:
- /* expect result to rset */
- cxn->lmtp_state = LMTP_READING_RSET;
- break;
-
- case LMTP_WRITING_CONTENTS:
- /* so we sent the whole DATA command
- let's see what the server responded */
-
- cxn->lmtp_state = LMTP_READING_CONTENTS;
-
- break;
-
- case LMTP_WRITING_NOOP:
- cxn->lmtp_state = LMTP_READING_NOOP;
- break;
-
- case LMTP_WRITING_QUIT:
- cxn->lmtp_state = LMTP_READING_QUIT;
- break;
-
- default:
-
- d_printf(0,"%s:%d:LMTP Unknown state. Internal error\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
-
- break;
- }
-}
-
-/************************** END LMTP write functions ********************/
-
-/************************** IMAP sending functions ************************/
-
-
-static void imap_writeCB (EndPoint e UNUSED, IoStatus i UNUSED, Buffer *b,
- void *d)
-{
- connection_t *cxn = (connection_t *) d;
- Buffer *readBuffers;
-
- clearTimer (cxn->imap_writeBlockedTimerId) ;
-
- /* free the string we just wrote out */
- freeBufferArray (b);
- if (cxn->imap_tofree_str!=NULL)
- {
- free(cxn->imap_tofree_str);
- cxn->imap_tofree_str=NULL;
- }
-
- /* set up to receive */
- readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
- prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
-
- /* set up the response timer. */
- clearTimer (cxn->imap_readBlockedTimerId) ;
-
- if (cxn->imap_readTimeout > 0)
- cxn->imap_readBlockedTimerId = prepareSleep(imap_readTimeoutCbk,
- cxn->imap_readTimeout,
- cxn);
-
- switch (cxn->imap_state) {
- case IMAP_WRITING_CAPABILITY:
- cxn->imap_state = IMAP_READING_CAPABILITY;
- break;
-
- case IMAP_WRITING_STEPAUTH:
- case IMAP_WRITING_STARTAUTH:
- cxn->imap_state = IMAP_READING_STEPAUTH;
- break;
-
- case IMAP_WRITING_CREATE:
- cxn->imap_state = IMAP_READING_CREATE;
- break;
-
- case IMAP_WRITING_DELETE:
- cxn->imap_state = IMAP_READING_DELETE;
- break;
-
- case IMAP_WRITING_SELECT:
- cxn->imap_state = IMAP_READING_SELECT;
- break;
-
- case IMAP_WRITING_SEARCH:
- cxn->imap_state = IMAP_READING_SEARCH;
- break;
-
- case IMAP_WRITING_STORE:
- cxn->imap_state = IMAP_READING_STORE;
- break;
-
- case IMAP_WRITING_CLOSE:
- cxn->imap_state = IMAP_READING_CLOSE;
- break;
-
- case IMAP_WRITING_NOOP:
- cxn->imap_state = IMAP_READING_NOOP;
- break;
-
- case IMAP_WRITING_QUIT:
- cxn->imap_state = IMAP_READING_QUIT;
- break;
-
- default:
- d_printf(0,"%s:%d:IMAP invalid connection state\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
- imap_Disconnect(cxn);
- break;
- }
-}
-
-/*
- * Tag is already allocated
- */
-
-static void imap_GetTag(connection_t *cxn)
-{
- sprintf(cxn->imap_currentTag,"%06d",cxn->imap_tag_num);
- cxn->imap_tag_num++;
- if (cxn->imap_tag_num >= 999999)
- {
- cxn->imap_tag_num = 0;
- }
-}
-
-#ifdef HAVE_SASL
-static conn_ret imap_sendAuthStep(connection_t *cxn, char *str)
-{
- conn_ret result;
- int saslresult;
- char in[4096];
- unsigned int inlen;
- const char *out;
- unsigned int outlen;
- char *inbase64;
- unsigned int inbase64len;
-
- /* base64 decode it */
-
- saslresult = sasl_decode64(str, strlen(str),
- in, strlen(str)+1, &inlen);
- if (saslresult != SASL_OK) {
- d_printf(0,"%s:%d:IMAP base64 decoding error\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
- return RET_FAIL;
- }
-
- saslresult=sasl_client_step(cxn->imap_saslconn,
- in,
- inlen,
- NULL,
- &out,
- &outlen);
-
- /* check if sasl succeeded */
- if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
-
- d_printf(0,"%s:%d:IMAP sasl_client_step failed with %s\n",
- hostPeerName (cxn->myHost), cxn->ident,
- sasl_errstring(saslresult,NULL,NULL));
- cxn->imap_state = IMAP_CONNECTED_NOTAUTH;
- return RET_FAIL;
- }
-
- inbase64 = xmalloc(outlen * 2 + 10);
-
- /* convert to base64 */
- saslresult = sasl_encode64(out, outlen,
- inbase64, outlen*2, (unsigned *) &inbase64len);
-
- if (saslresult != SASL_OK) return RET_FAIL;
-
- /* append endline */
- strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10 - inbase64len);
- inbase64len+=2;
-
- /* send to server */
- result = WriteToWire_imapstr(cxn,inbase64, inbase64len);
-
- cxn->imap_state = IMAP_WRITING_STEPAUTH;
-
- return result;
-}
-#endif /* HAVE_SASL */
-
-static conn_ret imap_sendAuthenticate(connection_t *cxn)
-{
- int result;
-
- char *p;
-
-#ifdef HAVE_SASL
- const char *mechusing;
- char *inbase64;
- int inbase64len;
- int saslresult=SASL_NOMECH;
-
- sasl_interact_t *client_interact=NULL;
-
- if (cxn->imap_capabilities->saslmechs) {
- saslresult=sasl_client_start(cxn->imap_saslconn,
- cxn->imap_capabilities->saslmechs,
- &client_interact,
- NULL, NULL,
- &mechusing);
- }
-
-
-
- /* If no mechs try "login" */
- if (saslresult == SASL_NOMECH)
- {
-
-#else /* HAVE_SASL */
-
- { /* always do login */
-
-#endif /* HAVE_SASL */
- d_printf(1,"%s:%d:IMAP No mechanism found. Trying login method\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
-
- if (cxn->imap_capabilities->logindisabled==1)
- {
- d_printf(0,"%s:%d:IMAP Login command w/o security layer not allowed on this server\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return RET_FAIL;
- }
-
- if (deliver_authname==NULL)
- {
- d_printf(0,"%s:%d:IMAP Unable to log in because can't find a authname\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
- return RET_FAIL;
- }
-
- if (deliver_password==NULL)
- {
- d_printf(0,"%s:%d:IMAP Unable to log in because can't find a password\n",
- hostPeerName (cxn->myHost), cxn->ident) ;
- return RET_FAIL;
- }
-
- imap_GetTag(cxn);
-
- p = concat(cxn->imap_currentTag, " LOGIN ", deliver_authname, " \"",
- deliver_password, "\"\r\n", (char *) 0);
-
- result = WriteToWire_imapstr(cxn, p, strlen(p));
-
- cxn->imap_state = IMAP_WRITING_STARTAUTH;
-
- return RET_OK;
- }
-
-#ifdef HAVE_SASL
- if ((saslresult != SASL_OK) &&
- (saslresult != SASL_CONTINUE)) {
-
- d_printf(0,"%s:%d:IMAP Error calling sasl_client_start (%s) mechusing = %s\n",
- hostPeerName (cxn->myHost), cxn->ident,
- sasl_errstring(saslresult, NULL, NULL), mechusing);
- return RET_FAIL;
- }
-
- d_printf(1,"%s:%d:IMAP Trying to authenticate to imap with %s mechanism\n",
- hostPeerName (cxn->myHost), cxn->ident,
- mechusing);
-
- imap_GetTag(cxn);
-
- p = concat(cxn->imap_currentTag, " AUTHENTICATE ", mechusing, "\r\n",
- (char *) 0);
- result = WriteToWire_imapstr(cxn, p, strlen(p));
-
- cxn->imap_state = IMAP_WRITING_STARTAUTH;
-
- return RET_OK;
-#endif /* HAVE_SASL */
-}
-
-static conn_ret imap_CreateGroup(connection_t *cxn, char *bboard)
-{
- conn_ret result;
- char *tosend;
-
- d_printf(1,"%s:%d:IMAP Ok creating group [%s]\n",
- hostPeerName (cxn->myHost), cxn->ident,
- bboard);
-
- imap_GetTag(cxn);
-
- tosend = concat(cxn->imap_currentTag, " CREATE ", bboard, "\r\n",
- (char *) 0);
-
- result = WriteToWire_imapstr(cxn, tosend, -1);
- if (result!=RET_OK) return result;
-
- cxn->imap_state = IMAP_WRITING_CREATE;
-
- return RET_OK;
-}
-
-static conn_ret imap_DeleteGroup(connection_t *cxn, char *bboard)
-{
- conn_ret result;
- char *tosend;
-
- d_printf(1,"%s:%d:IMAP Ok removing bboard [%s]\n",
- hostPeerName (cxn->myHost), cxn->ident, bboard);
-
- imap_GetTag(cxn);
-
- tosend = concat(cxn->imap_currentTag, " DELETE ", bboard, "\r\n",
- (char *) 0);
- result = WriteToWire_imapstr(cxn, tosend, -1);
- if (result!=RET_OK) return result;
-
- cxn->imap_state = IMAP_WRITING_DELETE;
-
- return RET_OK;
-}
-
-static conn_ret imap_CancelMsg(connection_t *cxn, char *newsgroup)
-{
- conn_ret result;
- char *tosend;
-
- ASSERT(newsgroup);
-
- imap_GetTag(cxn);
-
- /* select mbox */
- tosend = concat(cxn->imap_currentTag, " SELECT ", newsgroup, "\r\n",
- (char *) 0);
- result = WriteToWire_imapstr(cxn, tosend, -1);
- if (result != RET_OK) return result;
-
- cxn->imap_state = IMAP_WRITING_SELECT;
-
- hostArticleOffered (cxn->myHost, cxn);
-
- return RET_OK;
-}
-
-static conn_ret imap_sendSearch(connection_t *cxn, char *msgid)
-{
- conn_ret result;
- char *tosend;
-
- ASSERT(msgid);
-
- imap_GetTag(cxn);
-
- /* preform search */
- tosend = concat(cxn->imap_currentTag,
- " UID SEARCH header \"Message-ID\" \"", msgid, "\"\r\n",
- (char *) 0);
- result = WriteToWire_imapstr(cxn, tosend, -1);
- if (result != RET_OK) return result;
-
- cxn->imap_state = IMAP_WRITING_SEARCH;
-
- return RET_OK;
-}
-
-static conn_ret imap_sendKill(connection_t *cxn, unsigned uid)
-{
- conn_ret result;
- char *tosend;
- size_t length;
-
- imap_GetTag(cxn);
-
- length = 7 + 50 + 20;
- tosend = xmalloc(length);
- snprintf(tosend,length,"%s UID STORE %d +FLAGS.SILENT (\\Deleted)\r\n",
- cxn->imap_currentTag, uid);
-
- result = WriteToWire_imapstr(cxn, tosend, -1);
- if (result != RET_OK) return result;
-
- cxn->imap_state = IMAP_WRITING_STORE;
-
- return RET_OK;
-}
-
-static conn_ret imap_sendSimple(connection_t *cxn, const char *atom, int st)
-{
- char *tosend;
- conn_ret result;
-
- imap_GetTag(cxn);
- tosend = concat(cxn->imap_currentTag, " ", atom, "\r\n", (char *) 0);
-
- result = WriteToWire_imapstr(cxn, tosend, -1);
- if (result != RET_OK) return result;
-
- cxn->imap_state = st;
-
- return RET_OK;
-}
-
-static conn_ret imap_sendClose(connection_t *cxn)
-{
- return imap_sendSimple(cxn, "CLOSE", IMAP_WRITING_CLOSE);
-}
-
-static conn_ret imap_sendQuit(connection_t *cxn)
-{
- return imap_sendSimple(cxn, "LOGOUT", IMAP_WRITING_QUIT);
-}
-
-static conn_ret imap_noop(connection_t *cxn)
-{
- return imap_sendSimple(cxn, "NOOP", IMAP_WRITING_NOOP);
-}
-
-
-static conn_ret imap_sendCapability(connection_t *cxn)
-{
- return imap_sendSimple(cxn, "CAPABILITY", IMAP_WRITING_CAPABILITY);
-}
-
-/************************** END IMAP sending functions ************************/
-
-/************************** IMAP reading functions ***************************/
-
-static conn_ret imap_listenintro(connection_t *cxn)
-{
- Buffer *readBuffers;
-
- /* set up to receive */
- readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
- prepareRead(cxn->imap_endpoint, readBuffers, imap_readCB, cxn, 5);
-
- cxn->imap_state = IMAP_READING_INTRO;
-
- return RET_OK;
-}
-
-static conn_ret imap_ParseCapability(char *string, imap_capabilities_t **caps)
-{
- char *str = string;
- char *start = str;
- size_t mechlen;
-
- /* allocate the caps structure if it doesn't already exist */
- if ( (*caps) == NULL)
- (*caps) = xcalloc(1, sizeof(imap_capabilities_t));
-
- while ( (*str) != '\0')
- {
-
- while (((*str) != '\0') && ((*str)!=' '))
- {
- str++;
- }
-
- if ( (*str) != '\0')
- {
- *str = '\0';
- str++;
- }
-
- if ( strcasecmp(start,"IMAP4")==0)
- {
- (*caps)->imap4 = 1;
- } else if (strcasecmp(start,"LOGINDISABLED")==0) {
- (*caps)->logindisabled = 1;
- } else if ( strncmp(start, "AUTH=", 5)==0) {
-
- if ( (*caps)->saslmechs == NULL)
- {
- (*caps)->saslmechs = xstrdup (start + 5);
- } else {
- mechlen = strlen((*caps)->saslmechs) + 1;
- mechlen += strlen(start + 5) + 1;
- (*caps)->saslmechs = xrealloc((*caps)->saslmechs, mechlen);
- strlcat((*caps)->saslmechs, " ", mechlen);
- strlcat((*caps)->saslmechs, start + 5, mechlen);
- }
- }
-
- start = str;
-
- }
-
- if ((*caps)->saslmechs) {
- d_printf(1,"?:?:IMAP parsed capabilities: saslmechs = %s\n",
- (*caps)->saslmechs);
- }
-
- return RET_OK;
-}
-
-
-static void imap_readCB (EndPoint e, IoStatus i, Buffer *b, void *d)
-{
- connection_t *cxn = (connection_t *) d;
- Buffer *readBuffers ;
-
- int okno;
- char *str;
- char strbuf[4096];
- char *linestart;
- conn_ret ret;
- char *p;
-
- p = bufferBase(b[0]);
-
- /* Add what we got to our internal read buffer */
- bufferAddNullByte (b[0]) ;
-
- if (i != IoDone) {
- errno = endPointErrno(e);
-
- syslog(LOG_ERR, "%s:%d IMAP i/o failed: %m",
- hostPeerName (cxn->myHost), cxn->ident);
- freeBufferArray (b);
- imap_Disconnect(cxn);
- return;
- }
-
- if (strchr (p, '\n') == NULL) {
- /* partial read. expand buffer and retry */
-
- if (expandBuffer (b[0], BUFFER_EXPAND_AMOUNT)==false) {
- d_printf(0,"%s:%d:IMAP expanding buffer returned false\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- imap_Disconnect(cxn);
- return;
- }
- readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ;
-
- if (!prepareRead (e, readBuffers, imap_readCB, cxn, 1)) {
- imap_Disconnect(cxn);
- }
-
- freeBufferArray (b);
- return;
- }
-
- clearTimer (cxn->imap_readBlockedTimerId) ;
-
- /* we got something. add to our buffer and free b */
-
- strcat(cxn->imap_respBuffer, p);
-
- bufferSetDataSize( b[0], 0);
-
- freeBufferArray (b);
-
-
-
- /* goto here to take another step */
- reset:
-
- /* see if we have a full line */
- ret = GetLine( cxn->imap_respBuffer , strbuf, sizeof(strbuf));
- str = strbuf;
- linestart = str;
-
- /* if we don't have a full line */
- if ( ret == RET_NO_FULLLINE)
- {
-
- readBuffers = makeBufferArray (bufferTakeRef (cxn->imap_rBuffer), NULL) ;
-
- if ( !prepareRead (e, readBuffers, imap_readCB, cxn, 1) )
- {
- imap_Disconnect(cxn);
- }
- return;
-
- } else if (ret!=RET_OK)
- {
- return;
- }
-
- /* if untagged */
- if ((str[0]=='*') && (str[1]==' '))
- {
- str+=2;
-
- /* now figure out what kind of untagged it is */
- if (strncasecmp(str,"CAPABILITY ",11)==0)
- {
- str+=11;
-
- imap_ParseCapability(str,&(cxn->imap_capabilities));
-
- } else if (strncasecmp(str,"SEARCH",6)==0) {
-
- str+=6;
-
- if ( (*str) == ' ')
- {
- str++;
-
- cxn->current_control->data.control->uid = atoi(str);
-
- d_printf(1,"%s:%d:IMAP i think the UID = %ld\n",
- hostPeerName (cxn->myHost), cxn->ident,
- cxn->current_control->data.control->uid);
- } else {
- /* it's probably a blank uid (i.e. message doesn't exist) */
- cxn->current_control->data.control->uid = (unsigned long)-1;
- }
-
-
- } else if (strncasecmp(str,"OK ",3)==0) {
-
- if (cxn->imap_state==IMAP_READING_INTRO)
- {
- imap_sendCapability(cxn); /* xxx errors */
- return;
-
- } else {
-
- }
-
-
- } else {
- /* untagged command not understood */
- }
-
- /* always might be more to look at */
- goto reset;
-
- } else if ((str[0]=='+') && (str[1]==' ')) {
-
- str+=2;
-
- if (cxn->imap_state == IMAP_READING_STEPAUTH)
- {
-#ifdef HAVE_SASL
- if (imap_sendAuthStep(cxn, str)!=RET_OK)
- {
- imap_Disconnect(cxn);
- }
-#else
- d_printf(0,"%s:%d:IMAP got a '+ ...' without SASL. Something's wrong\n",
- hostPeerName (cxn->myHost), cxn->ident);
- imap_Disconnect(cxn);
-#endif /* HAVE_SASL */
-
- return;
- } else {
- d_printf(0,"%s:%d:IMAP got a '+ ...' in state where not expected\n",
- hostPeerName (cxn->myHost), cxn->ident);
- imap_Disconnect(cxn);
- return;
- }
-
-
-
- } else if (strncmp(str, cxn->imap_currentTag, IMAP_TAGLENGTH)==0) {
- /* matches our tag */
- str += IMAP_TAGLENGTH;
-
- if (str[0]!=' ')
- {
- d_printf(0,"%s:%d:IMAP Parse error: tag with no space afterward\n",
- hostPeerName (cxn->myHost), cxn->ident);
- imap_Disconnect(cxn);
- return;
- }
- str++;
-
- /* should be OK/NO */
- if (strncmp(str,"OK",2)==0)
- {
- okno = 1;
- } else {
- okno = 0;
- }
-
- switch(cxn->imap_state)
- {
- case IMAP_READING_CAPABILITY:
-
- if (okno==1) {
- if (imap_sendAuthenticate(cxn)!=RET_OK)
- {
- d_printf(0,"%s:%d:IMAP sendauthenticate failed\n",
- hostPeerName (cxn->myHost), cxn->ident);
- imap_Disconnect(cxn);
- }
- return;
- } else {
- d_printf(0,"%s:%d:IMAP CAPABILITY gave a NO response\n",
- hostPeerName (cxn->myHost), cxn->ident);
- imap_Disconnect(cxn);
- }
- return;
-
- break;
- case IMAP_READING_STEPAUTH:
-
- if (okno == 1) {
-
- cxn->imap_sleepTimeout = init_reconnect_period ;
-
- cxn->imap_timeCon = theTime () ;
- cxn->timeCon = theTime () ;
-
- d_printf(0,"%s:%d IMAP authentication succeeded\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- cxn->imap_disconnects=0;
-
- cxn->imap_state = IMAP_IDLE_AUTHED;
-
- /* try to send a message if we have one */
-
- imap_ProcessQueue(cxn);
- } else {
- d_printf(0,"%s:%d:IMAP Authentication failed with [%s]\n",
- hostPeerName (cxn->myHost), cxn->ident,str);
- imap_Disconnect(cxn);
- }
-
- return;
-
- break;
-
- case IMAP_READING_CREATE:
-
- if (okno==1) {
-
- d_printf(1,"%s:%d:IMAP Create of bboard successful\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- cxn->create_succeeded++;
-
- /* we can delete article now */
- QueueForgetAbout(cxn, cxn->current_control,
- MSG_SUCCESS);
- } else {
- d_printf(1,"%s:%d:IMAP Create failed with [%s] for %s\n",
- hostPeerName (cxn->myHost), cxn->ident,str,
- cxn->current_control->data.control->folder);
-
- ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
- }
-
- imap_ProcessQueue(cxn);
-
- break;
-
- case IMAP_READING_DELETE:
-
- if (okno==1) {
- d_printf(1,"%s:%d:IMAP Delete successful\n",
- hostPeerName (cxn->myHost), cxn->ident);
- cxn->remove_succeeded++;
-
- /* we can delete article now */
- QueueForgetAbout(cxn, cxn->current_control,
- MSG_SUCCESS);
- } else {
- d_printf(1,"%s:%d:IMAP Delete mailbox failed with [%s] for %s\n",
- hostPeerName (cxn->myHost), cxn->ident,str,
- cxn->current_control->data.control->folder);
-
- ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
-
- }
-
- imap_ProcessQueue(cxn);
- return;
-
- break;
-
- case IMAP_READING_SELECT:
-
- if (okno==1) {
-
- imap_sendSearch(cxn, cxn->current_control->data.control->msgid);
- return;
-
- } else {
- d_printf(1,"%s:%d:IMAP Select failed with [%s] for %s\n",
- hostPeerName (cxn->myHost), cxn->ident,str,
- cxn->current_control->data.control->folder);
-
- ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
-
- cxn->imap_state = IMAP_IDLE_AUTHED;
-
- imap_ProcessQueue(cxn);
- return;
- }
-
- break;
-
- case IMAP_READING_SEARCH:
- /* if no message let's forget about it */
- if (cxn->current_control->data.control->uid
- == (unsigned long)-1) {
- d_printf(2, "%s:%d:IMAP Search didn't find the message\n",
- hostPeerName (cxn->myHost), cxn->ident);
- QueueForgetAbout(cxn, cxn->current_control,
- MSG_FAIL_DELIVER);
- if (imap_sendClose(cxn) != RET_OK)
- imap_Disconnect(cxn);
- return;
- }
-
- if (okno==1) {
- /* we got a uid. let's delete it */
- if (imap_sendKill(cxn,
- cxn->current_control->data.control->uid)
- != RET_OK)
- imap_Disconnect(cxn);
- return;
- } else {
- d_printf(0, "%s:%d IMAP Received NO response to SEARCH\n",
- hostPeerName (cxn->myHost), cxn->ident);
- ReQueue(cxn, &(cxn->imap_controlMsg_q),
- cxn->current_control);
-
- if (imap_sendClose(cxn) != RET_OK)
- imap_Disconnect(cxn);
- return;
- }
- break;
-
- case IMAP_READING_STORE:
-
- if (okno==1) {
-
- d_printf(1,"%s:%d:IMAP Processed a Cancel fully\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- /* we can delete article now */
- QueueForgetAbout(cxn, cxn->current_control,
- MSG_SUCCESS);
-
- cxn->cancel_succeeded++;
-
- if (imap_sendClose(cxn) != RET_OK)
- imap_Disconnect(cxn);
- return;
-
- } else {
-
- d_printf(1,"%s:%d:IMAP Store failed\n",
- hostPeerName (cxn->myHost), cxn->ident);
- ReQueue(cxn, &(cxn->imap_controlMsg_q), cxn->current_control);
-
- if (imap_sendClose(cxn) != RET_OK)
- imap_Disconnect(cxn);
- return;
- }
-
- break;
-
- case IMAP_READING_NOOP:
- cxn->imap_state = IMAP_IDLE_AUTHED;
- return;
- break;
-
- case IMAP_READING_CLOSE:
- if (!okno) {
- /* we can't do anything about it */
- d_printf(1,"%s:%d:IMAP Close failed\n",
- hostPeerName (cxn->myHost), cxn->ident);
- }
-
- cxn->imap_state = IMAP_IDLE_AUTHED;
-
- imap_ProcessQueue(cxn);
- return;
- break;
-
- case IMAP_READING_QUIT:
-
- /* we don't care if the server said OK or NO just
- that it said something */
-
- d_printf(1,"%s:%d:IMAP Read quit response\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- cxn->imap_state = IMAP_DISCONNECTED;
-
- DeleteIfDisconnected(cxn);
- break;
-
-
- default:
- d_printf(0,"%s:%d:IMAP I don't understand state %d [%s]\n",
- hostPeerName (cxn->myHost), cxn->ident,
- cxn->imap_state,str);
- imap_Disconnect(cxn);
- break;
- }
-
-
- } else {
- d_printf(0,"%s:%d:IMAP tag (%s) doesn't match what we gave (%s). What's up with that??\n",
- hostPeerName (cxn->myHost), cxn->ident, str, cxn->imap_currentTag);
- imap_Disconnect(cxn);
- }
-
-}
-
-/************************** END IMAP reading functions ***************************/
-
-/*************************** LMTP reading functions ****************************/
-
-static void lmtp_readCB (EndPoint e, IoStatus i, Buffer *b, void *d)
-{
- connection_t *cxn = (connection_t *) d;
- char str[4096];
- Buffer *readBuffers;
- int result;
- int response_code;
- conn_ret ret;
-#ifdef HAVE_SASL
- int inlen;
- char *in;
- int outlen;
- const char *out;
- char *inbase64;
- int inbase64len;
- imt_stat status;
- int saslresult;
- sasl_interact_t *client_interact=NULL;
-#endif /* HAVE_SASL */
-
- char *p = bufferBase(b[0]);
-
- bufferAddNullByte (b[0]) ;
-
- if (i != IoDone) {
- errno = endPointErrno(e);
- syslog(LOG_ERR, "%s:%d LMTP i/o failed: %m",
- hostPeerName (cxn->myHost), cxn->ident);
-
- freeBufferArray (b);
- lmtp_Disconnect(cxn);
- return;
- }
-
- if (strchr (p, '\n') == NULL)
- {
- /* partial read. expand buffer and retry */
-
- d_printf(0,"%s:%d:LMTP Partial. retry\n",
- hostPeerName (cxn->myHost),cxn->ident);
- expandBuffer (b[0], BUFFER_EXPAND_AMOUNT) ;
- readBuffers = makeBufferArray (bufferTakeRef (b[0]), NULL) ;
-
- if ( !prepareRead (e, readBuffers, lmtp_readCB, cxn, 1) )
- {
- lmtp_Disconnect(cxn);
- }
-
- freeBufferArray (b);
- return;
- }
-
- clearTimer (cxn->lmtp_readBlockedTimerId) ;
-
- /* Add what we got to our internal read buffer */
- strcat(cxn->lmtp_respBuffer, p);
-
- bufferSetDataSize( b[0], 0);
-
- freeBufferArray (b);
-
- reset:
- /* see if we have a full line */
- ret = GetLine( cxn->lmtp_respBuffer, str, sizeof(str));
-
- /* get a line */
- if (ret!=RET_OK)
- {
- if (ret!=RET_NO_FULLLINE)
- {
- /* was a more serious error */
- d_printf(0,"%s:%d:LMTP Internal error getting line from server\n",
- hostPeerName (cxn->myHost),cxn->ident);
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* set up to receive some more */
- readBuffers = makeBufferArray (bufferTakeRef (cxn->lmtp_rBuffer), NULL) ;
- prepareRead(cxn->lmtp_endpoint, readBuffers, lmtp_readCB, cxn, 5);
- return;
- }
-
- switch (cxn->lmtp_state)
- {
-
- case LMTP_READING_INTRO:
-
- if (ask_code(str)!=220)
- {
- d_printf(0,"%s:%d:LMTP Initial server msg does not start with 220 (began with %d)\n",
- hostPeerName (cxn->myHost),cxn->ident,
- ask_code(str));
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* the initial intro could have many lines via
- continuations. see if we need to read more */
- if (ask_keepgoing(str)==1)
- {
- goto reset;
- }
-
- result = lmtp_getcapabilities(cxn);
-
- if (result != RET_OK)
- {
- d_printf(0,"%s:%d:LMTP lmtp_getcapabilities() failure\n",
- hostPeerName (cxn->myHost),cxn->ident);
- lmtp_Disconnect(cxn);
- return;
- }
-
- break;
-
- case LMTP_READING_LHLO:
- /* recieve the response(s) */
- response_code = ask_code(str);
-
- if (response_code != 250) /* was none */
- {
- d_printf(0,"%s:%d:LMTP Response code unexpected (%d)\n",
- hostPeerName (cxn->myHost),cxn->ident,
- response_code);
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* look for one we know about; ignore all others */
- if (strncmp(str+4,"8BITMIME",strlen("8BITMIME"))==0)
- {
- cxn->lmtp_capabilities->Eightbitmime = 1;
- } else if (strncmp(str+4, "ENHANCEDSTATUSCODES",
- strlen("ENHANCEDSTATUSCODES"))==0) {
- cxn->lmtp_capabilities->EnhancedStatusCodes = 1;
- } else if (strncmp(str+4, "AUTH",4)==0) {
- cxn->lmtp_capabilities->saslmechs = xstrdup(str + 4 + 5);
- } else if (strncmp(str+4,"PIPELINING",strlen("PIPELINING"))==0) {
- cxn->lmtp_capabilities->pipelining = 1;
- } else {
- /* don't care; ignore */
- }
-
- /* see if this is the last line of the capability */
- if (ask_keepgoing(str)==1)
- {
- goto reset;
- } else {
- /* we require a few capabilities */
- if (!cxn->lmtp_capabilities->pipelining) {
- d_printf(0,"%s:%d:LMTP We require PIPELINING\n",
- hostPeerName (cxn->myHost),cxn->ident);
-
- lmtp_Disconnect(cxn);
- return;
- }
-#ifdef HAVE_SASL
- if (cxn->lmtp_capabilities->saslmechs) {
- /* start the authentication */
- result = lmtp_authenticate(cxn);
-
- if (result != RET_OK) {
- d_printf(0,"%s:%d:LMTP lmtp_authenticate() error\n",
- hostPeerName (cxn->myHost),cxn->ident);
- lmtp_Disconnect(cxn);
- return;
- }
-#else
- if (0) {
- /* noop */
-#endif
- } else {
- /* either we can't authenticate or the remote server
- doesn't support it */
- cxn->lmtp_state = LMTP_AUTHED_IDLE;
- d_printf(1,"%s:%d:LMTP Even though we can't authenticate"
- " we're going to try to feed anyway\n",
- hostPeerName (cxn->myHost),cxn->ident);
- /* We just assume we don't need to authenticate
- (great assumption huh?) */
- hostRemoteStreams (cxn->myHost, cxn, true) ;
-
- cxn->lmtp_timeCon = theTime () ;
- cxn->timeCon = theTime () ;
-
- /* try to send a message if we have one */
- lmtp_sendmessage(cxn,NULL);
- return;
- }
- }
- break;
-
-#ifdef HAVE_SASL
- case LMTP_READING_STEPAUTH:
- inlen = 0;
- status = lmtp_getauthline(str, &in, &inlen);
-
- switch (status)
- {
-
- case STAT_CONT:
-
- saslresult=sasl_client_step(cxn->saslconn_lmtp,
- in,
- inlen,
- &client_interact,
- &out,
- &outlen);
-
- free(in);
-
- /* check if sasl succeeded */
- if (saslresult != SASL_OK && saslresult != SASL_CONTINUE) {
- d_printf(0,"%s:%d:LMTP sasl_client_step(): %s\n",
- hostPeerName (cxn->myHost),cxn->ident,
- sasl_errstring(saslresult,NULL,NULL));
-
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* convert to base64 */
- inbase64 = xmalloc(outlen*2+10);
-
- saslresult = sasl_encode64(out, outlen,
- inbase64, outlen*2+10,
- (unsigned *) &inbase64len);
-
- if (saslresult != SASL_OK)
- {
- d_printf(0,"%s:%d:LMTP sasl_encode64(): %s\n",
- hostPeerName (cxn->myHost),cxn->ident,
- sasl_errstring(saslresult,NULL,NULL));
-
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* add an endline */
- strlcpy(inbase64 + inbase64len, "\r\n", outlen * 2 + 10);
-
- /* send to server */
- result = WriteToWire_lmtpstr(cxn,inbase64, inbase64len+2);
-
- if (result != RET_OK)
- {
- d_printf(0,"%s:%d:LMTP WriteToWire() failure\n",
- hostPeerName (cxn->myHost),cxn->ident);
- lmtp_Disconnect(cxn);
- return;
- }
-
- cxn->lmtp_state = LMTP_WRITING_STEPAUTH;
- break;
-
- case STAT_OK:
- cxn->lmtp_sleepTimeout = init_reconnect_period ;
-
- d_printf(0,"%s:%d LMTP authentication succeeded\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- cxn->lmtp_disconnects=0;
-
- hostRemoteStreams (cxn->myHost, cxn, true) ;
-
- cxn->lmtp_timeCon = theTime () ;
- cxn->timeCon = theTime () ;
-
- cxn->lmtp_state = LMTP_AUTHED_IDLE;
-
-
- /* try to send a message if we have one */
- lmtp_sendmessage(cxn,NULL);
- return;
-
- break;
-
- default:
- d_printf(0,"%s:%d:LMTP failed authentication\n",
- hostPeerName (cxn->myHost),cxn->ident);
- lmtp_Disconnect(cxn);
- return;
- }
- break;
-#endif /* HAVE_SASL */
-
- case LMTP_READING_RSET:
- if (ask_keepgoing(str)) {
- goto reset;
- }
- if (ask_code(str) != 250) {
- d_printf(0,"%s:%d:LMTP RSET failed with (%d)\n",
- hostPeerName (cxn->myHost),cxn->ident,
- ask_code(str));
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* we pipelined so next we recieve the mail from response */
- cxn->lmtp_state = LMTP_READING_MAILFROM;
- goto reset;
-
- case LMTP_READING_MAILFROM:
- if (ask_keepgoing(str)) {
- goto reset;
- }
- if (ask_code(str) != 250) {
- d_printf(0,"%s:%d:LMTP MAILFROM failed with (%d)\n",
- hostPeerName (cxn->myHost),cxn->ident,
- ask_code(str));
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* we pipelined so next we recieve the rcpt's */
- cxn->lmtp_state = LMTP_READING_RCPTTO;
- goto reset;
- break;
-
- case LMTP_READING_RCPTTO:
- if (ask_keepgoing(str)) {
- goto reset;
- }
- if (ask_code(str)!=250) {
- d_printf(1,"%s:%d:LMTP RCPT TO failed with (%d) %s\n",
- hostPeerName (cxn->myHost),cxn->ident,
- ask_code(str), str);
-
- /* if got a 5xx don't try to send anymore */
- cxn->current_article->trys=100;
-
- cxn->current_rcpts_issued--;
- } else {
- cxn->current_rcpts_okayed++;
- }
-
- /* if issued equals number okayed then we're done */
- if ( cxn->current_rcpts_okayed == cxn->current_rcpts_issued) {
- cxn->lmtp_state = LMTP_READING_DATA;
- } else {
- /* stay in same state */
- }
- goto reset;
- break;
-
- case LMTP_READING_DATA:
- if (ask_keepgoing(str)) {
- goto reset;
- }
- if (cxn->current_rcpts_issued == 0) {
- if (cxn->current_article->trys < 100) {
- d_printf(1, "%s:%d:LMTP None of the rcpts "
- "were accepted for this message. Re-queueing\n",
- hostPeerName (cxn->myHost),cxn->ident);
- }
-
- ReQueue(cxn, &(cxn->lmtp_todeliver_q), cxn->current_article);
-
- cxn->lmtp_state = LMTP_AUTHED_IDLE;
- lmtp_sendmessage(cxn,NULL);
- } else {
- if (WriteArticle(cxn, cxn->current_bufs) != RET_OK)
- {
- d_printf(0, "%s:%d:LMTP Error writing article\n",
- hostPeerName (cxn->myHost),cxn->ident);
- lmtp_Disconnect(cxn);
- return;
- }
-
- cxn->lmtp_state = LMTP_WRITING_CONTENTS;
- }
-
- break;
-
- case LMTP_READING_CONTENTS:
- if (ask_keepgoing(str)) {
- goto reset;
- }
-
- /* need 1 response from server for every rcpt */
- cxn->current_rcpts_issued--;
-
- if (ask_code(str) != 250) {
- d_printf(1, "%s:%d:LMTP DATA failed with %d (%s)\n",
- hostPeerName (cxn->myHost),cxn->ident,
- ask_code(str), str);
- cxn->current_rcpts_okayed--;
- }
-
- if (cxn->current_rcpts_issued>0) {
- goto reset;
- }
-
- /*
- * current_rcpts_okayed is number that succeeded
- *
- */
- if (cxn->current_rcpts_okayed == 0) {
- cxn->lmtp_state = LMTP_AUTHED_IDLE;
- } else {
- cxn->lmtp_state = LMTP_AUTHED_IDLE;
- cxn->lmtp_succeeded++;
- d_printf(1, "%s:%d:LMTP Woohoo! message accepted\n",
- hostPeerName (cxn->myHost),cxn->ident);
- }
-
- /* we can delete article now */
- QueueForgetAbout(cxn, cxn->current_article, MSG_SUCCESS);
-
- /* try to send another if we have one and we're still idle
- * forgetting the msg might have made us unidle
- */
- if (cxn->lmtp_state == LMTP_AUTHED_IDLE) {
- lmtp_sendmessage(cxn,NULL);
- }
-
- break;
-
- case LMTP_READING_NOOP:
- if (ask_keepgoing(str)) {
- goto reset;
- }
- cxn->lmtp_state = LMTP_AUTHED_IDLE;
- break;
-
- case LMTP_READING_QUIT:
- d_printf(1,"%s:%d:LMTP read quit\n",
- hostPeerName (cxn->myHost),cxn->ident);
-
- cxn->lmtp_state = LMTP_DISCONNECTED;
-
- DeleteIfDisconnected(cxn);
- break;
-
- default:
-
- d_printf(0,"%s:%d:LMTP Bad state in lmtp_readCB %d\n",
- hostPeerName (cxn->myHost),cxn->ident,
- cxn->lmtp_state);
- lmtp_Disconnect(cxn);
- return;
- }
-
-
-}
-
-/*
- * Add a rcpt to:<foo> to the string
- *
- */
-
-static void addrcpt(char *newrcpt, int newrcptlen, char **out, int *outalloc)
-{
- int size = strlen(*out);
- int fsize = size;
- int newsize = size + 9+strlen(deliver_rcpt_to)+newrcptlen+3;
- char c;
-
- /* see if we need to grow the string */
- if (newsize > *outalloc) {
- (*outalloc) = newsize;
- (*out) = xrealloc(*out, *outalloc);
- }
-
- strlcpy((*out) + size,"RCPT TO:<", newsize - size);
- size += 9;
-
- c = newrcpt[newrcptlen];
- newrcpt[newrcptlen] = '\0';
- size += snprintf((*out) + size, newsize - size, deliver_rcpt_to, newrcpt);
- newrcpt[newrcptlen] = c;
-
- strlcpy((*out) + size, ">\r\n", newsize - size);
-
- /* has embedded '\n' */
- d_printf(2, "Attempting to send to: %s", (*out) + fsize);
-}
-
-/*
- * Takes the newsgroups header value and makes it into a list of RCPT TO:'s we can send over the wire
- *
- * in - newsgroups header start
- * in_end - end of newsgroups header
- * num - number of rcpt's we created
- */
-
-static char *ConvertRcptList(char *in, char *in_end, int *num)
-{
- int retalloc = 400;
- char *ret = xmalloc(retalloc);
- char *str = in;
- char *laststart = in;
-
- (*num) = 0;
-
- /* start it off empty */
- strlcpy(ret, "", retalloc);
-
- while ( str != in_end)
- {
- if ((*str) == ',')
- {
- /* eliminate leading whitespace */
- while (((*laststart) ==' ') || ((*laststart)=='\t'))
- {
- laststart++;
- }
-
-#ifndef SMTPMODE
- addrcpt(laststart, str - laststart, &ret, &retalloc);
- (*num)++;
-#endif /* SMTPMODE */
- laststart = str+1;
- }
-
- str++;
- }
-
- if (laststart<str)
- {
- addrcpt(laststart, str - laststart, &ret, &retalloc);
- (*num)++;
- }
-
- return ret;
-}
-
-static void addto(char *newrcpt, int newrcptlen, const char *sep,
- char **out, int *outalloc)
-{
- int size = strlen(*out);
- int newsize = size + strlen(sep)+1+strlen(deliver_to_header)+newrcptlen+1;
- char c;
-
- /* see if we need to grow the string */
- if (newsize > *outalloc) {
- (*outalloc) = newsize;
- (*out) = xrealloc(*out, *outalloc);
- }
-
- size += snprintf((*out) + size, newsize - size, "%s<", sep);
-
- c = newrcpt[newrcptlen];
- newrcpt[newrcptlen] = '\0';
- size += snprintf((*out) + size, newsize - size, deliver_to_header,newrcpt);
- newrcpt[newrcptlen] = c;
-
- strlcpy((*out) + size, ">", newsize - size);
-}
-
-/*
- * Takes the newsgroups header value and makes it into a To: header
- *
- * in - newsgroups header start
- * in_end - end of newsgroups header
- */
-
-static char *BuildToHeader(char *in, char *in_end)
-{
- int retalloc = 400;
- char *ret = xmalloc(retalloc);
- char *str = in;
- char *laststart = in;
- const char *sep = "";
-
- /* start it off with the header name */
- strlcpy(ret,"To: ", retalloc);
-
- while ( str != in_end)
- {
- if ((*str) == ',')
- {
- /* eliminate leading whitespace */
- while (((*laststart) ==' ') || ((*laststart)=='\t'))
- {
- laststart++;
- }
-
- addto(laststart, str - laststart, sep, &ret, &retalloc);
- laststart = str+1;
-
- /* separate multiple addresses with a comma */
- sep = ", ";
- }
-
- str++;
- }
-
- if (laststart<str)
- {
- addto(laststart, str - laststart, sep, &ret, &retalloc);
- }
-
- /* terminate the header */
- strlcat(ret, "\n\r", retalloc);
- return ret;
-}
-
-/*************************** END LMTP reading functions ****************************/
-
-
-
-/*
- * Process the control message queue. If we run out ask the host for more.
- *
- * cxn - connection object
- */
-
-static void imap_ProcessQueue(connection_t *cxn)
-{
- article_queue_t *item;
- int result;
-
- retry:
-
- /* pull an article off the queue */
- result = PopFromQueue(&(cxn->imap_controlMsg_q), &item);
-
- if (result==RET_QUEUE_EMPTY)
- {
- if (cxn->issue_quit)
- {
- imap_sendQuit(cxn);
- return;
- }
-
- cxn->imap_state = IMAP_IDLE_AUTHED;
-
- /* now we wait for articles from our Host, or we have some
- articles already. On infrequently used connections, the
- network link is torn down and rebuilt as needed. So we may
- be rebuilding the connection here in which case we have an
- article to send. */
-
- /* make sure imap has _lots_ of space too */
- if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0) &&
- (QueueItems(&(cxn->imap_controlMsg_q)) == 0))
- {
- if (hostGimmeArticle (cxn->myHost,cxn)==true)
- goto retry;
- }
-
- return;
- }
-
- cxn->current_control = item;
-
- switch (item->type)
- {
- case CREATE_FOLDER:
- imap_CreateGroup(cxn, item->data.control->folder);
- break;
-
- case CANCEL_MSG:
- imap_CancelMsg(cxn, item->data.control->folder);
- break;
-
- case DELETE_FOLDER:
- imap_DeleteGroup(cxn, item->data.control->folder);
- break;
- default:
- break;
- }
-
- return;
-}
-
-
-
-/*
- *
- * Pulls a message off the queue and trys to start sending it. If the
- * message is a control message put it in the control queue and grab
- * another message. If the message doesn't exist on disk or something
- * is wrong with it tell the host and try again. If we run out of
- * messages to get tell the host we want more
- *
- * cxn - connection object
- * justadded - the article that was just added to the queue
- */
-
-static void lmtp_sendmessage(connection_t *cxn, Article justadded)
-{
- bool res;
- conn_ret result;
- char *p;
- Buffer *bufs;
- char *control_header = NULL;
- char *control_header_end = NULL;
-
- article_queue_t *item;
- char *rcpt_list, *rcpt_list_end;
-
- /* retry point */
- retry:
-
- /* pull an article off the queue */
- result = PopFromQueue(&(cxn->lmtp_todeliver_q), &item);
-
- if (result==RET_QUEUE_EMPTY)
- {
- if (cxn->issue_quit) {
- lmtp_IssueQuit(cxn);
- return;
- }
- /* now we wait for articles from our Host, or we have some
- articles already. On infrequently used connections, the
- network link is torn down and rebuilt as needed. So we may
- be rebuilding the connection here in which case we have an
- article to send. */
-
- /* make sure imap has space too */
- d_printf(1,"%s:%d stalled waiting for articles\n",
- hostPeerName (cxn->myHost),cxn->ident);
- if ((QueueItems(&(cxn->lmtp_todeliver_q)) == 0) &&
- (QueueItems(&(cxn->imap_controlMsg_q)) == 0)) {
- if (hostGimmeArticle (cxn->myHost,cxn)==true)
- goto retry;
- }
-
- return;
- }
-
- /* make sure contents ok; this also should load it into memory */
- res = artContentsOk (item->data.article);
- if (res==false)
- {
- if (justadded == item->data.article) {
- ReQueue(cxn, &(cxn->lmtp_todeliver_q), item);
- return;
- } else {
- /* tell to reject taking this message */
- QueueForgetAbout(cxn,item, MSG_MISSING);
- }
-
- goto retry;
- }
-
- /* Check if it's a control message */
- bufs = artGetNntpBuffers (item->data.article);
- if (bufs == NULL)
- {
- /* tell to reject taking this message */
- QueueForgetAbout(cxn,item, MSG_MISSING);
- goto retry;
- }
-
- result = FindHeader(bufs, "Control", &control_header, &control_header_end);
- if (result == RET_OK) {
- result = AddControlMsg(cxn, item->data.article, bufs,
- control_header,control_header_end, 1);
- if (result != RET_OK) {
- d_printf(1,"%s:%d Error adding to [imap] control queue\n",
- hostPeerName (cxn->myHost),cxn->ident) ;
- ReQueue(cxn, &(cxn->lmtp_todeliver_q), item);
- return;
- }
-
- switch(cxn->imap_state) {
- case IMAP_IDLE_AUTHED:
- /* we're idle. let's process the queue */
- imap_ProcessQueue(cxn);
- break;
- case IMAP_DISCONNECTED:
- case IMAP_WAITING:
- /* Let's connect. Once we're connected we can
- worry about the message */
- if (cxn->imap_sleepTimerId == 0) {
- if (imap_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,0);
- }
- break;
- default:
- /* we're doing something right now */
- break;
- }
-
- /* all we did was add a control message.
- we still want to get an lmtp message */
- goto retry;
- }
-
- if (cxn->current_bufs != NULL) {
- /* freeBufferArray(cxn->current_bufs); */
- cxn->current_bufs = NULL;
- }
- cxn->current_bufs = bufs;
- cxn->current_article = item;
-
- /* we make use of pipelining here
- send:
- rset
- mail from
- rcpt to
- data
- */
-
- /* find out who it's going to */
- result = FindHeader(cxn->current_bufs, "Newsgroups",
- &rcpt_list, &rcpt_list_end);
-
- if ((result != RET_OK) || (rcpt_list == NULL)) {
- d_printf(1,"%s:%d Didn't find Newsgroups header\n",
- hostPeerName (cxn->myHost),cxn->ident) ;
- QueueForgetAbout(cxn, cxn->current_article, MSG_FAIL_DELIVER);
- goto retry;
- }
-
- /* free's original rcpt_list */
- rcpt_list = ConvertRcptList(rcpt_list, rcpt_list_end,
- &cxn->current_rcpts_issued);
- cxn->current_rcpts_okayed = 0;
-
- if(mailfrom_name == NULL)
- mailfrom_name = xstrdup("");
- p = concat("RSET\r\n"
- "MAIL FROM:<", mailfrom_name, ">\r\n",
- rcpt_list,
- "DATA\r\n", (char *) 0);
-
- cxn->lmtp_state = LMTP_WRITING_UPTODATA;
- result = WriteToWire_lmtpstr(cxn, p, strlen(p));
-
- if (result != RET_OK) {
- d_printf(0,"%s:%d failed trying to write\n",
- hostPeerName (cxn->myHost),cxn->ident) ;
- lmtp_Disconnect(cxn);
- return;
- }
-
- /* prepend To: header to article */
- if (deliver_to_header) {
- char *to_list, *to_list_end;
- int i, len;
-
- result = FindHeader(cxn->current_bufs, "Followup-To",
- &to_list, &to_list_end);
-
- if ((result != RET_OK) || (to_list == NULL)) {
- result = FindHeader(cxn->current_bufs, "Newsgroups",
- &to_list, &to_list_end);
- }
-
- /* free's original to_list */
- to_list = BuildToHeader(to_list, to_list_end);
-
- len = bufferArrayLen(cxn->current_bufs);
- cxn->current_bufs = xrealloc(cxn->current_bufs,
- sizeof(Buffer) * (len+2));
- cxn->current_bufs[len+1] = NULL;
-
- for (i = len; i > 0; i--) {
- cxn->current_bufs[i] = cxn->current_bufs[i-1];
- }
-
- cxn->current_bufs[0] = newBufferByCharP(to_list, strlen(to_list+1),
- strlen(to_list));
- }
-
- hostArticleOffered (cxn->myHost, cxn);
-}
-
-/*
- * Called by the EndPoint class when the timer goes off
- */
-static void dosomethingTimeoutCbk (TimeoutId id, void *data)
-{
- Connection cxn = (Connection) data ;
-
- ASSERT (id == cxn->dosomethingTimerId) ;
-
- show_stats(cxn);
-
- /* we're disconnected but there are things to send */
- if ((cxn->lmtp_state == LMTP_DISCONNECTED) &&
- (cxn->lmtp_sleepTimerId == 0) &&
- QueueItems(&(cxn->lmtp_todeliver_q)) > 0)
- {
- if (lmtp_Connect(cxn) != RET_OK)
- prepareReopenCbk(cxn, 1);
- }
-
- if ((cxn->imap_state == IMAP_DISCONNECTED) &&
- (cxn->imap_sleepTimerId == 0) &&
- (QueueItems(&(cxn->imap_controlMsg_q)) > 0))
- {
- if (imap_Connect(cxn) != RET_OK)
- prepareReopenCbk(cxn, 0);
- }
-
-
- /* if we're idle and there are items to send let's send them */
- if ((cxn->lmtp_state == LMTP_AUTHED_IDLE) &&
- QueueItems(&(cxn->lmtp_todeliver_q)) > 0) {
- lmtp_sendmessage(cxn,NULL);
- } else if (cxn->lmtp_state == LMTP_AUTHED_IDLE) {
- lmtp_noop(cxn);
- }
-
- if ((cxn->imap_state == IMAP_IDLE_AUTHED) &&
- (QueueItems(&(cxn->imap_controlMsg_q)) > 0)) {
- imap_ProcessQueue(cxn);
- } else if (cxn->imap_state == IMAP_IDLE_AUTHED) {
- imap_noop(cxn);
- }
-
- /* set up the timer. */
- clearTimer (cxn->dosomethingTimerId) ;
-
- cxn->dosomethingTimerId = prepareSleep (dosomethingTimeoutCbk,
- cxn->dosomethingTimeout, cxn);
-}
-
-/* Give all articles in the queue back to the host. We're probably
- * going to exit soon.
- * */
-
-static void DeferAllArticles(connection_t *cxn, Q_t *q)
-{
- article_queue_t *cur;
- conn_ret ret;
-
- while (1)
- {
- ret = PopFromQueue(q, &cur);
- if (ret == RET_QUEUE_EMPTY) return;
-
- if (ret == RET_OK)
- {
- QueueForgetAbout(cxn, cur, MSG_GIVE_BACK);
- } else {
- d_printf(0,"%s:%d Error emptying queue (deffering all articles)\n",
- hostPeerName (cxn->myHost),cxn->ident);
- return;
- }
- }
-}
-
-/*
- * Does the actual deletion of a connection and all its private data.
- */
-static void delConnection (Connection cxn)
-{
- bool shutDown;
- Connection c, q;
-
- if (cxn == NULL)
- return ;
-
- d_printf (1,"Deleting connection: %s:%d\n",
- hostPeerName (cxn->myHost),cxn->ident) ;
-
- for (c = gCxnList, q = NULL ; c != NULL ; q = c, c = c->next)
- if (c == cxn)
- {
- if (gCxnList == c)
- gCxnList = gCxnList->next ;
- else
- q->next = c->next ;
- break ;
- }
-
- ASSERT (c != NULL) ;
-
- if (cxn->lmtp_endpoint != NULL)
- delEndPoint (cxn->lmtp_endpoint) ;
- if (cxn->imap_endpoint != NULL)
- delEndPoint (cxn->imap_endpoint) ;
-
- delBuffer (cxn->imap_rBuffer) ;
- delBuffer (cxn->lmtp_rBuffer) ;
-
- /* tell the Host we're outta here. */
- shutDown = hostCxnGone (cxn->myHost, cxn) ;
-
- cxn->ident = 0 ;
- cxn->timeCon = 0 ;
-
- free (cxn->ServerName) ;
-
- clearTimer (cxn->imap_readBlockedTimerId) ;
- clearTimer (cxn->imap_writeBlockedTimerId) ;
- clearTimer (cxn->lmtp_readBlockedTimerId) ;
- clearTimer (cxn->lmtp_writeBlockedTimerId) ;
-
- clearTimer (cxn->imap_sleepTimerId);
- cxn->imap_sleepTimerId = 0;
- clearTimer (cxn->lmtp_sleepTimerId);
- cxn->lmtp_sleepTimerId = 0;
-
- clearTimer (cxn->dosomethingTimerId);
-
- free (cxn->imap_respBuffer);
- free (cxn->lmtp_respBuffer);
-
- free (cxn) ;
-
- if (shutDown)
- {
- /* exit program if that was the last connexion for the last host */
- /* XXX what about if there are ever multiple listeners?
- XXX this will be executed if all hosts on only one of the
- XXX listeners have gone */
- time_t now = theTime () ;
- char dateString [30] ;
-
- strlcpy (dateString,ctime (&now),sizeof (dateString)) ;
- dateString [24] = '\0' ;
-
- notice ("ME finishing at %s", dateString) ;
-
- exit (0) ;
- }
-}
-
-
-/******************** PUBLIC FUNCTIONS ****************************/
-
-
-
- /*
- * Create a new Connection.
- *
- * HOST is the host object we're owned by.
- * IDENT is an identifier to be added to syslog entries so we can tell
- * what's happening on different connections to the same peer.
- * IPNAME is the name (or ip address) of the remote)
- * MAXTOUT is the maximum amount of time to wait for a response before
- * considering the remote host dead.
- * PORTNUM is the portnum to contact on the remote end.
- * RESPTIMEOUT is the amount of time to wait for a response from a remote
- * before considering the connection dead.
- * CLOSEPERIOD is the number of seconds after connecting that the
- * connections should be closed down and reinitialized (due to problems
- * with old NNTP servers that hold history files open. Value of 0 means
- * no close down.
- */
-
-Connection newConnection (Host host,
- unsigned int ident,
- const char *ipname,
- unsigned int artTout UNUSED,
- unsigned int portNum UNUSED,
- unsigned int respTimeout,
- unsigned int closePeriod UNUSED,
- double lowPassLow UNUSED,
- double lowPassHigh UNUSED,
- double lowPassFilter UNUSED)
-{
- Connection cxn;
- /* check arguments */
-
- /* allocate connection structure */
- cxn = xcalloc (1, sizeof(connection_t)) ;
-
- cxn->ident = ident ;
- cxn->ServerName = xstrdup (ipname) ;
- cxn->myHost = host ;
-
- /* setup mailfrom user */
- if (gethostname(hostname, MAXHOSTNAMELEN)!=0)
- {
- d_printf(0,"%s gethostname failed\n",ipname);
- return NULL;
- }
-
-
- mailfrom_name = concat("news@", hostname, (char *) 0);
-
- cxn->next = gCxnList ;
- gCxnList = cxn ;
- gCxnCount++ ;
-
- /* init stuff */
- Initialize(cxn, respTimeout);
-
- return cxn;
-}
-
-
-/* Causes the Connection to build the network connection. */
-bool cxnConnect (Connection cxn)
-{
- /* make the lmtp connection */
- if (lmtp_Connect(cxn) != RET_OK) return false;
-
- if (imap_Connect(cxn) != RET_OK) return false;
-
- return true;
-}
-
-
-static void QuitIfIdle(Connection cxn)
-{
- if ((cxn->lmtp_state == LMTP_AUTHED_IDLE) &&
- (QueueItems(&(cxn->lmtp_todeliver_q))<=0)) {
- lmtp_IssueQuit(cxn);
- }
- if ((cxn->imap_state == IMAP_IDLE_AUTHED) &&
- (QueueItems(&(cxn->imap_controlMsg_q))<=0)) {
- imap_sendQuit(cxn);
- }
-}
-
-static void DeleteIfDisconnected(Connection cxn)
-{
- /* we want to shut everything down. if both connections disconnected now we can */
- if ((cxn->issue_quit >= 1) &&
- (cxn->lmtp_state == LMTP_DISCONNECTED) &&
- (cxn->imap_state == IMAP_DISCONNECTED))
- {
-
- switch (cxn->issue_quit)
- {
- case 1:
- if (cxn->lmtp_state == LMTP_DISCONNECTED)
- {
- cxn->lmtp_state = LMTP_WAITING;
- cxn->imap_state = IMAP_WAITING;
- cxn->issue_quit = 0;
- hostCxnWaiting (cxn->myHost,cxn) ; /* tell our Host we're waiting */
- }
- break;
- case 2:
- if (cxn->lmtp_state == LMTP_DISCONNECTED)
- {
- cxn->issue_quit = 0;
-
- if (imap_Connect(cxn)!=RET_OK) prepareReopenCbk(cxn,0);
- if (lmtp_Connect(cxn)!=RET_OK) prepareReopenCbk(cxn,1);
- }
- break;
- case 3:
- if (cxn->lmtp_state == LMTP_DISCONNECTED)
- {
- hostCxnDead (cxn->myHost,cxn) ;
- delConnection(cxn);
- }
- break;
-
- }
- }
-}
-
- /* puts the connection into the wait state (i.e. waits for an article
- before initiating a connect). Can only be called right after
- newConnection returns, or while the Connection is in the (internal)
- Sleeping state. */
-void cxnWait (Connection cxn)
-{
- cxn->issue_quit = 1;
-
- QuitIfIdle(cxn);
-}
-
- /* The Connection will disconnect as if cxnDisconnect were called and then
- it automatically reconnects to the remote. */
-void cxnFlush (Connection cxn)
-{
- cxn->issue_quit = 2;
-
- QuitIfIdle(cxn);
-}
-
-
-
- /* The Connection sends remaining articles, then issues a QUIT and then
- deletes itself */
-void cxnClose (Connection cxn)
-{
- d_printf(0,"%s:%d Closing cxn\n",hostPeerName (cxn->myHost), cxn->ident);
- cxn->issue_quit = 3;
-
- QuitIfIdle(cxn);
-
- DeleteIfDisconnected(cxn);
-}
-
- /* The Connection drops all queueed articles, then issues a QUIT and then
- deletes itself */
-void cxnTerminate (Connection cxn)
-{
- d_printf(0,"%s:%d Terminate\n",hostPeerName (cxn->myHost), cxn->ident);
-
- cxn->issue_quit = 3;
-
- /* give any articles back to host in both queues */
- DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
- DeferAllArticles(cxn, &(cxn->imap_controlMsg_q));
-
- QuitIfIdle(cxn);
-}
-
- /* Blow away the connection gracelessly and immedately clean up */
-void cxnNuke (Connection cxn)
-{
- d_printf(0,"%s:%d Nuking connection\n",cxn->ServerName, cxn->ident);
-
- cxn->issue_quit = 4;
-
- /* give any articles back to host in both queues */
- DeferAllArticles(cxn, &(cxn->lmtp_todeliver_q));
- DeferAllArticles(cxn, &(cxn->imap_controlMsg_q));
-
- imap_Disconnect(cxn);
- lmtp_Disconnect(cxn);
-
- hostCxnDead (cxn->myHost,cxn);
- delConnection(cxn);
-}
-
-/*
- * must
- * true - must queue article. Don't try sending
- * false - queue of article may fail. Try sending
- *
- * Always adds to lmtp queue even if control message
- *
- */
-
-static bool ProcessArticle(Connection cxn, Article art, bool must)
-{
- conn_ret result;
-
- /* Don't accept any articles when we're closing down the connection */
- if (cxn->issue_quit > 1) {
- return false;
- }
-
- /* if it's a regular message let's add it to the queue */
- result = AddToQueue(&(cxn->lmtp_todeliver_q), art, DELIVER,1,must);
-
- if (result == RET_EXCEEDS_SIZE) {
- return false;
- }
-
- if (result != RET_OK)
- {
- d_printf(0,"%s:%d Error adding to delivery queue\n",
- hostPeerName (cxn->myHost), cxn->ident);
- return must;
- }
-
- if (must == true) return true;
-
- switch (cxn->lmtp_state)
- {
- case LMTP_WAITING:
- case LMTP_DISCONNECTED:
- if (cxn->lmtp_sleepTimerId == 0)
- if (lmtp_Connect(cxn) != RET_OK) prepareReopenCbk(cxn,1);
- break;
-
- case LMTP_AUTHED_IDLE:
- lmtp_sendmessage(cxn,art);
- break;
- default:
- /* currently doing something */
- break;
- }
-
- return true;
-}
-
- /* Tells the Connection to take the article and handle its
- transmission. If it can't (due to queue size or whatever), then the
- function returns false. The connection assumes ownership of the
- article if it accepts it (returns true). */
-bool cxnTakeArticle (Connection cxn, Article art)
-{
- /* if we're closing down always refuse */
- if (cxn->issue_quit == 1) return false;
-
- return ProcessArticle (cxn,art,false);
-}
-
- /* Tell the Connection to take the article (if it can) for later
- processing. Assumes ownership of it if it takes it. */
-bool cxnQueueArticle (Connection cxn, Article art)
-{
- return ProcessArticle (cxn,art,true);
-}
-
-/* generate a syslog message for the connections activity. Called by Host. */
-void cxnLogStats (Connection cxn, bool final)
-{
- const char *peerName ;
- time_t now = theTime() ;
- int total, good, bad;
-
- ASSERT (cxn != NULL) ;
-
- peerName = hostPeerName (cxn->myHost) ;
-
- total = cxn->lmtp_succeeded + cxn->lmtp_failed;
- total += cxn->cancel_succeeded + cxn->cancel_failed;
- total += cxn->create_succeeded + cxn->create_failed;
- total += cxn->remove_succeeded + cxn->remove_failed;
-
- good = cxn->lmtp_succeeded;
- good += cxn->cancel_succeeded;
- good += cxn->create_succeeded;
- good += cxn->remove_succeeded;
-
- bad = cxn->lmtp_failed;
- bad += cxn->cancel_failed;
- bad += cxn->create_failed;
- bad += cxn->remove_failed;
- notice ("%s:%d %s seconds %ld accepted %d refused %d rejected %d",
- peerName, cxn->ident, (final ? "final" : "checkpoint"),
- (long) (now - cxn->timeCon), total, 0, bad);
- show_stats(cxn);
-
- if (final) {
- cxn->lmtp_succeeded = 0;
- cxn->lmtp_failed = 0;
- cxn->cancel_succeeded = 0;
- cxn->cancel_failed = 0;
- cxn->create_succeeded = 0;
- cxn->create_failed = 0;
- cxn->remove_succeeded = 0;
- cxn->remove_failed = 0;
-
- if (cxn->timeCon > 0)
- cxn->timeCon = theTime() ;
- }
-
-}
-
- /* return the number of articles the connection can be given. This lets
- the host shovel in as many as possible. May be zero. */
-size_t cxnQueueSpace (Connection cxn)
-{
- int lmtpsize;
- int imapsize;
-
- lmtpsize = QueueSpace(&(cxn->lmtp_todeliver_q));
- imapsize = QueueSpace(&(cxn->imap_controlMsg_q));
-
- if (lmtpsize >=1) lmtpsize--;
- if (imapsize >=1) imapsize--;
-
- d_printf(1,"%s:%d Q Space lmtp size = %d state = %d\n",
- hostPeerName (cxn->myHost), cxn->ident,
- lmtpsize,cxn->lmtp_state);
- d_printf(1,"%s:%d Q Space imap size = %d state = %d\n",
- hostPeerName (cxn->myHost), cxn->ident,
- imapsize,cxn->imap_state);
-
- /* return the smaller of our 2 queues */
- if (lmtpsize < imapsize)
- return lmtpsize;
- else
- return imapsize;
-}
-
- /* adjust the mode no-CHECK filter values */
-void cxnSetCheckThresholds (Connection cxn,
- double lowFilter UNUSED,
- double highFilter UNUSED,
- double lowPassFilter UNUSED)
-{
- d_printf(1,"%s:%d Threshold change. This means nothing to me\n",
- hostPeerName (cxn->myHost), cxn->ident);
-}
-
-/* print some debugging info. */
-void gPrintCxnInfo (FILE *fp, unsigned int indentAmt)
-{
- char indent [INDENT_BUFFER_SIZE] ;
- unsigned int i ;
- Connection cxn ;
-
- for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
- indent [i] = ' ' ;
- indent [i] = '\0' ;
-
- fprintf (fp,"%sGlobal Connection list : (count %d) {\n",
- indent,gCxnCount) ;
- for (cxn = gCxnList ; cxn != NULL ; cxn = cxn->next)
- printCxnInfo (cxn,fp,indentAmt + INDENT_INCR) ;
- fprintf (fp,"%s}\n",indent) ;
-}
-
-void printCxnInfo (Connection cxn, FILE *fp, unsigned int indentAmt)
-{
- char indent [INDENT_BUFFER_SIZE] ;
- unsigned int i ;
- article_queue_t *artH ;
-
- for (i = 0 ; i < MIN(INDENT_BUFFER_SIZE - 1,indentAmt) ; i++)
- indent [i] = ' ' ;
- indent [i] = '\0' ;
-
- fprintf (fp,"%sConnection : %p {\n",indent, (void *) cxn) ;
- fprintf (fp,"%s host : %p\n",indent, (void *) cxn->myHost) ;
- fprintf (fp,"%s endpoint (imap): %p\n",indent, (void *) cxn->imap_endpoint) ;
- fprintf (fp,"%s endpoint (lmtp): %p\n",indent, (void *) cxn->lmtp_endpoint) ;
- fprintf (fp,"%s state (imap) : %s\n",indent, imap_stateToString (cxn->imap_state)) ;
- fprintf (fp,"%s state (lmtp) : %s\n",indent, lmtp_stateToString (cxn->lmtp_state)) ;
- fprintf (fp,"%s ident : %d\n",indent,cxn->ident) ;
- fprintf (fp,"%s ip-name (imap): %s\n", indent, cxn->ServerName) ;
- fprintf (fp,"%s ip-name (lmtp): %s\n", indent, cxn->ServerName) ;
- fprintf (fp,"%s port-number (imap) : %d\n",indent,cxn->imap_port) ;
- fprintf (fp,"%s port-number (lmtp) : %d\n",indent,cxn->lmtp_port) ;
-
- fprintf (fp,"%s Issuing Quit : %d\n",indent, cxn->issue_quit) ;
-
- fprintf (fp,"%s time-connected (imap) : %ld\n",indent,(long) cxn->imap_timeCon) ;
- fprintf (fp,"%s time-connected (lmtp) : %ld\n",indent,(long) cxn->lmtp_timeCon) ;
- fprintf (fp,"%s articles from INN : %d\n",indent,
- cxn->lmtp_succeeded+
- cxn->lmtp_failed+
- cxn->cancel_succeeded+
- cxn->cancel_failed+
- cxn->create_succeeded+
- cxn->create_failed+
- cxn->remove_succeeded+
- cxn->remove_failed+
- QueueSpace(&(cxn->lmtp_todeliver_q))+
- QueueSpace(&(cxn->imap_controlMsg_q))
- );
- fprintf(fp,"%s LMTP STATS: yes: %d no: %d\n",indent,
- cxn->lmtp_succeeded, cxn->lmtp_failed);
- fprintf(fp,"%s control: yes: %d no: %d\n",indent,
- cxn->cancel_succeeded, cxn->cancel_failed);
- fprintf(fp,"%s create: yes: %d no: %d\n",indent,
- cxn->create_succeeded, cxn->create_failed);
- fprintf(fp,"%s remove: yes: %d no: %d\n",indent,
- cxn->remove_succeeded, cxn->remove_failed);
-
- fprintf (fp,"%s response-timeout : %d\n",indent,cxn->imap_readTimeout) ;
- fprintf (fp,"%s response-callback : %d\n",indent,cxn->imap_readBlockedTimerId) ;
-
- fprintf (fp,"%s write-timeout : %d\n",indent,cxn->imap_writeTimeout) ;
- fprintf (fp,"%s write-callback : %d\n",indent,cxn->imap_writeBlockedTimerId) ;
-
- fprintf (fp,"%s reopen wait : %d\n",indent,cxn->imap_sleepTimeout) ;
- fprintf (fp,"%s reopen id : %d\n",indent,cxn->imap_sleepTimerId) ;
-
- fprintf (fp,"%s IMAP queue {\n",indent) ;
- for (artH = cxn->imap_controlMsg_q.head; artH != NULL ; artH = artH->next)
- printArticleInfo (artH->data.article,fp,indentAmt + INDENT_INCR) ;
- fprintf (fp,"%s }\n",indent) ;
-
- fprintf (fp,"%s LMTP queue {\n",indent) ;
- for (artH = cxn->lmtp_todeliver_q.head ; artH != NULL ; artH = artH->next)
- printArticleInfo (artH->data.control->article,fp,indentAmt + INDENT_INCR) ;
- fprintf (fp,"%s }\n",indent) ;
-
- fprintf (fp,"%s}\n",indent) ;
-}
-
-/* config file load callback */
-int cxnConfigLoadCbk (void *data UNUSED)
-{
- long iv ;
- int rval = 1 ;
- FILE *fp = (FILE *) data ;
-
- if (getInteger (topScope,"max-reconnect-time",&iv,NO_INHERIT))
- {
- if (iv < 1)
- {
- rval = 0 ;
- logOrPrint (LOG_ERR,fp,
- "ME config: value of %s (%ld) in %s cannot be less"
- " than 1. Using %ld", "max-reconnect-time",
- iv,"global scope",(long) MAX_RECON_PER);
- iv = MAX_RECON_PER ;
- }
- }
- else
- iv = MAX_RECON_PER ;
- max_reconnect_period = (unsigned int) iv ;
-
- if (getInteger (topScope,"initial-reconnect-time",&iv,NO_INHERIT))
- {
- if (iv < 1)
- {
- rval = 0 ;
- logOrPrint (LOG_ERR,fp,
- "ME config: value of %s (%ld) in %s cannot be less"
- " than 1. Using %ld", "initial-reconnect-time",
- iv,"global scope",(long)INIT_RECON_PER);
- iv = INIT_RECON_PER ;
- }
- }
- else
- iv = INIT_RECON_PER ;
- init_reconnect_period = (unsigned int) iv ;
-
- return rval ;
-}
-
-/* check connection state is in cxnWaitingS, cxnConnectingS or cxnIdleS */
-bool cxnCheckstate (Connection cxn)
-{
- d_printf(5, "%s:%d Being asked to check state\n",
- hostPeerName (cxn->myHost), cxn->ident);
-
- /* return false if either connection is doing something */
- if (cxn->imap_state > IMAP_IDLE_AUTHED) return false;
- if (cxn->lmtp_state > LMTP_AUTHED_IDLE) return false;
-
- return true;
-}