/* $OpenBSD: pop_trans.c,v 1.6 2009/12/17 11:04:39 sobrado Exp $ */ /* * TRANSACTION state handling. */ #include #include #include "params.h" #include "protocol.h" #include "database.h" #include "mailbox.h" static int pop_trans_quit(char *params) { if (params) return POP_ERROR; return POP_STATE; } static int pop_trans_noop(char *params) { if (params) return POP_ERROR; return POP_OK; } static int pop_trans_stat(char *params) { if (params) return POP_ERROR; if (pop_reply("+OK %u %lu", db.visible_count, db.visible_size)) return POP_CRASH_NETFAIL; return POP_QUIET; } static int pop_trans_list_or_uidl_all(int uidl) { unsigned int number; struct db_message *msg; if (pop_reply_ok()) return POP_CRASH_NETFAIL; for (number = 1; number <= db.total_count; number++) { msg = db.array[number - 1]; if (msg->flags & MSG_DELETED) continue; if (uidl) { if (pop_reply("%u " "%02x%02x%02x%02x%02x%02x%02x%02x", number, msg->hash[3], msg->hash[2], msg->hash[1], msg->hash[0], msg->hash[7], msg->hash[6], msg->hash[5], msg->hash[4])) return POP_CRASH_NETFAIL; } else if (pop_reply("%u %lu", number, msg->size)) return POP_CRASH_NETFAIL; } if (pop_reply_terminate()) return POP_CRASH_NETFAIL; return POP_QUIET; } static int pop_trans_list_or_uidl(char *params, int uidl) { int number; struct db_message *msg; if (!params) return pop_trans_list_or_uidl_all(uidl); number = pop_get_int(¶ms); if (number < 1 || number > db.total_count || params) return POP_ERROR; msg = db.array[number - 1]; if (msg->flags & MSG_DELETED) return POP_ERROR; if (uidl) { if (pop_reply("+OK %d " "%02x%02x%02x%02x%02x%02x%02x%02x", number, msg->hash[3], msg->hash[2], msg->hash[1], msg->hash[0], msg->hash[7], msg->hash[6], msg->hash[5], msg->hash[4])) return POP_CRASH_NETFAIL; } else if (pop_reply("+OK %d %lu", number, msg->size)) return POP_CRASH_NETFAIL; return POP_QUIET; } static int pop_trans_list(char *params) { return pop_trans_list_or_uidl(params, 0); } static int pop_trans_uidl(char *params) { return pop_trans_list_or_uidl(params, 1); } static int pop_trans_retr(char *params) { int number; struct db_message *msg; int event; number = pop_get_int(¶ms); if (number < 1 || number > db.total_count || params) return POP_ERROR; msg = db.array[number - 1]; if (msg->flags & MSG_DELETED) return POP_ERROR; if ((event = mailbox_get(msg, -1)) != POP_OK) return event; #if POP_SUPPORT_LAST if (number > db.last) db.last = number; #endif return POP_QUIET; } static int pop_trans_top(char *params) { int number, lines; struct db_message *msg; int event; number = pop_get_int(¶ms); if (number < 1 || number > db.total_count) return POP_ERROR; lines = pop_get_int(¶ms); if (lines < 0 || params) return POP_ERROR; msg = db.array[number - 1]; if (msg->flags & MSG_DELETED) return POP_ERROR; if ((event = mailbox_get(msg, lines)) != POP_OK) return event; return POP_QUIET; } static int pop_trans_dele(char *params) { int number; struct db_message *msg; number = pop_get_int(¶ms); if (number < 1 || number > db.total_count || params) return POP_ERROR; msg = db.array[number - 1]; if (db_delete(msg)) return POP_ERROR; #if POP_SUPPORT_LAST if (number > db.last) db.last = number; #endif return POP_OK; } static int pop_trans_rset(char *params) { struct db_message *msg; if (params) return POP_ERROR; if ((msg = db.head)) do { msg->flags &= ~MSG_DELETED; } while ((msg = msg->next)); db.visible_count = db.total_count; db.visible_size = db.total_size; db.flags &= ~DB_DIRTY; #if POP_SUPPORT_LAST db.last = 0; #endif return POP_OK; } #if POP_SUPPORT_LAST static int pop_trans_last(char *params) { if (params) return POP_ERROR; if (pop_reply("+OK %u", db.last)) return POP_CRASH_NETFAIL; return POP_QUIET; } #endif static struct pop_command pop_trans_commands[] = { {"QUIT", pop_trans_quit}, {"NOOP", pop_trans_noop}, {"STAT", pop_trans_stat}, {"LIST", pop_trans_list}, {"UIDL", pop_trans_uidl}, {"RETR", pop_trans_retr}, {"TOP", pop_trans_top}, {"DELE", pop_trans_dele}, {"RSET", pop_trans_rset}, #if POP_SUPPORT_LAST {"LAST", pop_trans_last}, #endif {NULL, NULL} }; static int db_load(char *spool, char *mailbox) { db_init(); if (mailbox_open(spool, mailbox)) return 1; if (db_fix()) { mailbox_close(); return 1; } return 0; } int do_pop_trans(char *spool, char *mailbox) { int event; if (!pop_sane()) return 1; if (db_load(spool, mailbox)) { syslog(SYSLOG_PRI_HI, "Failure reading %s/%s or mailbox limits exceeded", spool, mailbox); pop_reply_error(); return 0; } syslog(SYSLOG_PRI_LO, "%u message%s (%lu byte%s) loaded", db.total_count, db.total_count == 1 ? "" : "s", db.total_size, db.total_size == 1 ? "" : "s"); if (pop_reply_ok()) event = POP_CRASH_NETFAIL; else switch ((event = pop_handle_state(pop_trans_commands))) { case POP_STATE: if (mailbox_update()) { if (db.flags & DB_STALE) break; syslog(SYSLOG_PRI_ERROR, "Failed to update %s/%s", spool, mailbox); pop_reply_error(); break; } syslog(SYSLOG_PRI_LO, "%u (%lu) deleted, %u (%lu) left", db.total_count - db.visible_count, db.total_size - db.visible_size, db.visible_count, db.visible_size); pop_reply_ok(); break; case POP_CRASH_NETFAIL: syslog(SYSLOG_PRI_LO, "Premature disconnect"); break; case POP_CRASH_NETTIME: syslog(SYSLOG_PRI_LO, "Connection timed out"); } if (db.flags & DB_STALE) syslog(SYSLOG_PRI_LO, "Another MUA active, giving up"); else if (event == POP_CRASH_SERVER) syslog(SYSLOG_PRI_ERROR, "Server failure accessing %s/%s", spool, mailbox); mailbox_close(); return 0; }