aboutsummaryrefslogblamecommitdiffstats
path: root/camel/providers/imapp/camel-imapp-driver.c
blob: 8028b942bfba22f952e8922b1e1ad4d22e76ef84 (plain) (tree)
1
2
3
4
5
6
7
8

 


                    

                   
                   

































































                                                                                                     
















































































                                                                                                                               

























































































































































































































































                                                                                                                                        


























                                                                                               
     









                                             
      
 



                                                                                                                                           
 


                                                                                                                                                    
 
                               

                              







                                            


















                                                                                                                             
      



                                                                              
                                        













                                                                               





















                                                                                                                

                                                                

                                                 
 
                                                                        
                                            
                                   
                 









                                                      


















































































































































































































                                                                                                                                      
                                                 
 
                                                                       


                                                                                                           
                                                            

















                                                                                                   


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "camel-imapp-driver.h"
#include "camel-imapp-utils.h"
#include "camel-imapp-folder.h"
#include "camel-imapp-engine.h"
#include "camel-imapp-summary.h"
#include "camel-imapp-exception.h"

#include <camel/camel-stream-mem.h>
#include <camel/camel-stream-null.h>

#include <camel/camel-folder-summary.h>
#include <camel/camel-store.h>
#include <camel/camel-mime-utils.h>
#include <camel/camel-sasl.h>

#define d(x) x

