/* elmo - ELectronic Mail Operator Copyright (C) 2002, 2003, 2004 rzyjontko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------------------------------- Design goals: ============= 1. Enable user to fetch / delete messages in non-interactive manner. This means that user should be able to retrieve, and delete all messages from given server (or all servers) and possibly combine these actions. 2. Enable user to check if there is new mail waiting for him at one of defined servers. 3. Enable user to fetch messages from servers in interactive manner, where he sees messages' headers, and may fetch and / or delete messages directly at server side. 4. It must be possible to use non-interactive commands with interval hooks, such that elmo checks / fetches / deletes mail regularly. 5. Interactive commands must be scheduled for execution if new command is issued before all previous have been completed. 6. Interactive and non-interactive commands may not interact. It may not happen, that checking for new mail is executed while user is connected in interactive mode. 7. Interval hooks may not be executed after some idle time. It may not happen, that user cannot read his mail because elmo connects to the server to check, whether there are some new messages. Non-interactive possibilities: + connect - list - fetch - disconnect - (pick next) + connect - stat - delete - disconnect - (pick next) + connect - list - fetch - delete - disconnect - (pick next) + connect - stat - disconnect - pick next Interactive possibilities: + connect + fetch + delete + reset + disconnect Few words about APOP: ===================== I noticed that some servers send digest, but do not support this kind of authentication. My assumption is based on the fact that I found no mistake in my implementation, checked if sample password and digest (from RFC 1725) match sample result, and that fetchmail couldn't authenticate itself either. This is why I don't try to use APOP authentication method without clear user's will. These servers reply with "authentication failed", which might be misleading. */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include "networking.h" #include "rstring.h" #include "error.h" #include "pop.h" #include "xmalloc.h" #include "mlex.h" #include "gettext.h" #include "str.h" #include "misc.h" #include "debug.h" #include "clock.h" #include "ask.h" #include "file.h" #include "hash.h" #include "md5.h" #include "procmail.h" #include "run.h" #include "status.h" #include "fetch.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define ONELINE_TERMINATOR "\r\n$" #define MULTILINE_TERMINATOR "(^\\-ERR.*\r\n$)|(\r\n\\.\r\n$)" // state information is only used by job dispatcher for interactive // commands which must be scheduled for execution when user enters // few without waiting for previous one to be completed. enum state { POP_DISCONNECTED, // no connection POP_IDLE, // accepting commands POP_PROCESSING // processing command }; // The operation mode is used to check what kind of operation is // requested by user. enum op_mode { INTERACTIVE, CHECK_NEW, FETCH, DELETE, FETCH_DEL, FETCH_ALL, DELETE_ALL, FETCH_DEL_ALL }; // These are interactive commands, that may be requested by user enum command { C_GET_HEADERS, // retrieve headers C_FETCH, // retrieve selected message C_DELETE, // delete selected message C_RESET, // reset connection state C_DISCONNECT // disconnect }; // Each operation (interactive or not) consists of many steps. Each step // is a POP3 operation. Initialy stage is set to S_GREETING which means // that we don't issue any POP3 command but just wait for a greeting from // server. Before issuing any POP3 operation the stage is set to the // appropriate value such that when elmo receives answer from server, it // knows what kind of command it is reply to. enum stage { S_GREETING, S_USER, S_PASS, S_RETR, S_DELE, S_QUIT, S_RSET, S_STAT, S_LIST, S_TOP }; /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ struct job { struct job *next; enum command cmd; int arg; }; class Pop : public Net { private: enum state state; enum op_mode mode; enum stage stage; ask_t *ask; int progress; str_t *send_buf; str_t *apop_digest; str_t *error_message; /* error message that was sent along with */ /* with negative response is displayed to */ /* user */ struct job *jobs; struct job *last; bool found_new_mail; // true if we have already encountered // server where there is new mail int mail_index; // this is used when fetching / deleting // all messages - this is an index of the // last fetched message int maildrop_count; // number of messages in the mailbox int maildrop_size; // total size of messages in the mailbox int *mail_sizes; // sizes of individual messages mail_array_t *marray; void send_pass (void); void send_user (char *user); void send_apop (char *user, char *pass); void send_user_apop (void); void send_quit (void); void send_retr (int num); void send_dele (int num); void send_rset (void); void send_stat (void); void send_list (void); void send_top (int num); void next_action_new (void); void retrieve_next_message (void); void next_action_fetch (void); void delete_next_message (void); void next_action_delete (void); void fetch_del_next_message (void); void next_action_fetch_dele (void); void next_action_interactive (void); void next_action (void); void connect (void); void s_greeting (char *msg, int len); void s_user (char *msg, int len); void s_pass (char *msg, int len); void s_quit (char *msg, int len); void s_rset (char *msg, int len); void s_dele (char *msg, int len); void s_retr (char *msg, int len); void s_stat (char *msg, int len); void s_list (char *msg, int len); void s_top (char *msg, int len); void tell_new_mail (void); void tell_no_mail (void); bool is_positive (char *buf); void report_error (const char *msg); void get_digest (char *msg); void get_sizes (char *msg); void cleanup (void); void add_job (enum command cmd, int arg = -1); void finish_job (void); enum command top_command (void); void dispatch_job (void); void destroy_jobs (struct job *job); protected: void recv_fun (char *buf, int size); void on_shutdown (void); public: Pop (void); ~Pop (void); void disconnect (void); void check_new_mail (void); void fetch (void); void purge (void); void fetch_purge (void); void fetch_all (void); void purge_all (void); void fetch_purge_all (void); void open_interactive (void); void fetch_interactive (int num); void delete_interactive (int num); void reset_interactive (void); void close_interactive (void); void delete_all_interactive (void); void fetch_all_interactive (void); mail_t *mail_info (int num); int mail_count (void); }; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ static Pop *pop = NULL; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * MESSAGE LIST FUNCTIONS ****************************************************************************/ /**************************************************************************** * TRANSITIONS ****************************************************************************/ // Transitions, are commands issued to server. Before sending a command // the stage is updated so that we know what to do with server's response. // // The only non-trivial function is next_action which has to decide which // action to execute next depending on mode of operation, stage, and // connection state. void Pop::send_pass (void) { char *pass = ask_get_field (ask, "password"); str_clear (send_buf); str_sprintf (send_buf, "PASS %s\r\n", pass); stage = S_PASS; combo (send_buf->str, send_buf->len, ONELINE_TERMINATOR); } void Pop::send_user (char *user) { str_clear (send_buf); str_sprintf (send_buf, "USER %s\r\n", user); stage = S_USER; combo (send_buf->str, send_buf->len, ONELINE_TERMINATOR); } void Pop::send_apop (char *user, char *pass) { int i; unsigned char digest[16]; int user_len = strlen (user); int pass_len = strlen (pass); MD5_CTX_ ctx; if (apop_digest == NULL){ error_ (0, "%s", _("Server doesn't support " "APOP authentication.")); close (); return; } str_clear (send_buf); MD5Init (&ctx); MD5Update (&ctx, (unsigned char *) apop_digest->str, apop_digest->len); MD5Update (&ctx, (unsigned char *) pass, pass_len); MD5Final (digest, &ctx); str_put_string_len (send_buf, "APOP ", 5); str_put_string_len (send_buf, user, user_len); str_put_char (send_buf, ' '); for (i = 0; i < 16; i++){ str_sprintf (send_buf, "%02x", digest[i]); } str_put_string_len (send_buf, "\r\n", 2); stage = S_PASS; combo (send_buf->str, send_buf->len, ONELINE_TERMINATOR); } void Pop::send_user_apop (void) { char *user = ask_get_field (ask, "username"); char *pass = ask_get_field (ask, "password"); if (user == NULL){ error_ (0, "%s", _("No username supplied, disconnecting...")); send_quit (); return; } if (pass == NULL){ error_ (0, "%s", _("No password supplied, disconnecting...")); send_quit (); return; } progress = progress_setup (1, "%s", _("logging in...")); if (ask_get_field_int_default (ask, "use_apop", 0)){ send_apop (user, pass); } else { send_user (user); } } void Pop::send_quit (void) { stage = S_QUIT; combo ("QUIT\r\n", 6, ONELINE_TERMINATOR); } void Pop::send_retr (int num) { str_clear (send_buf); str_sprintf (send_buf, "RETR %d\r\n", num); stage = S_RETR; if (mail_sizes != NULL){ expect (mail_sizes[num - 1], _("fetching message %d"), num); } combo (send_buf->str, send_buf->len, MULTILINE_TERMINATOR); } void Pop::send_dele (int num) { str_clear (send_buf); str_sprintf (send_buf, "DELE %d\r\n", num); stage = S_DELE; combo (send_buf->str, send_buf->len, ONELINE_TERMINATOR); } void Pop::send_rset (void) { stage = S_RSET; combo ("RSET\r\n", 6, ONELINE_TERMINATOR); } void Pop::send_stat (void) { stage = S_STAT; combo ("STAT\r\n", 6, ONELINE_TERMINATOR); } void Pop::send_list (void) { stage = S_LIST; combo ("LIST\r\n", 6, MULTILINE_TERMINATOR); } void Pop::send_top (int num) { str_clear (send_buf); str_sprintf (send_buf, "TOP %d 0\r\n", num); stage = S_TOP; combo (send_buf->str, send_buf->len, MULTILINE_TERMINATOR); } void Pop::connect (void) { char *name; char *host; int secure; int port; host = ask_get_field (ask, "server"); secure = ask_get_field_int_default (ask, "ssl", 0); port = ask_get_field_int_default (ask, "port", (secure) ? 995 : 110); if (host == NULL){ name = ask_get_field (ask, "name"); error_ (0, _("POP3 account %s is invalid: server address " "not specified."), name); close (); return; } if (! open (host, port, secure)){ close (); return; } stage = S_GREETING; net_recv (ONELINE_TERMINATOR); } void Pop::next_action_new (void) { switch (stage){ case S_PASS: send_stat (); break; case S_STAT: send_quit (); status_add_info (""); if (maildrop_count > 0){ found_new_mail = true; tell_new_mail (); } break; case S_QUIT: if (! found_new_mail){ connect (); } else { cleanup (); } break; default: error_ (0, _("Internal error in Pop::next_action_new")); break; } } void Pop::retrieve_next_message (void) { if (mail_index >= maildrop_count){ send_quit (); } else { send_retr (mail_index + 1); mail_index++; } } void Pop::next_action_fetch (void) { switch (stage){ case S_PASS: send_list (); break; case S_LIST: mail_index = 0; if (maildrop_count > 0) found_new_mail = true; case S_RETR: retrieve_next_message (); break; case S_QUIT: connect (); break; default: error_ (0, _("Internal error in Pop::next_action_fetch")); break; } } void Pop::delete_next_message (void) { if (mail_index >= maildrop_count){ send_quit (); } else { send_dele (mail_index + 1); mail_index++; } } void Pop::next_action_delete (void) { switch (stage){ case S_PASS: send_stat (); break; case S_STAT: mail_index = 0; case S_DELE: delete_next_message (); break; case S_QUIT: connect (); break; default: error_ (0, _("Internal error in Pop::next_action_delete")); break; } } void Pop::fetch_del_next_message (void) { if (mail_index >= maildrop_count){ mail_index = 0; delete_next_message (); } else { send_retr (mail_index + 1); mail_index++; } } void Pop::next_action_fetch_dele (void) { switch (stage){ case S_PASS: send_list (); break; case S_LIST: mail_index = 0; if (maildrop_count > 0) found_new_mail = true; case S_RETR: fetch_del_next_message (); break; case S_DELE: delete_next_message (); break; case S_QUIT: connect (); break; default: error_ (0, _("Internal error in Pop::next_action_fetch_dele")); break; } } // It is very easy to determine the next action for a non-interactive // task. However in interactive mode we have first check if the last // job has been already done (some jobs consist of few actions), and // may not decide what to do next in their body. // A best example for this is LIST action. The s_list state may not // determine what to do next, so it just hands the transition decision // to next_action. Then next_action may not just run dispatch_job // because this job (GET_HEADERS) has not yet been finished. void Pop::next_action_interactive (void) { switch (stage){ case S_PASS: send_list (); return; case S_LIST: mail_index = 0; if (maildrop_count > 0){ marray = mail_array_create_size (maildrop_count + 1, BOX_POP3, NULL); if (progress != -1) progress_close (progress); progress = progress_setup (maildrop_count, _("fetching headers...")); } else { tell_no_mail (); send_quit (); return; } break; case S_TOP: mail_index++; break; default: state = POP_IDLE; fetch_redraw (); finish_job (); dispatch_job (); return; } if (mail_index >= maildrop_count){ if (progress != -1) progress_close (progress); progress = -1; state = POP_IDLE; fetch_show (); finish_job (); dispatch_job (); return; } send_top (mail_index + 1); } // This function determines what is the next action for the task // given with mode. void Pop::next_action (void) { switch (mode){ case INTERACTIVE: next_action_interactive (); break; case CHECK_NEW: next_action_new (); break; case FETCH: case FETCH_ALL: next_action_fetch (); break; case DELETE: case DELETE_ALL: next_action_delete (); break; case FETCH_DEL: case FETCH_DEL_ALL: next_action_fetch_dele (); break; } } /**************************************************************************** * STATES ****************************************************************************/ // Here we have states - functions that parse server responses, and decide // whether the last action was successful or not. There are 2 possibiliies // of what such a function may do upon success: // 1. If there is only one possibility of taking next action (e.g. while // logging in), then use appropriate send_* function to do the // transition to the next state. // 2. If there are multiple choices, use next_action to make a transition. // // When the operation failed, or the connection is being closed, then there // is no furhter transition. The function on_shutdown does the transition // in case of multi-account operation. void Pop::s_greeting (char *msg, int len) { if (! is_positive (msg)){ report_error (_("Connection has been rejected.")); close (); return; } get_digest (msg); send_user_apop (); } void Pop::s_user (char *msg, int len) { if (! is_positive (msg)){ report_error (_("Username has been rejected.")); close (); return; } send_pass (); } void Pop::s_pass (char *msg, int len) { if (! is_positive (msg)){ report_error (_("Authorization has failed.")); close (); return; } if (apop_digest) str_destroy (apop_digest); apop_digest = NULL; if (progress != -1) progress_close (progress); progress = -1; next_action (); } void Pop::s_quit (char *msg, int len) { if (! is_positive (msg)){ report_error (_("Does your server like you so much?")); } close (); } void Pop::s_rset (char *msg, int len) { int i; mail_t *mail; if (! is_positive (msg)){ report_error (_("Couldn't reset mailbox status.")); } else if (marray) { for (i = 0; i < maildrop_count; i++){ mail = mail_array_get (marray, i); mail->flags &= ~ FLAG_TRASHED; } } next_action (); } void Pop::s_dele (char *msg, int len) { mail_t *mail; if (! is_positive (msg)){ report_error (_("Couldn't delete message.")); } else if (marray){ mail = mail_array_get (marray, jobs->arg - 1); mail->flags |= FLAG_TRASHED; } next_action (); } void Pop::s_retr (char *msg, int len) { char *seek; mail_t *mail; if (progress != -1) progress_close (progress); progress = -1; if (! is_positive (msg)){ report_error (_("Couldn't fetch message.")); } else { seek = strchr (msg, '\r'); if (seek == NULL) seek = msg; else seek += 2; procmail_deliver (seek); if (marray){ mail = mail_array_get (marray, jobs->arg - 1); mail->flags |= FLAG_FETCHED; } } next_action (); } void Pop::s_stat (char *msg, int len) { char *seek; char *rest; int new_m; if (! is_positive (msg)){ report_error (_("Couldn't obtain information about maildrop.")); next_action (); return; } seek = strchr (msg, ' '); if (! seek){ error_ (0, "%s: %s", _("Invalid response from server"), msg); next_action (); return; } maildrop_count = strtol (seek + 1, & rest, 10); if (*rest != ' '){ error_ (0, "%s: %s", _("Invalid response from server"), msg); next_action (); return; } maildrop_size = strtol (rest + 1, NULL, 10); next_action (); } void Pop::s_list (char *msg, int len) { if (! is_positive (msg)){ report_error (_("Couldn't obtain information about sizes of messages.")); next_action (); return; } get_sizes (msg); next_action (); } void Pop::s_top (char *msg, int len) { char *seek; if (! is_positive (msg)){ report_error (_("Couldn't fetch message header.")); next_action (); return; } seek = strchr (msg, '\r'); if (seek == NULL){ error_ (0, _("Server issued an invalid response.")); next_action (); return; } if (mlex_scan_buffer (seek + 2) != NEXT_MAIL){ error_ (0, _("Header of the %d message is invalid."), mail_index + 1); next_action (); return; } newmail->type = BOX_POP3; newmail->place.size = mail_sizes[mail_index]; mail_array_insert (marray, newmail); progress_advance (progress, 1); next_action (); } /**************************************************************************** * JOB FUNCTIONS ****************************************************************************/ void Pop::add_job (enum command cmd, int arg) { struct job *job = (struct job *) xmalloc (sizeof (struct job)); job->next = NULL; job->cmd = cmd; job->arg = arg; if (last == NULL){ jobs = last = job; } else { last->next = job; last = job; } } void Pop::finish_job (void) { struct job *job = jobs; jobs = jobs->next; if (last == job) last = jobs; xfree (job); state = POP_IDLE; } void Pop::dispatch_job (void) { mail_t *mail; struct job *job; if (state != POP_IDLE) return; job = jobs; if (job == NULL) return; switch (job->cmd){ case C_FETCH: if (marray){ mail = mail_array_get (marray, job->arg - 1); if (mail->flags & FLAG_FETCHED){ finish_job (); dispatch_job (); return; } } send_retr (job->arg); break; case C_DELETE: if (marray){ mail = mail_array_get (marray, job->arg - 1); if (mail->flags & FLAG_TRASHED){ finish_job (); dispatch_job (); return; } } send_dele (job->arg); break; case C_RESET: send_rset (); break; case C_DISCONNECT: send_quit (); break; } state = POP_PROCESSING; } void Pop::destroy_jobs (struct job *job) { if (job == NULL) return; destroy_jobs (job->next); xfree (job); } /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ void Pop::on_shutdown (void) { switch (mode){ case INTERACTIVE: case FETCH: case DELETE: case FETCH_DEL: cleanup (); break; case FETCH_ALL: case FETCH_DEL_ALL: ask = ask_select_next (ask); if (ask){ stage = S_QUIT; next_action (); } else { if (found_new_mail) run_sound (); cleanup (); } break; case DELETE_ALL: case CHECK_NEW: ask = ask_select_next (ask); if (ask){ stage = S_QUIT; next_action (); } else { cleanup (); } break; } } void Pop::cleanup (void) { if (ask) ask_destroy (ask); ask = NULL; if (progress != -1) progress_close (progress); progress = -1; if (apop_digest) str_destroy (apop_digest); apop_digest = NULL; if (error_message) str_destroy (error_message); error_message = NULL; if (mail_sizes) xfree (mail_sizes); mail_sizes = NULL; if (marray) mail_array_destroy (marray); marray = NULL; str_clear (send_buf); destroy_jobs (jobs); jobs = NULL; last = NULL; state = POP_DISCONNECTED; } void Pop::tell_new_mail (void) { str_t *str = str_create (); str_sprintf (str, _(" %d message(s) at %s"), maildrop_count, ask_get_field (ask, "name")); status_add_info (str->str); str_destroy (str); run_sound (); } void Pop::tell_no_mail (void) { str_t *str = str_create (); str_sprintf (str, _(" no mail at %s"), ask_get_field (ask, "name")); status_add_info (str->str); str_destroy (str); } void Pop::report_error (const char *str) { if (error_message){ error_ (0, "%s %s", str, error_message->str); } else { error_ (0, "%s", str); } } bool Pop::is_positive (char *msg) { char *end; if (msg == NULL) return false; end = strchr (msg, '\r'); if (end){ *end = '\0'; } if (strstr (msg, "+OK")){ *end = '\r'; return true; } if (error_message) str_destroy (error_message); error_message = NULL; if (strstr (msg, "-ERR")){ error_message = str_dup (msg + 5); return false; } for (end = msg; isprint (*end); end++) ; *end = '\0'; error_ (0, _("Unusual server response: \"%s\""), msg); return false; } void Pop::get_digest (char *msg) { int len; regmatch_t matches[1]; if (misc_regex ("<.+@.+>", msg, matches)){ if (apop_digest) str_destroy (apop_digest); len = matches[0].rm_eo - matches[0].rm_so; apop_digest = str_create_size (len); str_put_string_len (apop_digest, msg + matches[0].rm_so, len); } } void Pop::get_sizes (char *msg) { int i; char *seek; rstring_t *items = rstring_split_re (msg, "\r?\n"); if (items->count < 2){ error_ (0, _("Server issued an invalid response.")); rstring_delete (items); return; } maildrop_count = items->count - 2; maildrop_size = 0; mail_sizes = (int *) xmalloc (maildrop_count * sizeof (int)); for (i = 1; i < items->count - 1; i++){ seek = strchr (items->array[i], ' '); if (seek == NULL){ error_ (0, _("Server issued an invalid response.")); xfree (mail_sizes); mail_sizes = NULL; rstring_delete (items); return; } mail_sizes[i - 1] = atoi (seek + 1); maildrop_size += mail_sizes[i - 1]; } rstring_delete (items); } void Pop::recv_fun (char *msg, int len) { switch (stage){ case S_GREETING: s_greeting (msg, len); break; case S_USER: s_user (msg, len); break; case S_PASS: s_pass (msg, len); break; case S_RETR: s_retr (msg, len); break; case S_DELE: s_dele (msg, len); break; case S_QUIT: s_quit (msg, len); break; case S_RSET: s_rset (msg, len); break; case S_STAT: s_stat (msg, len); break; case S_LIST: s_list (msg, len); break; case S_TOP: s_top (msg, len); break; } } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void pop_init (void) { pop = new Pop (); } void pop_free_resources (void) { if (pop) delete (pop); pop = NULL; } void pop_check_new_mail (void) { if (pop == NULL) return; pop->check_new_mail (); } void pop_fetch (void) { if (pop == NULL) return; pop->fetch (); } void pop_purge (void) { if (pop == NULL) return; pop->purge (); } void pop_fetch_purge (void) { if (pop == NULL) return; pop->fetch_purge (); } void pop_fetch_all (void) { if (pop == NULL) return; pop->fetch_all (); } void pop_purge_all (void) { if (pop == NULL) return; pop->purge_all (); } void pop_fetch_purge_all (void) { if (pop == NULL) return; pop->fetch_purge_all (); } void pop_open_interactive (void) { if (pop == NULL) return; pop->open_interactive (); } void pop_fetch_interactive (int num) { if (pop == NULL) return; pop->fetch_interactive (num); } void pop_delete_interactive (int num) { if (pop == NULL) return; pop->delete_interactive (num); } void pop_reset_interactive (void) { if (pop == NULL) return; pop->reset_interactive (); } void pop_close_interactive (void) { if (pop == NULL) return; pop->close_interactive (); } void pop_delete_all_interactive (void) { if (pop == NULL) return; pop->delete_all_interactive (); } void pop_fetch_all_interactive (void) { if (pop == NULL) return; pop->fetch_all_interactive (); } mail_t * pop_mail_info (int num) { if (pop == NULL) return NULL; return pop->mail_info (num); } int pop_mail_count (void) { if (pop == NULL) return -1; return pop->mail_count (); } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ Pop::Pop (void) : Net () { state = POP_DISCONNECTED; jobs = NULL; last = NULL; ask = NULL; progress = -1; apop_digest = NULL; error_message = NULL; send_buf = str_create (); maildrop_count = 0; maildrop_size = -1; mail_sizes = NULL; marray = NULL; } Pop::~Pop (void) { cleanup (); if (send_buf) str_destroy (send_buf); } void Pop::disconnect (void) { } void Pop::check_new_mail (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select_default ("pop_acc"); if (ask == NULL){ error_ (0, _("No POP3 account has been defined.")); return; } state = POP_PROCESSING; mode = CHECK_NEW; found_new_mail = false; connect (); } void Pop::fetch (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select ("pop_acc"); if (ask == NULL) return; state = POP_PROCESSING; mode = FETCH; found_new_mail = false; connect (); } void Pop::purge (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select ("pop_acc"); if (ask == NULL) return; state = POP_PROCESSING; mode = DELETE; connect (); } void Pop::fetch_purge (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select ("pop_acc"); if (ask == NULL) return; state = POP_PROCESSING; mode = FETCH_DEL; found_new_mail = false; connect (); } void Pop::fetch_all (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select_default ("pop_acc"); if (ask == NULL){ error_ (0, _("No POP3 account has been defined.")); return; } state = POP_PROCESSING; mode = FETCH_ALL; found_new_mail = false; connect (); } void Pop::purge_all (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select_default ("pop_acc"); if (ask == NULL){ error_ (0, _("No POP3 account has been defined.")); return; } state = POP_PROCESSING; mode = DELETE_ALL; connect (); } void Pop::fetch_purge_all (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select_default ("pop_acc"); if (ask == NULL){ error_ (0, _("No POP3 account has been defined.")); return; } state = POP_PROCESSING; mode = FETCH_DEL_ALL; found_new_mail = false; connect (); } void Pop::open_interactive (void) { if (state != POP_DISCONNECTED){ error_ (0, _("Connection in progress.")); return; } ask = ask_select ("pop_acc"); if (ask == NULL) return; state = POP_PROCESSING; mode = INTERACTIVE; add_job (C_GET_HEADERS); connect (); } void Pop::fetch_interactive (int num) { if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return; } add_job (C_FETCH, num); dispatch_job (); } void Pop::delete_interactive (int num) { if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return; } add_job (C_DELETE, num); dispatch_job (); } void Pop::reset_interactive (void) { if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return; } add_job (C_RESET); dispatch_job (); } void Pop::close_interactive (void) { if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return; } add_job (C_DISCONNECT); dispatch_job (); } void Pop::delete_all_interactive (void) { int i; if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return; } for (i = 0; i < maildrop_count; i++){ add_job (C_DELETE, i + 1); } dispatch_job (); } void Pop::fetch_all_interactive (void) { int i; if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return; } for (i = 0; i < maildrop_count; i++){ add_job (C_FETCH, i + 1); } dispatch_job (); } mail_t * Pop::mail_info (int num) { if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return NULL; } return mail_array_get (marray, num); } int Pop::mail_count (void) { if (state == POP_DISCONNECTED || mode != INTERACTIVE){ error_ (0, _("Connect first.")); return -1; } return maildrop_count; } /**************************************************************************** * * END MODULE pop.c * ****************************************************************************/