📄 imap.c
字号:
/* * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org> * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> * Copyright (C) 1999-2006 Brendan Cully <brendan@kublai.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* Support for IMAP4rev1, with the occasional nod to IMAP 4. */#if HAVE_CONFIG_H# include "config.h"#endif#include "mutt.h"#include "mx.h"#include "mailbox.h"#include "globals.h"#include "sort.h"#include "browser.h"#include "message.h"#include "imap_private.h"#if defined(USE_SSL)# include "mutt_ssl.h"#endif#include "buffy.h"#if USE_HCACHE#include "hcache.h"#endif#include <unistd.h>#include <ctype.h>#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>/* imap forward declarations */static char* imap_get_flags (LIST** hflags, char* s);static int imap_check_capabilities (IMAP_DATA* idata);static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag, const char* str, char* flags, size_t flsize);/* imap_access: Check permissions on an IMAP mailbox. * TODO: ACL checks. Right now we assume if it exists we can * mess with it. */int imap_access (const char* path, int flags){ IMAP_DATA* idata; IMAP_MBOX mx; char buf[LONG_STRING]; char mailbox[LONG_STRING]; char mbox[LONG_STRING]; if (imap_parse_path (path, &mx)) return -1; if (!(idata = imap_conn_find (&mx.account, option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0))) { FREE (&mx.mbox); return -1; } imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); /* we may already be in the folder we're checking */ if (!ascii_strcmp(idata->mailbox, mx.mbox)) { FREE (&mx.mbox); return 0; } FREE (&mx.mbox); imap_munge_mbox_name (mbox, sizeof (mbox), mailbox); if (mutt_bit_isset (idata->capabilities, IMAP4REV1)) snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox); else if (mutt_bit_isset (idata->capabilities, STATUS)) snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox); else { dprint (2, (debugfile, "imap_access: STATUS not supported?\n")); return -1; } if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0) { dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox)); return -1; } return 0;}int imap_create_mailbox (IMAP_DATA* idata, char* mailbox){ char buf[LONG_STRING], mbox[LONG_STRING]; imap_munge_mbox_name (mbox, sizeof (mbox), mailbox); snprintf (buf, sizeof (buf), "CREATE %s", mbox); if (imap_exec (idata, buf, 0) != 0) return -1; return 0;}int imap_rename_mailbox (IMAP_DATA* idata, IMAP_MBOX* mx, const char* newname){ char oldmbox[LONG_STRING]; char newmbox[LONG_STRING]; char buf[LONG_STRING]; imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox); imap_munge_mbox_name (newmbox, sizeof (newmbox), newname); snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox); if (imap_exec (idata, buf, 0) != 0) return -1; return 0;}int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx){ char buf[LONG_STRING], mbox[LONG_STRING]; IMAP_DATA *idata; if (!ctx || !ctx->data) { if (!(idata = imap_conn_find (&mx.account, option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0))) { FREE (&mx.mbox); return -1; } } else { idata = ctx->data; } imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox); snprintf (buf, sizeof (buf), "DELETE %s", mbox); if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0) return -1; return 0;}/* imap_logout_all: close all open connections. Quick and dirty until we can * make sure we've got all the context we need. */void imap_logout_all (void) { CONNECTION* conn; CONNECTION* tmp; conn = mutt_socket_head (); while (conn) { tmp = conn->next; if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0) { mutt_message (_("Closing connection to %s..."), conn->account.host); imap_logout ((IMAP_DATA*) conn->data); mutt_clear_error (); mutt_socket_close (conn); mutt_socket_free (conn); } conn = tmp; }}/* imap_read_literal: read bytes bytes from server into file. Not explicitly * buffered, relies on FILE buffering. NOTE: strips \r from \r\n. * Apparently even literals use \r\n-terminated strings ?! */int imap_read_literal (FILE* fp, IMAP_DATA* idata, long bytes, progress_t* pbar){ long pos; char c; int r = 0; dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes)); for (pos = 0; pos < bytes; pos++) { if (mutt_socket_readchar (idata->conn, &c) != 1) { dprint (1, (debugfile, "imap_read_literal: error during read, %ld bytes read\n", pos)); idata->status = IMAP_FATAL; return -1; }#if 1 if (r == 1 && c != '\n') fputc ('\r', fp); if (c == '\r') { r = 1; continue; } else r = 0;#endif fputc (c, fp); if (pbar && !(pos % 1024)) mutt_progress_bar (pbar, pos);#ifdef DEBUG if (debuglevel >= IMAP_LOG_LTRL) fputc (c, debugfile);#endif } return 0;}/* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the * context. Must not be done while something has a handle on any headers * (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */void imap_expunge_mailbox (IMAP_DATA* idata){ HEADER* h; int i, cacheno;#if USE_HCACHE header_cache_t *hc; char uidbuf[32]; hc = mutt_hcache_open (HeaderCache, idata->ctx->path);#endif for (i = 0; i < idata->ctx->msgcount; i++) { h = idata->ctx->hdrs[i]; if (h->index == -1) { dprint (2, (debugfile, "Expunging message UID %d.\n", HEADER_DATA (h)->uid)); h->active = 0; imap_cache_del (idata, h);#if USE_HCACHE if (hc) { sprintf (uidbuf, "/%u", HEADER_DATA(h)->uid); mutt_hcache_delete (hc, uidbuf, imap_hcache_keylen); }#endif /* free cached body from disk, if necessary */ cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN; if (idata->cache[cacheno].uid == HEADER_DATA(h)->uid && idata->cache[cacheno].path) { unlink (idata->cache[cacheno].path); FREE (&idata->cache[cacheno].path); } imap_free_header_data (&h->data); } }#if USE_HCACHE mutt_hcache_close (hc);#endif /* We may be called on to expunge at any time. We can't rely on the caller * to always know to rethread */ mx_update_tables (idata->ctx, 0); mutt_sort_headers (idata->ctx, 1);}/* imap_check_capabilities: make sure we can log in to this server. */static int imap_check_capabilities (IMAP_DATA* idata){ if (imap_exec (idata, "CAPABILITY", 0) != 0) { imap_error ("imap_check_capabilities", idata->buf); return -1; } if (!(mutt_bit_isset(idata->capabilities,IMAP4) ||mutt_bit_isset(idata->capabilities,IMAP4REV1))) { mutt_error _("This IMAP server is ancient. Mutt does not work with it."); mutt_sleep (2); /* pause a moment to let the user see the error */ return -1; } return 0;}/* imap_conn_find: Find an open IMAP connection matching account, or open * a new one if none can be found. */IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags){ CONNECTION* conn = NULL; ACCOUNT* creds = NULL; IMAP_DATA* idata = NULL; int new = 0; while ((conn = mutt_conn_find (conn, account))) { if (!creds) creds = &conn->account; else memcpy (&conn->account, creds, sizeof (ACCOUNT)); idata = (IMAP_DATA*)conn->data; if (flags & M_IMAP_CONN_NONEW) { if (!idata) { /* This should only happen if we've come to the end of the list */ mutt_socket_free (conn); return NULL; } else if (idata->state < IMAP_AUTHENTICATED) continue; } if (flags & M_IMAP_CONN_NOSELECT && idata && idata->state >= IMAP_SELECTED) continue; break; } if (!idata) { /* The current connection is a new connection */ if (! (idata = imap_new_idata ())) { mutt_socket_free (conn); return NULL; } conn->data = idata; idata->conn = conn; new = 1; } if (idata->state == IMAP_DISCONNECTED) imap_open_connection (idata); if (idata->state == IMAP_CONNECTED) { if (!imap_authenticate (idata)) { idata->state = IMAP_AUTHENTICATED; if (idata->conn->ssf) dprint (2, (debugfile, "Communication encrypted at %d bits\n", idata->conn->ssf)); } else mutt_account_unsetpass (&idata->conn->account); FREE (&idata->capstr); } if (new && idata->state == IMAP_AUTHENTICATED) { /* get root delimiter, '/' as default */ idata->delim = '/'; imap_cmd_queue (idata, "LIST \"\" \"\""); if (option (OPTIMAPCHECKSUBSCRIBED)) imap_cmd_queue (idata, "LSUB \"\" \"*\""); /* we may need the root delimiter before we open a mailbox */ imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); } return idata;}int imap_open_connection (IMAP_DATA* idata){ char buf[LONG_STRING]; if (mutt_socket_open (idata->conn) < 0) return -1; idata->state = IMAP_CONNECTED; if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) { mutt_socket_close (idata->conn); idata->state = IMAP_DISCONNECTED; return -1; } if (ascii_strncasecmp ("* OK", idata->buf, 4) == 0) { if (ascii_strncasecmp ("* OK [CAPABILITY", idata->buf, 16) && imap_check_capabilities (idata)) goto bail;#if defined(USE_SSL) /* Attempt STARTTLS if available and desired. */ if (!idata->conn->ssf && (option(OPTSSLFORCETLS) || mutt_bit_isset (idata->capabilities, STARTTLS))) { int rc; if (option(OPTSSLFORCETLS)) rc = M_YES; else if ((rc = query_quadoption (OPT_SSLSTARTTLS, _("Secure connection with TLS?"))) == -1) goto err_close_conn; if (rc == M_YES) { if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1) goto bail; if (rc != -2) { if (mutt_ssl_starttls (idata->conn)) { mutt_error (_("Could not negotiate TLS connection")); mutt_sleep (1); goto err_close_conn; } else { /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */ if (imap_exec (idata, "CAPABILITY", 0)) goto bail; } } } } if (option(OPTSSLFORCETLS) && ! idata->conn->ssf) { mutt_error _("Encrypted connection unavailable"); mutt_sleep (1); goto err_close_conn; }#endif } else if (ascii_strncasecmp ("* PREAUTH", idata->buf, 9) == 0) { idata->state = IMAP_AUTHENTICATED; if (imap_check_capabilities (idata) != 0) goto bail; FREE (&idata->capstr); } else { imap_error ("imap_open_connection()", buf); goto bail; } return 0;#if defined(USE_SSL) err_close_conn: mutt_socket_close (idata->conn); idata->state = IMAP_DISCONNECTED;#endif bail: FREE (&idata->capstr); return -1;}/* imap_get_flags: Make a simple list out of a FLAGS response. * return stream following FLAGS response */static char* imap_get_flags (LIST** hflags, char* s){ LIST* flags; char* flag_word; char ctmp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -