2 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
3 * Copyright (C) 1999-2008 Brendan Cully <brendan@kublai.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 /* Mutt browser support routines */
30 #include "imap_private.h"
32 /* -- forward declarations -- */
33 static int browse_add_list_result (IMAP_DATA* idata, const char* cmd,
34 struct browser_state* state, short isparent);
35 static void imap_add_folder (char delim, char *folder, int noselect,
36 int noinferiors, struct browser_state *state, short isparent);
37 static int compare_names(struct folder_file *a, struct folder_file *b);
39 /* imap_browse: IMAP hook into the folder browser, fills out browser_state,
40 * given a current folder to browse */
41 int imap_browse (char* path, struct browser_state* state)
45 char buf[LONG_STRING];
46 char buf2[LONG_STRING];
47 char mbox[LONG_STRING];
52 short showparents = 0;
56 if (imap_parse_path (path, &mx))
58 mutt_error (_("%s is an invalid IMAP path"), path);
62 save_lsub = option (OPTIMAPCHECKSUBSCRIBED);
63 unset_option (OPTIMAPCHECKSUBSCRIBED);
64 strfcpy (list_cmd, option (OPTIMAPLSUB) ? "LSUB" : "LIST", sizeof (list_cmd));
66 if (!(idata = imap_conn_find (&(mx.account), 0)))
69 mutt_message _("Getting folder list...");
71 /* skip check for parents when at the root */
72 if (mx.mbox && mx.mbox[0] != '\0')
76 imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox));
77 ptr = safe_strdup (mbox);
78 imap_utf7_encode (&ptr);
79 mbox[sizeof (mbox) - 1] = '\0';
80 strncpy (mbox, ptr, sizeof (mbox) - 1);
82 n = mutt_strlen (mbox);
84 dprint (3, (debugfile, "imap_browse: mbox: %s\n", mbox));
86 /* if our target exists and has inferiors, enter it if we
87 * aren't already going to */
88 if (mbox[n-1] != idata->delim)
90 snprintf (buf, sizeof (buf), "%s \"\" \"%s\"", list_cmd, mbox);
91 imap_cmd_start (idata, buf);
92 idata->cmdtype = IMAP_CT_LIST;
93 idata->cmddata = &list;
97 rc = imap_cmd_step (idata);
98 if (rc == IMAP_CMD_CONTINUE && list.name)
100 if (!list.noinferiors && list.name[0] &&
101 !imap_mxcmp (list.name, mbox) &&
102 (n = strlen (mbox)) < sizeof (mbox) - 1)
104 mbox[n++] = list.delim;
109 while (rc == IMAP_CMD_CONTINUE);
110 idata->cmddata = NULL;
113 /* if we're descending a folder, mark it as current in browser_state */
114 if (mbox[n-1] == list.delim)
117 imap_qualify_path (buf, sizeof (buf), &mx, mbox);
118 state->folder = safe_strdup (buf);
122 /* Find superiors to list
123 * Note: UW-IMAP servers return folder + delimiter when asked to list
124 * folder + delimiter. Cyrus servers don't. So we ask for folder,
125 * and tack on delimiter ourselves.
126 * Further note: UW-IMAP servers return nothing when asked for
127 * NAMESPACES without delimiters at the end. Argh! */
128 for (n--; n >= 0 && mbox[n] != list.delim ; n--);
129 if (n > 0) /* "aaaa/bbbb/" -> "aaaa" */
131 /* forget the check, it is too delicate (see above). Have we ever
132 * had the parent not exist? */
138 dprint (3, (debugfile, "imap_init_browse: adding parent %s\n", mbox));
139 imap_add_folder (list.delim, mbox, 1, 0, state, 1);
142 /* if our target isn't a folder, we are in our superior */
145 /* store folder with delimiter */
149 imap_qualify_path (buf, sizeof (buf), &mx, mbox);
150 state->folder = safe_strdup (buf);
154 /* "/bbbb/" -> add "/", "aaaa/" -> add "" */
158 /* folder may be "/" */
159 snprintf (relpath, sizeof (relpath), "%c" , n < 0 ? '\0' : idata->delim);
161 imap_add_folder (idata->delim, relpath, 1, 0, state, 1);
164 imap_qualify_path (buf, sizeof (buf), &mx, relpath);
165 state->folder = safe_strdup (buf);
172 /* no namespace, no folder: set folder to host only */
175 imap_qualify_path (buf, sizeof (buf), &mx, NULL);
176 state->folder = safe_strdup (buf);
179 nsup = state->entrylen;
181 dprint (3, (debugfile, "imap_browse: Quoting mailbox scan: %s -> ", mbox));
182 snprintf (buf, sizeof (buf), "%s%%", mbox);
183 imap_quote_string (buf2, sizeof (buf2), buf);
184 dprint (3, (debugfile, "%s\n", buf2));
185 snprintf (buf, sizeof (buf), "%s \"\" %s", list_cmd, buf2);
186 if (browse_add_list_result (idata, buf, state, 0))
189 if (!state->entrylen)
191 mutt_error _("No such folder");
197 qsort(&(state->entry[nsup]),state->entrylen-nsup,sizeof(state->entry[0]),
198 (int (*)(const void*,const void*)) compare_names);
201 set_option (OPTIMAPCHECKSUBSCRIBED);
208 set_option (OPTIMAPCHECKSUBSCRIBED);
213 int imap_mailbox_state (const char* path, struct mailbox_state* state)
219 memset (state, 0, sizeof (*state));
220 if (imap_parse_path (path, &mx) < 0)
222 dprint (1, (debugfile, "imap_mailbox_state: bad path %s\n", path));
225 if (!(idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW)))
227 dprint (2, (debugfile, "imap_mailbox_state: no open connection for %s\n",
233 if (idata->ctx && !imap_mxcmp(mx.mbox, idata->mailbox))
235 state->new = idata->ctx->new;
236 state->messages = idata->ctx->msgcount;
238 else if ((status = imap_mboxcache_get (idata, mx.mbox, 0)))
240 state->new = status->unseen;
241 state->messages = status->messages;
249 /* imap_mailbox_create: Prompt for a new mailbox name, and try to create it */
250 int imap_mailbox_create (const char* folder)
254 char buf[LONG_STRING];
257 if (imap_parse_path (folder, &mx) < 0)
259 dprint (1, (debugfile, "imap_mailbox_create: Bad starting path %s\n",
264 if (!(idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW)))
266 dprint (1, (debugfile, "imap_mailbox_create: Couldn't find open connection to %s", mx.account.host));
270 strfcpy (buf, NONULL (mx.mbox), sizeof (buf));
272 /* append a delimiter if necessary */
273 n = mutt_strlen (buf);
274 if (n && (n < sizeof (buf) - 1) && (buf[n-1] != idata->delim))
276 buf[n++] = idata->delim;
280 if (mutt_get_field (_("Create mailbox: "), buf, sizeof (buf), M_FILE) < 0)
283 if (!mutt_strlen (buf))
285 mutt_error (_("Mailbox must have a name."));
290 if (imap_create_mailbox (idata, buf) < 0)
293 mutt_message _("Mailbox created.");
304 int imap_mailbox_rename(const char* mailbox)
308 char buf[LONG_STRING];
309 char newname[SHORT_STRING];
311 if (imap_parse_path (mailbox, &mx) < 0)
313 dprint (1, (debugfile, "imap_mailbox_rename: Bad source mailbox %s\n",
318 if (!(idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW)))
320 dprint (1, (debugfile, "imap_mailbox_rename: Couldn't find open connection to %s", mx.account.host));
324 snprintf(buf, sizeof (buf), _("Rename mailbox %s to: "), mx.mbox);
326 if (mutt_get_field (buf, newname, sizeof (newname), M_FILE) < 0)
329 if (!mutt_strlen (newname))
331 mutt_error (_("Mailbox must have a name."));
336 imap_fix_path (idata, newname, buf, sizeof (buf));
338 if (imap_rename_mailbox (idata, &mx, buf) < 0) {
339 mutt_error (_("Rename failed: %s"), imap_get_qualifier (idata->buf));
344 mutt_message (_("Mailbox renamed."));
355 static int browse_add_list_result (IMAP_DATA* idata, const char* cmd,
356 struct browser_state* state, short isparent)
362 if (imap_parse_path (state->folder, &mx))
364 dprint (2, (debugfile,
365 "browse_add_list_result: current folder %s makes no sense\n", state->folder));
369 imap_cmd_start (idata, cmd);
370 idata->cmdtype = IMAP_CT_LIST;
371 idata->cmddata = &list;
375 rc = imap_cmd_step (idata);
377 if (rc == IMAP_CMD_CONTINUE && list.name)
379 /* Let a parent folder never be selectable for navigation */
382 /* prune current folder from output */
383 if (isparent || mutt_strncmp (list.name, mx.mbox, strlen (list.name)))
384 imap_add_folder (list.delim, list.name, list.noselect, list.noinferiors,
388 while (rc == IMAP_CMD_CONTINUE);
389 idata->cmddata = NULL;
392 return rc == IMAP_CMD_OK ? 0 : -1;
395 /* imap_add_folder: add a folder name to the browser list, formatting it as
397 static void imap_add_folder (char delim, char *folder, int noselect,
398 int noinferiors, struct browser_state *state, short isparent)
400 char tmp[LONG_STRING];
401 char relpath[LONG_STRING];
404 if (imap_parse_path (state->folder, &mx))
407 imap_unmunge_mbox_name (folder);
409 if (state->entrylen + 1 == state->entrymax)
411 safe_realloc (&state->entry,
412 sizeof (struct folder_file) * (state->entrymax += 256));
413 memset (state->entry + state->entrylen, 0,
414 (sizeof (struct folder_file) * (state->entrymax - state->entrylen)));
417 /* render superiors as unix-standard ".." */
419 strfcpy (relpath, "../", sizeof (relpath));
420 /* strip current folder from target, to render a relative path */
421 else if (!mutt_strncmp (mx.mbox, folder, mutt_strlen (mx.mbox)))
422 strfcpy (relpath, folder + mutt_strlen (mx.mbox), sizeof (relpath));
424 strfcpy (relpath, folder, sizeof (relpath));
426 /* apply filemask filter. This should really be done at menu setup rather
427 * than at scan, since it's so expensive to scan. But that's big changes
429 if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not))
435 imap_qualify_path (tmp, sizeof (tmp), &mx, folder);
436 (state->entry)[state->entrylen].name = safe_strdup (tmp);
438 /* mark desc with delim in browser if it can have subfolders */
439 if (!isparent && !noinferiors && strlen (relpath) < sizeof (relpath) - 1)
441 relpath[strlen (relpath) + 1] = '\0';
442 relpath[strlen (relpath)] = delim;
445 (state->entry)[state->entrylen].desc = safe_strdup (relpath);
447 (state->entry)[state->entrylen].imap = 1;
448 /* delimiter at the root is useless. */
449 if (folder[0] == '\0')
451 (state->entry)[state->entrylen].delim = delim;
452 (state->entry)[state->entrylen].selectable = !noselect;
453 (state->entry)[state->entrylen].inferiors = !noinferiors;
459 static int compare_names(struct folder_file *a, struct folder_file *b)
461 return mutt_strcmp(a->name, b->name);