diff options
author | Kim Vandry <vandry@TZoNE.ORG> | 2014-09-05 10:30:45 -0400 |
---|---|---|
committer | Kim Vandry <vandry@TZoNE.ORG> | 2014-09-05 15:07:39 -0400 |
commit | 23375a7b5edb87aa0b8781125321e0196a8d4246 (patch) | |
tree | 2d38cfc8bbb7beb020185946a85728ec3d4028b5 | |
parent | 0d6473dc7f161d70a7f2baedaa40f13e3fe05a1e (diff) |
Added support for search results in an IMAP folder
-rw-r--r-- | imap.c | 13 | ||||
-rw-r--r-- | imap.h | 1 | ||||
-rw-r--r-- | imapinterface.c | 148 | ||||
-rw-r--r-- | imapinterface.h | 4 | ||||
-rw-r--r-- | mairix.c | 28 | ||||
-rw-r--r-- | mairix.h | 3 | ||||
-rw-r--r-- | search.c | 58 |
7 files changed, 243 insertions, 12 deletions
@@ -838,6 +838,19 @@ imap_ll_status(struct imap_ll_tokenlist *t) return t->leaf; } +int +imap_ll_is_trycreate(struct imap_ll_tokenlist *t) +{ + return ( + (0 == strcmp(imap_ll_status(t), "NO")) && + (t->last->first->next) && + ((t->last->first->next->type) == TLTYPE_SQLIST) && + (t->last->first->next->first) && + ((t->last->first->next->first->type) == TLTYPE_ATOM) && + (0 == strcmp(t->last->first->next->first->leaf, "TRYCREATE")) + ); +} + #ifdef USE_OPENSSL enum imap_ll_starttls_result imap_ll_starttls(struct imap_ll *ll, SSL_CTX *sslctx, const char *servername) @@ -41,6 +41,7 @@ void imap_ll_append(struct imap_ll_tokenlist *, struct imap_ll_tokenlist *); void imap_ll_pprint(struct imap_ll_tokenlist *, int indent, FILE *); struct imap_ll_tokenlist *imap_ll_command(struct imap_ll *, struct imap_ll_tokenlist *, int timeout); const char *imap_ll_status(struct imap_ll_tokenlist *); +int imap_ll_is_trycreate(struct imap_ll_tokenlist *); #ifdef USE_OPENSSL enum imap_ll_starttls_result { diff --git a/imapinterface.c b/imapinterface.c index 4f9063c..7d09517 100644 --- a/imapinterface.c +++ b/imapinterface.c @@ -270,3 +270,151 @@ struct make_rfc822_from_imap_s s; imap_fetch_message_raw(pseudopath, imapc, callback822, &s); return s.r; } + +void +imap_clear_folder(struct imap_ll *imapc, const char *folder) +{ +struct imap_ll_tokenlist *cmd, *result; + + if (!imap_select(imapc, folder, -1, 1, NULL)) return; + cmd = imap_ll_build( + TLTYPE_TAGGED, + TLTYPE_ATOM, "STORE", (size_t)-1, + TLTYPE_ATOM, "1:*", (size_t)-1, + TLTYPE_ATOM, "+FLAGS", (size_t)-1, + TLTYPE_ATOM, "\\Deleted", (size_t)-1, + TLTYPE_END + ); + result = imap_ll_command(imapc, cmd, 10); + imap_ll_freeline(cmd); + imap_ll_freeline(result); + cmd = imap_ll_build( + TLTYPE_TAGGED, + TLTYPE_ATOM, "EXPUNGE", (size_t)-1, + TLTYPE_END + ); + result = imap_ll_command(imapc, cmd, 10); + imap_ll_freeline(cmd); + imap_ll_freeline(result); +} + +void +create_folder(struct imap_ll *imapc, const char *folder) +{ +struct imap_ll_tokenlist *cmd, *result; + + cmd = imap_ll_build( + TLTYPE_TAGGED, + TLTYPE_ATOM, "CREATE", (size_t)-1, + TLTYPE_STRING, folder, (size_t)-1, + TLTYPE_END + ); + result = imap_ll_command(imapc, cmd, 10); + imap_ll_freeline(cmd); + imap_ll_freeline(result); +} + +void +imap_copy_message(struct imap_ll *imapc, const char *pseudopath, const char *to_folder) +{ +struct imap_ll_tokenlist *cmd, *result; +const char *uidvalidity, *uid, *folder, *p; +size_t uidvalidity_len, uid_len; +const char *actual_uidvalidity; + + /* first part is the uidvalidity */ + uidvalidity = pseudopath; + p = strchr(pseudopath, ':'); + if (!p) return; + uidvalidity_len = p-pseudopath; + uid = p+1; + + /* second part is the uid */ + p = strchr(uid, ':'); + if (!p) return; + uid_len = p-uid; + folder = p+1; + + actual_uidvalidity = imap_select(imapc, folder, -1, 0, NULL); + if (!actual_uidvalidity) return; + + if ( + (strlen(actual_uidvalidity) != uidvalidity_len) || + (0 != memcmp(uidvalidity, actual_uidvalidity, uidvalidity_len)) + ) { + return; + } + + cmd = imap_ll_build( + TLTYPE_TAGGED, + TLTYPE_ATOM, "UID", (size_t)-1, + TLTYPE_ATOM, "COPY", (size_t)-1, + TLTYPE_ATOM, uid, uid_len, + TLTYPE_STRING, to_folder, (size_t)-1, + TLTYPE_END + ); + result = imap_ll_command(imapc, cmd, 10); + + if (imap_ll_is_trycreate(result)) { + imap_ll_freeline(result); + create_folder(imapc, to_folder); + result = imap_ll_command(imapc, cmd, 10); + } + imap_ll_freeline(cmd); + + if (0 != strcmp(imap_ll_status(result), "OK")) { + fprintf(stderr, "unable to COPY message to folder \"%s\":\n", to_folder); + imap_ll_pprint(result, 0, stderr); + } + imap_ll_freeline(result); +} + +void +imap_append_new_message( + struct imap_ll *imapc, const char *folder, + const unsigned char *data, size_t len, + int seen, int answered, int flagged +) +{ +struct imap_ll_tokenlist *flags, *cmd, *result; + + if (!imap_select(imapc, folder, -1, 1, NULL)) return; + flags = imap_ll_build(TLTYPE_LIST, TLTYPE_END); + if (seen) { + imap_ll_append(flags, imap_ll_build( + TLTYPE_ATOM, "\\Seen", (size_t)-1, TLTYPE_END + )); + } + if (answered) { + imap_ll_append(flags, imap_ll_build( + TLTYPE_ATOM, "\\Answered", (size_t)-1, TLTYPE_END + )); + } + if (flagged) { + imap_ll_append(flags, imap_ll_build( + TLTYPE_ATOM, "\\Flagged", (size_t)-1, TLTYPE_END + )); + } + cmd = imap_ll_build( + TLTYPE_TAGGED, + TLTYPE_ATOM, "APPEND", (size_t)-1, + TLTYPE_STRING, folder, (size_t)-1, + TLTYPE_SUB, flags, + TLTYPE_STRING, data, len, + TLTYPE_END + ); + result = imap_ll_command(imapc, cmd, 10); + + if (imap_ll_is_trycreate(result)) { + imap_ll_freeline(result); + create_folder(imapc, folder); + result = imap_ll_command(imapc, cmd, 10); + } + imap_ll_freeline(cmd); + + if (0 != strcmp(imap_ll_status(result), "OK")) { + fprintf(stderr, "unable to APPEND message to folder \"%s\":\n", folder); + imap_ll_pprint(result, 0, stderr); + } + imap_ll_freeline(result); +} diff --git a/imapinterface.h b/imapinterface.h index 4c673e0..71f7a4f 100644 --- a/imapinterface.h +++ b/imapinterface.h @@ -24,4 +24,8 @@ int imap_fetch_message_raw( struct rfc822 * make_rfc822_from_imap(const char *pseudopath, struct imap_ll *); +void imap_clear_folder(struct imap_ll *, const char *); +void imap_append_new_message(struct imap_ll *, const char *folder, const unsigned char *data, size_t len, int seen, int answered, int flagged); +void imap_copy_message(struct imap_ll *, const char *pseudopath, const char *to_folder); + #endif @@ -106,6 +106,7 @@ int member_of (const char *complete_mfolder, break; case FT_RAW: /* cannot happen but to keep compiler happy */ case FT_EXCERPT: + case FT_IMAP: break; } for (i=0; i<n_paths; i++) { @@ -174,6 +175,8 @@ static void parse_output_folder(char *p)/*{{{*/ output_folder_type = FT_EXCERPT; } else if (!strncasecmp(temp, "mbox", 4)) { output_folder_type = FT_MBOX; + } else if (!strncasecmp(temp, "imap", 4)) { + output_folder_type = FT_IMAP; } else { fprintf(stderr, "Unrecognized mformat <%s>\n", temp); @@ -712,23 +715,30 @@ int main (int argc, char **argv)/*{{{*/ mfolder = new_string(""); } - /* complete_mfolder is needed by search_top() and member_of() so - compute it once here rather than in search_top() as well */ - if ((mfolder[0] == '/') || - ((mfolder[0] == '.') && (mfolder[1] == '/'))) { + if (output_folder_type == FT_IMAP) { complete_mfolder = new_string(mfolder); } else { - len = strlen(folder_base) + strlen(mfolder) + 2; - complete_mfolder = new_array(char, len); - strcpy(complete_mfolder, folder_base); - strcat(complete_mfolder, "/"); - strcat(complete_mfolder, mfolder); + /* complete_mfolder is needed by search_top() and member_of() so + compute it once here rather than in search_top() as well */ + if ((mfolder[0] == '/') || + ((mfolder[0] == '.') && (mfolder[1] == '/'))) { + complete_mfolder = new_string(mfolder); + } else { + len = strlen(folder_base) + strlen(mfolder) + 2; + complete_mfolder = new_array(char, len); + strcpy(complete_mfolder, folder_base); + strcat(complete_mfolder, "/"); + strcat(complete_mfolder, mfolder); + } } /* check whether mfolder output would destroy a mail folder or mbox */ switch (output_folder_type) { case FT_RAW: case FT_EXCERPT: break; + case FT_IMAP: + /* the same check as below could be implemented in the future */ + break; default: if ((member_of(complete_mfolder,folder_base, maildir_folders, FT_MAILDIR, omit_globs)|| member_of (complete_mfolder, folder_base, mh_folders, FT_MH, omit_globs) || @@ -248,7 +248,8 @@ enum folder_type {/*{{{*/ FT_MH, FT_MBOX, FT_RAW, - FT_EXCERPT + FT_EXCERPT, + FT_IMAP }; /*}}}*/ @@ -829,7 +829,7 @@ static void string_tolower(char *str) } } -static int do_search(struct read_db *db, char **args, char *output_path, int show_threads, enum folder_type ft, int verbose, const char *imap_pipe, const char *imap_server, const char *imap_username, const char *imap_password)/*{{{*/ +static int do_search(struct read_db *db, char **args, char *output_path, int show_threads, enum folder_type ft, int verbose, const char *imap_pipe, const char *imap_server, const char *imap_username, const char *imap_password, int please_clear)/*{{{*/ { char *colon, *start_words; int do_body, do_subject, do_from, do_to, do_cc, do_date, do_size; @@ -1330,6 +1330,54 @@ static int do_search(struct read_db *db, char **args, char *output_path, int sho } break; /*}}}*/ + case FT_IMAP:/*{{{*/ + GET_IMAP; + if (!imapc) break; + if (please_clear) { + imap_clear_folder(imapc, output_path); + } + for (i=0; i<db->n_msgs; i++) { + if (hit3[i]) { + int is_seen, is_replied, is_flagged; + get_flags_from_file(db, i, &is_seen, &is_replied, &is_flagged); + switch (rd_msg_type(db, i)) { + case DB_MSG_FILE: + { + int len; + unsigned char *data; + create_ro_mapping(db->data + db->path_offsets[i], &data, &len); + if (data) { + imap_append_new_message(imapc, output_path, data, len, is_seen, is_replied, is_flagged); + free_ro_mapping(data, len); + } + ++n_hits; + } + break; + case DB_MSG_IMAP: + { + imap_copy_message(imapc, db->data + db->path_offsets[i], output_path); + ++n_hits; + } + break; + case DB_MSG_MBOX: + { + unsigned char *start, *data; + int mbox_len, msg_len, mbi; + get_validated_mbox_msg(db, i, &mbi, &data, &mbox_len, &start, &msg_len); + imap_append_new_message(imapc, output_path, start, msg_len, is_seen, is_replied, is_flagged); + if (data) { + free_ro_mapping(data, mbox_len); + } + ++n_hits; + } + break; + case DB_MSG_DEAD: + break; + } + } + } + break; +/*}}}*/ default: assert(0); break; @@ -1500,6 +1548,7 @@ int search_top(int do_threads, int do_augment, char *database_path, char *comple { struct read_db *db; int result; + int please_clear = 0; db = open_db(database_path); @@ -1517,6 +1566,7 @@ int search_top(int do_threads, int do_augment, char *database_path, char *comple break; case FT_RAW: case FT_EXCERPT: + case FT_IMAP: /* Do it later, we do not yet have an IMAP connection */ break; default: assert(0); @@ -1537,12 +1587,16 @@ int search_top(int do_threads, int do_augment, char *database_path, char *comple case FT_RAW: case FT_EXCERPT: break; + case FT_IMAP: + /* Do it later: we do not yet have an IMAP connection */ + please_clear = 1; + break; default: assert(0); } } - result = do_search(db, argv, complete_mfolder, do_threads, ft, verbose, imap_pipe, imap_server, imap_username, imap_password); + result = do_search(db, argv, complete_mfolder, do_threads, ft, verbose, imap_pipe, imap_server, imap_username, imap_password, please_clear); free(complete_mfolder); close_db(db); return result; |