/* 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. ---------------------------------------------------------------------- This module implements RFC1725 pop3 client side protocol. */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #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 "md5.h" #include "misc.h" #include "debug.h" #include "clock.h" #include "ask.h" #include "file.h" #include "hash.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ enum state { POP_DISCONNECTED, POP_CONNECTED, POP_USER, POP_PASS, POP_STAT, POP_TRANSACTION, POP_LIST, POP_UIDL, POP_RETR, POP_TOP, POP_DELE, POP_RSET, POP_QUIT, }; /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ struct mailinfo { int num; int size; char *uidl; int fetched; int deleted; mail_t *mail; }; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ static struct { /* State is used to determine set of possible actions. */ enum state state; /* Error message sent by server in case of an error. It is then used as a part of the error message displayed to the user. */ str_t *error_message; str_t *send_buf; /* data sent to server */ int nd; /* networking descriptor */ int progress; /* progress descriptor */ /* The digest sent by server in an initial response. Used to encypher the password. 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. */ str_t *apop_digest; str_t *user_str; /* username */ str_t *pass_str; /* password */ enum auth_method method; int maildrop_count; /* messages count */ int maildrop_size; /* messages total size */ FILE *fp; /* used to write the message */ /* These arrays are used to fetch info about the messages available at the server, and already fetched to the local mailbox. Array net_info is built from the data received from the POP3 server (with LIST, and UIDL commands). Array local_info is based upon the data stored in pop_info_dir for each server. These arrays are compared and merged after establishing the connection to determine which messages have been already fetched. */ struct mailinfo *net_info; struct mailinfo *local_info; int local_size; /* Index of the message retrieved / deleted from the server. Used to report error message, and to update net_info array. */ int num; void (*ok_fun)(void); void (*fail_fun)(void); } conn; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ static void request_next_info (void); /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static int net_array_index (int num) { int i; if (conn.net_info == NULL) return -1; for (i = 0; i < conn.maildrop_count; i++){ if (conn.net_info[i].num == num) return i; } return -1; } static int net_array_num (int index) { int i; if (conn.maildrop_count < 1 || conn.net_info == NULL) return -1; for (i = 0; i < conn.maildrop_count; i++){ if (conn.net_info[i].mail && index == 0) return i; else if (conn.net_info[i].mail) index--; } return -1; } static int unfilled_count (void) { int i; int count = 0; if (conn.net_info == NULL) return -1; for (i = 0; i < conn.maildrop_count; i++){ if (! conn.net_info[i].fetched && ! conn.net_info[i].deleted && conn.net_info[i].mail == NULL) count++; } return count; } static int filled_count (void) { int i; int count = 0; if (conn.net_info == NULL) return 0; for (i = 0; i < conn.maildrop_count; i++){ if (conn.net_info[i].mail) count++; } return count; } static int first_unfilled (void) { int i; if (conn.net_info == NULL) return -1; for (i = 0; i < conn.maildrop_count; i++){ if (! conn.net_info[i].fetched && ! conn.net_info[i].deleted && conn.net_info[i].mail == NULL) return i; } return -1; } static int parse_answer (char *msg) { char *end; if (msg == NULL) return 1; end = strchr (msg, '\r'); if (end){ *end = '\0'; } if (strstr (msg, "+OK")){ *end = '\r'; return 0; } if (conn.error_message) str_destroy (conn.error_message); conn.error_message = NULL; if (strstr (msg, "-ERR")){ conn.error_message = str_dup (msg + 5); return 1; } for (end = msg; isprint (*end); end++) ; *end = '\0'; error_ (0, _("unusual server response: \"%s\""), msg); return 1; } static void report_error (const char *str) { if (conn.error_message){ error_ (0, "%s (%s)", str, conn.error_message->str); } else { error_ (0, "%s", str); } } static void stat_action (char *msg, int len) { char *seek; char *rest; if (conn.state != POP_STAT){ net_close (conn.nd); debug_msg (DEBUG_ERROR, "invalid state in stat_action"); return; } if (parse_answer (msg)){ report_error (_("couldn't aquire maildrop size")); net_close (conn.nd); return; } seek = strchr (msg, ' '); if (! seek){ report_error (_("invalid answer from server")); net_close (conn.nd); return; } conn.maildrop_count = strtol (seek + 1, & rest, 10); if (*rest != ' '){ report_error (_("invalid answer from server")); net_close (conn.nd); return; } conn.maildrop_size = atoi (rest + 1); if (conn.apop_digest) str_destroy (conn.apop_digest); if (conn.user_str) str_destroy (conn.user_str); if (conn.pass_str) str_destroy (conn.pass_str); if (conn.progress != -1) progress_close (conn.progress); conn.user_str = NULL; conn.pass_str = NULL; conn.apop_digest = NULL; conn.progress = -1; conn.state = POP_TRANSACTION; if (conn.ok_fun) conn.ok_fun (); } static void pass_action (char *msg, int len) { if (conn.state != POP_PASS){ net_close (conn.nd); debug_msg (DEBUG_ERROR, "invalid state in pass_action"); return; } if (parse_answer (msg)){ report_error (_("authorization failed")); net_close (conn.nd); return; } str_clear (conn.send_buf); str_put_string_len (conn.send_buf, "STAT\r\n", 6); conn.state = POP_STAT; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", stat_action); } static void user_action (char *msg, int len) { if (conn.state != POP_USER){ debug_msg (DEBUG_ERROR, "invalid state in user_action"); net_close (conn.nd); return; } if (parse_answer (msg)){ report_error (_("server rejected username")); net_close (conn.nd); return; } str_clear (conn.send_buf); str_sprintf (conn.send_buf, "PASS %s\r\n", conn.pass_str->str); conn.state = POP_PASS; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", pass_action); } static void authorize_apop (void) { int i; char digest[16]; MD5_CTX ctx; if (conn.apop_digest == NULL){ error_ (0, "%s", _("server doesn't support " "APOP authentication")); net_close (conn.nd); return; } str_clear (conn.send_buf); MD5Init (&ctx); MD5Update (&ctx, conn.apop_digest->str, conn.apop_digest->len); MD5Update (&ctx, conn.pass_str->str, conn.pass_str->len); MD5Final (digest, &ctx); str_put_string_len (conn.send_buf, "APOP ", 5); str_put_string_len (conn.send_buf, conn.user_str->str, conn.user_str->len); str_put_char (conn.send_buf, ' '); for (i = 0; i < 16; i++){ str_sprintf (conn.send_buf, "%02x", (unsigned char) digest[i]); } str_put_string_len (conn.send_buf, "\r\n", 2); conn.state = POP_PASS; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", pass_action); } static void authorize_plain (void) { str_clear (conn.send_buf); str_sprintf (conn.send_buf, "USER %s\r\n", conn.user_str->str); conn.state = POP_USER; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", user_action); } static void connected_action (char *msg, int nothing) { int len; regmatch_t matches[1]; if (conn.state != POP_CONNECTED){ debug_msg (DEBUG_ERROR, "invalid state in connected action"); net_close (conn.nd); return; } if (parse_answer (msg)){ report_error (_("server rejected connection")); net_close (conn.nd); return; } if (misc_regex ("<.+@.+>", msg, matches)){ if (conn.apop_digest) str_destroy (conn.apop_digest); len = matches[0].rm_eo - matches[0].rm_so; conn.apop_digest = str_create_size (len); str_put_string_len (conn.apop_digest, msg + matches[0].rm_so, len); } conn.progress = progress_setup (1, "%s", _("logging in...")); switch (conn.method){ case AUTH_PLAIN: authorize_plain (); break; case AUTH_APOP: authorize_apop (); break; } } static void list_action (char *msg, int len) { int i; char *seek; char *rest; if (conn.state != POP_LIST){ debug_msg (DEBUG_ERROR, "invalid state in list_action"); return; } conn.state = POP_TRANSACTION; if (parse_answer (msg)){ report_error (_("couldn't retrieve list")); if (conn.fail_fun) conn.fail_fun (); return; } if (conn.maildrop_count < 1){ if (conn.ok_fun) conn.ok_fun (); return; } conn.net_info = xcalloc (conn.maildrop_count, sizeof (struct mailinfo)); seek = strchr (msg, '\r'); if (seek == NULL) seek = msg; else seek += 2; for (i = 0; i < conn.maildrop_count; i++){ conn.net_info[i].num = strtol (seek, & rest, 10); conn.net_info[i].size = strtol (rest, & seek, 10); } if (conn.ok_fun) conn.ok_fun (); } static void uidl_action (char *msg, int len) { char *rest; char *seek; int i; int num; int index; if (conn.state != POP_UIDL){ debug_msg (DEBUG_ERROR, "invalid state in uidl_action"); return; } conn.state = POP_TRANSACTION; if (parse_answer (msg)){ report_error (_("couldn't get message identifiers")); if (conn.fail_fun) conn.fail_fun (); return; } if (conn.maildrop_count < 1){ if (conn.ok_fun) conn.ok_fun (); return; } if (conn.net_info == NULL){ if (conn.fail_fun) conn.fail_fun (); return; } seek = strchr (msg, '\r'); if (seek == NULL) seek = msg; else seek += 2; for (i = 0; i < conn.maildrop_count; i++){ num = strtol (seek, & rest, 10); index = net_array_index (num); if (index == -1){ debug_msg (DEBUG_WARN, "uidl for message num %d " "which was not found", num); break; } seek = strchr (rest, '\r'); if (seek == NULL) break; *seek = '\0'; while (isspace (*rest)) rest++; conn.net_info[index].uidl = xstrdup (rest); *seek = '\r'; seek += 2; } if (conn.ok_fun) conn.ok_fun (); } static void quit_action (char *msg, int len) { if (conn.state != POP_QUIT){ debug_msg (DEBUG_ERROR, "invalid state in quit_action"); net_close (conn.nd); return; } if (parse_answer (msg)){ report_error (_("does your server love you so much?")); net_close (conn.nd); return; } conn.state = POP_DISCONNECTED; net_close (conn.nd); if (conn.ok_fun) conn.ok_fun (); } static void rset_action (char *msg, int len) { int i; if (conn.state != POP_RSET){ debug_msg (DEBUG_ERROR, "invalid state in rset_action"); return; } conn.state = POP_TRANSACTION; if (parse_answer (msg)){ report_error (_("couldn't reset mailbox status")); if (conn.fail_fun) conn.fail_fun (); return; } if (conn.net_info){ for (i = 0; i < conn.maildrop_count; i++){ conn.net_info[i].deleted = 0; } } if (conn.ok_fun) conn.ok_fun (); } static void dele_action (char *msg, int len) { int index; if (conn.state != POP_DELE){ debug_msg (DEBUG_ERROR, "invalid state in dele_action"); return; } conn.state = POP_TRANSACTION; if (parse_answer (msg)){ report_error (_("couldn't delete a message")); if (conn.fail_fun) conn.fail_fun (); return; } index = net_array_index (conn.num); if (index != -1) conn.net_info[index].deleted = 1; if (conn.ok_fun) conn.ok_fun (); } static void retr_action (char *msg, int len) { int index; char *seek; if (conn.state != POP_RETR){ debug_msg (DEBUG_ERROR, "invalid state in retr_action"); return; } conn.state = POP_TRANSACTION; if (parse_answer (msg)){ report_error (_("couldn't retrieve message")); if (conn.fail_fun) conn.fail_fun (); return; } seek = strchr (msg, '\r'); if (seek == NULL) seek = msg; else seek++; while (*seek){ seek++; if (*seek == '.'){ if (seek - msg == len - 3) break; if (seek[-1] == '\n' && seek[-2] == '\r') continue; } else if (*seek == '\r'){ continue; } if (fputc (*seek, conn.fp) == EOF){ error_ (errno, "%s", _("writing to the stream")); if (conn.fail_fun) conn.fail_fun (); return; } } conn.fp = NULL; index = net_array_index (conn.num); if (index != -1) conn.net_info[index].fetched = 1; if (conn.ok_fun) conn.ok_fun (); } static void top_action (char *msg, int len) { int index; char *seek; if (conn.state != POP_TOP){ debug_msg (DEBUG_ERROR, "invalid state in top_action"); return; } conn.state = POP_TRANSACTION; if (parse_answer (msg)){ report_error (_("couldn't retrieve message header")); if (conn.fail_fun) conn.fail_fun (); return; } index = net_array_index (conn.num); if (index == -1){ debug_msg (DEBUG_WARN, "no place to write TOP result for %d", conn.num); return; } seek = strchr (msg, '\r'); if (seek == NULL) seek = msg; else seek += 2; if (mlex_scan_buffer (seek) != NEXT_MAIL){ error_ (0, _("error parsing message header (%d)"), conn.num); if (conn.fail_fun) conn.fail_fun (); return; } conn.net_info[index].mail = xmalloc (sizeof (mail_t)); memcpy (conn.net_info[index].mail, newmail, sizeof (mail_t)); conn.net_info[index].mail->place.num = conn.num; conn.net_info[index].mail->type = BOX_POP3; request_next_info (); } static void request_next_info (void) { int index = first_unfilled (); if (index == -1){ if (conn.progress != -1) progress_close (conn.progress); conn.progress = -1; conn.state = POP_TRANSACTION; if (conn.ok_fun) conn.ok_fun (); return; } conn.num = conn.net_info[index].num; conn.state = POP_TOP; str_clear (conn.send_buf); str_sprintf (conn.send_buf, "TOP %d 0\r\n", conn.num); if (conn.progress != -1) progress_advance (conn.progress, 1); net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "(\r\n.\r\n$)|(-ERR.*\r\n$)", top_action); } static void destroy_arrays (void) { int i; if (conn.net_info){ for (i = 0; i < conn.maildrop_count; i++){ if (conn.net_info[i].uidl) xfree (conn.net_info[i].uidl); if (conn.net_info[i].mail){ mail_destroy (conn.net_info[i].mail, BOX_POP3); xfree (conn.net_info[i].mail); } } xfree (conn.net_info); } conn.net_info = NULL; if (conn.local_info){ for (i = 0; i < conn.local_size; i++){ if (conn.local_info[i].uidl) xfree (conn.local_info[i].uidl); } xfree (conn.local_info); } conn.local_info = NULL; conn.local_size = 0; } static void cleanup (int n) { if (conn.fail_fun) conn.fail_fun (); if (conn.error_message) str_destroy (conn.error_message); if (conn.send_buf) str_destroy (conn.send_buf); if (conn.apop_digest) str_destroy (conn.apop_digest); if (conn.user_str) str_destroy (conn.user_str); if (conn.pass_str) str_destroy (conn.pass_str); if (conn.progress != -1 && n != -1) progress_close (conn.progress); destroy_arrays (); conn.state = POP_DISCONNECTED; conn.error_message = NULL; conn.send_buf = NULL; conn.nd = -1; conn.progress = -1; conn.apop_digest = NULL; conn.user_str = NULL; conn.pass_str = NULL; conn.method = AUTH_PLAIN; conn.maildrop_count = -1; conn.maildrop_size = -1; conn.fp = NULL; conn.local_size = 0; conn.local_info = NULL; conn.net_info = NULL; conn.num = -1; conn.ok_fun = NULL; conn.fail_fun = NULL; } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void pop_init (void) { cleanup (-1); } void pop_free_resources (void) { cleanup (0); } int pop_open (const char *hostname, unsigned short port, const char *user, const char *pass, enum auth_method method, int secure, void (*succ)(void), void (*fail)(void)) { port = (secure) ? ((port) ? port : 995) : ((port) ? port : 110); if (conn.state != POP_DISCONNECTED || conn.nd != -1){ error_ (0, "%s", _("connection in progress")); return 1; } if (hostname == NULL || user == NULL || pass == NULL){ error_ (0, "%s", _("invalid arguments")); return 1; } conn.nd = net_open (hostname, port, secure, cleanup); if (conn.nd == -1) return 1; conn.progress = -1; conn.fp = NULL; conn.ok_fun = succ; conn.fail_fun = fail; conn.send_buf = str_create (); conn.user_str = str_dup (user); conn.pass_str = str_dup (pass); conn.method = method; conn.state = POP_CONNECTED; net_recv_data (conn.nd, "\r\n$", connected_action); return 0; } void pop_close (void (*succ)(void), void (*fail)(void)) { if (conn.state != POP_DISCONNECTED && conn.nd != -1){ conn.ok_fun = succ; conn.fail_fun = fail; str_clear (conn.send_buf); str_put_string_len (conn.send_buf, "QUIT\r\n", 6); conn.state = POP_QUIT; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", quit_action); } else { cleanup (0); } } int pop_maildrop_size (void) { if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_maildrop_size"); return -1; } return conn.maildrop_size; } int pop_maildrop_count (void) { if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_maildrop_count"); return -1; } return conn.maildrop_count; } void pop_list (void (*succ)(void), void (*fail)(void)) { if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_list"); return; } str_clear (conn.send_buf); str_put_string_len (conn.send_buf, "LIST\r\n", 6); conn.ok_fun = succ; conn.fail_fun = fail; conn.state = POP_LIST; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "(\r\n.\r\n$)|(-ERR.*\r\n$)", list_action); } void pop_uidl (void (*succ)(void), void (*fail)(void)) { if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_uidl"); return; } str_clear (conn.send_buf); str_put_string_len (conn.send_buf, "UIDL\r\n", 6); conn.ok_fun = succ; conn.fail_fun = fail; conn.state = POP_UIDL; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "(\r\n.\r\n$)|(-ERR.*\r\n$)", uidl_action); } void pop_retr (int num, FILE *fp, void (*succ)(void), void (*fail)(void)) { int index; if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_retr"); return; } index = net_array_index (num); if (index != -1 && conn.net_info[index].fetched){ fail (); return; } str_clear (conn.send_buf); str_sprintf (conn.send_buf, "RETR %d\r\n", num); conn.ok_fun = succ; conn.fail_fun = fail; conn.fp = fp; conn.state = POP_RETR; conn.num = num; if (index != -1) net_expect (conn.nd, conn.net_info[index].size, _("fetching message %d"), num); net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "(\r\n.\r\n$)|(-ERR.*\r\n$)", retr_action); } void pop_dele (int num, void (*succ)(void), void (*fail)(void)) { int index; if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_dele"); return; } index = net_array_index (num); if (index != -1 && conn.net_info[index].deleted){ succ (); return; } str_clear (conn.send_buf); str_sprintf (conn.send_buf, "DELE %d\r\n", num); conn.ok_fun = succ; conn.fail_fun = fail; conn.num = num; conn.state = POP_DELE; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", dele_action); } void pop_rset (void (*succ)(void), void (*fail)(void)) { if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_rset"); return; } str_clear (conn.send_buf); str_put_string_len (conn.send_buf, "RSET\r\n", 6); conn.ok_fun = succ; conn.fail_fun = fail; conn.state = POP_RSET; net_combo (conn.nd, conn.send_buf->str, conn.send_buf->len, "\r\n$", rset_action); } void pop_get_infos (void (*succ)(void), void (*fail)(void)) { if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_get_infos"); return; } if (conn.maildrop_count < 1 || conn.net_info == NULL){ if (succ) succ (); return; } if (conn.progress != -1) progress_close (conn.progress); conn.ok_fun = succ; conn.fail_fun = fail; conn.state = POP_TOP; conn.progress = progress_setup (unfilled_count (), "%s", _("fetching message headers...")); request_next_info (); } void pop_save_list (void) { int i; int count = 0; FILE *fp; char *fname; char *dir; memchunk_t *chunk; if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_save_list"); return; } if (conn.maildrop_count < 1 || conn.net_info == NULL) return; dir = ask_for_default ("pop_info_dir", NULL); if (dir == NULL){ error_ (0, "%s", _("pop3 info dir not defined")); return; } fname = file_with_dir (dir, net_server_address (conn.nd)); fp = fopen (fname, "w"); if (fp == NULL){ error_ (errno, _("opening %s"), fname); xfree (fname); return; } chunk = memchunk_create_size (1000); for (i = 0; i < conn.maildrop_count; i++){ if (conn.net_info[i].fetched && ! conn.net_info[i].deleted) count++; } memchunk_intdump (chunk, count); for (i = 0; i < conn.maildrop_count; i++){ if (conn.net_info[i].fetched && ! conn.net_info[i].deleted){ memchunk_intdump (chunk, conn.net_info[i].num); memchunk_strdump (chunk, conn.net_info[i].uidl); } } memchunk_dump (chunk, fp); fclose (fp); xfree (fname); memchunk_destroy (chunk); } void pop_load_list (void) { int i; FILE *fp; char *fname; char *dir; memchunk_t *chunk; if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_load_list"); return; } if (conn.maildrop_count < 1 || conn.net_info == NULL) return; dir = ask_for_default ("pop_info_dir", NULL); if (dir == NULL){ error_ (0, "pop3 info dir not defined"); return; } fname = file_with_dir (dir, net_server_address (conn.nd)); fp = fopen (fname, "r"); if (fp == NULL){ error_ (errno, _("opening %s"), fname); xfree (fname); return; } chunk = memchunk_create_size (1000); if (memchunk_read (chunk, fp)){ fclose (fp); xfree (fname); memchunk_destroy (chunk); return; } conn.local_size = memchunk_intget (chunk); if (conn.local_size < 0){ debug_msg (DEBUG_ERROR, "negative size of array in pop_load_list"); fclose (fp); xfree (fname); memchunk_destroy (chunk); return; } conn.local_info = xcalloc (conn.local_size, sizeof (struct mailinfo)); for (i = 0; i < conn.local_size; i++){ conn.local_info[i].num = memchunk_intget (chunk); conn.local_info[i].uidl = memchunk_strget (chunk); conn.local_info[i].fetched = 1; } fclose (fp); xfree (fname); memchunk_destroy (chunk); } void pop_merge_lists (void) { int i; htable_t *table; if (conn.state != POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_merge_lists"); return; } if (conn.maildrop_count < 1 || conn.net_info == NULL || conn.local_info == NULL) return; table = htable_create (misc_logarithm (conn.local_size)); for (i = 0; i < conn.local_size; i++){ htable_insert (table, conn.local_info[i].uidl, NULL); } for (i = 0; i < conn.maildrop_count; i++){ if (htable_lookup (table, conn.net_info[i].uidl)) conn.net_info[i].fetched = 1; } htable_destroy (table, NULL); } int pop_mail_size (int num) { int index; if (conn.state < POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_mail_size"); return 0; } if (conn.maildrop_count < 1 || conn.net_info == NULL) return 0; index = net_array_index (num); return conn.net_info[index].size; } int pop_header_count (void) { if (conn.state < POP_TRANSACTION){ debug_msg (DEBUG_WARN, "invalid state in pop_header_count"); return 0; } if (conn.maildrop_count < 1 || conn.net_info == NULL) return 0; return filled_count (); } mail_t * pop_header_info (int index) { int i; if (conn.state < POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_header_info"); return NULL; } i = net_array_num (index); if (i == -1) return NULL; return conn.net_info[i].mail; } int pop_num (int index) { int i; if (conn.state < POP_TRANSACTION){ debug_msg (DEBUG_ERROR, "invalid state in pop_num"); return -1; } i = net_array_num (index); if (i == -1) return -1; return conn.net_info[i].num; } void pop_mark_mail (int index) { int i; if (conn.state < POP_TRANSACTION){ return; } i = net_array_num (index); if (i == -1) return; conn.net_info[i].fetched = 1; } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE pop.c * ****************************************************************************/