static int driver_resp_fetch(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
static int driver_resp_expunge(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
static int driver_resp_exists(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);
static int driver_resp_list(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata);

static void driver_status(CamelIMAPPEngine *ie, struct _status_info *sinfo, CamelIMAPPDriver *sdata);

static void
class_init(CamelIMAPPDriverClass *ieclass)
{
}

static void
object_init(CamelIMAPPDriver *ie, CamelIMAPPDriverClass *ieclass)
{
    ie->summary = g_ptr_array_new();
    e_dlist_init(&ie->body_fetch);
    e_dlist_init(&ie->body_fetch_done);
}

static void
object_finalise(CamelIMAPPDriver *ie, CamelIMAPPDriverClass *ieclass)
{
    if (ie->folder)
        camel_object_unref((CamelObject *)ie->folder);
    if (ie->engine)
        camel_object_unref((CamelObject *)ie->engine);
    if (ie->summary)
        g_ptr_array_free(ie->summary, TRUE);
}

CamelType
camel_imapp_driver_get_type (void)
{
    static CamelType type = CAMEL_INVALID_TYPE;

    if (type == CAMEL_INVALID_TYPE) {
        type = camel_type_register (
            camel_object_get_type (),
            "CamelIMAPPDriver",
            sizeof (CamelIMAPPDriver),
            sizeof (CamelIMAPPDriverClass),
            (CamelObjectClassInitFunc) class_init,
            NULL,
            (CamelObjectInitFunc) object_init,
            (CamelObjectFinalizeFunc) object_finalise);
    }
    
    return type;
}

CamelIMAPPDriver *
camel_imapp_driver_new(CamelIMAPPStream *stream)
{
    CamelIMAPPDriver *driver;
    CamelIMAPPEngine *ie;

    driver = CAMEL_IMAPP_DRIVER (camel_object_new (CAMEL_IMAPP_DRIVER_TYPE));
    ie = driver->engine = camel_imapp_engine_new(stream);
    
    camel_imapp_engine_add_handler(ie, "FETCH", (CamelIMAPPEngineFunc)driver_resp_fetch, driver);
    camel_imapp_engine_add_handler(ie, "EXPUNGE", (CamelIMAPPEngineFunc)driver_resp_expunge, driver);
    camel_imapp_engine_add_handler(ie, "EXISTS", (CamelIMAPPEngineFunc)driver_resp_exists, driver);
    camel_imapp_engine_add_handler(ie, "LIST", (CamelIMAPPEngineFunc)driver_resp_list, driver);
    camel_object_hook_event(ie, "status", (CamelObjectEventHookFunc)driver_status, driver);

        return driver;
}

void
camel_imapp_driver_set_sasl_factory(CamelIMAPPDriver *id, CamelIMAPPSASLFunc get_sasl, void *sasl_data)
{
    id->get_sasl = get_sasl;
    id->get_sasl_data = sasl_data;
}

void
camel_imapp_driver_set_login_query(CamelIMAPPDriver *id, CamelIMAPPLoginFunc get_login, void *login_data)
{
    id->get_login = get_login;
    id->get_login_data = login_data;
}

void
camel_imapp_driver_login(CamelIMAPPDriver *id)
/* throws SERVICE_CANT_AUTHENTICATE, SYSTEM_IO */
{
    CamelIMAPPCommand * volatile ic = NULL;

    /* connect? */
    /* camel_imapp_engine_connect() */
    /* or above? */

    CAMEL_TRY {
        CamelSasl *sasl;

        if (id->get_sasl
            && (sasl = id->get_sasl(id, id->get_sasl_data))) {
            ic = camel_imapp_engine_command_new(id->engine, "AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl);
            camel_object_unref(sasl);
        } else {
            char *user, *pass;

            g_assert(id->get_login);
            id->get_login(id, &user, &pass, id->get_login_data);
            ic = camel_imapp_engine_command_new(id->engine, "LOGIN", NULL, "LOGIN %s %s", user, pass);
            g_free(user);
            g_free(pass);
        }

        camel_imapp_engine_command_queue(id->engine, ic);
        while (camel_imapp_engine_iterate(id->engine, ic) > 0)
            ;

        if (ic->status->result != IMAP_OK)
            camel_exception_throw(CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, "Login failed: %s", ic->status->text);
        camel_imapp_engine_command_free(id->engine, ic);
    } CAMEL_CATCH(ex) {
        if (ic)
            camel_imapp_engine_command_free(id->engine, ic);
        camel_exception_throw_ex(ex);
    } CAMEL_DONE;
}

void
camel_imapp_driver_select(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder)
{
    CamelIMAPPCommand * volatile ic = NULL;
    CamelIMAPPCommand * volatile ic2 = NULL;
    guint32 count;
    CamelFolderSummary *summary;

    if (id->folder) {
        if (folder == id->folder)
            return;
        camel_imapp_driver_sync(id, FALSE, id->folder);
        if (camel_folder_change_info_changed(id->folder->changes)) {
            camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
            camel_folder_change_info_clear(id->folder->changes);
        }
        camel_object_unref(id->folder);
        id->folder = NULL;
    }

    summary = ((CamelFolder *)folder)->summary;
    
    ic = camel_imapp_engine_command_new(id->engine, "SELECT", NULL, "SELECT %t", folder->raw_name);
    camel_imapp_engine_command_queue(id->engine, ic);
    while (camel_imapp_engine_iterate(id->engine, ic)>0)
        ;
    camel_imapp_engine_command_free(id->engine, ic);

    id->folder = folder;
    camel_object_ref(folder);

    count = camel_folder_summary_count(summary);
    if (count > 0 && count <= id->exists) {
        ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
                           "FETCH 1:%u (UID FLAGS)", count);
        camel_imapp_engine_command_queue(id->engine, ic);
        if (count < id->exists) {
            ic2 = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
                                "FETCH %u:* (UID FLAGS ENVELOPE)", count+1);
            camel_imapp_engine_command_queue(id->engine, ic2);
        } else {
            ic2 = NULL;
        }

        while (camel_imapp_engine_iterate(id->engine, ic2?ic2:ic)>0)
            ;

        camel_imapp_engine_command_free(id->engine, ic);
        if (ic2)
            camel_imapp_engine_command_free(id->engine, ic2);
    } else {
        ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
                           "FETCH 1:* (UID FLAGS ENVELOPE)");
        camel_imapp_engine_command_queue(id->engine, ic);
        while (camel_imapp_engine_iterate(id->engine, ic)>0)
            ;
        camel_imapp_engine_command_free(id->engine, ic);
    }

    /* TODO: need to set exists/etc in summary */
    folder->exists = id->exists;
    folder->uidvalidity = id->uidvalidity;

    printf("saving summary '%s'\n", summary->summary_path);
    camel_folder_summary_save(summary);

    if (camel_folder_change_info_changed(id->folder->changes)) {
        camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
        camel_folder_change_info_clear(id->folder->changes);
    }
}

