/* imap_pretty_mailbox: called by mutt_pretty_mailbox to make IMAP paths * look nice. */ void imap_pretty_mailbox (char* path) { IMAP_MBOX home, target; ciss_url_t url; char* delim; int tlen; int hlen = 0; char home_match = 0; if (imap_parse_path (path, &target) < 0) return; tlen = mutt_strlen (target.mbox); /* check whether we can do '=' substitution */ if (mx_is_imap(Maildir) && !imap_parse_path (Maildir, &home)) { hlen = mutt_strlen (home.mbox); if (tlen && mutt_account_match (&home.account, &target.account) && !mutt_strncmp (home.mbox, target.mbox, hlen)) { if (! hlen) home_match = 1; else if (ImapDelimChars) for (delim = ImapDelimChars; *delim != '\0'; delim++) if (target.mbox[hlen] == *delim) home_match = 1; } FREE (&home.mbox); } /* do the '=' substitution */ if (home_match) { *path++ = '='; /* copy remaining path, skipping delimiter */ if (! hlen) hlen = -1; memcpy (path, target.mbox + hlen + 1, tlen - hlen - 1); path[tlen - hlen - 1] = '\0'; } else { mutt_account_tourl (&target.account, &url); url.path = target.mbox; /* FIXME: That hard-coded constant is bogus. But we need the actual * size of the buffer from mutt_pretty_mailbox. And these pretty * operations usually shrink the result. Still... */ url_ciss_tostring (&url, path, 1024, 0); } FREE (&target.mbox); }
/* BALSA: this function is needed because the lower-level imap_delete_mailbox segfaults when called from Balsa. It doesn't especially belong here, but it roughly complements imap_mailbox_create above. */ int imap_mailbox_delete(const char* folder) { IMAP_DATA* idata; IMAP_MBOX mx; if (imap_parse_path (folder, &mx) < 0) { dprint (1, (debugfile, "imap_mailbox_delete: Bad path %s\n", folder)); return -1; } if (!(idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW))) { dprint (1, (debugfile, "imap_mailbox_delete: Couldn't find open connection to %s", mx.account.host)); goto fail; } /* imap_delete_mailbox takes the CONTEXT as an argument, * but expects the data field to point to IMAP_DATA: */ if (!idata->ctx->data) idata->ctx->data = idata; if (!*mx.mbox || imap_delete_mailbox (idata->ctx, mx) < 0) goto fail; FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }
int imap_mailbox_rename (const char* url, const char* parent, const char* subfolder, int subscribe) { IMAP_DATA* idata; IMAP_MBOX mx; char buf[LONG_STRING]; short n; BUFFER new_url; memset (&new_url, 0, sizeof (new_url)); if (imap_parse_path (url, &mx) < 0) { dprint (1, (debugfile, "imap_mailbox_rename: Bad path %s\n", url)); return -1; } if (!(idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW))) { dprint (1, (debugfile, "imap_mailbox_rename: Couldn't find open connection to %s", mx.account.host)); goto fail; } strfcpy (buf, parent, sizeof (buf)); /* append a delimiter if necessary */ n = mutt_strlen (buf); if (n && (n < sizeof (buf) - 1) && (buf[n-1] != idata->delim)) { buf[n++] = idata->delim; buf[n] = '\0'; } strfcpy (buf + n, subfolder, sizeof (buf) - n); imap_subscribe(url, 0); if (imap_rename_mailbox (idata, mx.mbox, buf) < 0) goto fail; if (subscribe) { char* slash = strchr(url+8, '/'); if(slash) { mutt_buffer_add(&new_url, url, slash-url+1); mutt_buffer_addstr(&new_url, parent); mutt_buffer_addch (&new_url, idata->delim); mutt_buffer_addstr(&new_url, subfolder); if (imap_subscribe(new_url.data, 1) < 0) goto fail; } } FREE (&new_url.data); FREE (&mx.mbox); return 0; fail: FREE (&new_url.data); FREE (&mx.mbox); return -1; }
/* imap_expand_path: IMAP implementation of mutt_expand_path. Rewrite * an IMAP path in canonical and absolute form. * Inputs: a buffer containing an IMAP path, and the number of bytes in * that buffer. * Outputs: The buffer is rewritten in place with the canonical IMAP path. * Returns 0 on success, or -1 if imap_parse_path chokes or url_ciss_tostring * fails, which it might if there isn't enough room in the buffer. */ int imap_expand_path (char *path, size_t len) { IMAP_MBOX mx; ciss_url_t url; int rc; if (imap_parse_path (path, &mx) < 0) return -1; mutt_account_tourl (&mx.account, &url); url.path = mx.mbox; rc = url_ciss_tostring (&url, path, len, U_DECODE_PASSWD); mem_free (&mx.mbox); return rc; }
static int browse_add_list_result (IMAP_DATA* idata, const char* cmd, struct browser_state* state, short isparent) { char *name; int noselect; int noinferiors; IMAP_MBOX mx; if (imap_parse_path (state->folder, &mx)) { dprint (2, (debugfile, "browse_add_list_result: current folder %s makes no sense\n", state->folder)); return -1; } imap_cmd_start (idata, cmd); do { if (imap_parse_list_response(idata, &name, &noselect, &noinferiors, &idata->delim) != 0) { FREE (&mx.mbox); return -1; } if (name) { /* Let a parent folder never be selectable for navigation */ if (isparent) noselect = 1; /* prune current folder from output */ if (isparent || mutt_strncmp (name, mx.mbox, strlen (name))) imap_add_folder (idata->delim, name, noselect, noinferiors, state, isparent); } } while ((ascii_strncmp (idata->cmd.buf, idata->cmd.seq, SEQLEN) != 0)); FREE (&mx.mbox); return 0; }
/* imap_expand_path: IMAP implementation of mutt_expand_path. Rewrite * an IMAP path in canonical and absolute form. * Inputs: a buffer containing an IMAP path, and the number of bytes in * that buffer. * Outputs: The buffer is rewritten in place with the canonical IMAP path. * Returns 0 on success, or -1 if imap_parse_path chokes or url_ciss_tostring * fails, which it might if there isn't enough room in the buffer. */ int imap_expand_path (char* path, size_t len) { IMAP_MBOX mx; IMAP_DATA* idata; ciss_url_t url; char fixedpath[LONG_STRING]; int rc; if (imap_parse_path (path, &mx) < 0) return -1; idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW); mutt_account_tourl (&mx.account, &url); imap_fix_path (idata, mx.mbox, fixedpath, sizeof (fixedpath)); url.path = fixedpath; rc = url_ciss_tostring (&url, path, len, U_DECODE_PASSWD); FREE (&mx.mbox); return rc; }
header_cache_t* imap_hcache_open (IMAP_DATA* idata, const char* path) { IMAP_MBOX mx; ciss_url_t url; char cachepath[LONG_STRING]; char mbox[LONG_STRING]; if (path) imap_cachepath (idata, path, mbox, sizeof (mbox)); else { if (!idata->ctx || imap_parse_path (idata->ctx->path, &mx) < 0) return NULL; imap_cachepath (idata, mx.mbox, mbox, sizeof (mbox)); FREE (&mx.mbox); } mutt_account_tourl (&idata->conn->account, &url); url.path = mbox; url_ciss_tostring (&url, cachepath, sizeof (cachepath), U_PATH); return mutt_hcache_open (HeaderCache, cachepath, imap_hcache_namer); }
int imap_append_message (CONTEXT *ctx, MESSAGE *msg) { IMAP_DATA* idata; FILE *fp; char buf[LONG_STRING]; char mbox[LONG_STRING]; char mailbox[LONG_STRING]; char internaldate[IMAP_DATELEN]; char imap_flags[SHORT_STRING]; size_t len; progress_t progressbar; size_t sent; int c, last; IMAP_MBOX mx; int rc; idata = (IMAP_DATA*) ctx->data; if (imap_parse_path (ctx->path, &mx)) return -1; imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); if (!*mailbox) strfcpy (mailbox, "INBOX", sizeof (mailbox)); if ((fp = fopen (msg->path, "r")) == NULL) { mutt_perror (msg->path); goto fail; } /* currently we set the \Seen flag on all messages, but probably we * should scan the message Status header for flag info. Since we're * already rereading the whole file for length it isn't any more * expensive (it'd be nice if we had the file size passed in already * by the code that writes the file, but that's a lot of changes. * Ideally we'd have a HEADER structure with flag info here... */ for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c) { if(c == '\n' && last != '\r') len++; len++; } rewind (fp); mutt_progress_init (&progressbar, _("Uploading message..."), MUTT_PROGRESS_SIZE, NetInc, len); imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); imap_make_date (internaldate, msg->received); imap_flags[0] = imap_flags[1] = 0; if (msg->flags.read) safe_strcat (imap_flags, sizeof (imap_flags), " \\Seen"); if (msg->flags.replied) safe_strcat (imap_flags, sizeof (imap_flags), " \\Answered"); if (msg->flags.flagged) safe_strcat (imap_flags, sizeof (imap_flags), " \\Flagged"); if (msg->flags.draft) safe_strcat (imap_flags, sizeof (imap_flags), " \\Draft"); snprintf (buf, sizeof (buf), "APPEND %s (%s) \"%s\" {%lu}", mbox, imap_flags + 1, internaldate, (unsigned long) len); imap_cmd_start (idata, buf); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { char *pc; dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", idata->buf)); pc = idata->buf + SEQLEN; SKIPWS (pc); pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); safe_fclose (&fp); goto fail; } for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c) { if (c == '\n' && last != '\r') buf[len++] = '\r'; buf[len++] = c; if (len > sizeof(buf) - 3) { sent += len; flush_buffer(buf, &len, idata->conn); mutt_progress_update (&progressbar, sent, -1); } } if (len) flush_buffer(buf, &len, idata->conn); mutt_socket_write (idata->conn, "\r\n"); safe_fclose (&fp); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (!imap_code (idata->buf)) { char *pc; dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", idata->buf)); pc = idata->buf + SEQLEN; SKIPWS (pc); pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); goto fail; } FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }
/* imap_browse: IMAP hook into the folder browser, fills out browser_state, * given a current folder to browse */ int imap_browse (char* path, struct browser_state* state) { IMAP_DATA* idata; char buf[LONG_STRING]; char buf2[LONG_STRING]; char nsbuf[LONG_STRING]; char mbox[LONG_STRING]; char list_cmd[5]; IMAP_NAMESPACE_INFO nsi[16]; int home_namespace = 0; int n; int i; int nsup; char ctmp; int nns; char *cur_folder; short showparents = 0; int noselect; int noinferiors; IMAP_MBOX mx; if (imap_parse_path (path, &mx)) { mutt_error (_("%s is an invalid IMAP path"), path); return -1; } strfcpy (list_cmd, option (OPTIMAPLSUB) ? "LSUB" : "LIST", sizeof (list_cmd)); if (!(idata = imap_conn_find (&(mx.account), 0))) goto fail; if (!mx.mbox) { home_namespace = 1; mbox[0] = '\0'; /* Do not replace "" with "INBOX" here */ mx.mbox = safe_strdup(ImapHomeNamespace); nns = 0; if (mutt_bit_isset(idata->capabilities,NAMESPACE)) { mutt_message _("Getting namespaces..."); if (browse_get_namespace (idata, nsbuf, sizeof (nsbuf), nsi, sizeof (nsi), &nns) != 0) goto fail; if (browse_verify_namespace (idata, nsi, nns) != 0) goto fail; } } mutt_message _("Getting folder list..."); /* skip check for parents when at the root */ if (mx.mbox && mx.mbox[0] != '\0') { imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); imap_munge_mbox_name (buf, sizeof (buf), mbox); imap_unquote_string(buf); /* As kludgy as it gets */ mbox[sizeof (mbox) - 1] = '\0'; strncpy (mbox, buf, sizeof (mbox) - 1); n = mutt_strlen (mbox); dprint (3, (debugfile, "imap_browse: mbox: %s\n", mbox)); /* if our target exists and has inferiors, enter it if we * aren't already going to */ if (mbox[n-1] != idata->delim) { snprintf (buf, sizeof (buf), "%s \"\" \"%s\"", list_cmd, mbox); imap_cmd_start (idata, buf); do { if (imap_parse_list_response (idata, &cur_folder, &noselect, &noinferiors, &idata->delim) != 0) goto fail; if (cur_folder) { imap_unmunge_mbox_name (cur_folder); if (!noinferiors && cur_folder[0] && (n = strlen (mbox)) < LONG_STRING-1) { mbox[n++] = idata->delim; mbox[n] = '\0'; } } } while (ascii_strncmp (idata->cmd.buf, idata->cmd.seq, SEQLEN)); } /* if we're descending a folder, mark it as current in browser_state */ if (mbox[n-1] == idata->delim) { /* don't show parents in the home namespace */ if (!home_namespace) showparents = 1; imap_qualify_path (buf, sizeof (buf), &mx, mbox); state->folder = safe_strdup (buf); n--; } /* Find superiors to list * Note: UW-IMAP servers return folder + delimiter when asked to list * folder + delimiter. Cyrus servers don't. So we ask for folder, * and tack on delimiter ourselves. * Further note: UW-IMAP servers return nothing when asked for * NAMESPACES without delimiters at the end. Argh! */ for (n--; n >= 0 && mbox[n] != idata->delim ; n--); if (n > 0) /* "aaaa/bbbb/" -> "aaaa" */ { /* forget the check, it is too delicate (see above). Have we ever * had the parent not exist? */ ctmp = mbox[n]; mbox[n] = '\0'; if (showparents) { dprint (3, (debugfile, "imap_init_browse: adding parent %s\n", mbox)); imap_add_folder (idata->delim, mbox, 1, 0, state, 1); } /* if our target isn't a folder, we are in our superior */ if (!state->folder) { /* store folder with delimiter */ mbox[n++] = ctmp; ctmp = mbox[n]; mbox[n] = '\0'; imap_qualify_path (buf, sizeof (buf), &mx, mbox); state->folder = safe_strdup (buf); } mbox[n] = ctmp; } /* "/bbbb/" -> add "/", "aaaa/" -> add "" */ else { char relpath[2]; /* folder may be "/" */ snprintf (relpath, sizeof (relpath), "%c" , n < 0 ? '\0' : idata->delim); if (showparents) imap_add_folder (idata->delim, relpath, 1, 0, state, 1); if (!state->folder) { imap_qualify_path (buf, sizeof (buf), &mx, relpath); state->folder = safe_strdup (buf); } } } /* no namespace, no folder: set folder to host only */ if (!state->folder) { imap_qualify_path (buf, sizeof (buf), &mx, NULL); state->folder = safe_strdup (buf); } if (home_namespace && mbox[0] != '\0') { /* Listing the home namespace, so INBOX should be included. Home * namespace is not "", so we have to list it explicitly. We ask the * server to see if it has descendants. */ dprint (2, (debugfile, "imap_init_browse: adding INBOX\n")); if (browse_add_list_result (idata, "LIST \"\" \"INBOX\"", state, 0)) goto fail; if (!state->entrylen) { mutt_error _("No such folder"); goto fail; } } nsup = state->entrylen; dprint (3, (debugfile, "imap_browse: Quoting mailbox scan: %s -> ", mbox)); snprintf (buf, sizeof (buf), "%s%%", mbox); imap_quote_string (buf2, sizeof (buf2), buf); dprint (3, (debugfile, "%s\n", buf2)); snprintf (buf, sizeof (buf), "%s \"\" %s", list_cmd, buf2); if (browse_add_list_result (idata, buf, state, 0)) goto fail; mutt_clear_error (); qsort(&(state->entry[nsup]),state->entrylen-nsup,sizeof(state->entry[0]), (int (*)(const void*,const void*)) compare_names); if (home_namespace) { /* List additional namespaces */ for (i = 0; i < nns; i++) if (nsi[i].listable && !nsi[i].home_namespace) { imap_add_folder(nsi[i].delim, nsi[i].prefix, nsi[i].noselect, nsi[i].noinferiors, state, 0); dprint (3, (debugfile, "imap_init_browse: adding namespace: %s\n", nsi[i].prefix)); } } FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }
/* imap_add_folder: add a folder name to the browser list, formatting it as * necessary. */ static void imap_add_folder (char delim, char *folder, int noselect, int noinferiors, struct browser_state *state, short isparent) { char tmp[LONG_STRING]; char relpath[LONG_STRING]; IMAP_MBOX mx; if (imap_parse_path (state->folder, &mx)) return; imap_unmunge_mbox_name (folder); if (state->entrylen + 1 == state->entrymax) { safe_realloc ((void **) &state->entry, sizeof (struct folder_file) * (state->entrymax += 256)); memset (state->entry + state->entrylen, 0, (sizeof (struct folder_file) * (state->entrymax - state->entrylen))); } /* render superiors as unix-standard ".." */ if (isparent) strfcpy (relpath, "../", sizeof (relpath)); /* strip current folder from target, to render a relative path */ else if (!mutt_strncmp (mx.mbox, folder, mutt_strlen (mx.mbox))) strfcpy (relpath, folder + mutt_strlen (mx.mbox), sizeof (relpath)); else strfcpy (relpath, folder, sizeof (relpath)); /* apply filemask filter. This should really be done at menu setup rather * than at scan, since it's so expensive to scan. But that's big changes * to browser.c */ if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not)) { FREE (&mx.mbox); return; } imap_qualify_path (tmp, sizeof (tmp), &mx, folder); (state->entry)[state->entrylen].name = safe_strdup (tmp); /* mark desc with delim in browser if it can have subfolders */ if (!isparent && !noinferiors && strlen (relpath) < sizeof (relpath) - 1) { relpath[strlen (relpath) + 1] = '\0'; relpath[strlen (relpath)] = delim; } (state->entry)[state->entrylen].desc = safe_strdup (relpath); (state->entry)[state->entrylen].imap = 1; /* delimiter at the root is useless. */ if (folder[0] == '\0') delim = '\0'; (state->entry)[state->entrylen].delim = delim; (state->entry)[state->entrylen].selectable = !noselect; (state->entry)[state->entrylen].inferiors = !noinferiors; (state->entrylen)++; FREE (&mx.mbox); }
int imap_mailbox_create (const char* folder, const char* subfolder, int subscribe) #endif { IMAP_DATA* idata; IMAP_MBOX mx; char buf[LONG_STRING]; short n; if (imap_parse_path (folder, &mx) < 0) { dprint (1, (debugfile, "imap_mailbox_create: Bad starting path %s\n", folder)); return -1; } if (!(idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW))) { dprint (1, (debugfile, "imap_mailbox_create: Couldn't find open connection to %s", mx.account.host)); goto fail; } strfcpy (buf, NONULL (mx.mbox), sizeof (buf)); /* append a delimiter if necessary */ n = mutt_strlen (buf); if (n && (n < sizeof (buf) - 1) && (buf[n-1] != idata->delim)) { buf[n++] = idata->delim; buf[n] = '\0'; } #ifndef LIBMUTT if (mutt_get_field (_("Create mailbox: "), buf, sizeof (buf), M_FILE) < 0) goto fail; if (!mutt_strlen (buf)) { mutt_error (_("Mailbox must have a name.")); mutt_sleep(1); goto fail; } #else strfcpy (buf + n, subfolder, sizeof (buf) - n); #endif if (imap_create_mailbox (idata, buf) < 0) goto fail; #ifndef LIBMUTT mutt_message _("Mailbox created."); mutt_sleep (0); #else if (subscribe) { snprintf(buf, sizeof (buf), "%s%c%s", folder, idata->delim, subfolder); if (imap_subscribe(buf, 1) < 0) goto fail; } #endif FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }