/*
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.h>
#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
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1