Commit 23375a7b authored by Kim Vandry's avatar Kim Vandry

Added support for search results in an IMAP folder

parent 0d6473dc
......@@ -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 {
......
......@@ -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);
}
......@@ -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;
......@@ -1329,6 +1329,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);
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment