static void imap_handle_continue(ImapSession *session) { if (session->state < CLIENTSTATE_LOGOUT) { if (session->buff && p_string_len(session->buff) > 0) { int e = 0; if ((e = ci_write(session->ci, "%s", (char *)p_string_str(session->buff))) < 0) { int serr = errno; TRACE(TRACE_DEBUG,"ci_write returned error [%s]", strerror(serr)); imap_handle_abort(session); return; } dbmail_imap_session_buff_clear(session); } if (p_string_len(session->ci->write_buffer) > session->ci->write_buffer_offset) ci_write(session->ci, NULL); if (session->command_state == TRUE) imap_session_reset(session); } else { dbmail_imap_session_buff_clear(session); } // handle buffered pending input if (p_string_len(session->ci->read_buffer) > 0) imap_handle_input(session); }
int pop3_error(ClientSession_T * session, const char *formatstring, ...) { va_list ap, cp; char *s; ClientBase_T *ci = session->ci; if (session->error_count >= MAX_ERRORS) { ci_write(ci, "-ERR too many errors\r\n"); return -3; } va_start(ap, formatstring); va_copy(cp, ap); s = g_strdup_vprintf(formatstring, cp); va_end(cp); va_end(ap); ci_write(ci, s); g_free(s); if (ci->client_state & CLIENT_ERR) { return -3; } TRACE(TRACE_DEBUG, "an invalid command was issued"); session->error_count++; return 1; }
void send_greeting(ClientSession_T *session) { Field_T banner; GETCONFIGVALUE("banner", "LMTP", banner); if (! dm_db_ping()) { ci_write(session->ci, "500 database has gone fishing BYE\r\n"); session->state = QUIT; return; } if (strlen(banner) > 0) ci_write(session->ci, "220 %s %s\r\n", session->hostname, banner); else ci_write(session->ci, "220 %s LMTP\r\n", session->hostname); }
static void send_greeting(ClientSession_T *session) { Field_T banner; GETCONFIGVALUE("banner", "POP", banner); if (! dm_db_ping()) { ci_write(session->ci, "+ERR database has gone fishing\r\n"); session->state = CLIENTSTATE_QUIT; return; } if (strlen(banner) > 0) { ci_write(session->ci, "+OK %s %s\r\n", banner, session->apop_stamp); } else { ci_write(session->ci, "+OK DBMAIL pop3 server ready to rock %s\r\n", session->apop_stamp); } }
static int _pop3_session_authenticated(ClientSession_T *session, int user_idnr) { ClientBase_T *ci = session->ci; int result = 0; session->state = CLIENTSTATE_AUTHENTICATED; ci_authlog_init(ci, THIS_MODULE, (const char *)session->username, AUTHLOG_ACT); client_session_set_timeout(session, server_conf->timeout); /* user seems to be valid, let's build a session */ TRACE(TRACE_DEBUG, "validation OK, building a session for user [%s]", session->username); /* if pop_before_smtp is active, log this ip */ if (pop_before_smtp) db_log_ip(ci->src_ip); result = db_createsession(user_idnr, session); if (result == 1) { ci_write(ci, "+OK %s has %" PRIu64 " messages (%" PRIu64 " octets)\r\n", session->username, session->virtual_totalmessages, session->virtual_totalsize); TRACE(TRACE_NOTICE, "user %s logged in [messages=%" PRIu64 ", octets=%" PRIu64 "]", session->username, session->virtual_totalmessages, session->virtual_totalsize); } else session->SessionResult = 4; /* Database error. */ return result; }
int ec_responce_with_istag(ci_request_t * req, int ec) { char buf[256]; ci_service_xdata_t *srv_xdata; int len; srv_xdata = service_data(req->current_service_mod); ci_headers_reset(req->response_header); snprintf(buf, 256, "ICAP/1.0 %d %s", ci_error_code(ec), ci_error_code_string(ec)); ci_headers_add(req->response_header, buf); ci_headers_add(req->response_header, "Server: C-ICAP/" VERSION); if (req->keepalive) ci_headers_add(req->response_header, "Connection: keep-alive"); else ci_headers_add(req->response_header, "Connection: close"); ci_service_data_read_lock(srv_xdata); ci_headers_add(req->response_header, srv_xdata->ISTag); ci_service_data_read_unlock(srv_xdata); if (!ci_headers_is_empty(req->xheaders)) { ci_headers_addheaders(req->response_header, req->xheaders); } ci_headers_pack(req->response_header); len = ci_write(req->connection->fd, req->response_header->buf, req->response_header->bufused, TIMEOUT); if (len < 0) return -1; req->bytes_out += len; return len; }
/* * only the main thread may write to the network event * worker threads must use an async queue */ static int64_t imap_session_printf(ImapSession * session, char * message, ...) { va_list ap, cp; uint64_t l; int e = 0; p_string_truncate(session->buff, 0); assert(message); va_start(ap, message); va_copy(cp, ap); p_string_append_vprintf(session->buff, message, cp); va_end(cp); va_end(ap); if ((e = ci_write(session->ci, (char *)p_string_str(session->buff))) < 0) { TRACE(TRACE_DEBUG, "ci_write failed [%d]", e); imap_handle_abort(session); return e; } l = p_string_len(session->buff); p_string_truncate(session->buff, 0); return (int64_t)l; }
static void pop3_close(ClientSession_T *session) { ClientBase_T *ci = session->ci; TRACE(TRACE_DEBUG,"[%p] sessionResult [%d]", session, session->SessionResult); session->state = CLIENTSTATE_QUIT; if (session->username != NULL && (session->was_apop || session->password != NULL)) { switch (session->SessionResult) { case 0: TRACE(TRACE_NOTICE, "user %s logging out [messages=%" PRIu64 ", octets=%" PRIu64 "]", session->username, session->virtual_totalmessages, session->virtual_totalsize); /* if everything went well, write down everything and do a cleanup */ if (db_update_pop(session) == DM_SUCCESS) ci_write(ci, "+OK see ya later\r\n"); else ci_write(ci, "-ERR some deleted messages not removed\r\n"); break; case 1: ci_write(ci, "-ERR I'm leaving, you're too slow\r\n"); TRACE(TRACE_ERR, "client timed out, connection closed"); break; case 2: TRACE(TRACE_ERR, "alert! possible flood attempt, closing connection"); break; case 3: TRACE(TRACE_ERR, "authorization layer failure"); break; case 4: TRACE(TRACE_ERR, "storage layer failure"); break; } } else { ci_write(ci, "+OK see ya later\r\n"); } }
void ec_responce(ci_request_t * req, int ec) { char buf[256]; int len; snprintf(buf, 256, "ICAP/1.0 %d %s\r\n\r\n", ci_error_code(ec), ci_error_code_string(ec)); buf[255] = '\0'; len = strlen(buf); ci_write(req->connection->fd, buf, len, TIMEOUT); req->bytes_out += len; }
int lmtp_error(ClientSession_T * session, const char *formatstring, ...) { va_list ap, cp; char *s; if (session->error_count >= MAX_ERRORS) { ci_write(session->ci, "500 Too many errors, closing connection.\r\n"); session->SessionResult = 2; /* possible flood */ return -3; } va_start(ap, formatstring); va_copy(cp, ap); s = g_strdup_vprintf(formatstring, cp); va_end(cp); ci_write(session->ci, s); g_free(s); session->error_count++; return -1; }
int ci_writen(int fd, char *buf, int len, int timeout) { int ret = 0, remains; remains = len; while (remains) { if ((ret = ci_write(fd, buf, remains, timeout)) < 0) return ret; buf += ret; remains -= ret; } return len; }
int tims_error(ClientSession_t * session, const char *formatstring, ...) { va_list ap, cp; char *s; if (session->error_count >= MAX_ERRORS) { ci_write(session->ci, "BYE \"Too many errors, closing connection.\"\r\n"); session->SessionResult = 2; /* possible flood */ return -3; } va_start(ap, formatstring); va_copy(cp, ap); s = g_strdup_vprintf(formatstring, cp); va_end(cp); ci_write(session->ci, s); g_free(s); TRACE(TRACE_DEBUG, "an invalid command was issued"); session->error_count++; return 1; }
static void pop3_handle_input(void *arg) { char buffer[MAX_LINESIZE]; /* connection buffer */ ClientSession_t *session = (ClientSession_t *)arg; if (session->ci->write_buffer->len) { ci_write(session->ci, NULL); return; } memset(buffer, 0, sizeof(buffer)); if (ci_readln(session->ci, buffer) == 0) return; pop3(session, buffer); }
int recycle_request(ci_request_t * req, ci_connection_t * connection) { int access; int len; ci_request_reset(req); ci_copy_connection(req->connection, connection); if ((access = access_check_client(req)) == CI_ACCESS_DENY) { /*Check for client access */ len = strlen(FORBITTEN_STR); ci_write(connection->fd, FORBITTEN_STR, len, TIMEOUT); return 0; /*Or something that means authentication error */ } req->access_type = access; return 1; }
void tims_cb_write(void *arg) { ClientSession_t *session = (ClientSession_t *)arg; TRACE(TRACE_DEBUG, "[%p] state: [%d]", session, session->state); switch(session->state) { case QUIT: client_session_bailout(&session); break; default: if (session->ci->write_buffer->len > session->ci->write_buffer_offset) { ci_write(session->ci,NULL); break; } session->handle_input(session); break; } }
void tims_cb_write(void *arg) { ClientSession_T *session = (ClientSession_T *)arg; int state = session->state; TRACE(TRACE_DEBUG, "[%p] state: [%d]", session, state); switch(state) { case CLIENTSTATE_QUIT_QUEUED: case CLIENTSTATE_QUIT: break; default: if (p_string_len(session->ci->write_buffer) > session->ci->write_buffer_offset) { ci_write(session->ci,NULL); break; } session->handle_input(session); break; } }
static void pop3_handle_input(void *arg) { char buffer[MAX_LINESIZE]; /* connection buffer */ ClientSession_T *session = (ClientSession_T *)arg; if (p_string_len(session->ci->write_buffer)) { ci_write(session->ci, NULL); return; } memset(buffer, 0, sizeof(buffer)); if (ci_readln(session->ci, buffer) == 0) return; ci_cork(session->ci); if (pop3(session, buffer) <= 0) { client_session_bailout(&session); return; } ci_uncork(session->ci); }
ci_request_t *newrequest(ci_connection_t * connection) { ci_request_t *req; int access; int len; ci_connection_t *conn; conn = (ci_connection_t *) malloc(sizeof(ci_connection_t)); ci_copy_connection(conn, connection); req = ci_request_alloc(conn); if ((access = access_check_client(req)) == CI_ACCESS_DENY) { /*Check for client access */ len = strlen(FORBITTEN_STR); ci_write(connection->fd, FORBITTEN_STR, len, TIMEOUT); ci_request_destroy(req); return NULL; /*Or something that means authentication error */ } req->access_type = access; return req; }
static void send_greeting(ClientSession_t *session) { field_t banner; GETCONFIGVALUE("banner", "SIEVE", banner); if (! dm_db_ping()) { ci_write(session->ci, "BYE \"database has gone fishing\"\r\n"); session->state = QUIT; return; } if (strlen(banner) > 0) ci_write(session->ci, "\"IMPLEMENTATION\" \"%s\"\r\n", banner); else ci_write(session->ci, "\"IMPLEMENTATION\" \"DBMail timsieved %s\"\r\n", VERSION); ci_write(session->ci, "\"SASL\" \"PLAIN\"\r\n"); ci_write(session->ci, "\"SIEVE\" \"%s\"\r\n", sieve_extensions); ci_write(session->ci, "OK\r\n"); }
int lmtp_tokenizer(ClientSession_T *session, char *buffer) { char *command = NULL, *value; int command_type = 0; if (! session->command_type) { session->parser_state = FALSE; command = buffer; strip_crlf(command); g_strstrip(command); if (! strlen(command)) return FALSE; /* ignore empty commands */ value = strstr(command, " "); /* look for the separator */ if (value) { *value++ = '\0'; /* set a \0 on the command end */ if (strlen(value) == 0) value = NULL; /* no value specified */ } for (command_type = LMTP_LHLO; command_type < LMTP_END; command_type++) if (strcasecmp(command, commands[command_type]) == 0) break; /* Invalid command */ if (command_type == LMTP_END) return lmtp_error(session, "500 Invalid command.\r\n"); /* Commands that are allowed to have no arguments */ if ((value == NULL) && !((command_type == LMTP_LHLO) || (command_type == LMTP_DATA) || (command_type == LMTP_RSET) || (command_type == LMTP_QUIT) || (command_type == LMTP_NOOP) || (command_type == LMTP_HELP) )) { return lmtp_error(session, "500 This command requires an argument.\r\n"); } session->command_type = command_type; if (value) session->args = g_list_append(session->args, g_strdup(value)); } if (session->command_type == LMTP_DATA) { if (command) { if (session->state != AUTH) { return lmtp_error(session, "550 Command out of sequence\r\n"); } if (g_list_length(session->rcpt) < 1) { return lmtp_error(session, "503 No valid recipients\r\n"); } if (g_list_length(session->from) < 1) { return lmtp_error(session, "554 No valid sender.\r\n"); } ci_write(session->ci, "354 Start mail input; end with <CRLF>.<CRLF>\r\n"); return FALSE; } if (strncmp(buffer,".\n",2)==0 || strncmp(buffer,".\r\n",3)==0) session->parser_state = TRUE; else if (strncmp(buffer,".",1)==0) g_string_append(session->rbuff, &buffer[1]); else g_string_append(session->rbuff, buffer); } else session->parser_state = TRUE; TRACE(TRACE_DEBUG, "[%p] cmd [%d], state [%d] [%s]", session, session->command_type, session->parser_state, buffer); return session->parser_state; }
int tims(ClientSession_t *session) { /* returns values: * 0 to quit * -1 on failure * 1 on success */ char *arg; size_t scriptlen = 0; int ret; char *script = NULL, *scriptname = NULL; sort_result_t *sort_result = NULL; clientbase_t *ci = session->ci; TRACE(TRACE_DEBUG,"[%p] [%d][%s]", session, session->command_type, commands[session->command_type]); switch (session->command_type) { case TIMS_LOUT: ci_write(ci, "OK \"Bye.\"\r\n"); session->state = QUIT; return 1; case TIMS_STLS: ci_write(ci, "NO\r\n"); return 1; case TIMS_CAPA: send_greeting(session); return 1; case TIMS_AUTH: /* We currently only support plain authentication, * which means that the command we accept will look * like this: Authenticate "PLAIN" "base64-password" * */ session->args = g_list_first(session->args); if (! (session->args && session->args->data)) return 1; arg = (char *)session->args->data; if (strcasecmp(arg, "PLAIN") == 0) { int i = 0; u64_t useridnr; if (! g_list_next(session->args)) return tims_error(session, "NO \"Missing argument.\"\r\n"); session->args = g_list_next(session->args); arg = (char *)session->args->data; char **tmp64 = NULL; tmp64 = base64_decodev(arg); if (tmp64 == NULL) return tims_error(session, "NO \"SASL decode error.\"\r\n"); for (i = 0; tmp64[i] != NULL; i++) ; if (i < 3) tims_error(session, "NO \"Too few encoded SASL arguments.\"\r\n"); /* The protocol specifies that the base64 encoding * be made up of three parts: proxy, username, password * Between them are NULLs, which are conveniently encoded * by the base64 process... */ if (auth_validate(ci, tmp64[1], tmp64[2], &useridnr) == 1) { ci_authlog_init(ci, THIS_MODULE, tmp64[1], AUTHLOG_ACT); ci_write(ci, "OK\r\n"); session->state = AUTH; session->useridnr = useridnr; session->username = g_strdup(tmp64[1]); session->password = g_strdup(tmp64[2]); client_session_set_timeout(session, server_conf->timeout); } else { ci_authlog_init(ci, THIS_MODULE, tmp64[1], AUTHLOG_ERR); g_strfreev(tmp64); return tims_error(session, "NO \"Username or password incorrect.\"\r\n"); } g_strfreev(tmp64); } else return tims_error(session, "NO \"Authentication scheme not supported.\"\r\n"); return 1; case TIMS_PUTS: if (session->state != AUTH) { ci_write(ci, "NO \"Please authenticate first.\"\r\n"); break; } session->args = g_list_first(session->args); if (! (session->args && session->args->data)) return 1; scriptname = (char *)session->args->data; session->args = g_list_next(session->args); assert(session->args); script = (char *)session->args->data; scriptlen = strlen(script); TRACE(TRACE_INFO, "Client sending script of length [%ld]", scriptlen); if (scriptlen >= UINT_MAX) return tims_error(session, "NO \"Invalid script length.\"\r\n"); if (dm_sievescript_quota_check(session->useridnr, scriptlen)) return tims_error(session, "NO \"Script exceeds available space.\"\r\n"); /* Store the script temporarily, * validate it, then rename it. */ if (dm_sievescript_add(session->useridnr, "@!temp-script!@", script)) { dm_sievescript_delete(session->useridnr, "@!temp-script!@"); return tims_error(session, "NO \"Error inserting script.\"\r\n"); } sort_result = sort_validate(session->useridnr, "@!temp-script!@"); if (sort_result == NULL) { dm_sievescript_delete(session->useridnr, "@!temp-script!@"); return tims_error(session, "NO \"Error inserting script.\"\r\n"); } else if (sort_get_error(sort_result) > 0) { dm_sievescript_delete(session->useridnr, "@!temp-script!@"); return tims_error(session, "NO \"Script error: %s.\"\r\n", sort_get_errormsg(sort_result)); } /* According to the draft RFC, a script with the same * name as an existing script should [atomically] replace it. */ if (dm_sievescript_rename(session->useridnr, "@!temp-script!@", scriptname)) return tims_error(session, "NO \"Error inserting script.\"\r\n"); ci_write(ci, "OK \"Script successfully received.\"\r\n"); break; case TIMS_SETS: if (session->state != AUTH) { ci_write(ci, "NO \"Please authenticate first.\"\r\n"); break; } scriptname = NULL; session->args = g_list_first(session->args); if ((session->args && session->args->data)) scriptname = (char *)session->args->data; if (strlen(scriptname)) { if (! dm_sievescript_activate(session->useridnr, scriptname)) ci_write(ci, "NO \"Error activating script.\"\r\n"); else ci_write(ci, "OK \"Script activated.\"\r\n"); } else { ret = dm_sievescript_get(session->useridnr, &scriptname); if (scriptname == NULL) { ci_write(ci, "OK \"No scripts are active at this time.\"\r\n"); } else { if (! dm_sievescript_deactivate(session->useridnr, scriptname)) ci_write(ci, "NO \"Error deactivating script.\"\r\n"); else ci_write(ci, "OK \"All scripts deactivated.\"\r\n"); g_free(scriptname); } } return 1; case TIMS_GETS: if (session->state != AUTH) { ci_write(ci, "NO \"Please authenticate first.\"\r\n"); break; } session->args = g_list_first(session->args); if (! (session->args && session->args->data)) return 1; scriptname = (char *)session->args->data; if (! strlen(scriptname)) return tims_error(session, "NO \"Script name required.\"\r\n"); ret = dm_sievescript_getbyname(session->useridnr, scriptname, &script); if (script == NULL) { return tims_error(session, "NO \"Script not found.\"\r\n"); } else if (ret < 0) { g_free(script); return tims_error(session, "NO \"Internal error.\"\r\n"); } else { ci_write(ci, "{%u+}\r\n", (unsigned int)strlen(script)); ci_write(ci, "%s\r\n", script); ci_write(ci, "OK\r\n"); g_free(script); } return 1; case TIMS_DELS: if (session->state != AUTH) { ci_write(ci, "NO \"Please authenticate first.\"\r\n"); break; } session->args = g_list_first(session->args); if (! (session->args && session->args->data)) return 1; scriptname = (char *)session->args->data; if (! strlen(scriptname)) return tims_error(session, "NO \"Script name required.\"\r\n"); if (! dm_sievescript_delete(session->useridnr, scriptname)) return tims_error(session, "NO \"Error deleting script.\"\r\n"); else ci_write(ci, "OK \"Script deleted.\"\r\n", scriptname); return 1; case TIMS_SPAC: if (session->state != AUTH) { ci_write(ci, "NO \"Please authenticate first.\"\r\n"); break; } // Command is in format: HAVESPACE "scriptname" 12345 // TODO: this is a fake if (dm_sievescript_quota_check(session->useridnr, 12345) == DM_SUCCESS) ci_write(ci, "OK (QUOTA)\r\n"); else ci_write(ci, "NO (QUOTA) \"Quota exceeded\"\r\n"); return 1; case TIMS_LIST: if (session->state != AUTH) { ci_write(ci, "NO \"Please authenticate first.\"\r\n"); break; } GList *scriptlist = NULL; if (dm_sievescript_list (session->useridnr, &scriptlist) < 0) { ci_write(ci, "NO \"Internal error.\"\r\n"); } else { if (g_list_length(scriptlist) == 0) { /* The command hasn't failed, but there aren't any scripts */ ci_write(ci, "OK \"No scripts found.\"\r\n"); } else { scriptlist = g_list_first(scriptlist); while (scriptlist) { sievescript_info_t *info = (sievescript_info_t *) scriptlist->data; ci_write(ci, "\"%s\"%s\r\n", info->name, (info-> active == 1 ? " ACTIVE" : "")); if (! g_list_next(scriptlist)) break; scriptlist = g_list_next(scriptlist); } ci_write(ci, "OK\r\n"); } g_list_destroy(scriptlist); } return 1; default: return tims_error(session, "NO \"What are you trying to say here?\"\r\n"); } if (sort_result) sort_free_result(sort_result); return 1; }
void imap_handle_input(ImapSession *session) { char buffer[MAX_LINESIZE]; char *alloc_buf = NULL; uint64_t alloc_size = 0; int l, result; assert(session && session->ci && session->ci->write_buffer); // first flush the output buffer if (p_string_len(session->ci->write_buffer)) { TRACE(TRACE_DEBUG,"[%p] write buffer not empty", session); ci_write(session->ci, NULL); } // nothing left to handle if (p_string_len(session->ci->read_buffer) == 0) { TRACE(TRACE_DEBUG,"[%p] read buffer empty", session); return; } // command in progress if (session->command_state == FALSE && session->parser_state == TRUE) { TRACE(TRACE_DEBUG,"[%p] command in-progress", session); return; } // reset if we're done with the previous command if (session->command_state == TRUE) imap_session_reset(session); // Read in a line at a time if we don't have a string literal size defined // Otherwise read in rbuff_size amount of data while (TRUE) { char *input = NULL; FREE_ALLOC_BUF memset(buffer, 0, sizeof(buffer)); if (session->ci->rbuff_size <= 0) { l = ci_readln(session->ci, buffer); } else { alloc_size = session->ci->rbuff_size+1; alloc_buf = mempool_pop(session->pool, alloc_size); l = ci_read(session->ci, alloc_buf, session->ci->rbuff_size); } if (l == 0) break; // done if (session->error_count >= MAX_FAULTY_RESPONSES) { imap_session_printf(session, "* BYE [TRY RFC]\r\n"); imap_handle_abort(session); break; } // session is in a IDLE loop if (session->command_type == IMAP_COMM_IDLE && session->command_state == IDLE) { if (strlen(buffer) > 4 && strncasecmp(buffer,"DONE",4)==0) imap_session_printf(session, "%s OK IDLE terminated\r\n", session->tag); else imap_session_printf(session,"%s BAD Expecting DONE\r\n", session->tag); session->command_state = TRUE; // done imap_session_reset(session); continue; } if (alloc_buf != NULL) input = alloc_buf; else input = buffer; if (! imap4_tokenizer(session, input)) { FREE_ALLOC_BUF continue; } if ( session->parser_state < 0 ) { imap_session_printf(session, "%s BAD parse error\r\n", session->tag); imap_handle_retry(session); break; } if ( session->parser_state ) { result = imap4(session); TRACE(TRACE_DEBUG,"imap4 returned [%d]", result); if (result || (session->command_type == IMAP_COMM_IDLE && session->command_state == IDLE)) { imap_handle_exit(session, result); } break; } if (session->state == CLIENTSTATE_ERROR) { TRACE(TRACE_NOTICE, "session->state: ERROR. abort"); break; } }
void tims_cb_time(void * arg) { ClientSession_t *session = (ClientSession_t *)arg; session->state = QUIT; ci_write(session->ci, "BYE \"Connection timed out.\"\r\n"); }
void pop3_cb_time(void * arg) { ClientSession_T *session = (ClientSession_T *)arg; ci_write(session->ci, "-ERR I'm leaving, you're too slow\r\n"); client_session_bailout(&session); }
static void lmtp_cb_time(void *arg) { ClientSession_T *session = (ClientSession_T *)arg; session->state = QUIT; ci_write(session->ci, "221 Connection timeout BYE\r\n"); }
int pop3(ClientSession_T *session, const char *buffer) { /* returns a 0 on a quit * -1 on a failure * 1 on a success */ char *command, *value, *searchptr, *enctype, *s; Pop3Cmd cmdtype; int found = 0; //int indx = 0; int validate_result; bool login_disabled = FALSE; uint64_t result, top_lines, top_messageid, user_idnr; unsigned char *md5_apop_he; struct message *msg; ClientBase_T *ci = session->ci; s = (char *)buffer; strip_crlf(s); g_strstrip(s); TRACE(TRACE_DEBUG, "incoming buffer: [%s]", s); if (! strlen(s)) return 1; int state = session->state; /* check for command issued */ /* while (strchr(ValidNetworkChars, s[indx++])) ; */ command = s; value = strstr(command, " "); /* look for the separator */ if (value != NULL) { *value = '\0'; /* set a \0 on the command end */ value++; /* skip space */ if (strlen(value) == 0) value = NULL; /* no value specified */ else { TRACE(TRACE_DEBUG, "state[%d], command issued :cmd [%s], value [%s]\n", state, command, value); } } /* find command that was issued */ for (cmdtype = POP3_QUIT; cmdtype < POP3_FAIL; cmdtype++) if (strcasecmp(command, commands[cmdtype]) == 0) { session->was_apop = 1; break; } TRACE(TRACE_DEBUG, "command looked up as commandtype %d", cmdtype); /* commands that are allowed to have no arguments */ if (value == NULL) { switch (cmdtype) { case POP3_QUIT: case POP3_LIST: case POP3_STAT: case POP3_RSET: case POP3_NOOP: case POP3_LAST: case POP3_UIDL: case POP3_AUTH: case POP3_CAPA: case POP3_STLS: break; default: return pop3_error(session, "-ERR your command does not compute\r\n"); break; } } if (state == CLIENTSTATE_INITIAL_CONNECT) { if (server_conf->ssl) { Field_T val; GETCONFIGVALUE("login_disabled", "POP", val); if (SMATCH(val, "yes")) login_disabled = TRUE; } } switch (cmdtype) { case POP3_QUIT: /* We return 0 here, and then pop3_handle_connection cleans up * the connection, commits all changes, and sends the final * "OK" message indicating that QUIT has completed. */ session->state = CLIENTSTATE_LOGOUT; session->SessionResult = 0; pop3_close(session); return 0; case POP3_STLS: if (state != CLIENTSTATE_INITIAL_CONNECT) return pop3_error(session, "-ERR wrong command mode\r\n"); if (! server_conf->ssl) return pop3_error(session, "-ERR server error\r\n"); if (session->ci->sock->ssl_state) return pop3_error(session, "-ERR TLS already active\r\n"); ci_write(session->ci, "+OK Begin TLS now\r\n"); if (ci_starttls(session->ci) < 0) return -1; return 1; case POP3_USER: if (state != CLIENTSTATE_INITIAL_CONNECT) return pop3_error(session, "-ERR wrong command mode\r\n"); if (login_disabled && ! session->ci->sock->ssl_state) return pop3_error(session, "-ERR try STLS\r\n"); if (session->username != NULL) { /* reset username */ g_free(session->username); session->username = NULL; } if (session->username == NULL) { /* create memspace for username */ session->username = g_new0(char,strlen(value) + 1); strncpy(session->username, value, strlen(value) + 1); } ci_write(ci, "+OK Password required for %s\r\n", session->username); return 1; case POP3_PASS: if (state != CLIENTSTATE_INITIAL_CONNECT) return pop3_error(session, "-ERR wrong command mode\r\n"); if (login_disabled && ! session->ci->sock->ssl_state) return pop3_error(session, "-ERR try STLS\r\n"); if (session->password != NULL) { g_free(session->password); session->password = NULL; } if (session->password == NULL) { /* create memspace for password */ session->password = g_new0(char,strlen(value) + 1); strncpy(session->password, value, strlen(value) + 1); }
int lmtp(ClientSession_T * session) { DbmailMessage *msg; ClientBase_T *ci = session->ci; int helpcmd; const char *class, *subject, *detail; size_t tmplen = 0, tmppos = 0; char *tmpaddr = NULL, *tmpbody = NULL, *arg; switch (session->command_type) { case LMTP_QUIT: ci_write(ci, "221 %s BYE\r\n", session->hostname); session->state = QUIT; return 1; case LMTP_NOOP: ci_write(ci, "250 OK\r\n"); return 1; case LMTP_RSET: ci_write(ci, "250 OK\r\n"); lmtp_rset(session,TRUE); return 1; case LMTP_LHLO: /* Reply wth our hostname and a list of features. * The RFC requires a couple of SMTP extensions * with a MUST statement, so just hardcode them. * */ ci_write(ci, "250-%s\r\n250-PIPELINING\r\n" "250-ENHANCEDSTATUSCODES\r\n250 SIZE\r\n", session->hostname); /* This is a SHOULD implement: * "250-8BITMIME\r\n" * Might as well do these, too: * "250-CHUNKING\r\n" * "250-BINARYMIME\r\n" * */ client_session_reset(session); session->state = AUTH; client_session_set_timeout(session, server_conf->timeout); return 1; case LMTP_HELP: session->args = g_list_first(session->args); if (session->args && session->args->data) arg = (char *)session->args->data; else arg = NULL; if (arg == NULL) helpcmd = LMTP_END; else for (helpcmd = LMTP_LHLO; helpcmd < LMTP_END; helpcmd++) if (strcasecmp (arg, commands[helpcmd]) == 0) break; TRACE(TRACE_DEBUG, "LMTP_HELP requested for commandtype %d", helpcmd); if ((helpcmd == LMTP_LHLO) || (helpcmd == LMTP_DATA) || (helpcmd == LMTP_RSET) || (helpcmd == LMTP_QUIT) || (helpcmd == LMTP_NOOP) || (helpcmd == LMTP_HELP)) { ci_write(ci, "%s", LMTP_HELP_TEXT[helpcmd]); } else ci_write(ci, "%s", LMTP_HELP_TEXT[LMTP_END]); return 1; case LMTP_VRFY: /* RFC 2821 says this SHOULD be implemented... * and the goal is to say if the given address * is a valid delivery address at this server. */ ci_write(ci, "502 Command not implemented\r\n"); return 1; case LMTP_EXPN: /* RFC 2821 says this SHOULD be implemented... * and the goal is to return the membership * of the specified mailing list. */ ci_write(ci, "502 Command not implemented\r\n"); return 1; case LMTP_MAIL: /* We need to LHLO first because the client * needs to know what extensions we support. * */ if (session->state != AUTH) { ci_write(ci, "550 Command out of sequence.\r\n"); return 1; } if (g_list_length(session->from) > 0) { ci_write(ci, "500 Sender already received. Use RSET to clear.\r\n"); return 1; } /* First look for an email address. * Don't bother verifying or whatever, * just find something between angle brackets! * */ session->args = g_list_first(session->args); if (! (session->args && session->args->data)) return 1; arg = (char *)session->args->data; if (find_bounded(arg, '<', '>', &tmpaddr, &tmplen, &tmppos) < 0) { ci_write(ci, "500 No address found. Missing <> boundries.\r\n"); return 1; } /* Second look for a BODY keyword. * See if it has an argument, and if we * support that feature. Don't give an OK * if we can't handle it yet, like 8BIT! * */ /* Find the '=' following the address * then advance one character past it * (but only if there's more string!) * */ if ((tmpbody = strstr(arg + tmppos, "=")) != NULL) if (strlen(tmpbody)) tmpbody++; /* This is all a bit nested now... */ if (tmpbody) { if (MATCH(tmpbody, "8BITMIME")) { // RFC1652 ci_write(ci, "500 Please use 7BIT MIME only.\r\n"); return 1; } if (MATCH(tmpbody, "BINARYMIME")) { // RFC3030 ci_write(ci, "500 Please use 7BIT MIME only.\r\n"); return 1; } } session->from = g_list_prepend(session->from, g_strdup(tmpaddr)); ci_write(ci, "250 Sender <%s> OK\r\n", (char *)(session->from->data)); g_free(tmpaddr); return 1; case LMTP_RCPT: if (session->state != AUTH) { ci_write(ci, "550 Command out of sequence.\r\n"); return 1; } session->args = g_list_first(session->args); if (! (session->args && session->args->data)) return 1; arg = (char *)session->args->data; if (find_bounded(arg, '<', '>', &tmpaddr, &tmplen, &tmppos) < 0 || tmplen < 1) { ci_write(ci, "500 No address found. Missing <> boundries or address is null.\r\n"); return 1; } Delivery_T *dsnuser = g_new0(Delivery_T,1); dsnuser_init(dsnuser); /* find_bounded() allocated tmpaddr for us, and that's ok * since dsnuser_free() will free it for us later on. */ dsnuser->address = tmpaddr; if (dsnuser_resolve(dsnuser) != 0) { TRACE(TRACE_ERR, "dsnuser_resolve_list failed"); ci_write(ci, "430 Temporary failure in recipient lookup\r\n"); dsnuser_free(dsnuser); g_free(dsnuser); return 1; } /* Class 2 means the address was deliverable in some way. */ switch (dsnuser->dsn.class) { case DSN_CLASS_OK: ci_write(ci, "250 Recipient <%s> OK\r\n", dsnuser->address); session->rcpt = g_list_prepend(session->rcpt, dsnuser); break; default: ci_write(ci, "550 Recipient <%s> FAIL\r\n", dsnuser->address); dsnuser_free(dsnuser); g_free(dsnuser); break; } return 1; /* Here's where it gets really exciting! */ case LMTP_DATA: msg = dbmail_message_new(); dbmail_message_init_with_string(msg, session->rbuff); dbmail_message_set_header(msg, "Return-Path", (char *)session->from->data); g_string_truncate(session->rbuff,0); g_string_maybe_shrink(session->rbuff); if (insert_messages(msg, session->rcpt) == -1) { ci_write(ci, "430 Message not received\r\n"); dbmail_message_free(msg); return 1; } /* The DATA command itself it not given a reply except * that of the status of each of the remaining recipients. */ /* The replies MUST be in the order received */ session->rcpt = g_list_reverse(session->rcpt); while (session->rcpt) { Delivery_T * dsnuser = (Delivery_T *)session->rcpt->data; dsn_tostring(dsnuser->dsn, &class, &subject, &detail); /* Give a simple OK, otherwise a detailed message. */ switch (dsnuser->dsn.class) { case DSN_CLASS_OK: ci_write(ci, "%d%d%d Recipient <%s> OK\r\n", dsnuser->dsn.class, dsnuser->dsn.subject, dsnuser->dsn.detail, dsnuser->address); break; default: ci_write(ci, "%d%d%d Recipient <%s> %s %s %s\r\n", dsnuser->dsn.class, dsnuser->dsn.subject, dsnuser->dsn.detail, dsnuser->address, class, subject, detail); } if (! g_list_next(session->rcpt)) break; session->rcpt = g_list_next(session->rcpt); } dbmail_message_free(msg); /* Reset the session after a successful delivery; * MTA's like Exim prefer to immediately begin the * next delivery without an RSET or a reconnect. */ lmtp_rset(session,TRUE); return 1; default: return lmtp_error(session, "500 What are you trying to say here?\r\n"); } return 1; }
void pop3_cb_time(void * arg) { ClientSession_t *session = (ClientSession_t *)arg; session->state = CLIENTSTATE_QUIT; ci_write(session->ci, "-ERR I'm leaving, you're too slow\r\n"); }