balsa r7902 - in trunk: . libbalsa libbalsa/imap
- From: pawels svn gnome org
- To: svn-commits-list gnome org
- Subject: balsa r7902 - in trunk: . libbalsa libbalsa/imap
- Date: Tue, 1 Apr 2008 20:55:03 +0100 (BST)
Author: pawels
Date: Tue Apr 1 20:55:03 2008
New Revision: 7902
URL: http://svn.gnome.org/viewvc/balsa?rev=7902&view=rev
Log:
* libbalsa/mailbox_imap.c: use UIDPLUS information returned by server
to improve caching.
* libbalsa/imap/imap-handle.[ch]: parse returned UIDPLUS data.
* libbalsa/imap/imap_private.h: add ImapMboxHandle::uidplus field.
* libbalsa/imap/imap-commands.[ch]: add API for accessing UIDPLUS data.
Modified:
trunk/ChangeLog
trunk/libbalsa/imap/imap-commands.c
trunk/libbalsa/imap/imap-commands.h
trunk/libbalsa/imap/imap-handle.c
trunk/libbalsa/imap/imap-handle.h
trunk/libbalsa/imap/imap_private.h
trunk/libbalsa/imap/imap_tst.c
trunk/libbalsa/mailbox_imap.c
Modified: trunk/libbalsa/imap/imap-commands.c
==============================================================================
--- trunk/libbalsa/imap/imap-commands.c (original)
+++ trunk/libbalsa/imap/imap-commands.c Tue Apr 1 20:55:03 2008
@@ -419,16 +419,57 @@
{
struct SingleAppendData sad = { dump_cb, arg, sz, flags };
IMAP_REQUIRED_STATE2(handle,IMHS_AUTHENTICATED, IMHS_SELECTED, IMR_BAD);
- return imap_mbox_append_multi(handle, mbox, single_append_cb, &sad);
+ return imap_mbox_append_multi(handle, mbox, single_append_cb, &sad, NULL);
}
+
+static ImapResponse
+append_commit(ImapMboxHandle *handle, unsigned cmdno, ImapSequence *uid_seq)
+{
+ ImapResponse rc;
+
+ if(handle->uidplus.dst)
+ g_warning("Leaking memory in imap_append");
+
+ if(uid_seq) {
+ handle->uidplus.dst = uid_seq->ranges;
+ handle->uidplus.store_response = 1;
+ }
+ sio_write(handle->sio, "\r\n", 2);
+ sio_flush(handle->sio);
+ do {
+ rc = imap_cmd_step (handle, cmdno);
+ } while (rc == IMR_UNTAGGED);
+
+ if(uid_seq) {
+ if(uid_seq->uid_validity == 0) {
+ uid_seq->uid_validity = handle->uidplus.dst_uid_validity;
+ uid_seq->ranges = handle->uidplus.dst;
+ } else if(uid_seq->uid_validity != handle->uidplus.dst_uid_validity) {
+ printf("The IMAP server keeps changing UID validity, "
+ "ignoring UIDPLUS response (%u -> %u)\n",
+ uid_seq->uid_validity, handle->uidplus.dst_uid_validity);
+ uid_seq->uid_validity = handle->uidplus.dst_uid_validity;
+ g_list_free(uid_seq->ranges);
+ uid_seq->ranges = NULL;
+ } else {
+ uid_seq->ranges = handle->uidplus.dst;
+ }
+ }
+ handle->uidplus.dst = NULL;
+ handle->uidplus.store_response = 0;
+
+ return rc;
+}
+
static ImapResponse
imap_mbox_append_multi_real(ImapMboxHandle *handle,
const char *mbox,
ImapAppendMultiFunc dump_cb,
- void* cb_arg)
+ void* cb_arg,
+ ImapSequence *uid_sequence)
{
static const unsigned TRANSACTION_SIZE = 10*1024*1024;
- int use_literal, use_multiappend;
+ int use_literal, use_multiappend, use_uidplus;
unsigned cmdno;
ImapResponse rc = IMR_OK;
char *litstr;
@@ -442,6 +483,7 @@
use_literal = imap_mbox_handle_can_do(handle, IMCAP_LITERAL);
use_multiappend = imap_mbox_handle_can_do(handle, IMCAP_MULTIAPPEND);
+ use_uidplus = imap_mbox_handle_can_do(handle, IMCAP_UIDPLUS);
litstr = use_literal ? "+" : "";
imap_handle_idle_disable(handle);
@@ -518,26 +560,24 @@
* make sure on a higher level that they are not sent.
*/
/* sio_flush(handle->sio); */
- sio_write(handle->sio, "\r\n", 2);
- sio_flush(handle->sio);
- do {
- rc = imap_cmd_step (handle, cmdno);
- } while (rc == IMR_UNTAGGED);
- if(rc != IMR_OK)
- return rc;
+ if( (rc=append_commit(handle, cmdno,
+ use_uidplus ? uid_sequence : NULL)) != IMR_OK)
+ return rc;
}
/* And move to the next message... */
}
if(!new_append && use_multiappend) { /* We get the server response here... */
- sio_write(handle->sio, "\r\n", 2);
- sio_flush(handle->sio);
- do {
- rc = imap_cmd_step (handle, cmdno);
- } while (rc == IMR_UNTAGGED);
+ rc = append_commit(handle, cmdno, use_uidplus ? uid_sequence : NULL);
}
imap_handle_idle_enable(handle, 30);
+
+ if(uid_sequence) {
+ printf("Handle returns uid seq of length %u\n",
+ imap_sequence_length(uid_sequence));
+ uid_sequence->ranges = g_list_reverse(uid_sequence->ranges);
+ }
return rc;
}
@@ -561,11 +601,12 @@
imap_mbox_append_multi(ImapMboxHandle *handle,
const char *mbox,
ImapAppendMultiFunc dump_cb,
- void* cb_arg)
+ void* cb_arg,
+ ImapSequence *uids)
{
ImapResponse rc;
HANDLE_LOCK(handle);
- rc = imap_mbox_append_multi_real(handle, mbox, dump_cb, cb_arg);
+ rc = imap_mbox_append_multi_real(handle, mbox, dump_cb, cb_arg, uids);
HANDLE_UNLOCK(handle);
return rc;
}
@@ -632,8 +673,13 @@
ImapResponse
imap_mbox_expunge(ImapMboxHandle *handle)
{
+ ImapResponse rc;
+
IMAP_REQUIRED_STATE2(handle,IMHS_AUTHENTICATED, IMHS_SELECTED, IMR_BAD);
- return imap_cmd_exec(handle, "EXPUNGE");
+ HANDLE_LOCK(handle);
+ rc = imap_cmd_exec(handle, "EXPUNGE");
+ HANDLE_UNLOCK(handle);
+ return rc;
}
ImapResponse
@@ -1233,17 +1279,36 @@
selected in handle to given mailbox on same server. */
ImapResponse
imap_mbox_handle_copy(ImapMboxHandle* handle, unsigned cnt, unsigned *seqno,
- const gchar *dest)
+ const gchar *dest,
+ ImapSequence *ret_sequence)
{
+ ImapResponse rc;
IMAP_REQUIRED_STATE1(handle, IMHS_SELECTED, IMR_BAD);
+ HANDLE_LOCK(handle);
{
- gchar *mbx7 = imap_utf8_to_mailbox(dest);
- char *seq = imap_coalesce_set(cnt, seqno);
- gchar *cmd = g_strdup_printf("COPY %s \"%s\"", seq, mbx7);
- ImapResponse rc = imap_cmd_exec(handle, cmd);
- g_free(seq); g_free(mbx7); g_free(cmd);
- return rc;
+ gchar *mbx7 = imap_utf8_to_mailbox(dest);
+ char *seq = imap_coalesce_set(cnt, seqno);
+ gchar *cmd = g_strdup_printf("COPY %s \"%s\"", seq, mbx7);
+ unsigned cmdno;
+ gboolean use_uidplus = imap_mbox_handle_can_do(handle, IMCAP_UIDPLUS);
+
+ handle->uidplus.store_response = ret_sequence ? 1 : 0;
+
+ rc = imap_cmd_exec_cmdno(handle, cmd, &cmdno);
+ g_free(seq); g_free(mbx7); g_free(cmd);
+ if(use_uidplus && ret_sequence) {
+ if(rc == IMR_OK /* && cmdno == handle->uidplus.cmdno */ ) {
+ ret_sequence->uid_validity = handle->uidplus.dst_uid_validity;
+ ret_sequence->ranges = g_list_reverse(handle->uidplus.dst);
+ } else {
+ g_list_free(handle->uidplus.dst);
+ }
+ handle->uidplus.dst = NULL;
+ handle->uidplus.store_response = 0;
+ }
}
+ HANDLE_UNLOCK(handle);
+ return rc;
}
/* 6.4.8 UID Command */
Modified: trunk/libbalsa/imap/imap-commands.h
==============================================================================
--- trunk/libbalsa/imap/imap-commands.h (original)
+++ trunk/libbalsa/imap/imap-commands.h Tue Apr 1 20:55:03 2008
@@ -72,7 +72,8 @@
ImapResponse imap_mbox_append_multi(ImapMboxHandle *handle,
const char *mbox,
ImapAppendMultiFunc dump_cb,
- void* arg);
+ void* arg,
+ ImapSequence *uid_range);
#ifdef USE_IMAP_APPEND_STR /* not used currently */
ImapResponse imap_mbox_append_str(ImapMboxHandle *handle, const char *mbox,
ImapMsgFlags flags, size_t sz, char *txt);
@@ -94,8 +95,11 @@
unsigned imap_mbox_store_flag_a(ImapMboxHandle *r, unsigned cnt,
unsigned *seqno, ImapMsgFlag flg,
gboolean state);
-ImapResponse imap_mbox_handle_copy(ImapMboxHandle* handle, unsigned cnt,
- unsigned *seqno, const gchar *dest);
+
+ImapResponse imap_mbox_handle_copy(ImapMboxHandle* handle,
+ unsigned cnt, unsigned *seqno,
+ const gchar *dest,
+ ImapSequence *ret_sequence);
ImapResponse imap_mbox_find_unseen(ImapMboxHandle * h, unsigned *msgcnt,
unsigned **msgs);
Modified: trunk/libbalsa/imap/imap-handle.c
==============================================================================
--- trunk/libbalsa/imap/imap-handle.c (original)
+++ trunk/libbalsa/imap/imap-handle.c Tue Apr 1 20:55:03 2008
@@ -878,6 +878,7 @@
ImapMboxHandle* handle = IMAP_MBOX_HANDLE(gobject);
g_return_if_fail(handle);
+ HANDLE_LOCK(handle);
if(handle->state != IMHS_DISCONNECTED) {
handle->doing_logout = TRUE;
imap_cmd_exec(handle, "LOGOUT");
@@ -895,6 +896,8 @@
imap_mbox_resize_cache(handle, 0);
g_free(handle->msg_cache); handle->msg_cache = NULL;
g_array_free(handle->flag_cache, TRUE);
+
+ HANDLE_UNLOCK(handle);
#if defined(BALSA_USE_THREADS)
pthread_mutex_destroy(&handle->mutex);
#endif
@@ -1411,6 +1414,57 @@
}
+unsigned
+imap_sequence_length(ImapSequence *i_seq)
+{
+ unsigned length = 0;
+ GList *l;
+ for(l = i_seq->ranges; l; l = l->next) {
+ ImapUidRange *r = (ImapUidRange*)l->data;
+ length += r->hi - r->lo +1;
+ }
+ return length;
+}
+
+unsigned
+imap_sequence_nth(ImapSequence *i_seq, unsigned nth)
+{
+ GList *l;
+
+ g_return_val_if_fail(i_seq->ranges, 0);
+
+ for(l = i_seq->ranges; l; l = l->next) {
+ ImapUidRange *r = (ImapUidRange*)l->data;
+ unsigned range_length = r->hi - r->lo +1;
+ if( nth < range_length)
+ return r->lo + nth;
+ nth -= range_length;
+ }
+ g_warning("imap_sequence_nth: too large parameter; returning bogus data\n");
+ return 0;
+}
+
+void
+imap_sequence_foreach(ImapSequence *i_seq,
+ void(*cb)(unsigned uid, void *arg), void *cb_arg)
+{
+ GList *l;
+ for(l = i_seq->ranges; l; l = l->next) {
+ unsigned uid;
+ ImapUidRange *iur = (ImapUidRange*)l->data;
+ for(uid=iur->lo; uid<=iur->hi; uid++)
+ cb(uid, cb_arg);
+ }
+}
+
+
+void
+imap_sequence_release(ImapSequence *i_seq)
+{
+ g_list_foreach(i_seq->ranges, (GFunc)g_free, NULL);
+ i_seq->ranges = NULL;
+}
+
void
imap_body_append_part(ImapBody* body, ImapBody* sibling)
{
@@ -1903,17 +1957,19 @@
return lastcmd == cmdno ? rc : IMR_UNTAGGED;
}
-/* imap_cmd_exec:
- * execute a command, and wait for the response from the server.
+/**executes a command, and wait for the response from the server.
* Also, handle untagged responses.
* Returns ImapResponse.
*/
ImapResponse
-imap_cmd_exec(ImapMboxHandle* handle, const char* cmd)
+imap_cmd_exec_cmdno(ImapMboxHandle* handle, const char* cmd,
+ unsigned *ret_cmdno)
{
unsigned cmdno;
ImapResponse rc;
+ if(ret_cmdno) *ret_cmdno = 0;
+
g_return_val_if_fail(handle, IMR_BAD);
if (handle->state == IMHS_DISCONNECTED)
return IMR_SEVERED;
@@ -1923,6 +1979,7 @@
if (imap_cmd_start(handle, cmd, &cmdno)<0)
return IMR_SEVERED; /* irrecoverable connection error. */
+ if(ret_cmdno) *ret_cmdno = cmdno;
sio_flush(handle->sio);
do {
rc = imap_cmd_step (handle, cmdno);
@@ -2090,7 +2147,7 @@
#include "imap-handle.h"
-static void
+static int
ignore_bad_charset(struct siobuf *sio, int c)
{
while(c==' ') {
@@ -2099,13 +2156,19 @@
}
if(c != ')')
fprintf(stderr,"ignore_bad_charset: expected ')' got '%c'\n", c);
+ else c = sio_getc(sio);
+ return c;
}
-static void
+
+static int
ir_permanent_flags(ImapMboxHandle *h)
{
- while(sio_getc(h->sio) != ']')
+ int c;
+ while( (c=sio_getc(h->sio)) != EOF && c != ']')
;
+ return c;
}
+
static int
ir_capability_data(ImapMboxHandle *handle)
{
@@ -2117,7 +2180,7 @@
"LOGINDISABLED", "MULTIAPPEND", "NAMESPACE", "SASL-IR",
"SCAN", "STARTTLS",
"SORT", "THREAD=ORDEREDSUBJECT", "THREAD=REFERENCES",
- "UNSELECT"
+ "UIDPLUS", "UNSELECT"
};
unsigned x;
int c;
@@ -2137,48 +2200,151 @@
return c;
}
+typedef void (*ImapUidRangeCb)(ImapUidRange *iur, void *arg);
+
+static ImapResponse
+imap_get_sequence(ImapMboxHandle *h, ImapUidRangeCb seq_cb, void *seq_arg)
+{
+ char value[30];
+ gchar *p;
+ int offset = 0;
+ int c = ' ';
+
+ do {
+ size_t value_len;
+ if(c != '\r' && c != '\n') /* Dont try to read beyond the line. */
+ c = imap_get_atom(h->sio, value + offset, sizeof(value)-offset);
+ value_len = strlen(value);
+
+ if(seq_cb) { /* makes sense to parse it ... */
+ static const unsigned LENGTH_OF_LARGEST_UNSIGNED = 10;
+ ImapUidRange seq;
+
+ for(p=value; *p &&
+ (unsigned)(p-value) <=
+ sizeof(value)-(2*LENGTH_OF_LARGEST_UNSIGNED+1); ) {
+ if(sscanf(p, "%u", &seq.lo) != 1)
+ return IMR_PROTOCOL;
+ while(*p && isdigit(*p)) p++;
+ if( *p == ':') {
+ p++;
+ if(sscanf(p, "%u", &seq.hi) != 1)
+ return c;
+ } else seq.hi = seq.lo;
+
+ seq_cb(&seq, seq_arg);
+
+ while(*p && isdigit(*p)) p++;
+ if(*p == ',') p++;
+ } /* End of for */
+
+ /* Reuse what's left. */
+ if(*p) {
+ offset = value_len - (p-value);
+ memmove(value, p, offset+1);
+ } else offset = 0;
+
+ } /* End of if(search_cb) */
+
+ } while (isdigit(c)|| offset);
+
+ if(c != EOF)
+ sio_ungetc(h->sio);
+ return IMR_OK;
+}
+
+static void
+append_uid_range(ImapUidRange *iur, GList **dst)
+{
+ ImapUidRange *iur_copy = g_new(ImapUidRange, 1);
+ /* printf("Prepending %u:%u\n", iur->lo, iur->hi); */
+ iur_copy->lo = iur->lo;
+ iur_copy->hi = iur->hi;
+ *dst = g_list_prepend(*dst, iur_copy);
+}
+
+static ImapResponse
+ir_get_append_copy_uids(ImapMboxHandle *h, gboolean append_only)
+{
+ int c;
+ char buf[12];
+ ImapResponse rc;
+
+ if( (c=imap_get_atom(h->sio, buf, sizeof(buf))) == EOF)
+ return IMR_PROTOCOL;
+ h->uidplus.dst_uid_validity = strtol(buf, NULL, 10);
+
+ if(c != ' ')
+ return IMR_PROTOCOL;
+
+ if(!append_only) {
+ if( (rc = imap_get_sequence(h, NULL, NULL)) != IMR_OK)
+ return rc;
+ if( (c=sio_getc(h->sio)) != ' ') {
+ printf("Expected ' ' found '%c'\n", c);
+ return IMR_PROTOCOL;
+ }
+ }
+ return imap_get_sequence(h, (ImapUidRangeCb)append_uid_range,
+ &h->uidplus.dst);
+}
+
static ImapResponse
ir_resp_text_code(ImapMboxHandle *h)
{
static const char* resp_text_code[] = {
"ALERT", "BADCHARSET", "CAPABILITY","PARSE", "PERMANENTFLAGS",
"READ-ONLY", "READ-WRITE", "TRYCREATE", "UIDNEXT", "UIDVALIDITY",
- "UNSEEN"
+ "UNSEEN", "APPENDUID", "COPYUID"
};
unsigned o;
char buf[128];
int c = imap_get_atom(h->sio, buf, sizeof(buf));
ImapResponse rc = IMR_OK;
-
for(o=0; o<ELEMENTS(resp_text_code); o++)
if(g_ascii_strcasecmp(buf, resp_text_code[o]) == 0) break;
switch(o) {
case 0: rc = IMR_ALERT; break;
- case 1: ignore_bad_charset(h->sio, c); break;
- case 2: ir_capability_data(h); break;
+ case 1: c = ignore_bad_charset(h->sio, c); break;
+ case 2: c = ir_capability_data(h); break;
case 3: rc = IMR_PARSE; break;
- case 4: ir_permanent_flags(h); break;
+ case 4: c = ir_permanent_flags(h); break;
case 5: h->readonly_mbox = TRUE; /* read-only */; break;
case 6: h->readonly_mbox = FALSE; /* read-write */; break;
case 7: /* ignore try-create */; break;
case 8:
- imap_get_atom(h->sio, buf, sizeof(buf));
+ c = imap_get_atom(h->sio, buf, sizeof(buf));
h->uidnext = strtol(buf, NULL, 10);
break;
case 9:
- imap_get_atom(h->sio, buf, sizeof(buf));
+ c = imap_get_atom(h->sio, buf, sizeof(buf));
h->uidval = strtol(buf, NULL, 10);
break;
case 10:
- imap_get_atom(h->sio, buf, sizeof(buf));
+ c = imap_get_atom(h->sio, buf, sizeof(buf));
h->unseen =strtol(buf, NULL, 10);
break;
+ case 11: /* APPENDUID */
+ if( (rc=ir_get_append_copy_uids(h, TRUE)) != IMR_OK)
+ return rc;
+ /* printf("APPENDUID: uid_validity=\n"); */
+ c = sio_getc(h->sio);
+ break;
+ case 12: /* COPYUID */
+ /* printf("Copyuid\n"); */
+ if( (rc=ir_get_append_copy_uids(h, FALSE)) != IMR_OK)
+ return rc;
+ c = sio_getc(h->sio);
+ break;
default: while( c != ']' && (c=sio_getc(h->sio)) != EOF) ; break;
}
- return c != EOF ? rc : IMR_SEVERED;
+ if(c != ']')
+ printf("ir_resp_text_code, on exit c=%c\n", c);
+ return c == ']' ? rc : IMR_PROTOCOL;
}
+
static ImapResponse
ir_ok(ImapMboxHandle *h)
{
@@ -2398,6 +2564,15 @@
return ir_check_crlf(h, sio_getc(h->sio));
}
+static void
+esearch_cb(ImapUidRange *iur, void *arg)
+{
+ ImapMboxHandle *h = (ImapMboxHandle*)arg;
+ unsigned i;
+ for(i=iur->lo; i<= iur->hi; i++)
+ h->search_cb(h, i, h->search_arg);
+}
+
/** Process ESEARCH response. Consult RFC4466 and RFC4731 before
modification. */
static ImapResponse
@@ -2436,56 +2611,19 @@
}
if(c == EOF) return IMR_SEVERED;
- while(c == ' ') {
- static const unsigned LENGTH_OF_LARGEST_UNSIGNED = 10;
- char value[30];
- gchar *p;
- int offset = 0;
+ while(c == ' ') { /* search-return-data in rfc4466 speak */
+ ImapResponse rc;
/* atom contains search-modifier-name, time to fetch
- search-return-value. In ESEARCH, it is always an atom, so we
- cut the corners here. We get values in chunks. The chunk size
- is pretty arbitrary as long as it can fit two largest possible
- 32-bit unsigned numbers and a colon. */
- do {
- size_t value_len;
- if(c != '\r' && c != '\n') /* Dont try to read beyond the line. */
- c = imap_get_atom(h->sio, value + offset, sizeof(value)-offset);
- value_len = strlen(value);
-
- if(h->search_cb) {
- unsigned seq, seq1;
-
- for(p=value; *p &&
- (unsigned)(p-value) <=
- sizeof(value)-(2*LENGTH_OF_LARGEST_UNSIGNED+1); ) {
- if(sscanf(p, "%u", &seq) != 1)
- return IMR_PROTOCOL;
- while(*p && isdigit(*p)) p++;
- if( *p == ':') {
- p++;
- if(sscanf(p, "%u", &seq1) != 1)
- return IMR_PROTOCOL;
- } else seq1 = seq;
-
- for(; seq<=seq1; seq++) {
- h->search_cb(h, seq, h->search_arg);
- }
- while(*p && isdigit(*p)) p++;
- if(*p == ',') p++;
- } /* End of for */
-
- /* Reuse what's left. */
- if(*p) {
- offset = value_len - (p-value);
- memmove(value, p, offset+1);
- } else offset = 0;
-
- } /* End of if(h->search_cb) */
-
- } while (isdigit(c)|| offset);
+ search-return-value. In ESEARCH, it is always an
+ tagged-ext-simple=sequence-set/number, which are an atoms, so
+ we cut the corners here. We get values in chunks. The chunk
+ size is pretty arbitrary as long as it can fit two largest
+ possible 32-bit unsigned numbers and a colon. */
+ if ( (rc=imap_get_sequence(h, esearch_cb, h)) != IMR_OK)
+ return rc;
- if(c == ' ')
- c = imap_get_atom(h->sio, value, sizeof(value));
+ if( (c=sio_getc(h->sio)) == ' ')
+ c = imap_get_atom(h->sio, atom, sizeof(atom));
}
return ir_check_crlf(h, c);
Modified: trunk/libbalsa/imap/imap-handle.h
==============================================================================
--- trunk/libbalsa/imap/imap-handle.h (original)
+++ trunk/libbalsa/imap/imap-handle.h Tue Apr 1 20:55:03 2008
@@ -78,6 +78,7 @@
IMCAP_THREAD_ORDEREDSUBJECT,
IMCAP_THREAD_REFERENCES,
/* http://www.ietf.org/internet-drafts/draft-ietf-imapext-sort-13.txt */
+ IMCAP_UIDPLUS, /* RFC 4315 */
IMCAP_UNSELECT, /* RFC 3691 */
IMCAP_FETCHBODY, /* basic imap implemented correctly by
* most imap servers but not all. We
@@ -181,6 +182,24 @@
/* ================ END OF BODY STRUCTURE FUNCTIONS ==================== */
+/** Stores data returned when UIDPLUS extension is provided. */
+typedef struct ImapSequence_ {
+ GList *ranges; /**< list of ImapUidRange items */
+ unsigned uid_validity;
+} ImapSequence;
+
+/** defines range [lo, hi] */
+typedef struct ImapUidRange_ {
+ unsigned lo, hi;
+} ImapUidRange;
+
+#define imap_sequence_empty(i_seq) ( (i_seq)->ranges == NULL)
+unsigned imap_sequence_length(ImapSequence *i_seq);
+unsigned imap_sequence_nth(ImapSequence *i_seq, unsigned nth);
+void imap_sequence_foreach(ImapSequence *i_seq,
+ void(*cb)(unsigned uid, void *arg), void *cb_arg);
+void imap_sequence_release(ImapSequence *i_seq);
+
/* ================ BEGIN OF MBOX_VIEW FUNCTIONS ======================= */
typedef struct _MboxView MboxView;
void mbox_view_init(MboxView *mv);
Modified: trunk/libbalsa/imap/imap_private.h
==============================================================================
--- trunk/libbalsa/imap/imap_private.h (original)
+++ trunk/libbalsa/imap/imap_private.h Tue Apr 1 20:55:03 2008
@@ -76,6 +76,13 @@
* processing of current line is finished. */
GNode *thread_root; /* deprecated! */
+ struct {
+ GList* src; /**< returned by COPY */
+ GList* dst; /**< returned by APPEND and COPY */
+ unsigned dst_uid_validity;
+ unsigned store_response:1;
+ } uidplus;
+
/* BYE handling depends on the state */
gboolean doing_logout;
ImapInfoCb info_cb;
@@ -144,7 +151,10 @@
void imap_mbox_resize_cache(ImapMboxHandle *h, unsigned new_size);
-ImapResponse imap_cmd_exec(ImapMboxHandle* handle, const char* cmd);
+ImapResponse imap_cmd_exec_cmdno(ImapMboxHandle* handle, const char* cmd,
+ unsigned *cmdno);
+#define imap_cmd_exec(h, c) imap_cmd_exec_cmdno((h),(c),NULL)
+
ImapResponse imap_cmd_issue(ImapMboxHandle* handle, const char* cmd);
char* imap_mbox_gets(ImapMboxHandle *h, char* buf, size_t sz);
Modified: trunk/libbalsa/imap/imap_tst.c
==============================================================================
--- trunk/libbalsa/imap/imap_tst.c (original)
+++ trunk/libbalsa/imap/imap_tst.c Tue Apr 1 20:55:03 2008
@@ -488,7 +488,7 @@
mi.src_dir = src_dir;
mi.dir = dir;
mi.fh = NULL;
- res = imap_mbox_append_multi(h, mailbox, msg_iterator, &mi);
+ res = imap_mbox_append_multi(h, mailbox, msg_iterator, &mi, NULL);
} else {
for(res = IMR_OK; res == IMR_OK && (file = readdir(dir)) != NULL;) {
Modified: trunk/libbalsa/mailbox_imap.c
==============================================================================
--- trunk/libbalsa/mailbox_imap.c (original)
+++ trunk/libbalsa/mailbox_imap.c Tue Apr 1 20:55:03 2008
@@ -427,10 +427,10 @@
gchar *fname;
res[0] = get_cache_dir(is_persistent);
- fname = g_strdup_printf("%s %s-%s-%s-%u-%u",
+ fname = g_strdup_printf("%s %s-%s-%u-%u-%s",
s->user, s->host,
(mailbox->path ? mailbox->path : "INBOX"),
- type, uid_validity, uid);
+ uid_validity, uid, type);
res[1] = libbalsa_urlencode(fname);
g_free(fname);
res[2] = NULL;
@@ -2641,7 +2641,7 @@
LibBalsaAddMessageIterator msg_iterator;
void *iterator_data;
GMimeStream *outstream;
- gchar *outfile;
+ GList *outfiles;
GError **err;
guint copied;
};
@@ -2652,11 +2652,20 @@
if(macd->outstream) {
g_object_unref(macd->outstream);
macd->outstream = NULL;
- unlink(macd->outfile);
- g_free(macd->outfile);
}
}
+static void
+macd_destroy(struct MultiAppendCbData *macd)
+{
+ GList *outmsgs;
+ for(outmsgs = macd->outfiles; outmsgs; outmsgs = outmsgs->next) {
+ unlink(outmsgs->data);
+ g_free(outmsgs->data);
+ }
+ g_list_free(macd->outfiles);
+}
+
static size_t
multi_append_cb(char * buf, size_t buflen,
ImapAppendMultiStage stage,
@@ -2674,6 +2683,7 @@
gssize len;
LibBalsaMessageFlag flags;
GError**err = macd->err;
+ gchar *outf = NULL;
macd_clear(macd);
@@ -2701,7 +2711,7 @@
g_mime_stream_filter_add(GMIME_STREAM_FILTER(tmpstream), crlffilter);
g_object_unref(crlffilter);
- outfd = g_file_open_tmp("balsa-tmp-file-XXXXXX", &macd->outfile, err);
+ outfd = g_file_open_tmp("balsa-tmp-file-XXXXXX", &outf, err);
if (outfd < 0) {
g_warning("Could not create temporary file: %s", (*err)->message);
g_object_unref(tmpstream);
@@ -2710,6 +2720,7 @@
}
macd->outstream = g_mime_stream_fs_new(outfd);
+ macd->outfiles = g_list_append(macd->outfiles, outf);
libbalsa_mime_stream_shared_lock(stream);
g_mime_stream_write_to_stream(tmpstream, macd->outstream);
libbalsa_mime_stream_shared_unlock(stream);
@@ -2735,6 +2746,59 @@
return 0;
}
+struct append_to_cache_data {
+ const gchar *user, *host, *path, *cache_dir;
+ GList *curr_name;
+ unsigned uid_validity;
+};
+
+static void
+append_to_cache(unsigned uid, void *arg)
+{
+ struct append_to_cache_data *atcd = (struct append_to_cache_data*)arg;
+ gchar *name = g_strdup_printf("%s %s-%s-%u-%u-%s",
+ atcd->user, atcd->host, atcd->path,
+ atcd->uid_validity,
+ uid, "body");
+ gchar *fname = libbalsa_urlencode(name);
+ gchar *cache_name = g_build_filename(atcd->cache_dir, fname, NULL);
+ gchar *msg = atcd->curr_name->data;
+
+ g_free(name);
+ g_free(fname);
+
+ atcd->curr_name = g_list_next(atcd->curr_name);
+
+ g_return_if_fail(msg);
+
+ printf("Copy tmp file %s to cache %s\n", msg, cache_name);
+
+ if(link(msg, cache_name) == 0) {
+ printf("Linking msg for message UID %u succeeded.\n", uid);
+ } else {
+ FILE *in = fopen(msg, "r");
+
+ if(in) {
+ FILE *out = fopen(cache_name, "w");
+ char buf[65536];
+ size_t sz;
+ if(out) {
+ gboolean err;
+ while( (sz=fread(buf, 1, sizeof(buf), in)) > 0)
+ if(fwrite(buf, 1, sz, out) != sz)
+ break;
+ err = ferror(in) || ferror(out);
+ fclose(out);
+ if(err)
+ unlink(cache_name);
+ }
+ fclose(in);
+ }
+ printf("Copying msg for message UID %u succeeded.\n", uid);
+ }
+ g_free(cache_name);
+}
+
static guint
libbalsa_mailbox_imap_add_messages(LibBalsaMailbox * mailbox,
LibBalsaAddMessageIterator msg_iterator,
@@ -2744,6 +2808,7 @@
ImapMboxHandle *handle = libbalsa_mailbox_imap_get_handle(mimap, err);
struct MultiAppendCbData macd;
ImapResponse rc;
+ ImapSequence uid_sequence;
if (!handle) {
/* Perhaps the mailbox was closed and the authentication
@@ -2755,13 +2820,40 @@
macd.msg_iterator = msg_iterator;
macd.iterator_data = arg;
macd.outstream = NULL;
- macd.outfile = NULL;
+ macd.outfiles = NULL;
macd.err = err;
macd.copied = 0;
+ uid_sequence.uid_validity = 0;
+ uid_sequence.ranges = NULL;
rc = imap_mbox_append_multi(handle, mimap->path,
- multi_append_cb, &macd);
+ multi_append_cb, &macd, &uid_sequence);
libbalsa_mailbox_imap_release_handle(mimap);
macd_clear(&macd);
+ printf("Counts: tmp files : %u uids: %u\n",
+ g_list_length(macd.outfiles), imap_sequence_length(&uid_sequence));
+ if(!imap_sequence_empty(&uid_sequence) &&
+ g_list_length(macd.outfiles) == imap_sequence_length(&uid_sequence)) {
+ /* Hurray, server returned UID data on appended messages! */
+ LibBalsaMailboxImap *mimap = LIBBALSA_MAILBOX_IMAP(mailbox);
+ LibBalsaServer *s = LIBBALSA_MAILBOX_REMOTE(mailbox)->server;
+ LibBalsaImapServer *is = LIBBALSA_IMAP_SERVER(s);
+ gboolean is_persistent = libbalsa_imap_server_has_persistent_cache(is);
+ struct append_to_cache_data atcd;
+ gchar *cache_dir;
+
+ atcd.user = s->user;
+ atcd.host = s->host;
+ atcd.path = mimap->path ? mimap->path : "INBOX";
+ atcd.cache_dir = cache_dir = get_cache_dir(is_persistent);
+ atcd.curr_name = macd.outfiles;
+ atcd.uid_validity = uid_sequence.uid_validity;
+
+ imap_sequence_foreach(&uid_sequence, append_to_cache, &atcd);
+ imap_sequence_release(&uid_sequence);
+ g_free(cache_dir);
+ }
+
+ macd_destroy(&macd);
return rc == IMR_OK ? macd.copied : 0;
}
#endif
@@ -3057,14 +3149,25 @@
LIBBALSA_MAILBOX_REMOTE(dest)->server ==
LIBBALSA_MAILBOX_REMOTE(mailbox)->server) {
gboolean ret;
+ LibBalsaMailboxImap *mimap = LIBBALSA_MAILBOX_IMAP(mailbox);
ImapMboxHandle *handle = LIBBALSA_MAILBOX_IMAP(mailbox)->handle;
+ ImapSequence uid_sequence;
+ unsigned *seqno = (unsigned*)msgnos->data, *uids;
+ unsigned im;
g_return_val_if_fail(handle, FALSE);
-
+
/* User server-side copy. */
g_array_sort(msgnos, cmp_msgno);
+ uids = g_new(unsigned, msgnos->len);
+ for(im=0; im<msgnos->len; im++) {
+ ImapMessage * imsg = imap_mbox_handle_get_msg(handle, seqno[im]);
+ uids[im] = imsg ? imsg->uid : 0;
+ }
+
ret = imap_mbox_handle_copy(handle, msgnos->len,
(guint *) msgnos->data,
- LIBBALSA_MAILBOX_IMAP(dest)->path)
+ LIBBALSA_MAILBOX_IMAP(dest)->path,
+ &uid_sequence)
== IMR_OK;
if(!ret) {
gchar *msg = imap_mbox_handle_get_last_msg(handle);
@@ -3072,7 +3175,69 @@
LIBBALSA_MAILBOX_COPY_ERROR,
"%s", msg);
g_free(msg);
- }
+ } else {
+ /* Copy cache files. */
+ GDir *dir;
+ LibBalsaServer *s = LIBBALSA_MAILBOX_REMOTE(mailbox)->server;
+ LibBalsaImapServer *is = LIBBALSA_IMAP_SERVER(s);
+ LibBalsaMailboxImap *dst_imap = LIBBALSA_MAILBOX_IMAP(dest);
+ gboolean is_persistent =
+ libbalsa_imap_server_has_persistent_cache(is);
+ gchar *dir_name = get_cache_dir(is_persistent);
+ gchar *src_prefix = g_strdup_printf("%s %s-%s-%u-",
+ s->user, s->host,
+ (mimap->path
+ ? mimap->path : "INBOX"),
+ mimap->uid_validity);
+ gchar *encoded_path = libbalsa_urlencode(src_prefix);
+ g_free(src_prefix);
+ dir = g_dir_open(dir_name, 0, NULL);
+ if(dir) {
+ const gchar *filename;
+ size_t prefix_length = strlen(encoded_path);
+ unsigned nth;
+ printf("UIDVAL=%u Look for files that match %s \n",
+ uid_sequence.uid_validity, encoded_path);
+ while ((filename = g_dir_read_name(dir)) != NULL) {
+ unsigned msg_uid;
+ gchar *tail;
+ if(strncmp(encoded_path, filename, prefix_length))
+ continue;
+ msg_uid = strtol(filename + prefix_length, &tail, 10);
+ for(im = 0; im<msgnos->len; im++) {
+ if(uids[im]>msg_uid) break;
+ else if(uids[im]==msg_uid &&
+ (nth = imap_sequence_nth(&uid_sequence, im))
+ ) {
+ gchar *dst;
+ /* Copy by hardlinks! */
+ gchar *src =
+ g_build_filename(dir_name, filename, NULL);
+ gchar *dst_prefix =
+ g_strdup_printf("%s %s-%s-%u-%u%s",
+ s->user, s->host,
+ (dst_imap->path
+ ? dst_imap->path : "INBOX"),
+ uid_sequence.uid_validity,
+ nth, tail);
+ gchar *encoded = libbalsa_urlencode(dst_prefix);
+
+ g_free(dst_prefix);
+ dst = g_build_filename(dir_name, encoded, NULL);
+ printf("Linking %s to %s...\n", src, dst);
+ if(link(src, dst) != 0)
+ printf("...failed.\n");
+ g_free(dst);
+ break;
+ }
+ }
+ }
+ g_dir_close(dir);
+ }
+ g_free(dir_name);
+ }
+ g_free(uids);
+ imap_sequence_release(&uid_sequence);
return ret;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]