static void
imapp_driver_check(CamelIMAPPDriver *id)
{
    guint32 count;
    CamelIMAPPCommand *ic;

    /* FIXME: exception handling */

    if (id->folder->exists != id->exists) {
        count = camel_folder_summary_count(((CamelFolder *)id->folder)->summary);
        if (count < id->exists) {
            printf("fetching new messages\n");
            ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL,
                                "FETCH %u:* (UID FLAGS ENVELOPE)", count+1);
            camel_imapp_engine_command_queue(id->engine, ic);
            while (camel_imapp_engine_iterate(id->engine, ic)>0)
                ;
            camel_imapp_engine_command_free(id->engine, ic);
        } else if (count > id->exists) {
            printf("folder shrank with no expunge notificaitons!?  uh, dunno what to do\n");
        }
    }

    printf("checking for change info changes\n");
    if (camel_folder_change_info_changed(id->folder->changes)) {
        printf("got somechanges!  added=%d changed=%d removed=%d\n",
               id->folder->changes->uid_added->len,
               id->folder->changes->uid_changed->len,
               id->folder->changes->uid_removed->len);
        camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
        camel_folder_change_info_clear(id->folder->changes);
    }
}

void
camel_imapp_driver_update(CamelIMAPPDriver *id, CamelIMAPPFolder *folder)
{
    if (id->folder == folder) {
        CamelIMAPPCommand *ic;

        /* this will automagically update flags & expunge items */
        ic = camel_imapp_engine_command_new(id->engine, "NOOP", NULL, "NOOP");
        camel_imapp_engine_command_queue(id->engine, ic);
        while (camel_imapp_engine_iterate(id->engine, ic)>0)
            ;
        camel_imapp_engine_command_free(id->engine, ic);

        imapp_driver_check(id);
    } else {
        camel_imapp_driver_select(id, folder);
    }
}

