static char rcsid[] = "@(#)$Id: imap.c,v 1.12 2006/07/01 07:37:48 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.12 $ $State: Exp $
*
* Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
* or Kari Hurtta <elm@elmme-mailer.org>
*****************************************************************************/
#include "def_mbox.h"
#include "ss_imp.h"
#include "s_me.h"
#include "s_elm.h"
DEBUG_VAR(Debug,__FILE__,"imap");
#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif
#ifdef REMOTE_MBX
/* Seems that h_errno is macro on AIX */
#ifndef h_errno
extern int h_errno;
#endif
static char *us2s P_((unsigned char *str));
static char *us2s(str)
unsigned char *str;
{
return (char *)str;
}
static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str)
char *str;
{
return (unsigned char *)str;
}
static CONST unsigned char *cs2us P_((const char *str));
static CONST unsigned char *cs2us(str)
CONST char *str;
{
return (CONST unsigned char *)str;
}
#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif
/* IMAP connection -------------------------------------------------------- */
struct IMAP_CON {
long capability_bits;
#ifdef USE_DLOPEN
struct IMAP_capa_libs *capability_libs;
int capability_lib_count;
#endif
struct Read_Buffer read_buffer;
int wanna_literal;
long literal_len;
struct Imap_Token_Buffer read_tokens;
enum token_state { state_tag, state_response,
state_text, state_text_code,
state_list } tokenizer_state;
char * current_tag ; /* TAG of command waiting response */
imap_states imap_state;
char * command_result_code;
struct Imap_Token_Buffer write_tokens;
int token_index; /* -1 == tag, -2 == NOOP_tag */
struct Write_Buffer write_buffer;
enum { no_literal,
waiting_literal_continue,
continue_literal } literal_mode;
char * current_NOOP_tag ; /* TAG for NOOP command .... */
};
static void cache_zero_imap P_((struct connection_cache *c));
static void cache_free_imap P_((struct connection_cache *c));
static int cache_open_imap P_((struct connection_cache *c));
static int cache_login_imap P_((struct connection_cache *c,
const struct string *password /* May be NULL */));
static int cache_close_imap P_((struct connection_cache *c));
static void cache_folder_from_imap P_((struct connection_cache *c,
struct folder_info *f));
static void cache_browser_from_imap P_((struct connection_cache *c,
struct folder_browser *d));
struct connection_type IMAP_connection = {
CONNECTION_TYPE_magic,
"IMAP",
cache_zero_imap,
cache_free_imap,
cache_open_imap,
cache_login_imap,
cache_close_imap,
cache_folder_from_imap,
cache_browser_from_imap
};
void set_imap_connection_state(Ch,st)
struct connection_cache *Ch;
imap_states st;
{
Ch->a.imap_con->imap_state = st;
}
/* ------------------------------------------------------------------------ */
static void add_token P_((struct Imap_Token_Buffer *X, struct imap_token T));
static void add_token(X,T)
struct Imap_Token_Buffer *X;
struct imap_token T;
{
X->tokens = safe_realloc(X->tokens,
(X->token_count+1) * sizeof (struct imap_token));
X->tokens[X->token_count++] = T;
}
static void free_tokens P_((struct Imap_Token_Buffer *X));
static void free_tokens(X)
struct Imap_Token_Buffer *X;
{
if (X -> tokens) {
int i;
for (i = 0; i < X -> token_count; i++) {
if (X -> tokens[i].str) {
free (X -> tokens[i].str);
X -> tokens[i].str = NULL;
}
X -> tokens[i].imap_token = imap_zero;
}
free(X -> tokens);
X -> tokens = NULL;
X -> token_count = 0;
}
}
static struct kwd {
char * kw;
enum token_type t;
} keywords[] = {
{ "OK", imap_status_keyword },
{ "NO", imap_status_keyword },
{ "BAD", imap_status_keyword },
{ "PREAUTH", imap_status_keyword },
{ "BYE", imap_status_keyword }
};
#define CAPA_IMAP4rev1 1
static struct capab {
char * capa;
long mask;
} capabilities[] = {
{ "IMAP4rev1", CAPA_IMAP4rev1 }
};
#define IMAP_Seen 1
#define IMAP_Answered 2
#define IMAP_Flagged 4
#define IMAP_Deleted 8
#define IMAP_Draft 16
#define IMAP_Recent 32
static struct imap_flag {
char * flag;
int mask_elm;
int mask_elm1;
long mask_imap;
} IMAP_flags[] = {
{ "\\Seen", 0, 0, IMAP_Seen },
{ "\\Answered", REPLIED, 0, IMAP_Answered },
{ "\\Flagged", 0, S1_FLAGGED, IMAP_Flagged },
{ "\\Deleted", DELETED, 0, IMAP_Deleted },
{ "\\Draft", 0, 0, IMAP_Draft },
{ "\\Recent", NEW, 0, IMAP_Recent },
} ;
static struct browser_flag {
char * flag;
int browser_flag;
} BROWSER_flags[] = {
{ "\\Marked", BROWSER_MARKED },
{ "\\Noinferiors", BROWSER_NODIR },
{ "\\Noselect", BROWSER_NOFOLDER },
{ "\\Unmarked", 0 },
} ;
static void add_to_write_buffer P_((struct IMAP_CON * M,
char **str));
static void add_to_write_buffer(M,str)
struct IMAP_CON * M;
char **str;
{
int l = strlen(*str);
add_to_Write_Buffer(& (M->write_buffer), str, l);
}
static int imap_gen_writable_data P_((struct IMAP_CON * M));
static int imap_gen_writable_data(M)
struct IMAP_CON * M;
{
char * t;
if (M->write_buffer.write_len > 0) {
/* Have incomplete data ... */
return 1;
}
free_Write_Buffer(& (M->write_buffer));
if (M->imap_state != IMAP_command)
return 0; /* No data available */
if (M->token_index >= M->write_tokens.token_count)
return 0; /* No data available */
switch(M->token_index) {
case -2 : /* NOOP command */
M->write_buffer.write_buffer = elm_message (FRM("%s NOOP\r\n"),
M->current_NOOP_tag);
M->write_buffer.write_len = strlen(M->write_buffer.write_buffer);
return 1;
case -1: /* Normal command */
M->write_buffer.write_buffer = elm_message(FRM("%s "),
M->current_tag);
M->write_buffer.write_len = strlen(M->write_buffer.write_buffer);
M->literal_mode = no_literal;
M->token_index++;
}
while (M->token_index < M->write_tokens.token_count) {
char *s1 = NULL;
char * space = "";
if (M->token_index > 0 &&
M->write_tokens.tokens[M->token_index-1].imap_token !=
imap_list_begin)
space = " ";
switch(M->write_tokens.tokens[M->token_index].imap_token) {
case imap_literal:
switch(M->literal_mode) {
char *s;
case continue_literal:
if (M->write_buffer.write_buffer != NULL)
panic("MBX PANIC",__FILE__,__LINE__,
"imap_gen_writable_data",
"write_buffer not NULL",0);
M->write_buffer.write_buffer =
M->write_tokens.tokens[M->token_index].str;
M->write_buffer.write_len =
M->write_tokens.tokens[M->token_index].len_or_value;
/* So that it is not free()ed twice */
M->write_tokens.tokens[M->token_index].str = NULL;
M->literal_mode = no_literal;
M->token_index++;
continue; /* Write literal */
case waiting_literal_continue:
DPRINT(Debug,7,(&Debug,
"imap_gen_writable_data=0: Waiting literal continue\n"));
return 0;
case no_literal:
s = elm_message(FRM("%s{%d}\r\n"),
space,
M->write_tokens.tokens[M->token_index].
len_or_value);
add_to_write_buffer(M,&s); /* May free() s */
M->literal_mode = waiting_literal_continue;
return 1;
}
panic("MBX PANIC",__FILE__,__LINE__,
"imap_gen_writable_data",
"Bad literal mode",0);
case imap_number:
s1 = elm_message(FRM("%s%d"),space,
M->write_tokens.tokens[M->token_index].
len_or_value);
M->token_index++;
break;
case imap_list_begin:
s1 = elm_message(FRM("%s("),space);
M->token_index++;
break;
case imap_list_end:
s1 = elm_message(FRM(")"));
M->token_index++;
break;
case imap_atom:
s1 = elm_message(FRM("%s%s"),space,
M->write_tokens.tokens[M->token_index].str);
M->token_index++;
break;
case imap_string:
/* NOTE:
*
* 1) We must NOT convert date-time to literal:
* 16-Sep-2000 22:52:00 +0300
*
* 2) Also it is no effective to convert % to literal
*
*/
if (strspn(M->write_tokens.tokens[M->token_index].str,
" 1234567890%:+-.!*,()[]#_<>qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM") <
M->write_tokens.tokens[M->token_index].len_or_value) {
DPRINT(Debug,50,(&Debug,
"imap_gen_writable_data: %d string \"%s\" converting to literal...\n",
M->token_index,
M->write_tokens.tokens [M->token_index].str));
M->write_tokens.tokens[M->token_index].imap_token =
imap_literal;
break; /* RETRY */
}
s1 = elm_message(FRM("%s\"%s\""),space,
M->write_tokens.tokens[M->token_index].str);
M->token_index++;
break;
default:
panic("MBX PANIC",__FILE__,__LINE__,
"imap_gen_writable_data",
"Bad token type",0);
}
if (s1)
add_to_write_buffer(M,&s1); /* May free() s1 */
}
/* Write end of command (CRLF) */
t = safe_strdup("\r\n");
add_to_write_buffer(M,&t); /* May free() t */
return 1; /* have writable data */
}
/* -1 == failure
0 == continue (read more data)
1 == ready
*/
static int tokenize P_((struct IMAP_CON * M));
static int tokenize(M)
struct IMAP_CON * M;
{
int idx;
int len;
int ret;
if (M->wanna_literal) {
struct imap_token T;
try_literal:
if (M->tokenizer_state != state_list)
panic("MBX PANIC",__FILE__,__LINE__,"tokenize",
"Bad tokenizer_state (IMAP)",0);
if (M->read_buffer.read_len < M->literal_len) {
DPRINT(Debug,7,(&Debug,
"tokenize=0 (incomplete literal)\n"));
return 0;
}
T.imap_token = imap_literal;
T.str = safe_malloc(M->literal_len+1);
memcpy(T.str,M->read_buffer.read_buffer,M->literal_len);
T.str[M->literal_len] = '\0';
T.len_or_value = M->literal_len;
add_token(&(M->read_tokens),T);
cut_line(&(M->read_buffer),M->literal_len);
M->wanna_literal = 0;
}
idx = 0;
ret = 1;
len = find_crlf(&(M->read_buffer),1);
if (!len) {
DPRINT(Debug,7,(&Debug, "tokenize=0 (no data)\n"));
return 0;
}
while (idx < len) {
struct imap_token T;
char * p = M->read_buffer.read_buffer +idx;
int left = len - idx;
if (!*p) {
DPRINT(Debug,20,(&Debug, "tokenize: idx=%d END\n",idx));
break;
}
T.len_or_value = 0;
T.str = NULL;
T.imap_token = imap_zero;
switch (M->tokenizer_state) {
char *n;
int i;
case state_tag:
n = memchr(p,' ',left);
if (NULL == n || n == p) {
fail:
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnparseableResponse,
"Unparseable response from IMAP server"));
free_Read_Buffer(&(M->read_buffer));
DPRINT(Debug,7,(&Debug, "tokenize=-1 (failure)\n"));
return -1;
}
*n = '\0';
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d TAG '%.*s'\n",
idx,n-p,p));
M->tokenizer_state = state_response;
if (n-p == 1 && p[0] == '+') {
T.imap_token = imap_continue;
M->tokenizer_state = state_text;
} else if (n-p == 1 && p[0] == '*')
T.imap_token = imap_notag;
else {
T.imap_token = imap_atom;
T.str = safe_malloc(n-p+1);
memcpy(T.str,p,n-p+1);
T.len_or_value = n-p;
}
add_token(&(M->read_tokens),T);
idx = (n - M->read_buffer.read_buffer) +1;
break;
case state_response:
if (p[0] >= '0' && p[0] <= '9') {
char *c;
T.len_or_value = strtol(p,&c,10);
if (*c == ' ') {
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d RESPONSE (number) '%.*s'\n",
idx,c-p,p));
T.imap_token = imap_number;
add_token(&(M->read_tokens),T);
idx = (c - M->read_buffer.read_buffer) +1;
break;
}
}
n = memchr(p,' ',left);
if (n == p)
goto fail;
if (NULL == n)
n = p + left;
else
*n = '\0';
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d RESPONSE (keyword) '%.*s'\n",
idx,n-p,p));
for (i = 0; i < sizeof keywords / sizeof (struct kwd); i++)
if (0 == istrcmp(keywords[i].kw,p))
break;
if (i < sizeof keywords / sizeof (struct kwd))
T.imap_token = keywords[i].t;
else
T.imap_token = imap_other_keyword;
if (imap_status_keyword == T.imap_token)
M->tokenizer_state = state_text;
else if (imap_other_keyword == T.imap_token)
M->tokenizer_state = state_list;
T.str = safe_malloc(n-p+1);
memcpy(T.str,p,n-p+1);
T.len_or_value = n-p;
add_token(&(M->read_tokens),T);
idx = (n - M->read_buffer.read_buffer) +1;
break;
case state_text:
if ('[' == p[0]) {
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d TEXT [\n",
idx));
T.imap_token = imap_code_begin;
M->tokenizer_state = state_text_code;
idx++;
add_token(&(M->read_tokens),T);
break;
}
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d TEXT '%.*s'\n",
idx,left,p));
T.imap_token = imap_string;
T.str = safe_malloc(left);
memcpy(T.str,p,left);
T.len_or_value = left-2; /* Not find_crlf() */
add_token(&(M->read_tokens),T);
idx += left;
break;
case state_text_code:
if (']' == p[0]) {
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d CODE ]\n",
idx));
T.imap_token = imap_code_end;
M->tokenizer_state = state_text;
idx++;
if (' ' == p[1]) /* Skip separated space between code */
idx++; /* argument and textual code */
add_token(&(M->read_tokens),T);
break;
}
/* FALLTHRU */
case state_list:
if ('(' == p[0]) {
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d LIST (\n",
idx));
T.imap_token = imap_list_begin;
idx++;
add_token(&(M->read_tokens),T);
break;
}
if (')' == p[0]) {
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d LIST )\n",
idx));
T.imap_token = imap_list_end;
idx++;
if (' ' == p[1]) /* Skip separated space between list */
idx++; /* argument and next argument */
add_token(&(M->read_tokens),T);
break;
}
if (p[0] >= '0' && p[0] <= '9') {
char *c;
T.len_or_value = strtol(p,&c,10);
if (*c == ' ' || *c == ')' || *c == ']') {
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d LIST (number) '%.*s'\n",
idx,c-p,p));
T.imap_token = imap_number;
add_token(&(M->read_tokens),T);
if (*c == ' ')
idx = (c - M->read_buffer.read_buffer) +1;
else
idx = (c - M->read_buffer.read_buffer);
break;
}
}
if (M->tokenizer_state != state_text_code) {
if ('"' == p[0]) {
char * x, *y;
T.imap_token = imap_string;
T.str = safe_malloc(left);
y = T.str;
for (x = p+1; x < p + left && '\0' != *x && '"' != *x;
x++) {
if ('\\' == *x)
x++;
if (y < T.str + left -1)
*y++ = *x;
}
if (*x != '"')
goto fail;
x++;
*y = '\0';
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d LIST (string) '%.*s'\n",
idx,y-T.str,T.str));
T.len_or_value = y-T.str;
add_token(&(M->read_tokens),T);
if (' ' == *x) /* Skip separated space between string */
x++; /* argument and next argument */
idx = x - M->read_buffer.read_buffer;
break;
}
if (p[0] == '{' && left > 4 && p[left-3] == '}' &&
M->tokenizer_state == state_list) {
char *c;
M->literal_len = strtol(p+1,&c,10);
if (c == &(p[left-3])) { /* Start parsing literal */
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d LIST (literal) len=%d\n",
idx,M->literal_len));
M->wanna_literal = 1;
idx += left;
ret = 0; /* Continue reading ... */
break;
}
}
}
if (' ' == p[0]) {
/* Communigate Pro 3.3.1 produces extra space between
CAPABILITY response tokens before auth capabilities
*/
DPRINT(Debug,3,(&Debug,
"* Format error -- extra space between tokens on IMAP response:\n"));
DPRINT(Debug,3,(&Debug,
">>> %s\n",
p));
idx++;
continue;
}
T.imap_token = imap_atom;
if (M->tokenizer_state == state_text_code)
i = strcspn(p," ()]");
else
i = strcspn(p,"(){ *%\"");
if (i == 0) {
DPRINT(Debug,9,(&Debug,
"tokenize: Possible internal error:'%s'\n",
p));
goto fail;
}
DPRINT(Debug,20,(&Debug,
"tokenize: idx=%d LIST (atom) '%.*s'\n",
idx,i,p));
T.imap_token = imap_atom;
T.str = safe_malloc(i+1);
memcpy(T.str,p,i);
T.str[i] = '\0';
idx += i;
if (' ' == M->read_buffer.read_buffer[idx])
idx++;
add_token(&(M->read_tokens),T);
break;
default:
panic("MBX PANIC",__FILE__,__LINE__,"tokenize",
"Bad tokenizer_state (IMAP)",0);
}
}
if (!M->wanna_literal) {
/* Again beginning of line */
M->tokenizer_state = state_tag;
}
cut_line(&(M->read_buffer),len);
if (M->wanna_literal && M->read_buffer.read_len)
goto try_literal;
DPRINT(Debug,7,(&Debug, "tokenize=%d (token count %d)\n",
ret,M->read_tokens.token_count));
return ret; /* Response parsed? */
}
static void pick_text P_((struct IMAP_CON * M, struct string **Text,
char **code));
static void pick_text(M,Text,code)
struct IMAP_CON * M;
struct string **Text;
char **code;
{
int i;
int q = 0;
*Text = NULL;
*code = NULL;
for (i = 2; i < M->read_tokens.token_count; i++) {
if (imap_code_begin == M->read_tokens.tokens[i].imap_token)
q++;
else if (imap_code_end == M->read_tokens.tokens[i].imap_token)
q--;
else if (q && imap_atom == M->read_tokens.tokens[i].imap_token &&
!*code)
*code = M->read_tokens.tokens[i].str;
else if (!q && imap_string == M->read_tokens.tokens[i].imap_token &&
!*Text)
/* User readable code may use MIME encoded words on message */
*Text = hdr_to_string(HDR_TEXT,
M->read_tokens.tokens[i].str,
imap_charset,1);
}
}
static int scan_list P_((struct imap_token ** ptr,
int * left,
struct imap_token ** found_list,
int * list_len));
static int scan_list(ptr,left,found_list,list_len)
struct imap_token ** ptr;
int * left;
struct imap_token ** found_list;
int * list_len;
{
struct imap_token * p = *ptr;
int l = *left;
int i,q=0;
*found_list = NULL;
*list_len = 0;
if (l < 2 || imap_list_begin != p[0].imap_token)
return 0;
*found_list = p+1;
for (i = 0; i < l; i++) {
if (imap_list_begin == p[i].imap_token)
q++;
else if (imap_list_end == p[i].imap_token) {
q--;
if (0 == q) {
*list_len = i-1;
*ptr = p+ (i +1);
*left = l - (i +1);
return 1;
}
}
}
return 0;
}
static void free_reference P_((struct imap_reference *item));
static void free_reference(item)
struct imap_reference *item;
{
item->uid_number = -1;
item->imap_flags = 0;
item->rfc822_size = 0;
if (item->internaldate) {
free(item->internaldate);
item->internaldate = NULL;
}
if (item->header) {
free(item->header);
item->header = NULL;
item->header_len = 0;
}
if (item->body) {
free(item->body);
item->body = NULL;
item->body_len = 0;
}
}
static void zero_reference P_((struct imap_reference *item));
static void zero_reference(item)
struct imap_reference *item;
{
item->uid_number = -1;
item->imap_flags = 0;
item->rfc822_size = 0;
item->internaldate = NULL;
item->header_len = 0;
item->header = 0;
item->body_len = 0;
item->body = 00;
}
static int got_header P_((struct imap_reference * item,
struct imap_token * lst));
static int got_header(item,lst)
struct imap_reference * item;
struct imap_token * lst;
{
/* Originally malloced by tokenize() */
if (item->header) {
free(item->header);
item->header = NULL;
}
switch(lst->imap_token) {
case imap_atom:
if (0 == istrcmp(lst->str,"NIL")) {
item->header = NULL;
item->header_len = 0;
return 1; /* OK */
}
return 0; /* Bad */
case imap_string:
case imap_literal:
/* Move malloced data to struct imap_reference */
item->header = lst->str;
item->header_len = lst->len_or_value;
lst->str = NULL; /* So that free_tokens() does not try free
* it
*/
return 1; /* OK */
}
return 0; /* Bad */
}
static int got_body P_((struct imap_reference * item,
struct imap_token * lst));
static int got_body(item,lst)
struct imap_reference * item;
struct imap_token * lst;
{
/* Originally malloced by tokenize() */
if (item->body) {
free(item->body);
item->body = NULL;
}
switch(lst->imap_token) {
case imap_atom:
if (0 == istrcmp(lst->str,"NIL")) {
item->body = NULL;
item->body_len = 0;
return 1; /* OK */
}
return 0; /* Bad */
case imap_string:
case imap_literal:
/* Move malloced data to struct imap_reference */
item->body = lst->str;
item->body_len = lst->len_or_value;
lst->str = NULL; /* So that free_tokens() does not try free
* it
*/
return 1; /* OK */
}
return 0; /* Bad */
}
static void process_list_flags P_((struct imap_dir_entry * item,
struct imap_token * lst,
int lst_len));
static void process_list_flags(item,lst,lst_len)
struct imap_dir_entry * item;
struct imap_token * lst;
int lst_len;
{
DPRINT(Debug,9,(&Debug,
"process_list_flags: len=%d",
lst_len));
while (lst_len > 0) {
struct imap_token * kwd = lst;
lst_len--;
lst++;
if (imap_atom == kwd->imap_token) {
int j;
int found = 0;
for (j = 0;
j < sizeof BROWSER_flags /
sizeof (struct browser_flag);
j++) {
if (0 == istrcmp(kwd->str,
BROWSER_flags[j].flag)) {
item->flags |= BROWSER_flags[j].browser_flag;
found++;
break;
}
}
if (!found) {
DPRINT(Debug,9,(&Debug,
" [Unknown flag %s] ",
kwd->str));
}
DPRINT(Debug,10,(&Debug,
" [known flags:"));
for (j = 0;
j < sizeof BROWSER_flags / sizeof (struct browser_flag);
j++) {
if (BROWSER_flags[j].browser_flag &&
(item->flags & BROWSER_flags[j].browser_flag)
== BROWSER_flags[j].browser_flag)
DPRINT(Debug,10,(&Debug,
" %s",BROWSER_flags[j].flag));
}
DPRINT(Debug,9,(&Debug,
"]"));
} else {
DPRINT(Debug,9,(&Debug,
" ???\n"));
DPRINT(Debug,9,(&Debug,
"process_list_flags: List response (..) flag item have token type %d\n",
kwd->imap_token));
break; /* Quit processing */
}
}
DPRINT(Debug,9,(&Debug, "\n"));
}
static void process_fetch_items P_((struct imap_reference * item,
struct imap_token * lst,
int lst_len));
static void process_fetch_items(item,lst,lst_len)
struct imap_reference * item;
struct imap_token * lst;
int lst_len;
{
DPRINT(Debug,7,(&Debug,
"process_fetch_items: len=%d",
lst_len));
while (lst_len > 1) {
struct imap_token * kwd = lst;
lst_len--;
lst++;
if (imap_atom == kwd->imap_token) {
if (0 == istrcmp(kwd->str,"UID")) {
if (imap_number == lst->imap_token) {
item->uid_number = lst->len_or_value;
DPRINT(Debug,9,(&Debug,
" UID %d",item->uid_number));
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " UID ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after UID number expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"INTERNALDATE")) {
if (imap_string == lst->imap_token) {
DPRINT(Debug,9,(&Debug, " INTERNALDATE %s (len %d)",
lst->str,lst->len_or_value));
if (lst->len_or_value != 26) {
DPRINT(Debug,9,(&Debug, "\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after INTERNALDATE 26 char string expected, got %d chars\n",
lst->len_or_value));
} else {
if (item->internaldate)
free(item->internaldate);
item->internaldate = lst->str;
lst->str = NULL; /* Avoid double free() */
}
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " INTERNALDATE ????\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after INTERNALDATE string expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"RFC822.SIZE")) {
if (imap_number == lst->imap_token) {
item->rfc822_size = lst->len_or_value;
DPRINT(Debug,9,(&Debug, " RFC822.SIZE %d",
item->rfc822_size));
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " RFC822.SIZE ????\n"));
DPRINT(Debug,9,(&Debug,
"process_fetch_items: Fetch response (..) after RFC822.SIZE number expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"RFC822.HEADER")) {
if (got_header(item,lst)) {
DPRINT(Debug,9,(&Debug, " RFC822.HEADER ..."));
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " RFC822.HEADER ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after RFC822.HEADER nstring expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"RFC822.TEXT")) {
if (got_body(item,lst)) {
DPRINT(Debug,9,(&Debug, " RFC822.TEXT ..."));
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " RFC822.TEXT ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after RFC822.BODY nstring expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"BODY[HEADER]")) {
if (got_header(item,lst)) {
DPRINT(Debug,9,(&Debug, " BODY[HEADER] ..."));
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " BODY[HEADER] ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after BODY[HEADER] nstring expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"BODY[TEXT]")) {
if (got_body(item,lst)) {
DPRINT(Debug,9,(&Debug, " BODY[TEXT] ..."));
lst_len--;
lst++;
} else {
DPRINT(Debug,9,(&Debug, " BODY[TEXT] ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after BODY[TEXT] nstring expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else if (0 == istrcmp(kwd->str,"FLAGS")) {
struct imap_token * flg;
int flg_len;
item->imap_flags = 0;
DPRINT(Debug,9,(&Debug, " FLAGS "));
if (scan_list(&lst,&lst_len,&flg,&flg_len)) {
int i, j;
DPRINT(Debug,9,(&Debug, "(listlen=%d)",flg_len));
for (i = 0; i < flg_len; i++) {
if (imap_atom == flg[i].imap_token) {
int found = 0;
for (j = 0;
j < sizeof IMAP_flags /
sizeof (struct imap_flag);
j++) {
if (0 == istrcmp(flg[i].str,
IMAP_flags[j].flag)) {
item->imap_flags |=
IMAP_flags[j].mask_imap;
found++;
break;
}
}
if (!found) {
DPRINT(Debug,9,(&Debug,
" [Unknown flag %s] ",
flg[i].str));
}
}
}
DPRINT(Debug,10,(&Debug,
" [known flags:"));
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if ((item->imap_flags & IMAP_flags[j].mask_imap)
== IMAP_flags[j].mask_imap)
DPRINT(Debug,10,(&Debug, " %s",IMAP_flags[j].flag));
}
DPRINT(Debug,10,(&Debug, "]"));
} else {
DPRINT(Debug,9,(&Debug, " ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) after FLAGS (list) expected, got type %d\n",
lst->imap_token));
break; /* Quit processing */
}
} else {
DPRINT(Debug,9,(&Debug, " ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) list item %s unsupported\n",
kwd->str));
break; /* Quit processing */
}
} else {
DPRINT(Debug,9,(&Debug, " ???\n"));
DPRINT(Debug,1,(&Debug,
"process_fetch_items: Fetch response (..) list item have token type %d\n",
kwd->imap_token));
break; /* Quit processing */
}
}
DPRINT(Debug,7,(&Debug, "\n"));
}
static int imap_read_stream P_((struct streamsched *ss,void * data));
static void imap_clear_command P_((struct connection_cache *con));
static void imap_clear_command(con)
struct connection_cache *con;
{
struct IMAP_CON * CONST M = con->a.imap_con;
DPRINT(Debug,7,(&Debug,
"imap_clear_command: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (!M) {
DPRINT(Debug,7,(&Debug,
"imap_clear_command: NO IMAP_CON\n!"));
return;
}
if (IMAP_idle == M->imap_state) {
DPRINT(Debug,7,(&Debug,
"imap_clear_command: Already clear!\n"));
} else if (IMAP_closing == M->imap_state) {
DPRINT(Debug,7,(&Debug,
"imap_clear_command: On closing state!\n"));
} else if (IMAP_command_ready_NO != M->imap_state &&
IMAP_command_ready_BAD != M->imap_state &&
IMAP_command_ready_OK != M->imap_state) {
DPRINT(Debug,7,(&Debug,
"imap_clear_command: WRONG STATE!\n"));
M->imap_state = IMAP_error;
} else
M->imap_state = IMAP_idle;
if (M->current_NOOP_tag &&
M->imap_state != IMAP_error) {
DPRINT(Debug,3,(&Debug,
"imap_clear_command: Waiting for idle NOOP complete...\n"));
while(M->current_NOOP_tag &&
M->imap_state != IMAP_error &&
con->C.stream) {
WaitStreamFor(con->C.stream,SS_read_act);
/* TODO: Should user to have some way to interrupt progress ? */
}
DPRINT(Debug,3,(&Debug,
"imap_clear_command: idle NOOP wait completed\n"));
}
/* Reset possible incomplete send literal */
free_tokens(& (M->write_tokens));
M->literal_mode = no_literal;
M->token_index = -1;
free_Write_Buffer(& (M->write_buffer));
if (M->current_tag) {
free(M->current_tag);
M->current_tag = NULL;
}
if (M->command_result_code) {
free(M->command_result_code);
M->command_result_code = NULL;
}
}
#define IMAP_TIMEOUT 25
static void increment_uid_number P_((struct IMAP_MBX *M,
struct imap_uid_ref *UID));
static void increment_uid_number (M,UID)
struct IMAP_MBX *M;
struct imap_uid_ref *UID;
{
int val = UID->uid_number;
/*
M->reference_count == number of messages + 1
M->references [0] is not used
*/
if (UID->last_mbx_index > 0 && UID->last_mbx_index < M->reference_count) {
/* 1) IF mails are deleted by server meanwhile,
current UID->last_mbx_index may point to next message
*/
if (M->references[UID->last_mbx_index].uid_number > UID->uid_number) {
UID->uid_number = M->references[UID->last_mbx_index].uid_number;
if (UID->uid_number != val+1) {
DPRINT(Debug,4,(&Debug,"increment_uid_number: Jumping UID from %d to %d (index %d) -- mail deleted?\n",
val,UID->uid_number,UID->last_mbx_index));
}
return;
}
/* 2) UID->last_mbx_index was refering to last uid */
if (M->references[UID->last_mbx_index].uid_number == UID->uid_number &&
UID->last_mbx_index+1 < M->reference_count) {
if (M->references[UID->last_mbx_index+1].uid_number < UID->uid_number) {
DPRINT(Debug,1,(&Debug,"increment_uid_number: DANGER, UIDs not asceding: [%d]=%d and [%d]=%d\n",
UID->last_mbx_index, M->references[UID->last_mbx_index].uid_number,
UID->last_mbx_index+1, M->references[UID->last_mbx_index+1].uid_number));
/* Use default code */
} else {
UID->last_mbx_index++;
UID->uid_number = M->references[UID->last_mbx_index].uid_number;
if (UID->uid_number != val+1) {
DPRINT(Debug,4,(&Debug,"increment_uid_number: Jumping UID from %d to %d (index %d) -- whole on UID range\n",
val,UID->uid_number,UID->last_mbx_index));
}
return;
}
}
}
/* 3) UID->last_mbx_index is invalid and we are beginngin of mail */
if (-1 == UID->last_mbx_index && M->reference_count > 1 &&
UID->uid_number < M->references[1].uid_number) {
UID->last_mbx_index = 1;
UID->uid_number = M->references[UID->last_mbx_index].uid_number;
if (UID->uid_number != val+1) {
DPRINT(Debug,4,(&Debug,"increment_uid_number: Jumping UID from %d to %d (index %d) -- beginning of mail\n",
val,UID->uid_number,UID->last_mbx_index));
}
return;
}
UID->uid_number++;
}
static struct imap_reference *find_reference P_((struct IMAP_MBX *M,
struct imap_uid_ref *UID));
static struct imap_reference *find_reference(M,UID)
struct IMAP_MBX *M;
struct imap_uid_ref *UID;
{
int i;
if (UID->last_mbx_index > 0 && UID->last_mbx_index < M->reference_count &&
M->references[UID->last_mbx_index].uid_number ==
UID->uid_number) {
/* Found via cache */
return &(M->references[UID->last_mbx_index]);
}
for (i = 1; i < M->reference_count; i++)
if (M->references[i].uid_number == UID->uid_number) {
UID->last_mbx_index = i; /* Update cache */
return &(M->references[UID->last_mbx_index]);
}
DPRINT(Debug,1,(&Debug, "find_reference: No mail with IMAP uid %d\n",
UID->uid_number));
UID->last_mbx_index = -1; /* Invalidate cache */
return NULL;
}
static char * conv_to_imap_name P_((struct string *name));
static char * conv_to_imap_name(name)
struct string *name;
{
struct string * ConvRelative =
convert_string(IMAP_name_convention ? IMAP_ENCODING : imap_charset,
name,0);
char * ret = us2s(stream_from_string(ConvRelative,0,NULL));
free_string(&ConvRelative);
DPRINT(Debug,12,(&Debug,
"conv_to_imap_name=%s (name=%S, relative=%S)\n",
ret,name,ConvRelative));
return ret;
}
struct string *conv_from_imap_name P_((const char *name));
struct string *conv_from_imap_name(name)
CONST char *name;
{
struct string *ret = NULL;
int L;
/* Try IMAP encoding and if it fails, fall back to imap_charset.
Do not return IMAP encoding is string's charset, because
there is danger that it can leak out. Instead return string
with well known encoding of UNICODE (ie. UTF-7)
*/
if (IMAP_name_convention && name[0] && 0 < (L = strlen(name))) {
struct charset_state * P = new_state(IMAP_ENCODING);
uint16 * vector = safe_malloc(L * sizeof (vector[0]));
CONST char *s;
int X = 0;
int ok = 1;
charset_t utf7 = NULL;
DPRINT(Debug,12,(&Debug, "conv_from_imap_name: %s =",name));
for (s = name; *s && X < L; s++) {
if (!add_streambyte_to_state(P, (unsigned char)*s)) {
DPRINT(Debug,12,(&Debug, " FAIL"));
ok = 0;
break;
}
if (state_ready(P)) {
vector[X++] = give_unicode_from_state(P);
DPRINT(Debug,12,(&Debug, " %04X",vector[X-1]));
reset_state(P,0);
}
}
DPRINT(Debug,12,(&Debug, "\n"));
if (!ok) {
DPRINT(Debug,7,(&Debug,
"conv_from_imap_name: Not IMAP encoded: %s\n",
name));
} else if (NULL != (utf7 = MIME_name_to_charset("UTF-7",0))) {
ret = new_string(utf7);
add_unicode_to_string(ret,X,vector);
DPRINT(Debug,7,(&Debug,
"conv_from_imap_name: IMAP encoded, len=%d: %s (%S)\n",
X,name,ret));
}
free_state(&P);
free(vector);
}
if (!ret)
ret = new_string2(imap_charset,cs2us(name));
DPRINT(Debug,7,(&Debug,
"conv_from_imap_name=%S [type=%p '%s'] (name=%s) \n",
ret,
ret->string_type,
ret->string_type->MIME_name ?
ret->string_type->MIME_name :
"<no MIME name>",
name));
return ret;
}
/* Special return value 2 indivates that caller should re-enable
* imap_write_stream()
*/
static int parse_response P_((struct connection_cache *con));
static int parse_response(con)
struct connection_cache *con;
{
struct IMAP_CON * M_a = con->a.imap_con;
struct IMAP_MBX * M_b = NULL;
struct IMAP_BROWSER * M_c = NULL;
if (con->f) {
if (con->f->folder_type != &imap_mbx)
panic("MBX PANIC",__FILE__,__LINE__,"parse_response",
"Bad folder type attached to connection",0);
else
M_b = &(con->f->p->a.imap_mbx);
}
if (con->d) {
if (con->d->type != &imap_browser)
panic("MBX PANIC",__FILE__,__LINE__,"parse_response",
"Bad browser type attached to connection",0);
else
M_c = &(con->d->a.imap_browser);
}
if (!M_a) {
panic("MBX PANIC",__FILE__,__LINE__,"parse_response",
"No imap connection",0);
}
if (M_a->read_tokens.token_count < 2) {
DPRINT(Debug,1,(&Debug, "parse_response: token_count=%d\n",
M_a->read_tokens.token_count));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
switch (M_a->read_tokens.tokens[0].imap_token) {
case imap_notag:
switch (M_a->read_tokens.tokens[1].imap_token) {
struct string *Text;
char *code; /* Do not free() this -- this is just pointer
to read_tokens area
*/
case imap_status_keyword:
Text = NULL;
code = NULL;
pick_text(M_a,&Text,&code);
if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"BYE")) {
M_a->imap_state = IMAP_closing;
if (Text)
lib_transient(CATGETS(elm_msg_cat, MeSet,MeImapBye,
"IMAP server: %S"),
Text);
} else if (code && Text && 0 == istrcmp(code,"ALERT")) {
lib_error(CATGETS(elm_msg_cat, MeSet,MeImapAlert,
"IMAP alert: %S"),
Text);
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"OK")) {
if (IMAP_show_greeting && !code)
lib_error(CATGETS(elm_msg_cat, MeSet,MeImapServer,
"IMAP server: %S"),
Text);
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"NO")) {
if (IMAP_show_greeting)
lib_error(CATGETS(elm_msg_cat, MeSet,MeImapWarning,
"IMAP warning: %S"),
Text);
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"BAD")) {
if (IMAP_show_error)
lib_error(CATGETS(elm_msg_cat, MeSet,MeImapProtoError,
"IMAP error: %S"),
Text);
}
if (Text)
free_string(&Text);
break;
case imap_other_keyword:
if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"CAPABILITY")) {
int i,j;
M_a->capability_bits = 0;
#ifdef USE_DLOPEN
free_only_imap_capa_libs( & (M_a->capability_libs),
& (M_a->capability_lib_count));
#endif
for (i = 2; i < M_a->read_tokens.token_count; i++) {
if (imap_atom == M_a->read_tokens.tokens[i].imap_token) {
DPRINT(Debug,2,(&Debug,
"IMAP server capacity: '%s'\n",
M_a->read_tokens.tokens[i].str));
#ifdef USE_DLOPEN
probe_imap_capa_lib( & (M_a->capability_libs),
& (M_a->capability_lib_count),
M_a->read_tokens.tokens[i].str);
#endif
for (j = 0;
j < sizeof capabilities / sizeof (struct capab);
j++) {
if (0 == istrcmp(M_a->read_tokens.tokens[i].str,
capabilities[j].capa))
M_a->capability_bits |= capabilities[j].mask;
}
}
}
DPRINT(Debug,9,(&Debug,
"parse_response: (known) Capabilities:"));
for (j = 0;
j < sizeof capabilities / sizeof (struct capab);
j++) {
if ((M_a->capability_bits & capabilities[j].mask)
== capabilities[j].mask)
DPRINT(Debug,9,(&Debug, " %s",capabilities[j].capa));
}
DPRINT(Debug,9,(&Debug, "\n"));
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"LIST")) {
if (M_c) {
struct imap_token * ptr =
M_a->read_tokens.tokens+2;
int left =
M_a->read_tokens.token_count-2;
struct imap_token * lst;
int lst_len;
M_c->dir_entries =
safe_realloc(M_c->dir_entries,
(M_c->dir_entry_count +1) *
sizeof (struct imap_dir_entry));
M_c->dir_entries[M_c->dir_entry_count].flags = 0;
if (!scan_list(&ptr,&left,&lst,&lst_len) ||
left != 2) {
DPRINT(Debug,1,(&Debug,
"parse_response: scan_list failed to parse LIST response list, left=%d\n",
left));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
} else {
process_list_flags(M_c->dir_entries+
M_c->dir_entry_count,
lst,lst_len);
if (imap_atom == ptr[0].imap_token &&
0 == istrcmp(ptr[0].str,"nil"))
M_c->dir_entries[M_c->dir_entry_count].sep ='\0';
else if (imap_string == ptr[0].imap_token &&
1 == ptr[0].len_or_value)
M_c->dir_entries[M_c->dir_entry_count].sep =
ptr[0].str[0];
else {
DPRINT(Debug,1,(&Debug,
"parse_response: LIST: Bad separator, type=%d\n",
ptr[0].imap_token));
lib_error(CATGETS(elm_msg_cat, MeSet,
MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
if (imap_string == ptr[1].imap_token)
M_c->dir_entries[M_c->dir_entry_count].
imap_name = safe_strdup(ptr[1].str);
else if (imap_literal == ptr[1].imap_token)
M_c->dir_entries[M_c->dir_entry_count].
imap_name = safe_strdup(ptr[1].str);
else if (imap_atom == ptr[1].imap_token)
M_c->dir_entries[M_c->dir_entry_count].
imap_name = safe_strdup(ptr[1].str);
else {
DPRINT(Debug,1,(&Debug,
"parse_response: LIST: Bad mailbox, type=%d\n",
ptr[0].imap_token));
lib_error(CATGETS(elm_msg_cat, MeSet,
MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
M_c->dir_entries[M_c->dir_entry_count].
translated_name =
conv_from_imap_name(M_c->dir_entries
[M_c->dir_entry_count].
imap_name);
DPRINT(Debug,30,(&Debug,
"parse_response: %d: list item '%s' separator=%c\n",
M_c->dir_entry_count,
M_c->dir_entries[M_c->dir_entry_count].
imap_name ,
M_c->dir_entries[M_c->dir_entry_count].
sep));
DPRINT(Debug,30,(&Debug,
"parse_response: %d: %p '%S'\n",
M_c->dir_entry_count,
M_c->dir_entries[M_c->dir_entry_count].
translated_name,
M_c->dir_entries[M_c->dir_entry_count].
translated_name));
M_c->dir_entry_count++;
}
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected LIST response\n"));
}
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"LSUB")) {
/* TODO: Handle LSUB */
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"STATUS")) {
/* TODO: Handle STATUS */
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"SEARCH")) {
/* TODO: Handle STATUS */
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"FLAGS")) {
int i,j;
if (M_b) {
M_b->flag_bits = 0;
for (i = 2; i < M_a->read_tokens.token_count; i++) {
int found = 0;
if (imap_atom == M_a->
read_tokens.tokens[i].imap_token) {
for (j = 0;
j < sizeof IMAP_flags /
sizeof (struct imap_flag);
j++) {
if (0 == istrcmp(M_a->
read_tokens.tokens[i].str,
IMAP_flags[j].flag)) {
M_b->flag_bits |= IMAP_flags[j].mask_imap;
found++;
break;
}
}
if (!found) {
DPRINT(Debug,9,(&Debug,
"parse_response: Unknown flag %s\n",
M_a->read_tokens.tokens[i].str));
}
}
}
DPRINT(Debug,9,(&Debug,
"parse_response: (known) Flags:"));
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if ((M_b->flag_bits & IMAP_flags[j].mask_imap)
== IMAP_flags[j].mask_imap)
DPRINT(Debug,9,(&Debug, " %s",IMAP_flags[j].flag));
}
DPRINT(Debug,9,(&Debug, "\n"));
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected FLAGS response\n"));
}
}
break;
case imap_number:
if (imap_other_keyword == M_a->read_tokens.tokens[2].imap_token) {
if (0 == istrcmp(M_a->read_tokens.tokens[2].str,"RECENT")) {
if (M_b)
M_b->num_recent =
M_a->read_tokens.tokens[1].len_or_value;
else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected RECENT response\n"));
}
} else if (0 == istrcmp(M_a->read_tokens.tokens[2].str,
"EXISTS")) {
int val = M_a->read_tokens.tokens[1].len_or_value ;
DPRINT(Debug,9,(&Debug,
"parse_response: (mailbox index %d) EXISTS\n",
val));
if (M_b) {
/*
M_b->reference_count == number of messages + 1
M_b->references [0] is not used
*/
if (val+1 < M_b->reference_count) {
DPRINT(Debug,1,(&Debug,
"parse_response: Bad EXISTS value: %d\n",
val));
} else if (val+1 > M_b->reference_count) {
DPRINT(Debug,10,(&Debug,
"parse_response: %d mails\n",
val));
M_b->references = safe_realloc(M_b->references,
(val +1) *
sizeof (struct
imap_reference));
while (M_b->reference_count < val+1) {
struct imap_reference *item =
&(M_b->references[M_b->reference_count]);
zero_reference (item);
M_b->reference_count++;
}
}
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected RECENT response\n"));
}
} else if (0 == istrcmp(M_a->read_tokens.tokens[2].str,
"EXPUNGE")) {
int val = M_a->read_tokens.tokens[1].len_or_value ;
DPRINT(Debug,9,(&Debug,
"parse_response: (mailbox index %d) EXPUNGE\n",
val));
if (M_b) {
/* M_b->references[0] is not used ...
M_b->references[ 1 .. num of messages ] is on use
M_b->reference_count == num of messages +1
*/
if (val < 0 || val >= M_b->reference_count) {
DPRINT(Debug,1,(&Debug,
"parse_response: Bad EXPUNGE value: %d\n",
val));
} else {
int i;
free_reference (& (M_b->references[val]));
for (i = val+1; i < M_b->reference_count; i++) {
M_b->references[i-1] = M_b->references[i];
}
M_b->reference_count--;
}
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected EXPUNGE response\n"));
}
} else if (0 == istrcmp(M_a->read_tokens.tokens[2].str,
"FETCH")) {
int val = M_a->read_tokens.tokens[1].len_or_value ;
DPRINT(Debug,9,(&Debug,
"parse_response: (mailbox index %d) FETCH\n",val));
if (M_b) {
/* M_b->references[0] is not used ...
M_b->references[ 1 .. num of messages ] is on use
M_b->reference_count == num of messages +1
*/
if (val < 0 || val >= M_b->reference_count) {
DPRINT(Debug,1,(&Debug, "parse_response: Bad FETCH value: %d\n",
val));
} else {
struct imap_token * ptr =
M_a->read_tokens.tokens+3;
int left =
M_a->read_tokens.token_count-3;
struct imap_token * lst;
int lst_len;
if (!scan_list(&ptr,&left,&lst,&lst_len) ||
left > 0) {
DPRINT(Debug,1,(&Debug,
"parse_response: scan_list failed to parse FETCH response list\n"));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
} else {
process_fetch_items(M_b->references+val,
lst,lst_len);
}
}
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected FETCH response\n"));
}
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unsupported data %s\n",
M_a->read_tokens.tokens[2].str));
}
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: token[2] type =%d\n",
M_a->read_tokens.tokens[2].imap_token));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
break;
default:
DPRINT(Debug,1,(&Debug,
"parse_response: token[1] type =%d\n",
M_a->read_tokens.tokens[1].imap_token));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
break;
case imap_atom:
if (M_a->current_NOOP_tag &&
0 == strcmp(M_a->current_NOOP_tag,
M_a->read_tokens.tokens[0].str)) {
free(M_a->current_NOOP_tag);
M_a->current_NOOP_tag = NULL;
DPRINT(Debug,9,(&Debug,
"parse_response: Response for idle timer NOOP command -- ignored.\n"));
return 1;
}
if (! M_a->current_tag ||
0 != strcmp(M_a->current_tag,
M_a->read_tokens.tokens[0].str)) {
DPRINT(Debug,1,(&Debug, "parse_response: bad tag %s\n",
M_a->read_tokens.tokens[0].str));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
free(M_a->current_tag);
M_a->current_tag = NULL;
if (M_a->command_result_code) {
free(M_a->command_result_code);
M_a->command_result_code = NULL;
}
switch (M_a->read_tokens.tokens[1].imap_token) {
struct string *Text;
char *code; /* Do not free() this -- this is just pointer
to read_tokens area
*/
case imap_status_keyword:
Text = NULL;
code = NULL;
pick_text(M_a,&Text,&code);
if (code)
M_a->command_result_code = safe_strdup(code);
if (code && Text && 0 == istrcmp(code,"ALERT")) {
lib_transient(CATGETS(elm_msg_cat, MeSet,MeImapAlert,
"IMAP alert: %S"),
Text);
} else if (code && 0 == istrcmp(code,"READ-ONLY")) {
if (M_b) {
M_b->folder_status &= ~IMAP_writable;
DPRINT(Debug,5,(&Debug,
"parse_response: Folder is read-only\n"));
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected READ-ONLY response\n"));
}
} else if (code && 0 == istrcmp(code,"READ-WRITE")) {
if (M_b) {
M_b->folder_status |= IMAP_writable;
DPRINT(Debug,5,(&Debug,
"parse_response: Folder is read-write\n"));
} else {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected READ-WRITE response\n"));
}
}
if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"NO")) {
M_a->imap_state = IMAP_command_ready_NO;
if (Text)
lib_error(CATGETS(elm_msg_cat, MeSet,MeImapError,
"IMAP server: %S"),
Text);
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"BAD")) {
M_a->imap_state = IMAP_command_ready_BAD;
if (Text)
lib_error(CATGETS(elm_msg_cat, MeSet,MeImapError,
"IMAP server: %S"),
Text);
} else if (0 == istrcmp(M_a->read_tokens.tokens[1].str,"OK")) {
/* Don't forget closing state ... */
if (IMAP_closing != M_a->imap_state)
M_a->imap_state = IMAP_command_ready_OK;
} else {
DPRINT(Debug,1,(&Debug, "parse_response: status=%s\n",
M_a->read_tokens.tokens[1].str));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
if (Text)
free_string(&Text);
return 0;
}
if (Text)
free_string(&Text);
break;
default:
DPRINT(Debug,1,(&Debug, "parse_response: token[1] type =%d\n",
M_a->read_tokens.tokens[1].imap_token));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
break;
case imap_continue:
/* TODO: Handle AUTHENTICATE */
if (M_a->literal_mode != waiting_literal_continue) {
DPRINT(Debug,1,(&Debug,
"parse_response: Unexpected continuation request\n"));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
M_a->literal_mode = continue_literal;
if (!imap_gen_writable_data(M_a)) {
panic("MBX PANIC",__FILE__,__LINE__,"parse_response",
"Not literal data for writing (IMAP)",0);
}
DPRINT(Debug,9,(&Debug,
"parse_response=2 (continue literal sending)\n"));
return 2;
default:
DPRINT(Debug,1,(&Debug, "parse_response: token[0] type =%d\n",
M_a->read_tokens.tokens[0].imap_token));
lib_error(CATGETS(elm_msg_cat, MeSet,MeUnexpextedResponseImap,
"Bad response from IMAP server"));
return 0;
}
return 1;
}
static int imap_write_stream P_((struct streamsched *ss,void * data));
static int imap_idle_timer P_((struct streamsched *ss,void * data));
static int imap_read_stream (ss,data)
struct streamsched *ss;
void * data;
{
struct connection_cache *con = data;
int n, i;
int close_it = 0;
int tok_ret;
char * ERROR = NULL;
if (!data ||
con->type != &IMAP_connection ||
con->C.stream != ss) {
panic("MBX PANIC",__FILE__,__LINE__,"imap_read_stream",
"Bad data argument !!!",0);
}
DPRINT(Debug,13,(&Debug,
"imap_read_stream: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (! con->a.imap_con) {
DPRINT(Debug,13,(&Debug,
"imap_read_stream=0 (NO IMAP CONNECTION!)\n"));
return 0;
}
if (IMAP_error == con->a.imap_con->imap_state) {
DPRINT(Debug,13,(&Debug,
"imap_read_stream=0 (stream need closing)\n"));
return 0;
}
if (con->a.imap_con->wanna_literal)
n = ReadFromStream(ss, &(con->a.imap_con->read_buffer),
con->a.imap_con->literal_len -
con->a.imap_con->read_buffer.read_len);
else
n = ReadFromStream(ss, &(con->a.imap_con->read_buffer),-1);
DPRINT(Debug,50,(&Debug, "Buffer content="));
#ifdef DEBUG
if (Debug.active >= 50) {
for (i = 0; i < con->a.imap_con->read_buffer.read_len; i++) {
DPRINT(Debug,50,(&Debug, " %d",
con->a.imap_con->read_buffer.read_buffer[i]));
}
DPRINT(Debug,50,(&Debug, "\n (message?)="));
for (i = 0; i < con->a.imap_con->read_buffer.read_len; i++) {
DPRINT(Debug,50,(&Debug, " %c",
isascii(con->a.imap_con->read_buffer.
read_buffer[i]) &&
isprint(con->a.imap_con->read_buffer.
read_buffer[i]) ?
con->a.imap_con->read_buffer.read_buffer[i] :
'.'));
}
}
#endif
DPRINT(Debug,50,(&Debug, "\n"));
/* Parse response ... */
do {
tok_ret = tokenize(con->a.imap_con);
if (tok_ret < 0) {
DPRINT(Debug,7,(&Debug,
"imap_read_stream: Failed to tokenize response ...\n"));
close_it = 1;
} else if (tok_ret > 0) { /* Tokenized */
int X;
if (!(X = parse_response(con))) {
DPRINT(Debug,7,(&Debug,
"imap_read_stream: Failed to parse response ...\n"));
close_it = 1;
} else if (2 == X) {
/* Restart writing of literal ... */
ConfigStream(con->C.stream,
imap_read_stream,imap_write_stream,
imap_idle_timer,IMAP_TIMEOUT,con);
DPRINT(Debug,13,(&Debug,
"imap_read_stream: Continue literal sending...\n"));
}
free_tokens(& (con->a.imap_con->read_tokens));
}
} while (0 < tok_ret && !close_it &&
con->a.imap_con->read_buffer.read_len > 0);
if (n == 0) {
if (con->a.imap_con->imap_state != IMAP_closing)
lib_error(CATGETS(elm_msg_cat, MeSet,MeConnectionClosedImap,
"Connection closed to IMAP server"));
close_it = 1;
}
if (n < 0) {
ERROR = RemoveStreamError(ss);
if (ERROR) {
lib_error(CATGETS(elm_msg_cat, MeSet,MeErrorReadingImap,
"Error reading from IMAP server: %s"),
ERROR);
close_it = 1;
}
}
if (ERROR) {
free(ERROR);
ERROR = NULL;
}
if (close_it) {
con->a.imap_con->imap_state = IMAP_error;
DPRINT(Debug,9,(&Debug,
"imap_read_stream=0 (stream need closing)\n"));
return 0;
}
DPRINT(Debug,13,(&Debug,
"imap_read_stream=1\n"));
return 1;
}
static int imap_write_stream (ss,data)
struct streamsched *ss;
void * data;
{
struct connection_cache *con = data;
char * ERROR = NULL;
int n;
if (!data ||
con->type != &IMAP_connection ||
con->C.stream != ss) {
panic("MBX PANIC",__FILE__,__LINE__,"imap_write_stream",
"Bad data argument !!!",0);
}
DPRINT(Debug,13,(&Debug,
"imap_write_stream: folder=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (!con->a.imap_con) {
DPRINT(Debug,7,(&Debug,
"imap_write_stream=0 (NO imap connection)\n"));
return 0;
}
if (!imap_gen_writable_data(con->a.imap_con)) {
DPRINT(Debug,7,(&Debug,
"imap_write_stream: NO DATA TO WRITE!\n"));
}
if (IMAP_error == con->a.imap_con->imap_state) {
DPRINT(Debug,7,(&Debug,
"imap_write_stream=0 (stream need closing)\n"));
return 0;
}
n = WriteToStream(ss,& (con->a.imap_con->write_buffer));
if (n > 0) {
int i;
DPRINT(Debug,70,(&Debug, "Write content="));
for (i = 0; i < n; i++) {
DPRINT(Debug,70,(&Debug, " %d",
con->a.imap_con->write_buffer.write_buffer[i]));
}
DPRINT(Debug,70,(&Debug, "\n (text?)="));
for (i = 0; i < n; i++) {
DPRINT(Debug,70,(&Debug, " %c",
isascii(con->a.imap_con->write_buffer.write_buffer[i])
&&
isprint(con->a.imap_con->write_buffer.write_buffer[i])
?
con->a.imap_con->write_buffer.write_buffer[i] :
'.'));
}
DPRINT(Debug,70,(&Debug, "\n"));
cut_Write_Buffer(& (con->a.imap_con->write_buffer),n);
} else if (n < 0) {
ERROR = RemoveStreamError(ss);
DPRINT(Debug,7,(&Debug,
"imap_write_stream: write error %s\n",
ERROR ? ERROR : "(none)"));
}
if (!con->a.imap_con->write_buffer.write_len) {
if (!imap_gen_writable_data(con->a.imap_con)) {
if (ERROR) {
free(ERROR);
ERROR = NULL;
}
DPRINT(Debug,13,(&Debug,
"imap_write_stream=0 -- data written\n"));
return 0;
}
}
if (ERROR) {
lib_error(CATGETS(elm_msg_cat, MeSet,MeErrorWritingImap,
"Error writing to IMAP server: %s"),
ERROR);
con->a.imap_con->imap_state = IMAP_error;
DPRINT(Debug,7,(&Debug,
"imap_write_stream=0 (stream need closing)\n"));
free(ERROR);
ERROR = NULL;
return 0;
}
DPRINT(Debug,13,(&Debug, "imap_write_stream=1\n"));
return 1;
}
#define MAX_TAG 99999
static char * tag_generator P_((void));
static char * tag_generator()
{
/* NOTE: There is no need to have per imap connection tag
generating sequence
*/
static char tag_letter = 'A';
static int tag_number = 0;
tag_number++;
if (tag_number > MAX_TAG) {
tag_number = 1;
tag_letter++;
if ('Z' == tag_letter) {
tag_letter = 'A';
DPRINT(Debug,13,(&Debug,
"tag_generator: Tag wrapping around\n"));
}
}
return elm_message(FRM("%c%05d"),tag_letter,tag_number);
}
static int imap_idle_timer (ss,data)
struct streamsched *ss;
void * data;
{
struct connection_cache *con = data;
if (!data ||
con->type != &IMAP_connection ||
con->C.stream != ss) {
panic("MBX PANIC",__FILE__,__LINE__,"imap_idle_timer",
"Bad data argument !!!",0);
}
DPRINT(Debug,13,(&Debug,
"imap_idle_timer: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (IMAP_error == con->a.imap_con->imap_state) {
DPRINT(Debug,7,(&Debug,
"imap_idle_timer=0 (stream need closing)\n"));
return 0;
}
if (con->a.imap_con->current_NOOP_tag) {
DPRINT(Debug,13,(&Debug,
"imap_idle_timer=1 (previous NOOP command is not finished)\n"));
return 1;
}
if (con->a.imap_con->token_index >= 0 ||
con->a.imap_con->imap_state != IMAP_idle) {
DPRINT(Debug,13,(&Debug,
"imap_idle_timer=1 (NOT idle)\n"));
return 1;
}
con->a.imap_con->current_NOOP_tag = tag_generator();
con->a.imap_con->token_index = -2; /* Produce NOOP command */
con->a.imap_con->imap_state = IMAP_command; /* For imap_gen_writable_data */
if (!imap_gen_writable_data(con->a.imap_con)) {
panic("MBX PANIC",__FILE__,__LINE__,"imap_idle_timer",
"No NOOP command for writing (IMAP)",0);
}
ConfigStream(con->C.stream,imap_read_stream,imap_write_stream,
imap_idle_timer,IMAP_TIMEOUT,con);
/* Mark it again IDLE ... */
con->a.imap_con->imap_state = IMAP_idle;
return 1;
}
static void imap_wait P_((struct connection_cache *con));
static void imap_wait(con)
struct connection_cache *con;
{
if (!con->a.imap_con) {
DPRINT(Debug,7,(&Debug,"imap_wait: No imap connection\n"));
return;
}
while (IMAP_command_ready_OK != con->a.imap_con->imap_state &&
IMAP_command_ready_NO != con->a.imap_con->imap_state &&
IMAP_command_ready_BAD != con->a.imap_con->imap_state &&
IMAP_closing != con->a.imap_con->imap_state &&
IMAP_error != con->a.imap_con->imap_state &&
NULL != con->C.stream) {
WaitStreamFor(con->C.stream,SS_read_act);
/* TODO: Should user to have some way to interrupt progress ? */
}
}
static void imap_idle_wait_1 P_((struct connection_cache *con));
static void imap_idle_wait_1(con)
struct connection_cache *con;
{
struct IMAP_CON * Ma;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"imap_idle_wait_1",
"Wrong type connection attached to folder",0);
}
Ma = con->a.imap_con;
if (!Ma)
panic("MBX PANIC",__FILE__,__LINE__,"imap_idle_wait_1",
"No imap connection",0);
if (Ma->current_NOOP_tag &&
Ma->imap_state != IMAP_closing &&
Ma->imap_state != IMAP_error) {
DPRINT(Debug,3,(&Debug,
"imap_idle_wait_1: Waiting for idle NOOP complete...\n"));
while(Ma->current_NOOP_tag &&
Ma->imap_state != IMAP_closing &&
Ma->imap_state != IMAP_error &&
con->C.stream) {
WaitStreamFor(con->C.stream,SS_read_act);
/* TODO: Should user to have some way to interrupt progress ? */
}
DPRINT(Debug,3,(&Debug,
"imap_idle_wait_1: idle NOOP wait completed\n"));
}
if (IMAP_idle != Ma->imap_state) {
DPRINT(Debug,3,(&Debug,
"imap_idle_wait_1: Waiting for command complete...\n"));
imap_wait(con);
DPRINT(Debug,3,(&Debug,
"imap_idle_wait_1: command wait completed\n"));
}
}
static void imap_idle_wait P_((struct folder_info *folder));
static void imap_idle_wait(folder)
struct folder_info *folder;
{
struct IMAP_MBX * M = & (folder->p->a.imap_mbx);
imap_idle_wait_1(M->Ch);
}
static int imap_command_ok P_((struct connection_cache *con,
imap_states *cmd_result,
char **extra_code));
static int imap_command_ok(con,cmd_result,extra_code)
struct connection_cache *con;
imap_states *cmd_result;
char **extra_code;
{
int status = 0;
DPRINT(Debug,12,(&Debug,
"imap_command_ok: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (extra_code)
*extra_code = NULL;
if (IMAP_idle == con->a.imap_con->imap_state) {
DPRINT(Debug,7,(&Debug,
"imap_command_ok: WRONG STATE\n"));
status = 0;
*cmd_result = IMAP_error;
goto clean;
}
imap_wait(con);
status = IMAP_command_ready_OK == con->a.imap_con->imap_state;
*cmd_result = con->a.imap_con->imap_state;
if (IMAP_command_ready_OK == con->a.imap_con->imap_state ||
IMAP_command_ready_NO == con->a.imap_con->imap_state ||
IMAP_command_ready_BAD == con->a.imap_con->imap_state) {
if (extra_code) {
*extra_code = con->a.imap_con->command_result_code;
con->a.imap_con->command_result_code = NULL;
}
}
if (IMAP_error == con->a.imap_con->imap_state) {
if (con->C.stream) {
DPRINT(Debug,7,(&Debug,
"imap_command_ok: Closing stream\n"));
FreeStreamStack ( &(con->C.stream));
}
status = 0;
goto clean;
}
clean:
DPRINT(Debug,12,(&Debug,
"imap_command_ok=%d\n",
status));
return status;
}
static void imap_command_push_atom P_((struct connection_cache *con,
const char *st));
static int start_imap_command P_((struct connection_cache *con,
char * verb));
static int start_imap_command(con,verb)
struct connection_cache *con;
char * verb;
{
int status = 0;
DPRINT(Debug,12,(&Debug,
"start_imap_command: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (IMAP_error == con->a.imap_con->imap_state) {
failure:
if (con->C.stream ) {
DPRINT(Debug,12,(&Debug,
"start_imap_command: Closing stream\n"));
FreeStreamStack(& (con->C.stream));
}
status = 0;
goto clean;
}
if (IMAP_idle != con->a.imap_con->imap_state) {
DPRINT(Debug,7,(&Debug,
"start_imap_command: wrong state ... waiting...\n"));
imap_wait(con);
DPRINT(Debug,7,(&Debug,
"start_imap_command: ... wait ended\n"));
if (POP_error == con->a.imap_con->imap_state)
goto failure;
}
if (con->a.imap_con->current_tag) {
DPRINT(Debug,12,(&Debug,
"start_imap_command: Tag %s still active\n",
con->a.imap_con->current_tag));
status = 0;
goto clean;
}
con->a.imap_con->imap_state = IMAP_command;
free_tokens(& (con->a.imap_con->write_tokens));
con->a.imap_con->token_index = -1;
con->a.imap_con->literal_mode = no_literal;
if (NULL == con->C.stream) {
DPRINT(Debug,12,(&Debug,
"start_imap_command: stream not open\n"));
status = 0;
goto clean;
}
con->a.imap_con->current_tag = tag_generator();
imap_command_push_atom(con,verb);
status = 1;
DPRINT(Debug,15,(&Debug,
"start_imap_command: Command %s ...\n",verb));
clean:
DPRINT(Debug,12,(&Debug,
"start_imap_command=%d\n",
status));
return status;
}
static void imap_command_push_atom(con,st)
struct connection_cache *con;
CONST char *st;
{
struct imap_token T;
T.imap_token = imap_atom;
T.str = safe_strdup(st);
T.len_or_value = strlen(st);
add_token(&(con->a.imap_con->write_tokens),T);
}
static void imap_command_push_literal P_((struct connection_cache *con,
int len, const char *st));
static void imap_command_push_literal(con,len,st)
struct connection_cache *con;
int len;
CONST char *st;
{
struct imap_token T;
if (len < 0) {
panic("MBX PANIC",__FILE__,__LINE__,
"imap_command_push_literal","negative len",0);
}
T.imap_token = imap_literal;
T.str = safe_malloc(len+1);
memcpy(T.str,st,len);
T.str[len] = '\0';
T.len_or_value = len;
add_token(&(con->a.imap_con->write_tokens),T);
}
static void imap_command_push_string P_((struct connection_cache *con,
const char *st));
static void imap_command_push_string(con,st)
struct connection_cache *con;
CONST char *st;
{
struct imap_token T;
T.imap_token = imap_string;
T.str = safe_strdup(st);
T.len_or_value = strlen(st);
add_token(&(con->a.imap_con->write_tokens),T);
}
static void imap_command_push_astring P_((struct connection_cache *con,
const char *st));
static void imap_command_push_astring(con,st)
struct connection_cache *con;
CONST char *st;
{
if ('\0' == st[0] ||
strspn(st,"ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789")
!= strlen(st))
imap_command_push_string(con,st);
else
imap_command_push_atom(con,st);
}
static void imap_command_push_aliteral P_((struct connection_cache *con,
const char *st));
static void imap_command_push_aliteral(con,st)
struct connection_cache *con;
CONST char *st;
{
int len = strlen(st);
if ('\0' == st[0] ||
strspn(st,"ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789")
!= len)
imap_command_push_literal(con,len,st);
else
imap_command_push_atom(con,st);
}
static void imap_command_push_range P_((struct connection_cache *con,
int A, int B));
static void imap_command_push_range(con,A,B)
struct connection_cache *con;
int A,B;
{
char * st = elm_message(FRM("%d:%d"),A,B);
imap_command_push_atom(con,st);
free(st);
}
static void imap_command_push_number P_((struct connection_cache *con,
int A));
static void imap_command_push_number(con,A)
struct connection_cache *con;
int A;
{
char * st = elm_message(FRM("%d"),A);
imap_command_push_atom(con,st);
free(st);
}
static void imap_command_push_list P_((struct connection_cache *con,
const char **l));
static void imap_command_push_list(con,l)
struct connection_cache *con;
CONST char **l;
{
int i;
struct imap_token T;
T.imap_token = imap_list_begin;
T.str = NULL;
T.len_or_value = 0;
add_token(&(con->a.imap_con->write_tokens),T);
for (i = 0; l[i]; l++)
imap_command_push_atom(con,l[i]);
T.imap_token = imap_list_end;
T.str = NULL;
T.len_or_value = 0;
add_token(&(con->a.imap_con->write_tokens),T);
}
static void end_imap_command P_((struct connection_cache *con));
static void end_imap_command(con)
struct connection_cache *con;
{
DPRINT(Debug,12,(&Debug,
"end_imap_command: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (IMAP_error == con->a.imap_con->imap_state) {
DPRINT(Debug,12,(&Debug,
"end_imap_command: On error state\n"));
return;
}
if (IMAP_closing == con->a.imap_con->imap_state) {
DPRINT(Debug,12,(&Debug,
"end_imap_command: Is closing\n"));
return;
}
if (con->a.imap_con->imap_state != IMAP_command)
panic("MBX PANIC",__FILE__,__LINE__,
"end_imap_command","wrong state",0);
if (!imap_gen_writable_data(con->a.imap_con)) {
panic("MBX PANIC",__FILE__,__LINE__,"end_imap_command",
"No command for writing",0);
}
ConfigStream(con->C.stream,imap_read_stream,imap_write_stream,
imap_idle_timer,IMAP_TIMEOUT,con);
}
/* ----------------------------------------------------------------------- */
/* IMAP connection */
static void cache_zero_imap(con)
struct connection_cache *con;
{
DPRINT(Debug,11,(&Debug, "cache_zero_imap: con=%p\n",con));
con->a.imap_con = safe_malloc (sizeof (* (con->a.imap_con)));
bzero((void *)con->a.imap_con, sizeof (* (con->a.imap_con)));
con->a.imap_con->capability_bits = 0;
#ifdef USE_DLOPEN
con->a.imap_con->capability_libs = NULL;
con->a.imap_con->capability_lib_count = 0;
#endif
zero_Read_Buffer(&(con->a.imap_con->read_buffer));
con->a.imap_con->wanna_literal = 0;
con->a.imap_con->literal_len = 0;
con->a.imap_con->read_tokens.tokens = NULL;
con->a.imap_con->read_tokens.token_count = 0;
con->a.imap_con->tokenizer_state = state_tag;
con->a.imap_con->current_tag = NULL;
con->a.imap_con->imap_state = IMAP_idle;
con->a.imap_con->command_result_code = NULL;
con->a.imap_con->write_tokens.tokens = NULL;
con->a.imap_con->write_tokens.token_count = 0;
con->a.imap_con->token_index = -1;
zero_Write_Buffer(& (con->a.imap_con->write_buffer));
con->a.imap_con->literal_mode = no_literal;
con->a.imap_con->current_NOOP_tag = NULL;
}
static void cache_imap_clear_buffers P_((struct connection_cache *con));
static void cache_imap_clear_buffers(con)
struct connection_cache *con;
{
DPRINT(Debug,12,(&Debug,
"cache_imap_clear_buffers: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
con->a.imap_con->capability_bits = 0;
#ifdef USE_DLOPEN
free_only_imap_capa_libs( & (con->a.imap_con->capability_libs),
& (con->a.imap_con->capability_lib_count));
#endif
free_Read_Buffer(&(con->a.imap_con->read_buffer));
con->a.imap_con->wanna_literal = 0;
con->a.imap_con->literal_len = 0;
free_tokens(&(con->a.imap_con->read_tokens));
con->a.imap_con->tokenizer_state = state_tag;
if (con->a.imap_con->current_tag) {
free(con->a.imap_con->current_tag);
con->a.imap_con->current_tag = NULL;
}
con->a.imap_con->imap_state = IMAP_error;
if (con->a.imap_con->command_result_code) {
free(con->a.imap_con->command_result_code);
con->a.imap_con->command_result_code = NULL;
}
free_tokens(&(con->a.imap_con->write_tokens));
con->a.imap_con->token_index = -1;
free_Write_Buffer ( &(con->a.imap_con->write_buffer));
con->a.imap_con->literal_mode = no_literal;
if (con->a.imap_con->current_NOOP_tag) {
free(con->a.imap_con->current_NOOP_tag);
con->a.imap_con->current_NOOP_tag = NULL;
}
}
#ifdef USE_DLOPEN
static struct imap_callbacks imap_commands = {
imap_wait,
imap_idle_wait_1,
imap_command_ok,
start_imap_command,
imap_command_push_atom,
imap_command_push_literal,
imap_command_push_string,
imap_command_push_astring,
imap_command_push_aliteral,
imap_command_push_range,
imap_command_push_number,
imap_command_push_list,
end_imap_command,
imap_clear_command
};
#endif
static void cache_free_imap(con)
struct connection_cache *con;
{
DPRINT(Debug,11,(&Debug,
"cache_free_imap: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (con->a.imap_con) {
cache_imap_clear_buffers(con);
bzero((void *)con->a.imap_con, sizeof (* (con->a.imap_con)));
free(con->a.imap_con);
con->a.imap_con = NULL;
}
}
static int cache_open_imap(con)
struct connection_cache *con;
{
int status = 0;
imap_states res;
DPRINT(Debug,11,(&Debug,
"cache_open_imap: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
/* Clear buffer from old data ... */
cache_imap_clear_buffers(con);
if (NULL == con->C.stream ||
con->state == CON_error) {
struct service_entry *se =
give_service_entry(con->C.host,STFLAG_is_imap);
PORTS IMAP_ports[] = { PORT_imap4, PORT_end };
PORTS force_port = PORT_end;
int got;
if (!se) {
status = 0;
con->state = CON_error;
goto clean;
}
free(con->C.host);
con->C.host = safe_strdup(se->official_name);
if (con->port) {
force_port = con->port;
DPRINT(Debug,7,(&Debug,
"cache_open_imap: force_port = %d\n"));
}
if (!connect_remote_account(&(con->C),
&got,se,
IMAP_ports,
force_port)) {
status = 0;
con->state = CON_error;
free_temporary_service_entry(&se);
goto clean;
}
free_temporary_service_entry(&se);
con->state = CON_greeting;
}
if (con->state == CON_greeting) {
DPRINT(Debug,7,(&Debug,
"cache_open_imap: Waiting for server greeting...\n"));
/* We want server's initial response to connection ...
.. maybe untagged BYE?
*/
con->a.imap_con->imap_state = IMAP_idle;
ConfigStream(con->C.stream,
imap_read_stream,ss_noaction_routine,imap_idle_timer,
IMAP_TIMEOUT,con);
WaitStreamFor(con->C.stream,SS_read_act);
if (NULL == con->C.stream ||
IMAP_error == con->a.imap_con->imap_state ||
IMAP_closing == con->a.imap_con->imap_state) {
DPRINT(Debug,7,(&Debug,
"cache_open_imap: Server does not accept connection\n"));
con->state = CON_error;
goto clean;
}
con->state = CON_open;
}
/* Assume that everything OK */
status = 1;
con->a.imap_con->imap_state = IMAP_idle;
#ifdef USE_DLOPEN
retry_prelogin_capa:
#endif
if (!start_imap_command(con,"CAPABILITY"))
goto clean;
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
#ifdef USE_DLOPEN
else {
enum CAPA_phase phase = capa_prelogin;
DPRINT(Debug,13,(&Debug, "%d libraries available\n",
con->a.imap_con->capability_lib_count));
if (!handle_imap_capa_libs(con,
& (con->a.imap_con->capability_libs),
& (con->a.imap_con->capability_lib_count),
&phase, &imap_commands)) {
status = 0;
con->state = CON_error;
}
switch(phase) {
case capa_prelogin_again:
goto retry_prelogin_capa;
case capa_logged:
con->state = CON_logged;
break;
}
}
#endif
clean:
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"cache_open_imap=%d\n",status));
return status;
}
static int cache_login_imap(con,password)
struct connection_cache *con;
const struct string *password; /* May be NULL */
{
char * data1 = NULL;
imap_states res;
int status = 0;
int bits;
DPRINT(Debug,11,(&Debug,
"cache_login_imap: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (con->state != CON_open ||
NULL == con->C.stream) {
DPRINT(Debug,11,(&Debug,
"cache_login_imap: state not open\n"));
con->state = CON_error;
status = 0;
goto clean;
}
StreamInfo(con -> C.stream,SS_ssf,&bits,NULL);
if (!start_imap_command(con,"LOGIN"))
goto clean;
imap_command_push_astring(con,con->C.username);
if (bits < 2)
lib_transient(CATGETS(elm_msg_cat, MeSet,MeIMAPLoginClear,
"IMAP login to %s as %s ... (clear text)"),
con->C.host,
con->C.username);
else
lib_transient(CATGETS(elm_msg_cat, MeSet,MeIMAPLogin,
"IMAP login to %s as %s ..."),
con->C.host,
con->C.username);
if (password) {
struct string * pass1;
if (! can_ascii_string(password)) {
DPRINT(Debug,1,(&Debug,
"cache_login_imap: Non ascii password are not supported\n"));
}
/* NOTE: Original string is kept, if it can not converted
to US-ASCII -- then this produces garbage
*/
pass1 = ascify_string(password);
data1 = us2s(stream_from_string(pass1,0,NULL));
free_string(&pass1);
} else
data1 = lib_prompt(1,CATGETS(elm_msg_cat, MeSet,MeLoginPasswd,
"Password for %s@%s:"),
con->C.username,
con->C.host);
if (!data1) {
status = 0;
goto clean;
}
/* Put is as literal
*/
imap_command_push_aliteral(con,data1);
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
con->state = CON_logged;
status = 1;
lib_transient(CATGETS(elm_msg_cat, MeSet,MeIMAPLoginOK,
"IMAP login to %s as %s ... OK"),
con->C.host,
con->C.username);
clean:
imap_clear_command(con);
if (data1) {
free(data1);
data1 = NULL;
}
DPRINT(Debug,11,(&Debug,
"cache_login_imap=%d\n",status));
return status;
}
static int cache_close_imap(con)
struct connection_cache *con;
{
DPRINT(Debug,12,(&Debug,
"cache_close_imap: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (con->state != CON_error) {
DPRINT(Debug,12,(&Debug,
"cache_close_imap: LOGOUT\n"));
if (start_imap_command(con,"LOGOUT")) {
imap_states res;
end_imap_command(con);
imap_command_ok(con,&res,NULL);
imap_clear_command(con);
}
}
if (con->C.stream ) {
DPRINT(Debug,12,(&Debug,
"cache_close_imap: Closing stream\n"));
FreeStreamStack( &(con->C.stream));
}
con->state = CON_error;
return 1;
}
static void cache_folder_from_imap(con,folder)
struct connection_cache *con;
struct folder_info *folder;
{
DPRINT(Debug,12,(&Debug,
"cache_folder_from_imap: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (folder->folder_type != &imap_mbx)
panic("MBX PANIC",__FILE__,__LINE__,"cache_folder_from_imap",
"Can't attach folder type to connection",0);
if (folder->p->a.imap_mbx.Ch &&
folder->p->a.imap_mbx.Ch != con) {
DPRINT(Debug,12,(&Debug,
"cache_folder_from_imap: Freeing old connection: %p\n",
folder->p->a.imap_mbx.Ch));
free_connection(&(folder->p->a.imap_mbx.Ch));
}
folder->p->a.imap_mbx.Ch = con;
folder->p->a.imap_mbx.folder_status = 0;
/* TODO : Check necessary actions */
}
static void cache_browser_from_imap(con,dir)
struct connection_cache *con;
struct folder_browser *dir;
{
DPRINT(Debug,12,(&Debug,
"cache_browser_from_imap: con=%p (%s@%s)\n",
con,
con->C.username ? con->C.username : "<NULL>",
con->C.host ? con->C.host : "<NULL>"));
if (dir->type != &imap_browser)
panic("MBX PANIC",__FILE__,__LINE__,"cache_browser_from_imap",
"Can't attach browser type to connection",0);
DPRINT(Debug,12,(&Debug, " ... caching %s\n",
IMAP_connection_cache ? "enabled" : "disabled"));
if (dir->a.imap_browser.Ch &&
dir->a.imap_browser.Ch != con) {
if (dir->a.imap_browser.Ch->state != CON_error &&
IMAP_connection_cache) {
DPRINT(Debug,12,(&Debug,
"cache_browser_from_imap: Caching old connection: %p\n",
dir->a.imap_browser.Ch));
cache_connection(dir->a.imap_browser.Ch);
} else {
DPRINT(Debug,12,(&Debug,
"cache_browser_from_imap: freeing old connection\n"));
free_connection(&(dir->a.imap_browser.Ch));
}
}
dir->a.imap_browser.Ch = con;
/* TODO : Check necessary actions */
}
/* ----------------------------------------------------------------------- */
/* IMAP browser */
static void browser_zero_imap P_((struct folder_browser *dir));
static void browser_zero_imap(dir)
struct folder_browser *dir;
{
DPRINT(Debug,11,(&Debug, "browser_zero_imap: dir=%p\n", dir));
dir->a.imap_browser.Ch = create_connection(&IMAP_connection);
dir->a.imap_browser.Ch->d = dir; /* Back link */
dir->a.imap_browser.dir_name_cache = NULL;
dir->a.imap_browser.cur_sep = '\0';
dir->a.imap_browser.dir_entries = NULL;
dir->a.imap_browser.dir_entry_count = 0;
}
static void imap_free_dirlist P_((struct folder_browser *dir));
static void imap_free_dirlist(dir)
struct folder_browser *dir;
{
DPRINT(Debug,12,(&Debug,
"imap_free_dirlist: dir=%p\n",
dir));
if (dir->a.imap_browser.dir_entries) {
int i;
for (i = 0; i < dir->a.imap_browser.dir_entry_count; i++) {
if (dir->a.imap_browser.dir_entries[i].imap_name) {
free(dir->a.imap_browser.dir_entries[i].imap_name);
dir->a.imap_browser.dir_entries[i].imap_name = NULL;
}
if (dir->a.imap_browser.dir_entries[i].translated_name)
free_string(&(dir->a.imap_browser.dir_entries[i].
translated_name));
dir->a.imap_browser.dir_entries[i].sep = '\0';
dir->a.imap_browser.dir_entries[i].flags = 0;
}
free(dir->a.imap_browser.dir_entries);
dir->a.imap_browser.dir_entries = NULL;
dir->a.imap_browser.dir_entry_count = 0;
}
}
static void browser_free_imap P_((struct folder_browser *dir));
static void browser_free_imap(dir)
struct folder_browser *dir;
{
DPRINT(Debug,11,(&Debug,
"browser_free_imap: dir=%p (%s)\n",
dir,dir->sys_dir ? dir->sys_dir : "<NULL>"));
if (dir->a.imap_browser.Ch) {
DPRINT(Debug,12,(&Debug, " ... caching %s\n",
IMAP_connection_cache ? "enabled" : "disabled"));
if (dir->a.imap_browser.Ch->state != CON_error &&
IMAP_connection_cache) {
/* Give connection back to connection cache */
cache_connection(dir->a.imap_browser.Ch);
dir->a.imap_browser.Ch = NULL;
} else
free_connection( &(dir->a.imap_browser.Ch));
}
dir->a.imap_browser.cur_sep = '\0';
if (dir->a.imap_browser.dir_name_cache)
free_string(&(dir->a.imap_browser.dir_name_cache));
imap_free_dirlist(dir);
}
/* Does actual 'change' of directory */
static int browser_update_imaplisting P_((struct folder_browser *
dir,
struct string *dirname,
char *sysname,
int sep));
static int browser_update_imaplisting(dir,dirname,sysname,sep)
struct folder_browser *dir;
struct string *dirname;
char *sysname;
int sep;
{
imap_states ret1;
int ret = 0;
char * newsysdir = NULL;
struct string * newdirname = NULL;
DPRINT(Debug,12,(&Debug,
"browser_update_imaplisting: dir=%p, sysname=%s\n",
dir,sysname ? sysname : "<NULL>" ));
if (sysname && sysname[0] && !sep) {
DPRINT(Debug,12,(&Debug,
"browser_update_imaplisting -- no separator character ...\n"));
goto fail;
}
/* browser_folder_from_imap may close connection ... */
if (dir->a.imap_browser.Ch->state != CON_logged) {
if (!login_connection(dir->a.imap_browser.Ch,NULL))
goto fail;
}
imap_free_dirlist(dir);
if (!start_imap_command(dir->a.imap_browser.Ch,"LIST"))
goto fail;
/* Reference name -- currently empty */
imap_command_push_string(dir->a.imap_browser.Ch,"");
/* Listing pattern -- % match one component on hierarchy */
if (!sysname || !sysname[0]) {
imap_command_push_astring(dir->a.imap_browser.Ch,"%");
newsysdir = safe_strdup("");
newdirname = new_string(ASCII_SET);
} else {
int l1;
char * pattern = NULL;
char buf[2];
newsysdir = safe_strdup(sysname);
l1 = strlen(newsysdir);
if (l1 > 0 && sep == newsysdir[l1-1]) {
DPRINT(Debug,12,(&Debug,
"browser_update_imaplisting: Removing %c from %s\n",
sep,sysname));
newsysdir[l1-1] = '\0';
}
/* IMAP character set is assumed to be ASCII compatible on
here! */
l1 = string_len(dirname);
if (l1 > 0 && sep == give_unicode_from_string(dirname,l1-1)) {
int X = 0;
DPRINT(Debug,12,(&Debug,
"browser_update_imaplisting: Removing %04X from %S\n",
sep,dirname));
newdirname = clip_from_string(dirname,&X,l1-1);
} else
newdirname = dup_string(dirname);
pattern = safe_strdup(newsysdir);
buf[0] = sep;
buf[1] = '\0';
pattern = strmcat(pattern,buf);
pattern = strmcat(pattern,"%");
imap_command_push_astring(dir->a.imap_browser.Ch,pattern);
free(pattern);
}
end_imap_command(dir->a.imap_browser.Ch);
if (!imap_command_ok(dir->a.imap_browser.Ch,&ret1,NULL))
goto fail;
if (dir->sys_dir)
free(dir->sys_dir);
dir->sys_dir = newsysdir; newsysdir = NULL;
if (dir->a.imap_browser.dir_name_cache)
free_string(&(dir->a.imap_browser.dir_name_cache));
dir->a.imap_browser.dir_name_cache = dup_string(newdirname);
if (dir->dirname)
free_string(&(dir->dirname));
dir->dirname = format_string(FRM("%s@%s%s%S"),
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?",
dir->sys_dir[0] != '/' &&
dir->sys_dir[0] != '\0' ? ":" : "",
newdirname);
/* Update current separator */
dir->a.imap_browser.cur_sep = sep;
/* Make empty selection folder */
clear_dir_vector(dir);
if (!(dir->sys_dir[0])) { /* Top level directory */
int i;
for (i = 0; i < dir->a.imap_browser.dir_entry_count; i++) {
/* WARNING: add_dir_vector does not allocate strings --
* it just assign pointers!
*/
add_dir_vector(dir,
safe_strdup(dir->a.imap_browser.
dir_entries[i].imap_name),
dup_string(dir->a.imap_browser.
dir_entries[i].translated_name),
dir->a.imap_browser.dir_entries[i].flags
);
}
} else {
/* newdirname and dir->sys_dir does NOT include separator
character on end
*/
int L = string_len(newdirname);
int l1 = strlen(dir->sys_dir);
int m = 0,i;
for (i = 0; i < dir->a.imap_browser.dir_entry_count; i++) {
struct imap_dir_entry * E = &(dir->a.imap_browser.dir_entries[i]);
int L1 = string_len(E->translated_name);
int l2 = strlen(E->imap_name);
if (L < L1 && l1 < l2) {
int X = 0;
struct string * Z1 = clip_from_string(E->translated_name,&X,L);
/* IMAP character set is assumed to be ASCII compatible on
here! */
if (0 == string_cmp(newdirname,
Z1,-99 /* Unknown indicator */) &&
X < L1 && E->sep == sep &&
give_unicode_from_string(E->translated_name,X) == sep &&
0 == strncmp(dir->sys_dir,E->imap_name,l1) &&
E->imap_name[l1] == sep) {
X++;
if (X < L1) { /* No just directory name */
/* WARNING: add_dir_vector does not allocate strings --
* it just assign pointers!
*/
struct string * Z2 = NULL;
Z2 = clip_from_string(E->translated_name,&X,L1);
add_dir_vector(dir,safe_strdup(&(E->imap_name[l1+1])),
Z2,
dir->a.imap_browser.dir_entries[i].flags
);
m++;
}
}
}
}
DPRINT(Debug,12,(&Debug,
"browser_update_imaplisting -- %d entries of %d matches to dirname\n",
m,dir->a.imap_browser.dir_entry_count));
}
ret = 1;
fail:
imap_clear_command(dir->a.imap_browser.Ch);
if (newsysdir)
free(newsysdir);
if (newdirname)
free_string(&newdirname);
DPRINT(Debug,12,(&Debug, "browser_update_imaplisting=%d\n",ret));
return ret;
}
/* Returns -1 on failure */
static int match_relname_to_imaplisting P_((struct folder_browser *dir,
const struct string *rel_name,
int *is_full));
static int match_relname_to_imaplisting(dir,rel_name,is_full)
struct folder_browser *dir;
CONST struct string *rel_name;
int *is_full;
{
int match_len = 0;
int ret = -1;
int L,i;
DPRINT(Debug,12,(&Debug,
"match_relname_to_imaplisting: dir=%p\n",
dir));
DPRINT(Debug,12,(&Debug,
"match_relname_to_imaplisting: relname=%S\n",
rel_name));
L = string_len(rel_name);
*is_full = 0;
for (i = 0; i < dir->a.imap_browser.dir_entry_count && !*is_full; i++) {
int L1 = string_len(dir->a.imap_browser.dir_entries[i].
translated_name);
if (L1 <= L) {
int X = 0;
struct string * Z1 = clip_from_string(rel_name,&X,L1);
if (0 == string_cmp(dir->a.imap_browser.dir_entries[i].
translated_name,
Z1,-99 /* Unknown indicator */)) {
/* IMAP character set is assumed to be ASCII compatible
on here! */
if (X < L &&
dir->a.imap_browser.dir_entries[i].sep ==
give_unicode_from_string(rel_name,X)) {
DPRINT(Debug,12,(&Debug,
"match_relname_to_imaplisting: %d: sep idx=%d -- found matching prefix\n",
i,X));
if (X > match_len) {
match_len = X;
ret = i;
}
}
if (X == L) {
DPRINT(Debug,12,(&Debug,
"match_relname_to_imaplisting: %d: len = %d -- found matching name\n",
i,X));
match_len = X;
ret = i;
*is_full = 1;
}
}
free_string(&Z1);
}
}
DPRINT(Debug,12,(&Debug,
"match_relname_to_imaplisting=%d%s, match len = %d, *is_full=%d\n",
ret, ret == -1 ? " (no match)" : "",
match_len,
*is_full
));
return ret;
}
/* Returns name relative to directory of browser */
static struct string * browser_descend_imap P_((struct folder_browser *dir,
const struct string *rel_name,
struct string **disp_path,
char *retsep));
static struct string * browser_descend_imap(dir,rel_name,disp_path, retsep)
struct folder_browser *dir;
CONST struct string *rel_name;
struct string **disp_path;
char *retsep;
{
struct string * ret = NULL;
char used_sep = '\0';
int L,idx,is_full;
int last_idx = 0;
struct string * dirname_disp = NULL;
char * dirname_sys = NULL;
DPRINT(Debug,12,(&Debug, "browser_descend_imap: dir=%p\n", dir));
*retsep = '\0';
*disp_path = NULL;
if (!rel_name) {
DPRINT(Debug,12,(&Debug, "browser_descend_imap=NULL\n"));
return NULL;
}
L = string_len(rel_name);
idx = match_relname_to_imaplisting(dir,rel_name,&is_full);
if (idx < 0) {
/* Start from root */
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: Restarting search from root\n"));
if (!browser_update_imaplisting(dir,NULL,NULL,'\0'))
goto fail;
idx = match_relname_to_imaplisting(dir,rel_name,&is_full);
}
if (idx >= 0 && !is_full && imap_fast_lookup &&
'\0' != (used_sep = dir->a.imap_browser.dir_entries[idx].sep)) {
int idxN;
int found = -1;
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: imap_fast_lookup: Going to last directory on path, sep=%c\n",used_sep));
for (idxN = 0; idxN < L; idxN++) {
uint16 code2 = give_unicode_from_string(rel_name,idxN);
/* IMAP character set is assumed to be ASCII compatible on
here! */
if (used_sep == code2)
found = idxN;
}
DPRINT(Debug,12,(&Debug,
"... last found idx=%d\n",found));
if (found > 0) {
int X = 0;
struct string * dir_name = clip_from_string(rel_name,&X,found);
char * sys_name = conv_to_imap_name(dir_name);
DPRINT(Debug,12,(&Debug,
"... Using generated name %s (%S)\n",
sys_name,dir_name));
if (!browser_update_imaplisting(dir,dir_name,sys_name,
dir->a.imap_browser.
dir_entries[idx].sep)) {
free_string(&dir_name);
free(sys_name);
goto fast_fail;
}
idx = match_relname_to_imaplisting(dir,rel_name,&is_full);
free_string(&dir_name);
free(sys_name);
}
}
fast_fail:
/* Descend to rigth directory */
while (idx >= 0 && !is_full) {
struct string * dir_name = dup_string(dir->a.imap_browser.
dir_entries[idx].
translated_name);
char * sys_name = safe_strdup(dir->a.imap_browser.
dir_entries[idx].imap_name);
if (!browser_update_imaplisting(dir,dir_name,sys_name,
dir->a.imap_browser.
dir_entries[idx].sep)) {
free_string(&dir_name);
free(sys_name);
goto fail;
}
idx = match_relname_to_imaplisting(dir,rel_name,&is_full);
free_string(&dir_name);
free(sys_name);
}
if (is_full) {
int x1 = -1;
int x2 = -1;
int i;
int Llen = string_len(dir->a.imap_browser.dir_entries[idx].
translated_name);
used_sep = dir->a.imap_browser.dir_entries[idx].sep;
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: Using entry %d from listing: %s\n",
idx,dir->a.imap_browser.dir_entries[idx].imap_name));
if ('\0' != used_sep) {
for (i = 0; dir->a.imap_browser.dir_entries[idx].imap_name[i];
i++) {
if (used_sep == dir->a.imap_browser.dir_entries[idx].imap_name[i])
x1 = i;
}
for (i = 0; i < Llen; i++) {
/* IMAP character set is assumed to be ASCII compatible on
here! */
if (used_sep ==
give_unicode_from_string(dir->a.imap_browser.dir_entries[idx].
translated_name,i))
x2 = i;
}
}
last_idx = x2+1;
if (x2 > 0) {
int X = 0;
dirname_disp = clip_from_string(dir->a.imap_browser.dir_entries[idx].
translated_name,&X,x2);
dirname_sys = safe_malloc(x1+1);
strncpy(dirname_sys,dir->a.imap_browser.dir_entries[idx].imap_name,x1);
dirname_sys[x1] = '\0';
} else {
dirname_disp = new_string(imap_charset);
dirname_sys = safe_strdup("");
}
} else {
int x2 = -1;
if (dir->a.imap_browser.cur_sep) {
int i;
used_sep = dir->a.imap_browser.cur_sep;
for (i = 0; i < L; i++) {
/* IMAP character set is assumed to be ASCII compatible on
here! */
if (used_sep == give_unicode_from_string(rel_name,i))
x2 =i;
}
}
last_idx = x2 +1;
if (x2 > 0) {
int X = 0;
dirname_disp = clip_from_string(rel_name,&X,x2);
dirname_sys = conv_to_imap_name(dirname_disp);
} else {
dirname_disp = new_string(imap_charset);
dirname_sys = safe_strdup("");
}
}
if (dir->dirname) {
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: index %d, directory=%S sysname=%s\n",
last_idx,dirname_disp,dirname_sys));
}
if (used_sep) {
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: Directory separator %c\n",
used_sep));
*retsep = used_sep;
}
ret = clip_from_string(rel_name,&last_idx,string_len(rel_name));
/* Check that current directory is right
if not change to it ... */
if ((dirname_sys[0] && !dir->sys_dir) ||
(dir->sys_dir && 0 != strcmp(dirname_sys,dir->sys_dir))) {
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: Match have found, but trying update directory %s -> %s\n",
dir->sys_dir ? dir->sys_dir : "<NULL>",
dirname_sys));
if (!browser_update_imaplisting(dir,dirname_disp,dirname_sys,
used_sep)) {
DPRINT(Debug,12,(&Debug,
"browser_descend_imap: OOPS! Can't update directory\n"));
free_string(&ret); /* Delete answer */
goto fail;
}
}
if (dir->dirname) {
DPRINT(Debug,12,(&Debug,
"*** %S as relative to %S is %S\n",
rel_name,dir->dirname,ret));
}
if (dirname_disp) {
DPRINT(Debug,12,(&Debug,
"*** on directyry %S\n",
dirname_disp));
}
fail:
if (ret) {
*disp_path = dirname_disp;
} else {
if (dirname_disp)
free_string(&dirname_disp);
}
if (dirname_sys)
free(dirname_sys);
if (!ret) {
DPRINT(Debug,12,(&Debug, "browser_descend_imap=NULL\n"));
} else {
DPRINT(Debug,12,(&Debug, "browser_descend_imap=%S\n",ret));
}
return ret;
}
/* rel_dirname is relative to type -- not include user@hostname */
static int browser_change_imap P_((struct folder_browser *dir,
struct string *rel_dirname,
struct string **dispname));
static int browser_change_imap(dir,rel_dirname,dispname)
struct folder_browser *dir;
struct string *rel_dirname;
struct string **dispname;
{
int ret = 0;
DPRINT(Debug,11,(&Debug, "browser_change_imap: dir=%p\n", dir));
if (NULL == rel_dirname) {
DPRINT(Debug,11,(&Debug,
"browser_change_imap: To default directory\n"));
ret = browser_update_imaplisting(dir,NULL,NULL,'\0');
} else {
char sep = '\0';
struct string * relative1 = NULL;
struct string * relative = browser_descend_imap(dir,
rel_dirname,
&relative1,
&sep);
struct string * Lstr = NULL;
char * str = NULL;
/* browser_select_generic uses 'rel_dirname' if
'relative' is null
*/
browser_select_generic(dir,relative1,
rel_dirname,relative,&Lstr,&str,
sep,imap_charset);
ret = browser_update_imaplisting(dir,Lstr,str,
sep);
if (!ret)
lib_error(CATGETS(elm_msg_cat, MeSet, MeDirErrorUnreadable,
"Directory %S unreadable on %s@%s"),
Lstr,
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?");
if (Lstr)
free_string(&Lstr);
if (str)
free(str);
if (relative)
free_string(&relative);
if (relative1)
free_string(&relative1);
}
DPRINT(Debug,11,(&Debug, "browser_change_imap=%d\n", ret));
return ret;
}
static int browser_change_v_imap P_((struct folder_browser *dir,
struct name_vector *X,
struct string **dispname));
static int browser_change_v_imap(dir,X,dispname)
struct folder_browser *dir;
struct name_vector *X;
struct string **dispname;
{
int ret = 0;
DPRINT(Debug,11,(&Debug, "browser_change_v_imap: dir=%p\n", dir));
if (dir->dirname && dir->sys_dir) {
struct string * relative1 = NULL;
struct string * Lstr = NULL;
char * str = NULL;
int l1 = strlen(dir->sys_dir);
int add_sep = 0;
int idx = -1;
int is_full; /* Not used */
if (l1 > 0 && dir->a.imap_browser.cur_sep == '\0') {
DPRINT(Debug,11,(&Debug, "browser_change_v_imap: No separator"));
goto fail;
}
if (! dir->a.imap_browser.dir_name_cache) {
DPRINT(Debug,11,(&Debug, "browser_change_v_imap: No dir_name_cache"));
goto fail;
}
relative1 = dup_string(dir->a.imap_browser.dir_name_cache);
if (l1 > 0 && dir->a.imap_browser.cur_sep != dir->sys_dir[l1-1]) {
fill_ascii_to_string(relative1,1,dir->a.imap_browser.cur_sep);
add_sep = 1;
}
Lstr = cat_strings(relative1,
X->disp_name,
0);
str = safe_strdup(dir->sys_dir);
if (add_sep) {
char buf[2];
buf[0] = dir->a.imap_browser.cur_sep;
buf[1] = '\0';
str = strmcat(str,buf);
}
str = strmcat(str,X->sys_name);
if (dir->a.imap_browser.cur_sep == '\0' &&
(idx = match_relname_to_imaplisting(dir,Lstr,&is_full)) < 0) {
DPRINT(Debug,12,(&Debug,
"browser_change_v_imap: Not found from IMAP listing -- trying update...\n"));
if (browser_update_imaplisting(dir,relative1,dir->sys_dir,
dir->a.imap_browser.cur_sep))
idx = match_relname_to_imaplisting(dir,Lstr,&is_full);
}
if (dir->a.imap_browser.cur_sep == '\0' &&
idx >= 0) {
DPRINT(Debug,12,(&Debug,
"browser_change_v_imap: Using name %s from listing, with idx=%d imap listing, separator=%c\n",
str,idx,dir->a.imap_browser.dir_entries[idx].sep));
ret = browser_update_imaplisting(dir,Lstr,str,
dir->a.imap_browser.
dir_entries[idx].sep);
} else {
DPRINT(Debug,12,(&Debug,
"browser_change_v_imap: Using name %s from listing, with separator=%c\n",
str,dir->a.imap_browser.cur_sep));
ret = browser_update_imaplisting(dir,Lstr,str,
dir->a.imap_browser.cur_sep);
}
if (!ret) {
lib_error(CATGETS(elm_msg_cat, MeSet, MeDirErrorUnreadable,
"Directory %S unreadable on %s@%s"),
Lstr,
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?");
}
fail:
if (relative1)
free_string(&relative1);
}
DPRINT(Debug,11,(&Debug, "browser_change_v_imap=%d\n", ret));
return ret;
}
static int browser_change_up_imap P_((struct folder_browser *dir,
struct string **dispname));
static int browser_change_up_imap(dir,dispname)
struct folder_browser *dir;
struct string **dispname;
{
int ret = 0;
DPRINT(Debug,11,(&Debug, "browser_change_up_imap: dir=%p\n", dir));
if (dir->a.imap_browser.cur_sep != '\0' &&
dir->a.imap_browser.dir_name_cache) {
CONST struct string * relative1 =
dir->a.imap_browser.dir_name_cache;
int L = string_len(relative1);
int L1 = -1;
int idx;
for (idx = 0; idx < L ; idx++) {
uint16 code = give_unicode_from_string(relative1,idx);
/* ASCII assumed on separator */
if (dir->a.imap_browser.cur_sep == code)
L1 = idx;
}
if (L > 0) { /* In case of L1 == 0 returns default imap directory */
struct string * A1;
int X = 0;
if (L1 < L-1)
L1++;
A1 = clip_from_string(relative1,&X,L1);
/* Need not generate actual directory listing */
if (*dispname)
free_string(dispname);
*dispname = format_string(FRM("%s@%s%s%S"),
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?",
dir->sys_dir[0] != '/' &&
dir->sys_dir[0] != '\0' ? ":" : "",
A1);
free_string(&A1);
ret = 1;
} else
ret = -1; /* Signals caller to generate default menu */
} else
ret = -1; /* Signals caller to generate default menu */
DPRINT(Debug,11,(&Debug, "browser_change_up_imap=%d\n", ret));
return ret;
}
/* rel_dirname is relative to type -- not include user@hostname */
static int browser_select_imap P_((struct folder_browser *dir,
const struct string *rel_itemname,
struct string **dispname));
static int browser_select_imap(dir,rel_itemname,dispname)
struct folder_browser *dir;
CONST struct string *rel_itemname;
struct string **dispname;
{
int ret = 0;
char sep = '\0';
struct string * relative1 = NULL;
struct string * relative = NULL;
DPRINT(Debug,11,(&Debug, "browser_select_imap: dir=%p\n", dir));
if (rel_itemname) {
DPRINT(Debug,11,(&Debug, "browser_select_imap: rel_itemname=%S\n",
rel_itemname));
} else {
DPRINT(Debug,11,(&Debug, "browser_select_imap: rel_itemname=NULL\n"));
}
relative = browser_descend_imap(dir,rel_itemname,&relative1,&sep);
if (relative) {
struct string * Lstr = NULL;
char * str = NULL;
int idx = browser_select_generic(dir,relative1,
rel_itemname,relative,
&Lstr,&str,
sep,imap_charset);
if (idx >= 0) {
DPRINT(Debug,11,(&Debug,
"browser_select_imap: Using index %d: %s\n",
idx,dir->vector[idx].sys_name));
/* WARNING: set_dir_selection does not allocate strings --
* it just assign pointers!
*/
set_dir_selection(dir,
safe_strdup(dir->vector[idx].sys_name),
dup_string(dir->vector[idx].disp_name),
dir->vector[idx].flags
|BROWSER_MAILFILE /* Assume folder */
|BROWSER_EXIST /* Exists because on listing */
);
} else {
char * rel_str = conv_to_imap_name(relative);
int F = 0;
DPRINT(Debug,11,(&Debug,
"browser_select_imap: Using given name: %s\n",
rel_str));
if (-2 == idx) {
DPRINT(Debug,11,(&Debug,
"browser_select_imap: Directory prefix was given.\n"));
F |= BROWSER_DIRPREFIX;
} else
F |= BROWSER_MAILFILE; /* Assume folder */
/* WARNING: set_dir_selection does not allocate strings --
* it just assign pointers!
*/
set_dir_selection(dir,rel_str,dup_string(relative),
F);
}
ret = 1;
free_string(&relative);
free_string(&Lstr);
free(str);
}
if (relative1)
free_string(&relative1);
DPRINT(Debug,11,(&Debug, "browser_select_imap=%d\n", ret));
return ret;
}
static struct string * browser_imap_N P_((struct folder_browser *dir));
static struct string * browser_imap_N(dir)
struct folder_browser *dir;
{
static struct string *ret;
if (!dir->sys_dir)
dir->sys_dir = safe_strdup("");
/* OOPS */
if (!dir->dirname) {
dir->dirname = format_string(FRM("%s@%s%s%s"),
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?",
dir->sys_dir[0] != '/' &&
dir->sys_dir[0] != '\0' ? ":" : "",
dir->sys_dir);
}
if (dir->filter)
ret = format_string(CATGETS(elm_msg_cat, MeSet,MeImapDirFilter,
"IMAP directory %S with filter %S"),
dir->dirname,dir->filter);
else
ret = format_string(CATGETS(elm_msg_cat, MeSet,MeImapDir,
"IMAP directory %S"),
dir->dirname);
return ret;
}
static struct string * browser_give_title_imap P_((struct folder_browser *dir));
static struct string * browser_give_title_imap(dir)
struct folder_browser *dir;
{
static struct string *ret;
DPRINT(Debug,11,(&Debug, "browser_give_title_imap: dir=%p\n", dir));
ret = browser_imap_N(dir);
DPRINT(Debug,12,(&Debug, "browser_give_title_imap=%p\n", ret));
return ret;
}
static char browser_separator_imap P_((struct folder_browser *dir));
static char browser_separator_imap(dir)
struct folder_browser *dir;
{
char ret;
DPRINT(Debug,11,(&Debug, "browser_separator_imap: dir=%p\n", dir));
ret = dir->a.imap_browser.cur_sep;
if ('\0' == ret) {
DPRINT(Debug,11,(&Debug, "browser_separator_imap=NUL"));
} else {
DPRINT(Debug,11,(&Debug, "browser_separator_imap=%c\n", ret));
}
return ret;
}
static struct string * browser_name_imap P_((struct folder_browser *dir));
static struct string * browser_name_imap(dir)
struct folder_browser *dir;
{
static struct string *ret;
DPRINT(Debug,11,(&Debug,
"browser_name_imap: dir=%p\n",dir));
ret = browser_imap_N(dir);
DPRINT(Debug,11,(&Debug, "browser_name_imap=%p\n", ret));
return ret;
}
static struct string * browser_cat_imap P_((struct folder_browser *dir,
struct string * item));
static struct string * browser_cat_imap(dir,item)
struct folder_browser *dir;
struct string * item;
{
struct string * ret = NULL;
DPRINT(Debug,11,(&Debug, "browser_cat_imap: dir=%p\n",
dir));
if (dir->dirname) {
struct string * relative1 = NULL;
int L;
if (! dir->a.imap_browser.dir_name_cache) {
DPRINT(Debug,11,(&Debug, "browser_cat_imap: No dir_name_cache!\n"));
goto fail;
}
relative1 = dup_string(dir->a.imap_browser.dir_name_cache);
L = string_len(relative1);
if (L > 0) {
if (dir->a.imap_browser.cur_sep == '\0') {
DPRINT(Debug,11,(&Debug, "browser_cat_imap: No separator!\n"));
goto fail;
} else if ( dir->a.imap_browser.cur_sep != /* ASCII assumed! */
give_unicode_from_string(dir->dirname,L-1))
fill_ascii_to_string(relative1,1,dir->a.imap_browser.cur_sep);
}
ret = format_string(FRM("%s@%s%s%S"),
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?",
dir->sys_dir[0] != '/' &&
(L > 0 || item) ? ":" : "",
relative1);
/* Some local directory! */
if (item) {
struct string * XX = ret;
ret = cat_strings(XX,item,0);
free_string(&XX);
}
fail:
if (relative1)
free_string(&relative1);
} else {
ret = format_string(FRM("%s@%s%s"),
dir->a.imap_browser.Ch->C.username ?
dir->a.imap_browser.Ch->C.username :
"?",
dir->a.imap_browser.Ch->C.host ?
dir->a.imap_browser.Ch->C.host :
"?",
item ? ":" : "");
if (item) {
struct string * XX = ret;
ret = cat_strings(XX,item,0);
free_string(&XX);
}
}
if (ret) {
DPRINT(Debug,11,(&Debug, "browser_cat_imap=%p\n",ret));
} else {
DPRINT(Debug,11,(&Debug, "browser_cat_imap=NULL\n"));
}
return ret;
}
static int fullname_from_imap_selection P_((struct folder_browser *dir,
struct string ** Lstr,
char ** str));
static int fullname_from_imap_selection(dir,Lstr,str)
struct folder_browser *dir;
struct string ** Lstr;
char ** str;
{
int ret = 0;
struct string * relative1 = NULL;
if (!dir->sys_dir) {
DPRINT(Debug,12,(&Debug,
"fullname_from_imap_selection: On default directory\n"));
*str = safe_strdup(dir->selection->sys_name);
*Lstr = dup_string(dir->selection->disp_name);
ret = 1;
} else {
int add_sep = 0;
struct string * relative1 = NULL;
if (! dir->a.imap_browser.dir_name_cache) {
DPRINT(Debug,12,(&Debug,
"fullname_from_imap_selection: No dir_name_cache\n"));
goto fail;
}
relative1 = dup_string(dir->a.imap_browser.dir_name_cache);
if (dir->sys_dir[0]) {
int l1 = strlen(dir->sys_dir);
if ('\0' == dir->a.imap_browser.cur_sep) {
DPRINT(Debug,12,(&Debug,
"fullname_from_imap_selection: No separator\n"));
free_string(&relative1);
goto fail;
}
if (l1 > 0 && dir->a.imap_browser.cur_sep != dir->sys_dir[l1-1]) {
fill_ascii_to_string(relative1,1,dir->a.imap_browser.cur_sep);
add_sep = 1;
}
}
*Lstr = cat_strings(relative1,
dir->selection->disp_name,
0);
*str = safe_strdup(dir->sys_dir);
if (add_sep) {
char buf[2];
buf[0] = dir->a.imap_browser.cur_sep;
buf[1] = '\0';
*str = strmcat(*str,buf);
}
*str = strmcat(*str,dir->selection->sys_name);
ret = 1;
free_string(&relative1);
}
DPRINT(Debug,12,(&Debug,
"fullname_from_imap_selection: selection %S, sysname %s\n",
*Lstr,*str));
fail:
DPRINT(Debug,11,(&Debug, "fullname_from_imap_selection=%d\n",
ret));
return ret;
}
static void fix_imap_selection P_((struct folder_browser *dir));
static void fix_imap_selection(dir)
struct folder_browser *dir;
{
if (!dir->selection->sys_name) {
if (dir->selection->disp_name)
panic("MBX PANIC",__FILE__,__LINE__,
"fix_imap_selection",
"selection sys_name not given but disp_name exists",0);
dir->selection->sys_name = safe_strdup("INBOX");
dir->selection->disp_name = new_string2(display_charset,
s2us(dir->selection->sys_name));
DPRINT(Debug,11,(&Debug,
"fix_imap_selection: Assuming %s (%S)\n",
dir->selection->sys_name,
dir->selection->disp_name));
}
}
static struct folder_info *
browser_folder_from_imap P_((struct folder_browser *dir));
static struct folder_info * browser_folder_from_imap(dir)
struct folder_browser *dir;
{
struct folder_info * res = NULL;
struct string * Lstr = NULL;
char * str = NULL;
char * str1 = NULL;
/* Make backup */
char * username = safe_strdup(dir->a.imap_browser.Ch->C.username);
char * host = safe_strdup(dir->a.imap_browser.Ch->C.host);
char * Y;
DPRINT(Debug,11,(&Debug, "browser_folder_from_imap: dir=%p\n",
dir));
fix_imap_selection(dir);
if (!fullname_from_imap_selection(dir,&Lstr,&str))
goto fail;
DPRINT(Debug,12,(&Debug,
"browser_folder_from_imap: Using name %s from selection\n",
str));
res = mbx_new_folder();
if (str[0] != '/' && str[0] != '\0')
str1 = safe_strdup(":");
str1 = strmcat(str1,str);
res -> cur_folder_sys =
elm_message(FRM("%s@%s%s"),username,host,
str1);
res -> cur_folder_disp =
format_string(FRM("%s@%s%s%S"),username,host,
str[0] != '/' &&
str[0] != '\0' ? ":" : "",
Lstr);
res -> folder_type = IMAP_MBX;
res->folder_type->init_it(res);
res->p->a.imap_mbx.folder = safe_strdup(str);
res->p->a.imap_mbx.folder_name_cache = dup_string(Lstr);
/* IMAP temporary mailboxs are always put to
temp_dir */
set_remote_tempmbox(res,
& (dir->a.imap_browser.Ch->C));
if (dir->a.imap_browser.Ch->state == CON_logged) {
DPRINT(Debug,12,(&Debug,
"browser_folder_from_imap: Transfering connection to folder...\n"));
/* Transfer connection from browser to folder */
if (!join_connection(res -> p->a.imap_mbx.Ch,
&(dir->a.imap_browser.Ch->C),
dir->a.imap_browser.Ch->state))
res->p->a.imap_mbx.Ch->a.imap_con->imap_state = IMAP_error;
else {
res->p->a.imap_mbx.Ch->a.imap_con->imap_state = IMAP_idle;
res->p->a.imap_mbx.Ch->state = CON_logged;
}
/* Signal to browser_update_imaplisting() for reopening ...*/
if (NULL == dir->a.imap_browser.Ch->C.stream)
dir->a.imap_browser.Ch->state = CON_error;
/* return from backup is needed */
if (!dir->a.imap_browser.Ch->C.username) {
dir->a.imap_browser.Ch->C.username = username;
username = NULL;
}
if (!dir->a.imap_browser.Ch->C.host) {
dir->a.imap_browser.Ch->C.host = host;
host = NULL;
}
} else {
/* Glue */
if (!res -> p->a.imap_mbx.Ch->C.username) {
res -> p->a.imap_mbx.Ch->C.username = username;
username = NULL;
}
if (!res -> p->a.imap_mbx.Ch->C.host) {
res -> p->a.imap_mbx.Ch->C.host = host;
host = NULL;
}
}
fail:
if (username)
free(username);
if (host)
free(host);
free(str);
free(str1);
free_string(&Lstr);
if (res) {
DPRINT(Debug,11,(&Debug, "browser_folder_from_imap=%p\n",res));
} else {
DPRINT(Debug,11,(&Debug, "browser_folder_from_imap=NULL\n"));
}
return res;
}
static int browser_create_selection_imap P_((struct folder_browser *dir));
static int browser_create_selection_imap(dir)
struct folder_browser *dir;
{
int ret = 0;
struct string * Lstr = NULL;
char * str = NULL;
imap_states ret1;
DPRINT(Debug,11,(&Debug, "browser_create_selection_imap: dir=%p\n",
dir));
fix_imap_selection(dir);
if (!fullname_from_imap_selection(dir,&Lstr,&str))
goto fail;
DPRINT(Debug,12,(&Debug,
"browser_create_selection_imap: Using name %s (%S) from selection\n",
str,Lstr));
/* browser_folder_from_imap may close connection ... */
if (dir->a.imap_browser.Ch->state != CON_logged) {
if (!login_connection(dir->a.imap_browser.Ch,NULL))
goto fail;
}
if (!start_imap_command(dir->a.imap_browser.Ch,"CREATE"))
goto fail;
/* Mailbox name */
imap_command_push_astring(dir->a.imap_browser.Ch,str);
end_imap_command(dir->a.imap_browser.Ch);
if (!imap_command_ok(dir->a.imap_browser.Ch,&ret1,NULL))
goto fail;
ret = 1;
fail:
imap_clear_command(dir->a.imap_browser.Ch);
if (str)
free(str);
if (Lstr)
free_string(&Lstr);
DPRINT(Debug,11,(&Debug, "browser_create_selection_imap=%d\n", ret));
return ret;
}
static void zero_ws_fields_imap P_((WRITE_STATE ptr));
static void zero_ws_fields_imap(ptr)
WRITE_STATE ptr;
{
DPRINT(Debug,12,(&Debug,
"zero_ws_fields_imap; ptr=%p\n",ptr));
ptr->a.imap.literal_len = 0;
ptr->a.imap.literal = NULL;
}
static void free_ws_fields_imap P_((WRITE_STATE ptr));
static void free_ws_fields_imap(ptr)
WRITE_STATE ptr;
{
DPRINT(Debug,12,(&Debug,
"free_ws_fields_imap; ptr=%p\n",ptr));
if (ptr->a.imap.literal) {
free(ptr->a.imap.literal);
ptr->a.imap.literal_len = 0;
ptr->a.imap.literal = NULL;
}
}
static int browser_prepare_write_imap P_((struct folder_browser *dir,
WRITE_STATE ptr));
static int browser_prepare_write_imap(dir,ptr)
struct folder_browser *dir;
WRITE_STATE ptr;
{
int ret = 0;
DPRINT(Debug,11,(&Debug,
"browser_prepare_write_imap: dir=%p, ws=%p\n",
dir,ptr));
/* browser_folder_from_imap may close connection ... */
if (dir->a.imap_browser.Ch->state != CON_logged) {
if (!login_connection(dir->a.imap_browser.Ch,NULL))
goto fail;
}
/* Should we check on here that mailbox exists? */
ret = 1;
fail:
DPRINT(Debug,11,(&Debug,
"browser_prepare_write_imap=%d\n",ret));
return ret;
}
static int browser_sync_write_imap P_((struct folder_browser *dir,
WRITE_STATE ptr));
static int browser_sync_write_imap(dir,ptr)
struct folder_browser *dir;
WRITE_STATE ptr;
{
DPRINT(Debug,11,(&Debug,
"browser_sync_write_imap=1: dir=%p, ws=%p\n",
dir,ptr));
return 1;
}
static int browser_end_write_imap P_((struct folder_browser *dir,
WRITE_STATE ptr));
static int browser_end_write_imap(dir,ptr)
struct folder_browser *dir;
WRITE_STATE ptr;
{
DPRINT(Debug,11,(&Debug,
"browser_end_write_imap=1: dir=%p, ws=%p\n",
dir,ptr));
return 1;
}
static long browser_tell_imap_ws P_((struct folder_browser *dir,
WRITE_STATE write_state_ptr));
static long browser_tell_imap_ws(dir,write_state_ptr)
struct folder_browser *dir;
WRITE_STATE write_state_ptr;
{
DPRINT(Debug,11,(&Debug, "browser_tell_dummy_ws=-1: dir=%p\n",
dir));
return -1;
}
static int browser_seek_imap_ws P_((struct folder_browser *dir,
WRITE_STATE write_state_ptr,
long pos));
static int browser_seek_imap_ws(dir,write_state_ptr,pos)
struct folder_browser *dir;
WRITE_STATE write_state_ptr;
long pos;
{
DPRINT(Debug,11,(&Debug, "browser_seek_imap_ws=0: dir=%p\n",
dir));
return 0;
}
static int browser_write_imap_ws P_((struct folder_browser *dir,
WRITE_STATE ptr,
int l, const char *buffer));
static int browser_write_imap_ws(dir,ptr,l,buffer)
struct folder_browser *dir;
WRITE_STATE ptr;
int l;
CONST char *buffer;
{
int ret = 0;
DPRINT(Debug,11,(&Debug, "browser_write_imap_ws: dir=%p, ptr=%p\n",
dir,ptr));
if (l < 0) {
panic("MBX PANIC",__FILE__,__LINE__,
"browser_write_imap_ws",
"Negative write len",0);
}
ptr->a.imap.literal =
safe_realloc(ptr->a.imap.literal,
ptr->a.imap.literal_len + l + 1);
memcpy(ptr->a.imap.literal + ptr->a.imap.literal_len, buffer,l);
ptr->a.imap.literal_len += l;
ptr->a.imap.literal[ptr->a.imap.literal_len] = '\0';
ret = 1;
DPRINT(Debug,11,(&Debug, "browser_write_imap_ws=%d\n",ret));
return ret;
}
static int browser_start_we_imap P_((struct folder_browser *dir,
WRITE_STATE write_state_ptr,
int write_envelope,
struct header_rec *current_header,
int *env_flags));
static int browser_start_we_imap(dir,write_state_ptr,
write_envelope,current_header,
env_flags)
struct folder_browser *dir;
WRITE_STATE write_state_ptr;
int write_envelope;
struct header_rec *current_header;
int *env_flags;
{
int ret = 0;
struct string * Lstr = NULL;
char * str = NULL;
long flags = 0;
CONST char * LIST[ sizeof IMAP_flags /
sizeof (struct imap_flag) +1];
int j;
int X;
char *dtime = NULL;
int len;
struct tm tm_val; /* Time structure, see CTIME(3C) */
long tzmin; /* number of minutes off gmt */
int tzsign; /* + or - gmt */
DPRINT(Debug,11,(&Debug,
"browser_start_we_imap: dir=%p, ws=%p, header=%p\n",
dir,write_state_ptr,current_header));
*env_flags = WE_ADD_RETURN_PATH | WE_USE_CRLF;
free_ws_fields_imap(write_state_ptr);
fix_imap_selection(dir);
if (!fullname_from_imap_selection(dir,&Lstr,&str))
goto fail;
DPRINT(Debug,12,(&Debug,
"browser_start_we_imap: Using name %s from selection\n",
str));
if (!start_imap_command(dir->a.imap_browser.Ch,"APPEND"))
goto fail;
/* Mailbox name */
imap_command_push_astring(dir->a.imap_browser.Ch,str);
if (0 == (current_header->status & UNREAD))
flags |= IMAP_Seen;
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if (0 != (IMAP_flags[j].mask_elm & current_header->status))
flags |= IMAP_flags[j].mask_imap;
if (0 != (IMAP_flags[j].mask_elm1 & current_header->status1))
flags |= IMAP_flags[j].mask_imap;
}
DPRINT(Debug,9,(&Debug,
"browser_start_we_imap: Flags"));
X = 0;
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if (0 != (flags & IMAP_flags[j].mask_imap) &&
0 == (IMAP_Recent & IMAP_flags[j].mask_imap)) {
LIST[X++] = IMAP_flags[j].flag;
DPRINT(Debug,9,(&Debug, " %s",IMAP_flags[j].flag));
}
}
LIST[X] = NULL;
DPRINT(Debug,9,(&Debug, "\n"));
/* Flag list */
imap_command_push_list(dir->a.imap_browser.Ch,LIST);
/*
* The get_tz_mins() routine steps on the static data returned
* by localtime(), so we need to save off the value obtained here.
*/
tm_val = *localtime(& current_header->received_time);
if ((tzmin = -get_tz_mins(current_header->received_time)) >= 0) {
tzsign = '+';
} else {
tzsign = '-';
tzmin = -tzmin;
}
dtime = elm_message(FRM("%02d-%s-%04d %02d:%02d:%02d %c%02d%02d"),
tm_val.tm_mday,arpa_monname[tm_val.tm_mon],
tm_val.tm_year + 1900,
tm_val.tm_hour,tm_val.tm_min,tm_val.tm_sec,
tzsign, tzmin / 60, tzmin % 60);
if ((len = strlen(dtime)) != 26) {
DPRINT(Debug,1,(&Debug,
"browser_start_we_imap: generated date 26 char string expected, got %d chars: %s\n",
len,dtime));
panic("MBX PANIC",__FILE__,__LINE__,
"browser_start_we_imap",
"generated date-time have wrong length",0);
}
/* date-time */
imap_command_push_string(dir->a.imap_browser.Ch,dtime);
ret = 1;
fail:
/* Clear command only if we fail ... */
if (!ret)
imap_clear_command(dir->a.imap_browser.Ch);
if (str)
free(str);
if (Lstr)
free_string(&Lstr);
if (dtime)
free(dtime);
DPRINT(Debug,11,(&Debug, "browser_start_we_imap=%d\n", ret));
return ret;
}
static int browser_end_we_imap P_((struct folder_browser *dir,
WRITE_STATE write_state_ptr,
int write_envelope,
struct header_rec *current_header));
static int browser_end_we_imap(dir,write_state_ptr,
write_envelope,current_header)
struct folder_browser *dir;
WRITE_STATE write_state_ptr;
int write_envelope;
struct header_rec *current_header;
{
imap_states ret1;
int ret = 0;
DPRINT(Debug,11,(&Debug,
"browser_end_we_imap: dir=%p, ws=%p, header=%p\n",
dir,write_state_ptr,current_header));
if (0 == write_state_ptr->a.imap.literal_len)
imap_command_push_literal(dir->a.imap_browser.Ch,
write_state_ptr->a.imap.literal_len,
"");
else
imap_command_push_literal(dir->a.imap_browser.Ch,
write_state_ptr->a.imap.literal_len,
write_state_ptr->a.imap.literal);
end_imap_command(dir->a.imap_browser.Ch);
if (!imap_command_ok(dir->a.imap_browser.Ch,&ret1,NULL))
goto fail;
ret = 1;
fail:
imap_clear_command(dir->a.imap_browser.Ch);
free_ws_fields_imap(write_state_ptr);
DPRINT(Debug,11,(&Debug, "browser_end_we_imap=%d\n", ret));
return ret;
}
static int browser_selection_is_imap P_((struct folder_browser *dir,
struct folder_info *folder));
static int browser_selection_is_imap(dir,folder)
struct folder_browser *dir;
struct folder_info *folder;
{
int ret = 0;
DPRINT(Debug,11,(&Debug, "browser_selection_is_imap: dir=%p\n",
dir));
if (folder->folder_type != &imap_mbx) {
DPRINT(Debug,11,(&Debug,
"browser_selection_is_imap: Not a imap folder\n"));
ret = 0;
} else {
struct connection_cache * ch1 = dir->a.imap_browser.Ch;
struct connection_cache * ch2 = folder->p->a.imap_mbx.Ch;
if (0 != strcmp(ch1->C.username,ch2->C.username) ||
0 != strcmp(ch1->C.host,ch2->C.host)) {
DPRINT(Debug,11,(&Debug, "browser_selection_is_imap: Host or username does not match\n"));
ret = 0;
} else {
struct string * Lstr = NULL;
char * str = NULL;
fix_imap_selection(dir);
if (!fullname_from_imap_selection(dir,&Lstr,&str))
goto fail;
ret = 0 == strcmp(str,folder->p->a.imap_mbx.folder);
if (!ret) {
DPRINT(Debug,11,(&Debug,
"browser_selection_is_imap: %s <> %s\n",
str,folder->p->a.imap_mbx.folder));
}
fail:
if (str)
free(str);
if (Lstr)
free_string(&Lstr);
}
}
DPRINT(Debug,11,(&Debug, "browser_selection_is_imap=%d\n",ret));
return ret;
}
static int browser_make_ref_imap P_((struct folder_browser *dir,
char **refname, int *iscopy,
int is_text));
static int browser_make_ref_imap(dir,refname,iscopy,is_text)
struct folder_browser *dir;
char **refname;
int *iscopy;
int is_text;
{
DPRINT(Debug,11,(&Debug, "browser_make_ref_imap=0: dir=%p\n",
dir));
return 0;
}
static void browser_update_imap P_((struct folder_browser *dir));
static void browser_update_imap(dir)
struct folder_browser *dir;
{
panic("BROWSER PANIC",__FILE__,__LINE__,"browser_update_imap",
"browser_update_imap() called",0);
}
static void browser_imap_do_stat P_((struct folder_browser *dir,
int idx));
static void browser_imap_do_stat(dir,idx)
struct folder_browser *dir;
int idx;
{
panic("BROWSER PANIC",__FILE__,__LINE__,"browser_update_imap",
"browser_update_imap() called",0);
}
static int sort_by_name P_((const void *A, const void *B));
static int sort_by_name(A,B)
CONST void *A;
CONST void *B;
{
CONST struct name_vector * A1 = A;
CONST struct name_vector * B1 = B;
int r = string_cmp(A1->disp_name,
B1->disp_name,
0 /* compare fialure */);
if (0 == r)
r = strcmp(A1->sys_name,B1->sys_name);
return r;
}
static int sort_by_revname P_((const void *A, const void *B));
static int sort_by_revname(A,B)
CONST void *A;
CONST void *B;
{
return -sort_by_name(A,B);
}
S_(browser_folder_sort_dir browser_folder_sort_imap)
static void browser_folder_sort_imap P_((struct folder_browser *dir,
print_sort_message * print));
static void browser_folder_sort_imap(dir,print)
struct folder_browser *dir;
print_sort_message * print;
{
struct string * msg = NULL;
int i;
if (dir->vector_len < 2)
return;
switch (give_dt_sort_as_int(&imap_dir_sortby)) {
case ID_NONE: return;
case ID_NAME_SORT:
if (dir->vector_len > 10) {
msg = format_string(CATGETS(elm_msg_cat, MeSet, MeSortingDirByName,
"Sorting directory %S by Name"),
dir->dirname);
print(msg);
}
qsort(dir->vector,dir->vector_len,sizeof (dir->vector[0]),
sort_by_name);
break;
case REVERSE ID_NAME_SORT:
if (dir->vector_len > 10) {
msg = format_string(CATGETS(elm_msg_cat, MeSet, MeSortingDirByRevName,
"Sorting directory %S by Reverse Name"),
dir->dirname);
print(msg);
}
qsort(dir->vector,dir->vector_len,sizeof (dir->vector[0]),
sort_by_revname);
break;
}
if (msg) {
print(NULL);
free_string(&msg);
}
}
struct browser_type imap_browser = { browser_zero_imap,
browser_free_imap,
browser_change_imap,
browser_give_title_imap,
browser_separator_imap,
browser_name_imap,
browser_cat_imap,
browser_select_imap,
browser_folder_from_imap,
browser_change_v_imap,
browser_change_up_imap,
browser_create_selection_imap,
zero_ws_fields_imap,
free_ws_fields_imap,
browser_prepare_write_imap,
browser_end_write_imap,
browser_tell_imap_ws,
browser_seek_imap_ws,
browser_write_imap_ws,
browser_start_we_imap,
browser_end_we_imap,
browser_selection_is_imap,
browser_make_ref_imap,
browser_update_imap,
browser_imap_do_stat,
browser_sync_write_imap,
browser_folder_sort_imap
};
/* ----------------------------------------------------------------------- */
/* IMAP mailbox */
static void mbx_close_imap P_((struct folder_info *folder,
enum close_mode mode));
static void mbx_close_imap(folder,mode)
struct folder_info *folder;
enum close_mode mode;
{
struct connection_cache *con;
DPRINT(Debug,11,(&Debug,
"mbx_close_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
con = folder->p->a.imap_mbx.Ch;
if (con) {
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_close_imap",
"Wrong type connection attached to folder",0);
}
DPRINT(Debug,12,(&Debug, " ... caching %s\n",
IMAP_connection_cache ? "enabled" : "disabled"));
if ( 0 != (folder->p->a.imap_mbx.folder_status & IMAP_can_CLOSE) &&
IMAP_connection_cache) {
int OK = 0;
DPRINT(Debug,11,(&Debug,
"mbx_close_imap: IMAP_can_CLOSE set: Giving connection back to cache\n"));
if (start_imap_command(con,"CLOSE")) {
imap_states res;
end_imap_command(con);
OK = imap_command_ok(con,&res,NULL);
imap_clear_command(con);
}
if (OK) {
cache_connection(con);
folder->p->a.imap_mbx.Ch = NULL;
} else {
DPRINT(Debug,11,(&Debug,
"mbx_close_imap: CLOSE failed .. closing instead\n"));
close_connection(con);
}
} else {
close_connection(con);
}
} else {
DPRINT(Debug,11,(&Debug,
"mbx_close_imap: Connection already given out -- duplicate close?\n"));
}
switch (mode) {
case CLOSE_NORMAL:
/* falltru */
case CLOSE_LEAVE_LOCKED:
if (!folder -> p->fh_temp) {
DPRINT(Debug,1,(&Debug,
"mbx_close_imap: tempfile (%s) not open -- will not unlink\n",
folder -> cur_tempfolder));
} else if (unlink(folder -> cur_tempfolder) != 0) {
if (errno != ENOENT) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSorryCantUnlinkTemp,
"Sorry, can't unlink the temp file %s [%s]!\n\r"),
folder -> cur_tempfolder, error_description(errno));
}
} else {
DPRINT(Debug,1,(&Debug,
"close_folder: Unlinking tempfile (%s).\n",
folder -> cur_tempfolder));
}
}
return;
}
static int mbx_lock_imap P_((int direction,struct folder_info *folder));
static int mbx_lock_imap(direction,folder)
int direction;
struct folder_info *folder;
{
int status;
DPRINT(Debug,11,(&Debug,
"mbx_lock_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
status = 1;
folder->p->a.imap_mbx.folder_status &= ~IMAP_can_CLOSE;
DPRINT(Debug,11,(&Debug,
"mbx_lock_imap=%d\n",
status));
return status;
}
static void mbx_init_imap P_((struct folder_info *folder));
static void mbx_init_imap(folder)
struct folder_info *folder;
{
DPRINT(Debug,11,(&Debug,
"mbx_init_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
folder -> cur_tempfolder[0] = '\0';
/* remote_mbox.c sets temporary folder name ... */
folder -> p = safe_malloc(sizeof (struct private_data));
/* defined in hdrs/defs.h */
bzero((void *)folder -> p,sizeof( struct private_data));
folder -> p->fh_temp = NULL;
folder -> p->fh_folder = NULL;
folder ->p->flags1 = 0;
folder -> p->a.imap_mbx.Ch = create_connection(&IMAP_connection);
folder -> p->a.imap_mbx.Ch->f = folder; /* Back link */
folder -> p->a.imap_mbx.folder = NULL; /* Set by remote_mbox.c */
folder -> p->a.imap_mbx.folder_name_cache = NULL;
folder -> p->a.imap_mbx.folder_status = 0;
folder -> p->a.imap_mbx.flag_bits = 0;
folder -> p->a.imap_mbx.num_recent = 0;
folder -> p->a.imap_mbx.last_uid_number = 0;
folder -> p->a.imap_mbx.references = NULL;
folder -> p->a.imap_mbx.reference_count = 0;
}
static void imap_clear_buffers P_((struct folder_info *folder));
static void imap_clear_buffers(folder)
struct folder_info *folder;
{
DPRINT(Debug,12,(&Debug,
"imap_clear_buffers: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
folder -> p->a.imap_mbx.folder_status = 0;
folder -> p->a.imap_mbx.flag_bits = 0;
folder -> p->a.imap_mbx.last_uid_number = 0;
folder -> p->a.imap_mbx.num_recent = 0;
if (folder -> p->a.imap_mbx.references) {
int i;
for (i = 0; i < folder -> p->a.imap_mbx.reference_count; i++) {
free_reference(&(folder -> p->a.imap_mbx.references[i]));
}
free(folder -> p->a.imap_mbx.references);
folder -> p->a.imap_mbx.references = NULL;
folder -> p->a.imap_mbx.reference_count = 0;
}
}
static int imap_open_connection P_((struct folder_info *folder));
static int imap_open_connection(folder)
struct folder_info *folder;
{
struct connection_cache *con;
CONST struct string *XXX;
int status = 0;
DPRINT(Debug,12,(&Debug,
"imap_open_connection: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"imap_open_connection",
"Wrong type connection attached to folder",0);
}
/* Clear buffer from old data ... */
imap_clear_buffers(folder);
if (!login_connection(con,NULL)) {
status = 0;
goto clean;
}
status = 1;
/* canonify folder name .... */
free(folder->cur_folder_sys);
folder->cur_folder_sys = elm_message(FRM("%s@%s%s%s"),
con->C.username,
con->C.host,
folder->p->a.imap_mbx.folder[0] == '/'
? "" : ":",
folder->p->a.imap_mbx.folder);
XXX = folder->p->a.imap_mbx.folder_name_cache;
free_string(&(folder->cur_folder_disp));
if (!XXX || 0 == string_len(XXX)) {
DPRINT(Debug,12,(&Debug,
"imap_open_connection: No printable folder name available\n"));
DPRINT(Debug,12,(&Debug,
"imap_open_connection: using raw folder name: %s\n",folder->p->a.imap_mbx.folder));
folder->cur_folder_disp = format_string(FRM("%s@%s%s%s"),
con->C.username,
con->C.host,
folder->p->a.imap_mbx.folder[0] == '/'
? "" : ":",
folder->p->a.imap_mbx.folder);
} else {
folder->cur_folder_disp = format_string(FRM("%s@%s%s%S"),
con->C.username,
con->C.host,
folder->p->a.imap_mbx.folder[0]
== '/' ? "" : ":",
XXX);
}
clean:
DPRINT(Debug,12,(&Debug,
"imap_open_connection=%d\n",
status));
return status;
}
static int mbx_sessionlock_imap P_((struct folder_info *folder,
enum sessionlock_mode mode));
static int mbx_sessionlock_imap(folder,mode)
struct folder_info *folder;
enum sessionlock_mode mode;
{
struct connection_cache *con;
imap_states res;
int status = 0;
DPRINT(Debug,11,(&Debug,
"mbx_sessionlock_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_sessionlock_imap",
"Wrong type connection attached to folder",0);
}
if (NULL == con->C.stream ||
con->state != CON_logged ) {
if (!imap_open_connection(folder)) {
status = 0;
goto clean;
}
folder->p->a.imap_mbx.folder_status = 0;
}
if (0 == (folder->p->a.imap_mbx.folder_status & IMAP_folder_open)) {
switch(mode) {
case SESSIONLOCK_NONE:
if (IMAP_use_examine) {
if (!start_imap_command(con,"EXAMINE"))
goto clean;
break;
}
/* FALLTHRU */
default:
DPRINT(Debug,5,(&Debug,
"mbx_sessionlock_imap: Assuming writable folder (for while)\n"));
folder->p->a.imap_mbx.folder_status |= IMAP_writable;
if (!start_imap_command(con,"SELECT"))
goto clean;
break;
}
imap_command_push_astring(con,folder -> p->a.imap_mbx.folder);
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
folder->p->a.imap_mbx.folder_status |= IMAP_folder_open;
}
switch(mode) {
int need_reopen;
case SESSIONLOCK_NONE:
status = 1;
goto clean;
case SESSIONLOCK_TRUNCATE:
need_reopen = 1;
#ifdef FTRUNCATE
if (folder->p->fh_temp) {
rewind(folder->p->fh_temp);
if (0 == ftruncate(fileno(folder->p->fh_temp),0)) {
need_reopen = 0;
DPRINT(Debug,10,(&Debug,
"truncate_tempfolder: tempfile %s truncated (not recreated)\n",
folder->cur_tempfolder));
}
else
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmSorryCantTruncateTemp,
"Sorry, can't truncate the temp file %s [%s]!"),
folder -> cur_tempfolder, error_description(errno));
/* IF FAIL REOPEN IT INSTEAD */
}
#endif
if (!need_reopen)
break; /* DONE */
if (folder->p->fh_temp) {
fclose (folder->p->fh_temp);
folder->p->fh_temp = NULL;
}
if (0 != unlink(folder -> cur_tempfolder)) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSorryCantTruncateTemp,
"Sorry, can't truncate the temp file %s [%s]!"),
folder -> cur_tempfolder, error_description(err));
status = 0;
goto clean;
}
goto create_it;
case SESSIONLOCK_REOPEN:
if (folder -> p->fh_temp) {
int temp_handle;
fclose(folder -> p->fh_temp);
folder -> p->fh_temp = NULL;
if ( (temp_handle = open(folder-> cur_tempfolder,
O_RDWR,
0600)) == -1 ||
NULL == (folder -> p->fh_temp = fdopen(temp_handle,"w+"))) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmSorryCantReopenTemp,
"Sorry, can't reopen the temp file %s [%s]!"),
folder -> cur_tempfolder, error_description(err));
if (-1 != temp_handle)
close(temp_handle);
status = 0;
goto clean;
}
break;
}
/* FALLTHRU */
case SESSIONLOCK_NORMAL:
case SESSIONLOCK_CHECK:
create_it:
if (!folder -> p->fh_temp) {
int temp_handle;
/* If we are changing files and we are changing to a spool file,
* make sure there isn't a temp file for it, because if
* there is, someone else is using ELM to read the new file,
* and we don't want to be reading it at the same time.
*/
if ( (temp_handle = open(folder-> cur_tempfolder,
O_RDWR|O_CREAT|O_EXCL,
0600)) == -1) {
int err = errno;
if (err == EEXIST) {
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmAlreadyRunningX,
"You seem to have ELM already reading this mail! You may not have two copies of\n\
ELM running simultaneously. If this is in error, then you'll need to remove \n\
the following file: %s"),
folder -> cur_tempfolder);
wait_for_timeout(5 + 6 * sleepmsg);
status = 0;
goto clean;
}
}
if (-1 == temp_handle ||
NULL == (folder -> p->fh_temp = fdopen(temp_handle,"w+"))) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFailedCreateTmpFolder,
"Failed to create %.50s: %.60s"),
folder -> cur_tempfolder,
error_description(err));
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmIGiveUp,
"Ahhhh... I give up."));
if (-1 != temp_handle)
close(temp_handle);
status = 0;
goto clean;
}
DPRINT(Debug,1,(&Debug,
"Creating tempfile (%s).\n",
folder -> cur_tempfolder));
elm_chown(folder->cur_tempfolder, userid, groupid);
chmod(folder -> cur_tempfolder, 0700);
/* shut off file for other people! */
}
break;
}
status = 1;
clean:
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"mbx_sessionlock_imap=%d\n",
status));
return status;
}
static int mbx_unlock_imap P_((int interrupt, struct folder_info *folder));
static int mbx_unlock_imap(interrupt,folder)
int interrupt;
struct folder_info *folder;
{
int status = 0;
DPRINT(Debug,11,(&Debug,
"mbx_unlock_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
status = 1;
DPRINT(Debug,11,(&Debug,
"mbx_unlock_imap=%d\n",
status));
return status;
}
static void mbx_flush_imap P_((struct folder_info *folder));
static void mbx_flush_imap(folder)
struct folder_info *folder;
{
struct connection_cache *con;
DPRINT(Debug,11,(&Debug,
"mbx_flush_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
con = folder->p->a.imap_mbx.Ch;
if (con) {
#if 0
imap_states res;
#endif
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_flush_imap",
"Wrong type connection attached to folder",0);
}
mbx_flush_temp(folder);
#if 0
/* It does not make sense on here to force IMAP server to flush
* mailbox -- that flushing is needed only in
* mbx_end_keep_imap()
*/
if (!start_imap_command(con,"CHECK")) {
DPRINT(Debug,11,(&Debug,
"mbx_flush_imap: Setting IMAP_flusherr\n"));
folder->p->a.imap_mbx.folder_status |= IMAP_flusherr;
goto clean;
}
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL)) {
folder->p->a.imap_mbx.folder_status |= IMAP_flusherr;
DPRINT(Debug,11,(&Debug,
"mbx_flush_imap: Setting IMAP_flusherr\n"));
}
clean:
#endif
imap_clear_command(con);
} else {
DPRINT(Debug,11,(&Debug,
"mbx_flush_imap: No connection structure left...\n"));
}
}
static int mbx_ferror_imap P_((struct folder_info *folder, int clean));
static int mbx_ferror_imap(folder,clean)
struct folder_info *folder;
int clean;
{
int status = 0;
DPRINT(Debug,11,(&Debug,
"mbx_ferror_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
if (0 != (folder->p->a.imap_mbx.folder_status & IMAP_flusherr)) {
DPRINT(Debug,11,(&Debug,
"mbx_ferror_imap: folder_status ~ IMAP_flusherr is set\n"));
status = 1;
}
if (clean)
folder->p->a.imap_mbx.folder_status &= ~IMAP_flusherr;
if (folder->p->fh_temp) {
if (ferror(folder->p->fh_temp))
status = 1;
if (clean)
clearerr(folder->p->fh_temp);
}
DPRINT(Debug,11,(&Debug,
"mbx_ferror_imap=%d\n",status));
return status;
}
static int mbx_prepare_read_imap P_((struct folder_info *folder,
enum prepare_mode mode,
READ_STATE read_state_ptr));
static int mbx_prepare_read_imap(folder,mode,read_state_ptr)
struct folder_info *folder;
enum prepare_mode mode;
READ_STATE read_state_ptr;
{
struct connection_cache *con;
int status = 0, i,start;
int add_new_only = PREPARE_NEW_ONLY == mode ||
PREPARE_NEW_ONLY_NOLOCK == mode ||
PREPARE_ACCESS == mode;
DPRINT(Debug,11,(&Debug,
"mbx_prepare_read_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_prepare_read_imap",
"Wrong type connection attached to folder",0);
}
/* Wait that IDLE timer command is completetd so that there
* is no EXPUNGE coming...
*/
imap_idle_wait(folder);
if (add_new_only) {
/* start from unread messages */
read_state_ptr->a.imap_mbx.current_message.uid_number =
folder -> p->a.imap_mbx.last_uid_number+1;
start = 1;
while (start < folder -> p->a.imap_mbx.reference_count &&
folder -> p->a.imap_mbx.references[start].uid_number != -1 &&
folder -> p->a.imap_mbx.references[start].uid_number <
read_state_ptr->a.imap_mbx.current_message.uid_number)
start++;
if (!folder->p->fh_temp) {
DPRINT(Debug,11,(&Debug,
"mbx_prepare_read_imap -- NO temp file %s\n",
folder->cur_tempfolder));
} else if (fseek(folder -> p->fh_temp,
folder -> mailfile_size, SEEK_SET) == -1) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCouldntSeekTempEnd,
"\nCouldn't fseek to end of temp mbox.\n"));
lib_error(FRM("** %s. **\n"), error_description(err));
DPRINT(Debug,1,(&Debug,
"Error: Couldn't fseek to end of reopened temp mbox. errno %s (%s)\n",
error_description(err), "mbx_prepare_read_imap"));
status = 0;
goto clean;
}
read_state_ptr->fbytes = folder-> mailfile_size; /* start correctly */
} else {
/* Start from beginning */
read_state_ptr->a.imap_mbx.current_message.uid_number = 1;
start = 1;
}
DPRINT(Debug,11,(&Debug,
" : UID start=%d start=%d refcount=%d\n",
read_state_ptr->a.imap_mbx.current_message.uid_number,start,
folder -> p->a.imap_mbx.reference_count));
if (start <= folder -> p->a.imap_mbx.reference_count-1) {
CONST char * L[] = { "RFC822.SIZE", "UID", NULL };
imap_states res;
if (!start_imap_command(con,"FETCH")) {
status = 0;
goto clean;
}
imap_command_push_range(con,
start,
folder -> p->a.imap_mbx.reference_count-1);
imap_command_push_list(con,L);
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
}
/* Estimate new size for percent display */
for ( i = start; i < folder -> p->a.imap_mbx.reference_count; i++) {
folder -> mailfile_size +=
folder-> p->a.imap_mbx.references[i].rfc822_size;
folder -> p->a.imap_mbx.last_uid_number =
folder-> p->a.imap_mbx.references[i].uid_number;
}
read_state_ptr->a.imap_mbx.data_idx = 0;
status = 1;
DPRINT(Debug,11,(&Debug,
" new last UID %d\n",
folder -> p->a.imap_mbx.last_uid_number));
clean:
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"mbx_prepare_read_imap=%d\n",status));
return status;
}
static int mbx_end_read_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr,
int silent));
static int mbx_end_read_imap(folder,read_state_ptr,silent)
struct folder_info *folder;
READ_STATE read_state_ptr;
int silent;
{
int status = 0;
DPRINT(Debug,11,(&Debug,
"mbx_end_read_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
if (PREPARE_ACCESS != read_state_ptr->mode) {
if (folder -> p->a.imap_mbx.reference_count > 1 &&
folder-> p->a.imap_mbx.references[folder ->
p->a.imap_mbx.reference_count-1].
uid_number > read_state_ptr->a.imap_mbx.current_message.uid_number) {
if (!silent) {
lib_error(CATGETS(elm_msg_cat, MeSet,MeMessagesNotReadImap,
"Messages not read; current UID=%d, last UID=%d"),
read_state_ptr->a.imap_mbx.current_message.uid_number,
folder-> p->a.imap_mbx.references[folder ->
p->a.imap_mbx.
reference_count-1].
uid_number);
}
status = 0;
goto clean;
}
}
if (!folder->p->fh_temp) {
DPRINT(Debug,11,(&Debug,
"mbx_end_read_imap=1 -- NO temp file %s\n",
folder->cur_tempfolder));
return 1;
}
if ((ferror(folder->p->fh_temp)) ||
(fflush(folder->p->fh_temp) == EOF)) {
if (!silent) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet, ElmFlushOnTempFailed,
"Flush on temp file %s failed: %s"),
folder -> cur_tempfolder,
error_description(err));
}
DPRINT(Debug,1,(&Debug, "Can't flush on temp file %s!!\n",
folder -> cur_tempfolder));
status = 0;
goto clean;
}
/* Use tempfile size as folder size */
folder-> mailfile_size = file_bytes(folder-> cur_tempfolder);
rewind(folder->p->fh_temp);
status = 1;
clean:
DPRINT(Debug,11,(&Debug,
"mbx_end_read_imap=%d\n",status));
return status;
}
static void info_zero_imap_mbx P_((struct mbx_hdr_info *info));
static void info_zero_imap_mbx(info)
struct mbx_hdr_info *info;
{
info->a.imap_mbx.last_mbx_index = -1;
info->a.imap_mbx.uid_number = 0;
}
static void info_free_imap_mbx P_((struct mbx_hdr_info *info));
static void info_free_imap_mbx(info)
struct mbx_hdr_info *info;
{
info->a.imap_mbx.last_mbx_index = -1;
info->a.imap_mbx.uid_number = 0;
}
static void info_imap_mbx_status P_((struct mbx_hdr_info *info,
char status_buffer[3]));
static void info_imap_mbx_status(info,status_buffer)
struct mbx_hdr_info *info;
char status_buffer[3];
{
if (-1 == info->a.imap_mbx.last_mbx_index)
status_buffer[0] = 'X';
}
static struct info_type imap_mbx_info = { info_zero_imap_mbx,
info_free_imap_mbx,
info_imap_mbx_status
};
static int mbx_copy_envelope_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr,
struct header_rec *entry,
int force));
static int mbx_copy_envelope_imap(folder,read_state_ptr,entry,force)
struct folder_info *folder;
READ_STATE read_state_ptr;
struct header_rec *entry;
int force;
{
struct connection_cache *con;
int status = 0;
imap_states res;
struct imap_reference *ref = NULL;
int l,j;
CONST char * L[] = { "FLAGS", "INTERNALDATE", "BODY.PEEK[HEADER]", NULL };
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_copy_envelope_imap",
"Wrong type connection attached to folder",0);
}
/* Wait that IDLE timer command is completed so that there
* is no EXPUNGE coming...
*/
imap_idle_wait(folder);
if (PREPARE_ACCESS == read_state_ptr->mode) {
if(entry->mbx_info &&
&imap_mbx_info == entry->mbx_info->type_code) {
/* Hack it */
read_state_ptr->a.imap_mbx.current_message =
entry->mbx_info->a.imap_mbx;
ref =
find_reference(& (folder -> p->a.imap_mbx),
& (entry->mbx_info->a.imap_mbx));
}
if (!ref) {
DPRINT(Debug,11,(&Debug,
" : Unable to access message\n"));
status = 0;
goto clean;
}
} else {
do {
ref = find_reference(& (folder -> p->a.imap_mbx),
& (read_state_ptr->a.imap_mbx.current_message));
if (!ref) {
CONST int count = folder -> p->a.imap_mbx.reference_count;
if (count <= 1 ||
folder-> p->a.imap_mbx.references[count-1].uid_number <
read_state_ptr->a.imap_mbx.current_message.uid_number) {
DPRINT(Debug,11,(&Debug,
" : End of messages\n"));
status = 0;
goto clean;
}
increment_uid_number(& (folder -> p->a.imap_mbx),
& (read_state_ptr->a.imap_mbx.
current_message));
}
} while (!ref);
/* Do not reset status if PREPARE_ACCESS */
entry->status = VISIBLE;
}
read_state_ptr -> linecounter = 0; /* Linecounter of current
message */
read_state_ptr -> skipping = 0; /* not skipping */
entry->offset = read_state_ptr -> fbytes; /* Offset of current message */
DPRINT(Debug,12,(&Debug,
" : temp file offset %ld\n",
entry->offset));
if (!start_imap_command(con,"FETCH"))
goto clean;
imap_command_push_number(con,
read_state_ptr->
a.imap_mbx.current_message.last_mbx_index);
imap_command_push_list(con,L);
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
/* Check that message is not deleted meanwhile --
Should not happen: EXPUNGE response is not allowed during of FETCH
*/
ref = find_reference(& (folder -> p->a.imap_mbx),
& (read_state_ptr->a.imap_mbx.current_message));
if (!ref) {
DPRINT(Debug,1,(&Debug,
"mbx_copy_envelope_imap: EXPUNGE middle of FETCH ???\n"));
goto clean;
}
if (!ref->header) {
DPRINT(Debug,1,(&Debug,
"mbx_copy_envelope_imap: BODY[HEADER] not got\n"));
goto clean;
}
if (!ref->internaldate) {
DPRINT(Debug,1,(&Debug,
"mbx_copy_envelope_imap: INTERNADATE not got\n"));
goto clean;
}
if (imap_max_dl_size >= 0 &&
ref->rfc822_size > imap_max_dl_size &&
!force) {
/* Need skip this */
read_state_ptr-> skipping = 1;
entry->offset = -1;
DPRINT(Debug,8,(&Debug,
"mbx_copy_envelope_imap: Size %ld -- skipping this message (only read headers)\n",
ref->rfc822_size));
read_state_ptr->skip_count++;
read_state_ptr->skip_bytes += ref->rfc822_size;
/* Hack -- no line numbers available */
entry->lines = -1;
}
DPRINT(Debug,14,(&Debug,
"mbx_copy_envelope_imap: Flags"));
for (j = 0; j < sizeof IMAP_flags / sizeof (struct imap_flag); j++)
if (0 != (ref->imap_flags & IMAP_flags[j].mask_imap)) {
entry->status |= IMAP_flags[j].mask_elm;
entry->status1 |= IMAP_flags[j].mask_elm1;
DPRINT(Debug,14,(&Debug, " %s", IMAP_flags[j].flag));
}
DPRINT(Debug,14,(&Debug, "\n"));
if (0 == (ref->imap_flags & IMAP_Seen))
entry->status |= UNREAD;
else if (0 != (entry->status & NEW)) {
/* If both \Recent and \Seen is set then we have re$syncroning
* with new mail for this session (which however is already
* read), so ignore NEW flag
*/
DPRINT(Debug,14,(&Debug,
"mbx_copy_envelope_imap: Both \\Seen and \\Recent set -- ignore \\Recent (NEW)\n"));
clearit(entry->status, NEW); /* it's been read now! */
}
if (! read_state_ptr -> skipping) {
char * data = NULL;
char * tmp;
/* Very lazy method to get something to put "From " -separator */
if (0 == memcmp("Return-Path: ",ref->header,13)) {
int i;
char * XXX;
for (i=13; i < ref->header_len; i++) {
if ('\r' == ref->header[i] ||
'\n' == ref->header[i])
break;
}
XXX = safe_malloc(i-12);
memcpy(XXX,ref->header+13,i-13);
XXX[i-13]='\0';
data = return_path_to_env_from_1(XXX); /* result MALLOCED */
free(XXX);
if (!data)
data = safe_strdup("nobody@localhost");
} else
data = safe_strdup("nobody@localhost");
/* We do not generate weekday -- real_from() does not care */
tmp = elm_message(FRM("From %s XXX %3.3s %2.2s %8.8s %5.5s %4.4s\n"),
data,
ref->internaldate+3,
ref->internaldate,
ref->internaldate+12,
ref->internaldate+21,
ref->internaldate+7);
free(data);
data = tmp;
if (!real_from(tmp,entry)) {
DPRINT(Debug,1,(&Debug,
"mbx_copy_envelope_imap: real_from() failed with generated separator line: %s\n",
tmp));
free(data);
data = NULL;
goto clean;
}
entry->env_from[0] = '\0'; /* Env from is bogus ... */
data = tmp;
l = strlen(data);
/* notice that on mailbox separator line we don't use \r\n
* that even when on 'binary' mail message lines are terminated with
* \r\n
*/
if (!mbx_copy_line_to_temp(folder,data,l)) {
status = 0;
free(data);
data = NULL;
goto clean;
}
read_state_ptr -> fbytes += l;
read_state_ptr -> linecounter++;
free(data);
data = NULL;
}
read_state_ptr->a.imap_mbx.data_idx = 0;
change_rec_mbx_info(entry,&imap_mbx_info);
entry->mbx_info->a.imap_mbx = read_state_ptr->a.imap_mbx.current_message;
status = 1;
clean:
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_imap=%d\n",
status));
return status;
}
static CONST char * mbx_is_forwarded_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr));
static CONST char * mbx_is_forwarded_imap(folder,read_state_ptr)
struct folder_info *folder;
READ_STATE read_state_ptr;
{
DPRINT(Debug,11,(&Debug,
"mbx_is_forwarded_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
DPRINT(Debug,11,(&Debug,
"mbx_is_forwarded_imap=NULL\n"));
return NULL;
}
static int imap_got_header_line P_((struct folder_info *folder,
READ_STATE read_state_ptr,
char **buffer,
int *len,
struct imap_reference *ref));
static int imap_got_header_line(folder,read_state_ptr,buffer,len,ref)
struct folder_info *folder;
READ_STATE read_state_ptr;
char **buffer;
int *len;
struct imap_reference *ref;
{
int i;
if (read_state_ptr->a.imap_mbx.data_idx >= ref->header_len) {
DPRINT(Debug,65,(&Debug,
"imap_got_header_line=0\n"));
return 0;
}
for (i = read_state_ptr->a.imap_mbx.data_idx;
i < ref->header_len-1; i++) {
if ('\r' == ref->header[i] &&
'\n' == ref->header[i+1]) {
i += 2;
goto found;
}
}
i = ref->header_len;
DPRINT(Debug,10,(&Debug,"imap_got_header_line: EOLN NOT FOUND\n"));
found:
*len = i - read_state_ptr->a.imap_mbx.data_idx;
*buffer = safe_malloc(*len+1);
memcpy(*buffer,
ref->header + read_state_ptr->a.imap_mbx.data_idx,
*len);
(*buffer)[*len] = '\0';
DPRINT(Debug,65,(&Debug,
"imap_got_header_line=1: len=%d, buffer=%.*s",
*len,*len,(*buffer) ? (*buffer) : ""));
if (*len < 1 || !(*buffer) || (*buffer)[*len -1] != '\n') {
DPRINT(Debug,65,(&Debug,
"\nimap_got_header_line: NO NEWLINE\n"));
}
return 1;
}
static int imap_got_body_line P_((struct folder_info *folder,
READ_STATE read_state_ptr,
char **buffer,
int *len,
struct imap_reference *ref));
static int imap_got_body_line(folder,read_state_ptr,buffer,len,ref)
struct folder_info *folder;
READ_STATE read_state_ptr;
char **buffer;
int *len;
struct imap_reference *ref;
{
int i;
if (read_state_ptr->a.imap_mbx.data_idx >= ref->body_len) {
DPRINT(Debug,65,(&Debug,
"imap_got_body_line=0\n"));
return 0;
}
for (i = read_state_ptr->a.imap_mbx.data_idx;
i < ref->body_len-1; i++) {
if ('\r' == ref->body[i] &&
'\n' == ref->body[i+1]) {
i += 2;
goto found;
}
}
i = ref->body_len;
DPRINT(Debug,10,(&Debug,"imap_got_body_line: EOLN NOT FOUND\n"));
found:
*len = i - read_state_ptr->a.imap_mbx.data_idx;
*buffer = safe_malloc(*len+1);
memcpy(*buffer,
ref->body + read_state_ptr->a.imap_mbx.data_idx,
*len);
DPRINT(Debug,65,(&Debug,
"imap_got_body_line=1: len=%d, buffer=%.*s",
*len,*len,(*buffer) ? (*buffer) : ""));
if (*len < 1 || !(*buffer) || (*buffer)[*len -1] != '\n') {
DPRINT(Debug,65,(&Debug,
"\nimap_got_body_line: NO NEWLINE\n"));
}
return 1;
}
static void imap_accept_line P_((struct folder_info *folder,
READ_STATE read_state_ptr,
char **buffer, int *len));
static void imap_accept_line(folder,read_state_ptr,buffer,len)
struct folder_info *folder;
READ_STATE read_state_ptr;
char **buffer;
int *len;
{
if (!*buffer)
return;
if (*len < 1 || (*buffer)[*len -1] != '\n') {
} else {
read_state_ptr -> linecounter++;
}
if (! read_state_ptr -> skipping)
read_state_ptr -> fbytes += *len;
read_state_ptr->a.imap_mbx.data_idx += *len;
free(*buffer);
*buffer = NULL;
*len = 0;
}
static int mbx_copy_header_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr,
char **buffer, int *len));
static int mbx_copy_header_imap(folder,read_state_ptr,buffer,len)
struct folder_info *folder;
READ_STATE read_state_ptr;
char **buffer;
int *len;
{
struct connection_cache *con;
int status = 0;
char *buffer1 = NULL;
int len1;
struct imap_reference *ref = NULL;
CONST char * L[] = { "BODY.PEEK[TEXT]", NULL };
DPRINT(Debug,11,(&Debug,
"mbx_copy_header_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_copy_header_imap",
"Wrong type connection attached to folder",0);
}
/* Wait that IDLE timer command is completetd so that there
* is no EXPUNGE coming...
*/
imap_idle_wait(folder);
/* Check that message is not deleted meanwhile --
Should not happen: EXPUNGE response is not allowed during of FETCH
*/
ref = find_reference(& (folder -> p->a.imap_mbx),
& (read_state_ptr->a.imap_mbx.current_message));
if (!ref) {
DPRINT(Debug,1,(&Debug,
"mbx_copy_header_imap=0: EXPUNGE middle of FETCH ???\n"));
return 0;
}
hide:
if (buffer1) {
DPRINT(Debug,11,(&Debug, " ... Freeing buffer1=%s\n",buffer1));
free(buffer1);
buffer1 = NULL;
}
if (!imap_got_header_line(folder,read_state_ptr,&buffer1,&len1,ref)) {
status = 0;
goto clean;
}
if ((len1 == 1 && 0 == memcmp(buffer1,"\n",len1)) ||
(len1 == 2 && 0 == memcmp(buffer1,"\r\n",len1))) {
imap_states res;
if (! read_state_ptr -> skipping) {
/* End of headers */
if (!mbx_copy_line_to_temp(folder,buffer1,len1)) {
status = 0;
goto clean;
}
}
imap_accept_line(folder,read_state_ptr,
&buffer1,&len1);
status = 0;
read_body:
/* Free used data */
free(ref->header);
ref->header = NULL;
ref->header_len = 0;
if (! read_state_ptr -> skipping) {
/* FETCH body */
if (!start_imap_command(con,"FETCH"))
goto clean;
imap_command_push_number(con,
read_state_ptr->
a.imap_mbx.current_message.last_mbx_index);
imap_command_push_list(con,L);
end_imap_command(con);
imap_command_ok(con,&res,NULL);
}
goto clean;
}
if (NULL == memchr(buffer1,':',len1)) {
/* End of headers -- bad header */
DPRINT(Debug,1,(&Debug,
"mbx_copy_header_imap: IMAP server is returning bad header...\n"));
status = 0;
goto read_body;
}
do {
append_buffer(buffer,len,buffer1,len1);
if (! read_state_ptr -> skipping) {
if (!mbx_copy_line_to_temp(folder,buffer1,len1)) {
status = 0;
goto clean;
}
}
imap_accept_line(folder,read_state_ptr,
&buffer1,&len1);
if (!imap_got_header_line(folder,read_state_ptr,
&buffer1,&len1,ref)) {
break;
}
} while (len1 > 0 && whitespace(buffer1[0]));
if (*buffer && *len > 6 &&
header_cmp(*buffer, "Status", NULL)) {
DPRINT(Debug,11,(&Debug,
" : Hiding header %s",*buffer));
free(*buffer);
*buffer = NULL;
*len = 0;
goto hide;
}
status = 1;
clean:
if (buffer1) {
free(buffer1);
buffer1 = NULL;
}
if (!status) {
/* So that reading of body starts correctly */
read_state_ptr->a.imap_mbx.data_idx = 0;
if (*buffer) {
free(*buffer);
*buffer = NULL;
}
}
DPRINT(Debug,40,(&Debug,
"mbx_copy_header_imap=%d | len=%d, buffer=%s",
status,*len,
*buffer ? *buffer : "NULL"));
if (*len < 1 || !*buffer || (*buffer)[*len -1] != '\n') {
DPRINT(Debug,40,(&Debug,
"\nmbx_copy_header_imap <- NO NEWLINE\n"));
}
return status;
}
static int mbx_copy_body_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr,
char **buffer, int *len,
long *content_remaining));
static int mbx_copy_body_imap(folder,read_state_ptr,buffer,len,
content_remaining)
struct folder_info *folder;
READ_STATE read_state_ptr;
char **buffer;
int *len;
long *content_remaining;
{
char *buffer1 = NULL;
int len1;
int status = 0;
struct imap_reference *ref = NULL;
DPRINT(Debug,11,(&Debug,
"mbx_copy_body_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
if (read_state_ptr -> skipping) {
return 0;
}
/* Check that message is not deleted meanwhile --
Should not happen: EXPUNGE response is not allowed during of FETCH
*/
ref = find_reference(& (folder -> p->a.imap_mbx),
& (read_state_ptr->a.imap_mbx.current_message));
if (!ref) {
DPRINT(Debug,1,(&Debug,
"mbx_copy_body_imap=0: EXPUNGE middle of FETCH ???\n"));
return 0;
}
if (!imap_got_body_line(folder,read_state_ptr,
&buffer1,&len1,ref)) {
status = 0;
goto clean;
}
append_buffer(buffer,len,buffer1,len1);
if (!mbx_copy_line_to_temp(folder,buffer1,len1)) {
status = 0;
goto clean;
}
imap_accept_line(folder,read_state_ptr,
&buffer1,&len1);
status = 1;
clean:
if (buffer1) {
free(buffer1);
buffer1 = NULL;
}
if (!status) {
if (*buffer) {
free(*buffer);
*buffer = NULL;
}
}
DPRINT(Debug,40,(&Debug,
"mbx_copy_body_imap=%d | len=%d, buffer=%s",
status,*len,
*buffer ? *buffer : "NULL"));
if (*len < 1 || !*buffer || (*buffer)[*len -1] != '\n') {
DPRINT(Debug,40,(&Debug,
"\nmbx_copy_body_imap <- NO NEWLINE\n"));
}
return status;
}
static int mbx_copy_envelope_end_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr));
static int mbx_copy_envelope_end_imap(folder,read_state_ptr)
struct folder_info *folder;
READ_STATE read_state_ptr;
{
struct connection_cache *con;
int status = 0;
struct imap_reference *ref = NULL;
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_end_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_copy_envelope_end_imap",
"Wrong type connection attached to folder",0);
}
/* Check that message is not deleted meanwhile --
Should not happen: EXPUNGE response is not allowed during of FETCH
*/
ref = find_reference(& (folder -> p->a.imap_mbx),
& (read_state_ptr->a.imap_mbx.current_message));
if (ref) {
if (ref->header) {
/* Free used data */
free(ref->header);
ref->header = NULL;
ref->header_len = 0;
}
if (ref->body) {
/* Free used data */
free(ref->body);
ref->body = NULL;
ref->body_len = 0;
}
/* Got next UID */
if (ref+1 <
folder -> p->a.imap_mbx.references +
folder -> p->a.imap_mbx.reference_count) {
read_state_ptr->a.imap_mbx.current_message.uid_number =
(ref+1)->uid_number;
read_state_ptr->a.imap_mbx.current_message.last_mbx_index =
(ref+1) - folder -> p->a.imap_mbx.references;
} else {
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_end_imap: End of mailbox\n"));
read_state_ptr->a.imap_mbx.current_message.uid_number++;
}
} else {
read_state_ptr->a.imap_mbx.current_message.uid_number++;
DPRINT(Debug,1,(&Debug,
"mbx_copy_envelope_end_imap: EXPUNGE middle of FETCH ???\n"));
}
if (! read_state_ptr -> skipping) {
/* On mailbox format there is empty line (\n\n)
* on end of mail...
*/
if (!mbx_copy_line_to_temp(folder,"\n",1)) {
status = 0;
goto clean;
}
read_state_ptr -> fbytes ++; /* Increment position */
}
status = 1;
clean:
read_state_ptr -> skipping = 0;
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_end_imap=%d\n",
status));
return status; /* Always end of message */
}
static int mbx_copy_envelope_reset_body_imap P_((struct folder_info *folder,
READ_STATE read_state_ptr));
static int mbx_copy_envelope_reset_body_imap(folder,read_state_ptr)
struct folder_info *folder;
READ_STATE read_state_ptr;
{
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_reset_body_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
" : read_state_ptr=%p\n",
read_state_ptr));
DPRINT(Debug,11,(&Debug,
"mbx_copy_envelope_reset_body_imap=0\n"));
return 0;
}
static FILE * mbx_imap_to_fd P_((struct folder_info *folder,long offset));
static FILE * mbx_imap_to_fd(folder,offset)
struct folder_info *folder;
long offset;
{
FILE * status = NULL;
DPRINT(Debug,11,(&Debug,
"mbx_imap_to_fd: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
if (offset != -1L &&
fseek(folder->p->fh_temp, offset ,
SEEK_SET) == -1) {
int err = errno;
lib_error(CATGETS(elm_msg_cat, ElmSet,
ElmCouldntSeekBytesIntoFolder,
"\nCouldn't seek %ld bytes into folder.\n"),
offset);
lib_error(FRM("** %s. **\n"), error_description(err));
DPRINT(Debug,1,(&Debug,
"Error: Couldn't seek folder %s: (offset %ld) Errno %s (%s)\n",
folder->cur_tempfolder, offset,
error_description(err), "mbx_imap_to_fd"));
status = NULL;
goto clean;
}
status = folder->p->fh_temp;
clean:
DPRINT(Debug,11,(&Debug,
"mbx_imap_to_fd=%s (%p)\n",
status ? "NON-NULL" : "NULL",
status));
return status;
}
static int mbx_new_mail_on_imap P_((struct folder_info *folder,int *bytes));
static int mbx_new_mail_on_imap(folder,bytes)
struct folder_info *folder;
int *bytes;
{
struct connection_cache *con;
int status = 0;
int i;
int start = 1;
imap_states res;
DPRINT(Debug,11,(&Debug,
"mbx_new_mail_on_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_new_mail_on_imap",
"Wrong type connection attached to folder",0);
}
*bytes = 0;
if (!start_imap_command(con,"NOOP"))
goto clean;
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
imap_clear_command(con);
/* Messages which do not have UID have unknown ... */
start = 1;
while (start < folder -> p->a.imap_mbx.reference_count &&
folder -> p->a.imap_mbx.references[start].uid_number != -1) {
start++;
}
DPRINT(Debug,11,(&Debug,
" : start=%d refcount=%d last uid number=%d\n",
start,folder -> p->a.imap_mbx.reference_count,
folder -> p->a.imap_mbx.last_uid_number));
if (start <= folder -> p->a.imap_mbx.reference_count-1) {
CONST char * L[] = { "RFC822.SIZE", "UID", NULL };
if (!start_imap_command(con,"FETCH")) {
status = 0;
goto clean;
}
imap_command_push_range(con,
start,
folder -> p->a.imap_mbx.reference_count-1);
imap_command_push_list(con,L);
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
}
for (i = 1; i < folder -> p->a.imap_mbx.reference_count; i++) {
if (folder -> p->a.imap_mbx.references[i].uid_number >
folder -> p->a.imap_mbx.last_uid_number) {
status = 1;
*bytes += folder-> p->a.imap_mbx.references[i].rfc822_size;
}
}
clean:
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"mbx_new_mail_on_imap=%d (*bytes=%d)\n",
status,*bytes));
return status;
}
static int mbx_consider_remove_imap P_((struct folder_info *folder));
static int mbx_consider_remove_imap(folder)
struct folder_info *folder;
{
DPRINT(Debug,11,(&Debug,
"mbx_consider_remove_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
"mbx_consider_remove_imap=0\n"));
return 0;
}
static int mbx_prepare_keep_imap P_((struct folder_info *folder,
KEEP_STATE keep_state_ptr));
static int mbx_prepare_keep_imap(folder,keep_state_ptr)
struct folder_info *folder;
KEEP_STATE keep_state_ptr;
{
int status = 0;
DPRINT(Debug,11,(&Debug,
"mbx_prepare_keep_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
folder->p->a.imap_mbx.folder_status &= ~IMAP_can_CLOSE;
status = 1;
DPRINT(Debug,11,(&Debug,
"mbx_prepare_keep_imap=%d\n",
status));
return status;
}
static int mbx_end_keep_imap P_((struct folder_info *folder,
KEEP_STATE keep_state_ptr));
static int mbx_end_keep_imap(folder,keep_state_ptr)
struct folder_info *folder;
KEEP_STATE keep_state_ptr;
{
struct connection_cache *con;
int status = 0;
imap_states res;
DPRINT(Debug,11,(&Debug,
"mbx_end_keep_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_end_keep_imap",
"Wrong type connection attached to folder",0);
}
folder->p->a.imap_mbx.folder_status &= ~IMAP_can_CLOSE;
if (0 != (folder->p->a.imap_mbx.folder_status & IMAP_flusherr))
goto clean;
if (!start_imap_command(con,"EXPUNGE"))
goto clean;
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
imap_clear_command(con);
if (!start_imap_command(con,"CHECK"))
goto clean;
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL))
goto clean;
status = 1;
folder->p->a.imap_mbx.folder_status |= IMAP_can_CLOSE;
clean:
folder->p->a.imap_mbx.folder_status &= ~IMAP_flusherr;
imap_clear_command(con);
DPRINT(Debug,11,(&Debug,
"mbx_end_keep_imap=%d\n",
status));
return status;
}
static void mbx_mark_keep_imap P_((struct folder_info *folder,
KEEP_STATE keep_state_ptr,
struct header_rec *entry,
int keep));
static void mbx_mark_keep_imap(folder,keep_state_ptr,entry,keep)
struct folder_info *folder;
KEEP_STATE keep_state_ptr;
struct header_rec *entry;
int keep;
{
struct connection_cache *con;
DPRINT(Debug,11,(&Debug,
"mbx_mark_keep_imap: folder=%p (%s), ks=%p, header=%p\n",
folder,folder->cur_folder_sys,keep_state_ptr,entry));
con = folder->p->a.imap_mbx.Ch;
if (con->type != &IMAP_connection) {
panic("MBX PANIC",__FILE__,__LINE__,"mbx_mark_keep_imap",
"Wrong type connection attached to folder",0);
}
/* Wait that IDLE timer command is completetd so that there
* is no EXPUNGE coming...
*/
imap_idle_wait(folder);
if(entry->mbx_info &&
&imap_mbx_info == entry->mbx_info->type_code) {
struct imap_reference *ref =
find_reference(& (folder -> p->a.imap_mbx),
& (entry->mbx_info->a.imap_mbx));
if (ref) {
long old_bits = ref->imap_flags;
int minus_delta = 0;
int j;
CONST char * LIST[ sizeof IMAP_flags /
sizeof (struct imap_flag) +1];
imap_states res;
int X;
if (0 == (entry->status & UNREAD)) {
ref->imap_flags |= IMAP_Seen;
} else {
/* So that 'U' (unread) works: */
ref->imap_flags &= ~IMAP_Seen;
}
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if (0 != (IMAP_flags[j].mask_elm & entry->status) ||
0 != (IMAP_flags[j].mask_elm1 & entry->status1))
ref->imap_flags |= IMAP_flags[j].mask_imap;
/* So that 't' (tag / untag) works:
* Note that IMAP_flags[j].mask_elm == 0 for \Seen !!!
*/
else if (0 != IMAP_flags[j].mask_elm ||
0 != IMAP_flags[j].mask_elm1)
ref->imap_flags &= ~IMAP_flags[j].mask_imap;
}
if (keep)
ref->imap_flags &= ~IMAP_Deleted;
else
ref->imap_flags |= IMAP_Deleted;
DPRINT(Debug,9,(&Debug,
"mbx_mark_keep_imap: (mailbox index %d) Flags",
entry->mbx_info->a.imap_mbx.last_mbx_index));
X = 0;
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if (0 != (ref->imap_flags & IMAP_flags[j].mask_imap) &&
0 == (IMAP_Recent & IMAP_flags[j].mask_imap)) {
LIST[X++] = IMAP_flags[j].flag;
DPRINT(Debug,9,(&Debug, " %s",IMAP_flags[j].flag));
}
}
LIST[X] = NULL;
DPRINT(Debug,9,(&Debug, "\n"));
/* Cyrys imapd 2.0.14 fails parse command
nnnn STORE xxx FLAGS ()
Therefore do not issue store commands if
FLAGS is empty and status_chgd is not set
*/
if (0 == X) {
minus_delta = 1;
DPRINT(Debug,9,(&Debug,
"mbx_mark_keep_imap: (mailbox index %d) -Flags",
entry->mbx_info->a.imap_mbx.last_mbx_index));
X = 0;
for (j = 0;
j < sizeof IMAP_flags / sizeof (struct imap_flag);
j++) {
if (0 == (ref->imap_flags & IMAP_flags[j].mask_imap) &&
0 != (old_bits & IMAP_flags[j].mask_imap) &&
0 == (IMAP_Recent & IMAP_flags[j].mask_imap)) {
LIST[X++] = IMAP_flags[j].flag;
DPRINT(Debug,9,(&Debug, " %s",IMAP_flags[j].flag));
}
}
LIST[X] = NULL;
if (0 == X) {
DPRINT(Debug,9,(&Debug, " (empty)\n"));
goto clean;
}
}
if (!start_imap_command(con,"STORE")) {
folder->p->a.imap_mbx.folder_status |= IMAP_flusherr;
goto clean;
}
imap_command_push_number(con,
entry->mbx_info->
a.imap_mbx.last_mbx_index);
if (minus_delta)
imap_command_push_atom(con,"-FLAGS");
else
imap_command_push_atom(con,"FLAGS");
imap_command_push_list(con,LIST);
end_imap_command(con);
if (!imap_command_ok(con,&res,NULL)) {
folder->p->a.imap_mbx.folder_status |= IMAP_flusherr;
goto clean;
}
}
}
clean:
imap_clear_command(con);
}
CONST char * mbx_imap_type P_((struct folder_info *folder));
CONST char * mbx_imap_type(folder)
struct folder_info *folder;
{
char *result = NULL;
DPRINT(Debug,11,(&Debug,
"mbx_imap_type: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
if (0 != (folder -> p->a.imap_mbx.folder_status & IMAP_writable)) {
if (0 == istrcmp(folder -> p->a.imap_mbx.folder,
"INBOX")) {
static char *mailbox = NULL;
if (!mailbox)
mailbox = catgets(elm_msg_cat, MeSet, MeImapMailbox,
"IMAP mailbox");
result = mailbox;
} else {
static char *mailbox_normal = NULL;
if (!mailbox_normal)
mailbox_normal = catgets(elm_msg_cat, MeSet, MeImapFolder,
"IMAP folder");
result = mailbox_normal;
}
} else {
static char *ro_mailbox = NULL;
if (!ro_mailbox)
ro_mailbox = catgets(elm_msg_cat, MeSet, MeROImapFolder,
"Read-only IMAP folder");
result = ro_mailbox;
}
DPRINT(Debug,11,(&Debug,
"mbx_imap_type=%s\n",
result));
return result;
}
static int mbx_start_edit_imap P_((struct folder_info *folder,
CONST char **buffer));
static int mbx_start_edit_imap(folder,buffer)
struct folder_info *folder;
CONST char **buffer;
{
DPRINT(Debug,11,(&Debug,
"mbx_start_edit_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
"mbx_start_edit_imap=0\n"));
return 0;
}
static int mbx_end_edit_imap P_((struct folder_info *folder));
static int mbx_end_edit_imap(folder)
struct folder_info *folder;
{
DPRINT(Debug,11,(&Debug,
"mbx_end_edit_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
DPRINT(Debug,11,(&Debug,
"mbx_end_edit_imap=0\n"));
return 0;
}
static void mbx_free_imap P_((struct folder_info *folder));
static void mbx_free_imap(folder)
struct folder_info *folder;
{
DPRINT(Debug,11,(&Debug,
"mbx_free_imap: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
if (folder -> p->fh_temp) {
fclose(folder -> p->fh_temp);
folder -> p -> fh_temp = NULL;
}
if (folder -> p -> fh_folder) {
fclose(folder -> p -> fh_folder);
folder -> p -> fh_folder = NULL;
}
if (folder -> p->a.imap_mbx.folder_name_cache)
free_string(&(folder -> p->a.imap_mbx.folder_name_cache));
if (folder -> p->a.imap_mbx.Ch)
free_connection(&(folder -> p->a.imap_mbx.Ch));
if (folder -> p->a.imap_mbx.folder) {
free(folder -> p->a.imap_mbx.folder);
folder -> p->a.imap_mbx.folder = NULL;
}
imap_clear_buffers(folder);
free(folder -> p);
folder -> p = NULL;
}
static void mbx_zero_rs_fields_imap P_((struct read_folder_state *rs));
static void mbx_zero_rs_fields_imap(rs)
struct read_folder_state *rs;
{
DPRINT(Debug,11,(&Debug,
"mbx_zero_rs_fields_imap: rs=%p\n",
rs));
rs->a.imap_mbx.current_message.last_mbx_index = -1;
rs->a.imap_mbx.current_message.uid_number = 0;
rs->a.imap_mbx.data_idx = 0;
}
static void mbx_free_rs_fields_imap P_((struct read_folder_state *rs));
static void mbx_free_rs_fields_imap(rs)
struct read_folder_state *rs;
{
DPRINT(Debug,11,(&Debug,
"mbx_free_rs_fields_imap: rs=%p\n",
rs));
rs->a.imap_mbx.current_message.last_mbx_index = -1;
rs->a.imap_mbx.current_message.uid_number = 0;
rs->a.imap_mbx.data_idx = 0;
}
static void mbx_zero_ks_fields_imap P_((struct keep_folder_state *rs));
static void mbx_zero_ks_fields_imap(rs)
struct keep_folder_state *rs;
{
DPRINT(Debug,11,(&Debug,
"mbx_zero_ks_fields_imap: rs=%p\n",
rs));
}
static void mbx_free_ks_fields_imap P_((struct keep_folder_state *rs));
static void mbx_free_ks_fields_imap(rs)
struct keep_folder_state *rs;
{
DPRINT(Debug,11,(&Debug,
"mbx_free_ks_fields_imap: rs=%p\n",
rs));
}
static int mbx_get_imap_mode P_((struct folder_info *folder));
static int mbx_get_imap_mode(folder)
struct folder_info *folder;
{
DPRINT(Debug,11,(&Debug,
"mbx_get_imap_mode: folder=%p (%s)\n",
folder,folder->cur_folder_sys));
if (0 != (folder -> p->a.imap_mbx.folder_status & IMAP_writable)) {
if (0 == istrcmp(folder -> p->a.imap_mbx.folder,
"INBOX")) {
DPRINT(Debug,11,(&Debug,
"mbx_get_imap_mode=FOLDER_MBOX\n"));
return FOLDER_MBOX;
} else {
DPRINT(Debug,11,(&Debug,
"mbx_get_imap_mode=0\n"));
return 0;
}
} else {
DPRINT(Debug,11,(&Debug,
"mbx_get_imap_mode=FOLDER_RDONLY\n"));
return FOLDER_RDONLY;
}
}
struct folder_type imap_mbx = { "IMAP",
mbx_close_imap,
mbx_lock_imap,
mbx_init_imap,
mbx_sessionlock_imap,
mbx_unlock_imap,
mbx_flush_imap,
mbx_ferror_imap,
mbx_prepare_read_imap,
mbx_end_read_imap,
mbx_copy_envelope_imap,
mbx_is_forwarded_imap,
mbx_copy_header_imap,
mbx_copy_body_imap,
mbx_copy_envelope_end_imap,
mbx_copy_envelope_reset_body_imap,
mbx_imap_to_fd,
mbx_new_mail_on_imap,
mbx_consider_remove_imap,
mbx_prepare_keep_imap,
mbx_end_keep_imap,
mbx_mark_keep_imap,
mbx_imap_type,
mbx_start_edit_imap,
mbx_end_edit_imap,
mbx_free_imap,
mbx_zero_rs_fields_imap,
mbx_free_rs_fields_imap,
mbx_zero_ks_fields_imap,
mbx_free_ks_fields_imap,
mbx_get_imap_mode };
#endif
/* URL handling --------------------------------------- */
#ifdef REMOTE_MBX
S_(url_default_port_f url_default_port_imap)
static uint16 url_default_port_imap P_((
const struct browser_url_method *schema));
static uint16 url_default_port_imap(schema)
const struct browser_url_method *schema;
{
return PORT_imap4;
}
S_(url_make_connection_f url_make_connection_imap)
/* May not be
uin16 port
because this is promoted to int
*/
static struct connection_cache * url_make_connection_imap P_((
const struct browser_url_method *schema,
const struct string *user /* May be NULL */,
const struct string *password /* May be NULL */,
const char *host /* May be NULL */,
int port /* May be NULL */));
static struct connection_cache * url_make_connection_imap(schema,user,
password,host,
port)
const struct browser_url_method *schema;
const struct string *user /* May be NULL */;
const struct string *password /* May be NULL */;
const char *host /* May be NULL */;
int port /* May be NULL */;
{
struct remote_account ra;
struct string * user2;
struct service_entry * se;
struct connection_cache * CX;
if (!host || !user) {
DPRINT(Debug,7,(&Debug,
"url_make_connection_imap=NULL: No host or user\n"));
return NULL; /* Not supported currently */
}
/* FIXME --- STFLAG_mbox not necessary correct? */
se = give_service_entry(host, STFLAG_mbox);
if (!se) {
DPRINT(Debug,7,(&Debug,
"url_make_connection_imap=NULL: No service entry\n"));
return NULL;
}
zero_remote_account(&ra);
/* Canonify host name ... */
ra.host = safe_strdup(se->official_name);
if (! can_ascii_string(user))
lib_error(CATGETS(elm_msg_cat, MeSet,
MeNonAsciiUsernameUrl,
"Non-ascii usernames on url are not supported: %S"),
user);
/* NOTE: Original string is kept, if it can not converted
to US-ASCII -- then this produces garbage, but
user is warned
*/
user2 = ascify_string(user);
ra.username = us2s(stream_from_string(user2,0,NULL));
free_string(&user2);
CX = locate_from_cache(ra.username,ra.host,&IMAP_connection,port);
if (!CX) {
int got;
PORTS ports_imaponly[] = { PORT_imap4, PORT_end };
PORTS force_port = PORT_end;
if (port)
force_port = port;
/* Make connection, if not available from cache */
if (!connect_remote_account(&ra,&got,se,
ports_imaponly,
force_port)) {
DPRINT(Debug,7,(&Debug,
"url_make_connection_imap: connect_remote_account failed\n"));
goto fail;
}
CX = create_connection(&IMAP_connection);
CX->C = ra;
zero_remote_account(&ra); /* Avoid double free */
CX->port = port;
CX->state = CON_open; /* connect_remote_account opened it */
/* browser_from_connection() requires that connection is cached */
cache_connection(CX);
}
/* If not lohhed, must be logged first */
if (CX->state != CON_logged) {
if (! login_connection(CX,password)) {
CX = NULL; /* Is on cache so this do not cause leak */
goto fail;
}
}
fail:
free_remote_account(&ra);
if (CX) {
DPRINT(Debug,7,(&Debug,
"url_make_connection_imap=%p\n",CX));
}
return CX;
}
/* Returns -1 on failure */
static int match_item_to_imaplisting P_((struct folder_browser *dir,
struct string * dirname_disp,
char * dirname_sys,
const struct string * item));
static int match_item_to_imaplisting(dir,dirname_disp,
dirname_sys,item)
struct folder_browser *dir;
struct string * dirname_disp;
char * dirname_sys;
const struct string * item;
{
int L = 0;
int match = -1;
int i;
if (dirname_sys)
L = strlen(dirname_sys);
for (i = 0; i < dir->a.imap_browser.dir_entry_count && -1 == match; i++) {
char *tail = dir->a.imap_browser.dir_entries[i].imap_name;
if (dirname_sys) {
if (0 != strncmp(dirname_sys,tail,L) ||
tail[L] != dir->a.imap_browser.dir_entries[i].sep) {
DPRINT(Debug,12,(&Debug,
"match_item_to_imaplisting: item %d '%s' do not match prefix '%s' separator '%c'\n",
i,tail,
dirname_sys,
dir->a.imap_browser.dir_entries[i].sep));
continue;
}
tail = tail + (L+1);
}
if (RAW_BUFFER != item->string_type) {
char * buf;
int buflen;
bytestream_from_string(item,&buf,&buflen);
if (0 == strcmp(tail,buf) && strlen(tail) == buflen) {
DPRINT(Debug,12,(&Debug,
"match_item_to_imaplisting: raw item %d '%s' matches\n",
i,tail));
match = i;
}
free(buf);
} else {
struct string * buf2 = conv_from_imap_name(tail);
if (0 == string_cmp(item,
buf2,-99 /* Unknown indicator */)) {
DPRINT(Debug,12,(&Debug,
"match_item_to_imaplisting: translated item %d '%S' matches\n",
i,buf2));
match = i;
}
free_string(&buf2);
}
}
return match;
}
/* As strncmp */
static int string_prefix_match P_((const struct string *str1,
const struct string *str2,
int L,int unknown_val));
static int string_prefix_match(str1,str2,L,unknown_val)
CONST struct string *str1;
CONST struct string *str2;
int L;
int unknown_val;
{
int ret;
int X1 = 0;
struct string * Z1 = clip_from_string(str1,&X1,L);
int X2 = 0;
struct string * Z2 = clip_from_string(str1,&X2,L);
ret = string_cmp(Z1,Z2,unknown_val);
free_string(&Z2);
free_string(&Z1);
return ret;
}
static int translate_path_to_imap_selection P_((struct folder_browser *
dir,
struct string *dirname,
char *sysname,
int sep));
static int translate_path_to_imap_selection(dir,dirname,sysname,sep)
struct folder_browser *dir;
struct string *dirname;
char *sysname;
int sep;
{
int ret = 0;
if (!dir->sys_dir) {
DPRINT(Debug,12,(&Debug,
"translate_path_to_imap_selection: On default directory\n"));
/* WARNING: set_dir_selection does not allocate strings --
* it just assign pointers!
*/
set_dir_selection(dir,
safe_strdup(sysname),
dup_string(dirname),
BROWSER_MAILFILE /* Assume folder */
| BROWSER_EXIST /* Exists because on listing */
);
ret = 1;
} else {
char * s1;
struct string * s2;
int L1 = strlen(dir->sys_dir);
int L2;
CONST struct string * relative1 = NULL;
int X;
if (0 != strncmp(dir->sys_dir,sysname,L1) ||
sep != sysname[L1]) {
DPRINT(Debug,12,(&Debug,
"translate_path_to_imap_selection: Directory not match\n"));
goto fail;
}
relative1 = dir->a.imap_browser.dir_name_cache;
if (!relative1) {
DPRINT(Debug,12,(&Debug,
"translate_path_to_imap_selection: No dir_name_cache\n"));
goto fail;
}
L2 = string_len(relative1);
/* IMAP character set is assumed to be ASCII compatible on
here! */
if (L2 +1 > string_len(dirname) ||
0 != string_prefix_match(relative1,dirname,L2,
99 /* unknown val */) ||
sep != give_unicode_from_string(dirname,L2)) {
DPRINT(Debug,12,(&Debug,
"translate_path_to_imap_selection: Directory not match\n"));
goto fail;
}
s1 = sysname + L1 + 1;
X = L2+1;
s2 = clip_from_string(dirname,&X,string_len(dirname));
/* WARNING: set_dir_selection does not allocate strings --
* it just assign pointers!
*/
set_dir_selection(dir,
safe_strdup(s1),
s2,
BROWSER_MAILFILE /* Assume folder */
| BROWSER_EXIST /* Exists because on listing */
);
ret = 1;
}
fail:
DPRINT(Debug,12,(&Debug,
"translate_path_to_imap_selection=%d\n",
ret));
return ret;
}
/* This is always absolute path -- from root as defined for URL */
S_(url_select_item_from_URL_f url_select_item_from_imap_path)
static int url_select_item_from_imap_path P_((
const struct browser_url_method *schema,
struct folder_browser *dir,
int elems_count,
const struct string **elems));
static int url_select_item_from_imap_path(schema,dir,elems_count,elems)
const struct browser_url_method *schema;
struct folder_browser *dir;
int elems_count; /* 0 elems is root if absolute */
CONST struct string **elems;
{
char used_sep = '\0';
struct string * dirname_disp = NULL;
char * dirname_sys = NULL;
int i;
int ret = 0;
if (!browser_update_imaplisting(dir,NULL,NULL,'\0'))
goto fail;
/* Assume native separators */
if (1 == elems_count) {
if (RAW_BUFFER != elems[0]->string_type) {
struct string * dispname_not_used = NULL;
ret = browser_select_imap(dir,elems[0],&dispname_not_used);
if (dispname_not_used)
free_string(&dispname_not_used);
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path = %d (native result)\n",
ret));
goto done;
} else {
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path: Raw buffer type currently not supported ...\n"));
goto not_supported;
}
} else {
/* / is used as separator */
int i;
not_supported:
for (i = 0; i < elems_count; i++) {
struct string * rel_name = NULL;
int is_full;
int match = match_item_to_imaplisting(dir,dirname_disp,
dirname_sys,elems[i]);
if (match >= 0) {
if (match >= dir->a.imap_browser.dir_entry_count) {
panic("MBX PANIC",__FILE__,__LINE__,
"url_descend_to_imap_directory",
"Bad match",0);
}
if (dirname_disp)
free_string(&dirname_disp);
dirname_disp = dup_string(dir->a.imap_browser.
dir_entries[match].
translated_name);
if (dirname_sys)
free(dirname_sys);
dirname_sys = safe_strdup(dir->a.imap_browser.
dir_entries[match].
imap_name);
used_sep = dir->a.imap_browser.dir_entries[match].sep;
if (i < elems_count-1) {
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path: %d .. going translated='%S' sysname='%s'\n",
i,dirname_disp,dirname_sys));
if (! browser_update_imaplisting(dir,dirname_disp,dirname_sys,
used_sep)) {
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path: %d ... failed\n"));
goto fail;
}
} else {
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path: %d .. selecting translated='%S' sysname='%s'\n",
i,dirname_disp,dirname_sys));
ret = translate_path_to_imap_selection(dir,dirname_disp,dirname_sys,
used_sep);
}
continue;
}
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path: %d failed at %S\n",
i,elems[i]));
goto fail;
}
DPRINT(Debug,13,(&Debug,
"url_select_item_from_imap_path: loop ended, not selected item\n"));
}
fail:
done:
DPRINT(Debug,12,(&Debug,
"url_select_item_from_imap_path=%d\n",
ret));
return ret;
}
CONST struct browser_url_method URL_imap = {
BROWSER_URL_method_magic,
& imap_browser,
url_default_port_imap,
url_make_connection_imap,
url_select_item_from_imap_path
};
#endif
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1