/* FIXME: this is basically a copy of the same in camel-imapp-utils.c */
static struct {
    char *name;
    guint32 flag;
} flag_table[] = {
    { "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
    { "\\DELETED", CAMEL_MESSAGE_DELETED },
    { "\\DRAFT", CAMEL_MESSAGE_DRAFT },
    { "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
    { "\\SEEN", CAMEL_MESSAGE_SEEN },
    /* { "\\RECENT", CAMEL_IMAPP_MESSAGE_RECENT }, */
};

/*
  flags 00101000
 sflags 01001000
 ^      01100000
~flags  11010111
&       01000000

&flags  00100000
*/

static void
imapp_write_flags(CamelIMAPPDriver *id, guint32 orset, gboolean on, CamelFolderSummary *summary)
{
    guint32 i, j, count;
    CamelIMAPPMessageInfo *info;
    CamelIMAPPCommand *ic = NULL;
    struct _uidset_state ss;
    GSList *commands = NULL;

    /* FIXME: exception handling */

    count = camel_folder_summary_count(summary);
    for (j=0;j<sizeof(flag_table)/sizeof(flag_table[0]);j++) {
        int flush;
        
        if ((orset & flag_table[j].flag) == 0)
            continue;

        printf("checking/storing %s flags '%s'\n", on?"on":"off", flag_table[j].name);

        flush = 0;
        imapp_uidset_init(&ss, id->engine);
        for (i=0;i<count;i++) {
            info = (CamelIMAPPMessageInfo *)camel_folder_summary_index(summary, i);
            if (info) {
                guint32 flags = info->info.flags & CAMEL_IMAPP_SERVER_FLAGS;
                guint32 sflags = info->server_flags & CAMEL_IMAPP_SERVER_FLAGS;

                if ( (on && (((flags ^ sflags) & flags) & flag_table[j].flag))
                     || (!on && (((flags ^ sflags) & ~flags) & flag_table[j].flag))) {
                    if (ic == NULL)
                        ic = camel_imapp_engine_command_new(id->engine, "STORE", NULL, "UID STORE ");
                    flush = imapp_uidset_add(&ss, ic, camel_message_info_uid(info));                
                }
                camel_folder_summary_info_free(summary, (CamelMessageInfo *)info);
            }

            if (i == count-1 && ic != NULL)
                flush |= imapp_uidset_done(&ss, ic);

            if (flush) {
                flush = 0;
                camel_imapp_engine_command_add(id->engine, ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flag_table[j].name);
                camel_imapp_engine_command_queue(id->engine, ic);
                commands = g_slist_prepend(commands, ic);
                ic = NULL;
            }
        }
    }

    /* flush off any requests we may have outstanding */
    /* TODO: for max benefit, should have this routine do both on and off flags in one go */
    while (commands) {
        GSList *next = commands->next;

        ic = commands->data;
        g_slist_free_1(commands);
        commands = next;

        while (camel_imapp_engine_iterate(id->engine, ic)>0)
            ;
        camel_imapp_engine_command_free(id->engine, ic);
    }
}

void
camel_imapp_driver_sync(CamelIMAPPDriver *id, gboolean expunge, CamelIMAPPFolder *folder)
{
    CamelFolderSummary *summary;
    guint i, count, on_orset, off_orset;
    CamelIMAPPMessageInfo *info;
    CamelIMAPPCommand *ic;

    /* FIXME: exception handling */

    camel_imapp_driver_update(id, folder);

    summary = ((CamelFolder *)folder)->summary;
    count = camel_folder_summary_count(summary);
    /* find out which flags have turned on, which have tunred off */
    off_orset = on_orset = 0;
    for (i=0;i<count;i++) {
        guint32 flags, sflags;

        info = (CamelIMAPPMessageInfo *)camel_folder_summary_index(summary, i);
        if (info == NULL)
            continue;
        flags = info->info.flags & CAMEL_IMAPP_SERVER_FLAGS;
        sflags = info->server_flags & CAMEL_IMAPP_SERVER_FLAGS;
        if (flags != sflags) {
            off_orset |= ( flags ^ sflags ) & ~flags;
            on_orset |= (flags ^ sflags) & flags;
        }
        camel_folder_summary_info_free(summary, (CamelMessageInfo *)info);
    }

    if (on_orset || off_orset) {
        /* turn on or off all messages matching */
        if (on_orset)
            imapp_write_flags(id, on_orset, TRUE, summary);
        if (off_orset)
            imapp_write_flags(id, off_orset, FALSE, summary);

        /* success (no exception), make sure we match what we're supposed to */
        for (i=0;i<count;i++) {
            info = (CamelIMAPPMessageInfo *)camel_folder_summary_index(summary, i);
            if (info == NULL)
                continue;
            info->server_flags = info->info.flags & CAMEL_IMAPP_SERVER_FLAGS;
            camel_folder_summary_info_free(summary, (CamelMessageInfo *)info);
        }
        camel_folder_summary_touch(summary);
        /* could save summary here, incase of failure? */
    }

    if (expunge) {
        ic = camel_imapp_engine_command_new(id->engine, "EXPUNGE", NULL, "EXPUNGE");
        camel_imapp_engine_command_queue(id->engine, ic);
        while (camel_imapp_engine_iterate(id->engine, ic)>0)
            ;
        camel_imapp_engine_command_free(id->engine, ic);
    }

    printf("saving summary '%s'\n", summary->summary_path);
    camel_folder_summary_save(summary);

    if (camel_folder_change_info_changed(id->folder->changes)) {
        camel_object_trigger_event(id->folder, "folder_changed", id->folder->changes);
        camel_folder_change_info_clear(id->folder->changes);
    }
}

#if 0
static void
fetch_data_free(CamelIMAPPFetch *fd)
{
    if (fd->body)
        camel_object_unref(fd->body);
    camel_object_unref(fd->folder);
    g_free(fd->uid);
    g_free(fd->section);
    g_free(fd);
}
#endif

struct _CamelStream *   camel_imapp_driver_fetch(CamelIMAPPDriver *id, struct _CamelIMAPPFolder *folder, const char *uid, const char *body)
{
    return NULL;
}

#if 0
void
camel_imapp_driver_fetch(CamelIMAPPDriver *id, CamelIMAPPFolder *folder, const char *uid, const char *section, CamelIMAPPFetchFunc done, void *data)
{
    struct _fetch_data *fd;
    CamelIMAPPCommand *ic;

    fd = g_malloc0(sizeof(*fd));
    fd->folder = folder;
    camel_object_ref(folder);
    fd->uid = g_strdup(uid);
    fd->section = g_strdup(fd->section);
    fd->done = done;
    fd->data = data;

    e_dlist_addtail(&id->body_fetch, (EDListNode *)&fd);

    CAMEL_TRY {
        camel_imapp_driver_select(id, folder);

        ic = camel_imapp_engine_command_new(id->engine, "FETCH", NULL, "UID FETCH %t (BODY.PEEK[%t])", uid, section);
        camel_imapp_engine_command_queue(id->engine, ic);
        while (camel_imapp_engine_iterate(id->engine, ic)>0)
            ;
        camel_imapp_engine_command_free(id->engine, ic);
        imapp_driver_check(id);
    } CAMEL_CATCH(e) {
        /* FIXME: do exception properly */
    } CAMEL_DONE;

    e_dlist_remove((EDListNode *)&fd);

    return fd.data;
}
#endif

GPtrArray *
camel_imapp_driver_list(CamelIMAPPDriver *id, const char *name, guint32 flags)
{
    CamelIMAPPCommand * volatile ic;
    GPtrArray *res;

    g_assert(id->list_commands == NULL);
    g_assert(id->list_result == NULL);

    /* FIXME: make sure we only have a single list running at a time */
    /* sem_wait(id->list_sem); */

    /* FIXME: namespace stuff (done in store code?) */

    /* FIXME: if name != "", we need to also do list "name.%" (. == sep) */

    id->list_result = g_ptr_array_new();
    id->list_flags = flags;
    CAMEL_TRY {
        ic = camel_imapp_engine_command_new(id->engine, "LIST", NULL, "LIST \"\" %f", name[0]?name:"%");
        camel_imapp_engine_command_queue(id->engine, ic);
        while (ic) {
            while (camel_imapp_engine_iterate(id->engine, ic)>0)
                ;
            camel_imapp_engine_command_free(id->engine, ic);

            if (id->list_commands) {
                GSList *top = id->list_commands;
                
                id->list_commands = top->next;
                ic = top->data;
                g_slist_free_1(top);
            } else {
                ic = NULL;
            }
        }
    } CAMEL_CATCH(e) {
        GSList *top = id->list_commands;
        int i;

        camel_imapp_engine_command_free(id->engine, ic);

        while (top) {
            GSList *topn = top->next;

            camel_imapp_engine_command_free(id->engine, ic);
            g_slist_free_1(top);
            top = topn;
        }
        id->list_commands = NULL;

        res = id->list_result;
        for (i=0;i<res->len;i++)
            imap_free_list(res->pdata[i]);
        g_ptr_array_free(res, TRUE);
        id->list_result = NULL;

        camel_exception_throw_ex(e);
    } CAMEL_DONE;

    res = id->list_result;
    id->list_result = NULL;

    /* sem_post(id->list_sem); */

    return res;
}

static int
driver_resp_list(CamelIMAPPEngine *ie, guint32 idx, CamelIMAPPDriver *id)
{
    struct _list_info *linfo;

    /* FIXME: exceptions */

    linfo = imap_parse_list(ie->stream);
    printf("store list:  '%s' ('%c')\n", linfo->name, linfo->separator);
    if (id->list_result) {
        if ((linfo->flags & CAMEL_FOLDER_NOINFERIORS) == 0
            && (id->list_flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)
            && linfo->separator) {
            int depth = 0;
            char *p = linfo->name;
            char c = linfo->separator;

            /* this is expensive ... but if we've listed this deep we're going slow anyway */
            while (*p && depth < 10) {
                if (*p == c)
                    depth++;
                p++;
            }

            if (depth < 10
                && (linfo->name[0] == 0 || linfo->name[strlen(linfo->name)-1] != c)) {
                CamelIMAPPCommand *ic;
                
                ic = camel_imapp_engine_command_new(id->engine, "LIST", NULL, "LIST \"\" %t%c%%", linfo->name, c);
                id->list_commands = g_slist_prepend(id->list_commands, ic);
                camel_imapp_engine_command_queue(id->engine, ic);
            }
        }
        /* FIXME: dont add to list if name ends in separator */
        g_ptr_array_add(id->list_result, linfo);
    } else {
        g_warning("unexpected list response\n");
        imap_free_list(linfo);
    }

    return camel_imapp_engine_skip(ie);
}

/* ********************************************************************** */

static void
driver_status(CamelIMAPPEngine *ie, struct _status_info *sinfo, CamelIMAPPDriver *sdata)
{
    printf("got status response ...\n");
    switch(sinfo->condition) {
    case IMAP_READ_WRITE:
        printf("folder is read-write\n");
        break;
    case IMAP_READ_ONLY:
        printf("folder is read-only\n");
        break;
    case IMAP_UIDVALIDITY:
        sdata->uidvalidity = sinfo->u.uidvalidity;
        break;
#if 0   
            /* not defined yet ... */
    case IMAP_UIDNEXT:
        printf("got uidnext for folder: %d\n", sinfo->u.uidnext);
        break;
#endif  
    case IMAP_UNSEEN:
        sdata->unseen = sinfo->u.unseen;
        break;
    case IMAP_PERMANENTFLAGS:
        sdata->permanentflags = sinfo->u.permanentflags;
        break;
    case IMAP_ALERT:
        printf("ALERT!: %s\n", sinfo->text);
        break;
    case IMAP_PARSE:
        printf("PARSE: %s\n", sinfo->text);
        break;
    default:
        break;
    }
}

static int
driver_resp_exists(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata)
{
    /* should this be an event instead? */

    sdata->exists = id;

    return camel_imapp_engine_skip(ie);
}

static int
driver_resp_expunge(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata)
{
    printf("got expunge response %u\n", id);
    if (sdata->folder != NULL) {
        CamelMessageInfo *info;
        CamelFolderSummary *summary = ((CamelFolder *)sdata->folder)->summary;

        info = camel_folder_summary_index(summary, id-1);
        if (info) {
            printf("expunging msg %d\n", id);
            camel_folder_summary_remove(summary, info);
            camel_folder_summary_info_free(summary, info);
            camel_folder_change_info_remove_uid(sdata->folder->changes, camel_message_info_uid(info));
        } else {
            printf("can not find msg %u from expunge\n", id);
        }
    }

    return camel_imapp_engine_skip(ie);
}

static int
driver_resp_fetch(CamelIMAPPEngine *ie, guint32 id, CamelIMAPPDriver *sdata)
{
    struct _fetch_info *finfo = NULL;
    CamelMessageInfo *info, *uinfo;
    unsigned int i;
    CamelFolderSummary *summary;

    printf("got fetch response %d\n", id);

    if (sdata->folder == NULL)
        goto done;

    summary = ((CamelFolder *)sdata->folder)->summary;

    finfo = imap_parse_fetch(ie->stream);
    imap_dump_fetch(finfo);

    info = camel_folder_summary_index(summary, id-1);
    if (info == NULL) {
        if (finfo->uid == NULL) {
            printf("got fetch response for currently unknown message %u\n", id);
            goto done;
        }
        uinfo = camel_folder_summary_uid(summary, finfo->uid);
        if (uinfo) {
            /* we have a problem ... index mismatch */
            printf("index mismatch, uid '%s' not at index '%u'\n",
                   finfo->uid, id);
            camel_folder_summary_info_free(summary, uinfo);
        }
        /* pad out the summary till we have enough indexes */
        for (i=camel_folder_summary_count(summary);i<id;i++) {
            info = camel_folder_summary_info_new(summary);
            if (i == id-1) {
                printf("inserting new info @ %u\n", i);
                camel_message_info_set_uid(info, g_strdup(finfo->uid));
            } else {
                char uidtmp[32];

                sprintf(uidtmp, "blank-%u", i);
                camel_message_info_set_uid(info, g_strdup(uidtmp));
                printf("inserting empty uid %s\n", uidtmp);
            }
        
            camel_folder_summary_add(summary, info);
        }
        info = camel_folder_summary_index(summary, id-1);
        g_assert(info != NULL);
    } else {
        if (finfo->uid) {
            /* FIXME: need to handle blank-* uids, somehow */
            while (info && strcmp(camel_message_info_uid(info), finfo->uid) != 0) {
                printf("index mismatch, uid '%s' not at index '%u', got '%s' instead (removing)\n",
                       finfo->uid, id, camel_message_info_uid(info));

                camel_folder_change_info_remove_uid(sdata->folder->changes, camel_message_info_uid(info));
                camel_folder_summary_remove(summary, info);
                camel_folder_summary_info_free(summary, info);
                info = camel_folder_summary_index(summary, id-1);
            }
        } else {
            printf("got info for unknown message %u\n", id);
        }
    }

    if (info) {
        if (finfo->got & FETCH_MINFO) {
            /* if we only use ENVELOPE? */
            camel_message_info_set_subject(info, g_strdup(camel_message_info_subject(finfo->minfo)));
            camel_message_info_set_from(info, g_strdup(camel_message_info_from(finfo->minfo)));
            camel_message_info_set_to(info, g_strdup(camel_message_info_to(finfo->minfo)));
            camel_message_info_set_cc(info, g_strdup(camel_message_info_cc(finfo->minfo)));
            info->date_sent = finfo->minfo->date_sent;
            camel_folder_change_info_add_uid(sdata->folder->changes, camel_message_info_uid(info));
            printf("adding change info uid '%s'\n", camel_message_info_uid(info));
        }

        if (finfo->got & FETCH_FLAGS) {
            if ((info->flags & CAMEL_IMAPP_SERVER_FLAGS) != (finfo->flags & CAMEL_IMAPP_SERVER_FLAGS)) {
                camel_folder_change_info_change_uid(sdata->folder->changes, camel_message_info_uid(info));
                info->flags = (info->flags & ~(CAMEL_IMAPP_SERVER_FLAGS)) | (finfo->flags & CAMEL_IMAPP_SERVER_FLAGS);
                camel_folder_summary_touch(summary);
            }
            ((CamelIMAPPMessageInfo *)info)->server_flags = finfo->flags & CAMEL_IMAPP_SERVER_FLAGS;
        }

        if ((finfo->got & (FETCH_BODY|FETCH_UID)) == (FETCH_BODY|FETCH_UID)) {
            CamelIMAPPFetch *fd, *fn;

            fd = (CamelIMAPPFetch *)sdata->body_fetch.head;
            fn = fd->next;
            while (fn) {
                if (!strcmp(finfo->uid, fd->uid) && !strcmp(finfo->section, fd->section)) {
                    fd->done(sdata, fd);
                    e_dlist_remove((EDListNode *)fd);
                    e_dlist_addtail(&sdata->body_fetch_done, (EDListNode *)fd);
                    break;
                }
                fd = fn;
                fn = fn->next;
            }
        }

        camel_folder_summary_info_free(summary, info);
    } else {
        printf("dont know what to do with message\n");
    }
 done:
    imap_free_fetch(finfo);

    return camel_imapp_engine_skip(ie);
}