config.cache
config.log
config.status
+keymap_alldefs.h
keymap_defs.h
makedoc
mutt_dotlock
muttbug_SOURCES = muttbug.sh.in
-mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(GSSLIBS) $(INTLLIBS)
+mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) \
+ $(INTLLIBS)
+
mutt_DEPENDENCIES = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAPDEPS) $(INTLDEPS)
makedoc_SOURCES = makedoc.c
DEFS=-DSHAREDIR=\"$(sharedir)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
-DBINDIR=\"$(bindir)\" -DHAVE_CONFIG_H=1
-INCLUDES=-I. $(IMAP_INCLUDES) -I$(includedir)
+
+# top_srcdir is for building outside of the source tree
+INCLUDES=-I$(top_srcdir) -I. $(IMAP_INCLUDES) -I$(includedir)
non_us_sources = pgp.c pgpinvoke.c pgpkey.c pgplib.c sha1dgst.c \
gnupgparse.c sha.h sha_locl.h \
case 'f':
#ifdef USE_IMAP
- if (mx_is_imap (folder->ff->name))
+ if (folder->ff->imap)
strfcpy (fn, NONULL(folder->ff->desc), sizeof (fn));
else
#endif
else
{
#ifdef USE_IMAP
- if (mx_is_imap (folder->ff->name))
+ if (folder->ff->imap)
{
+ sprintf (permission, "IMAP %c%c",
+ folder->ff->inferiors ? '+' : ' ',
+ folder->ff->selectable ? 'S' : ' ');
snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
- snprintf (dest, destlen, tmp, "IMAP");
+ snprintf (dest, destlen, tmp, permission);
}
#endif
}
(state->entry)[state->entrylen].name = safe_strdup (name);
(state->entry)[state->entrylen].desc = safe_strdup (name);
#ifdef USE_IMAP
- (state->entry)[state->entrylen].notfolder = 0;
+ (state->entry)[state->entrylen].imap = 0;
#endif
-
(state->entrylen)++;
}
do
{
#ifdef USE_IMAP
- if (tmp->path[0] == '{')
+ if (mx_is_imap (tmp->path))
{
add_folder (menu, state, tmp->path, NULL, tmp->new);
continue;
(S_ISLNK (state.entry[menu->current].mode) &&
link_is_dir (LastDir, state.entry[menu->current].name))
#ifdef USE_IMAP
- || state.entry[menu->current].notfolder
+ || state.entry[menu->current].inferiors
#endif
)
{
if ((mx_get_magic (buf) <= 0)
#ifdef USE_IMAP
- || state.entry[menu->current].notfolder
+ || state.entry[menu->current].inferiors
#endif
)
{
#ifdef USE_IMAP
else if (state.imap_browse)
{
+ int n;
+
strfcpy (LastDir, state.entry[menu->current].name,
sizeof (LastDir));
+ /* tack on delimiter here */
+ if ((state.entry[menu->current].delim != '\0') &&
+ (n = strlen (LastDir)+1) < sizeof (LastDir))
+ {
+ LastDir[n] = '\0';
+ LastDir[n-1] = state.entry[menu->current].delim;
+ }
}
#endif
else
break;
case OP_DELETE_MAILBOX:
- if (!mx_is_imap (state.entry[menu->current].name))
+ if (!state.entry[menu->current].imap)
mutt_error (_("Delete is only supported for IMAP mailboxes"));
else
{
break;
}
+#ifdef USE_IMAP
+ if (state.entry[menu->current].selectable)
+ {
+ strfcpy (f, state.entry[menu->current].name, flen);
+ destroy_state (&state);
+ mutt_menuDestroy (&menu);
+ return;
+ }
+ else
+#endif
if (S_ISDIR (state.entry[menu->current].mode) ||
(S_ISLNK (state.entry[menu->current].mode) &&
link_is_dir (LastDir, state.entry[menu->current].name)))
char *name;
char *desc;
#ifdef USE_IMAP
- short notfolder;
+ char delim;
+
+ unsigned imap : 1;
+ unsigned selectable : 1;
+ unsigned inferiors : 1;
#endif
unsigned tagged : 1;
unsigned is_new : 1;
else
SUBVERSION="i"
- AC_ARG_ENABLE(pgp, [ --disable-pgp Disable PGP support],
+ AC_ARG_ENABLE(pgp, [ --disable-pgp Disable PGP support],
[ if test x$enableval = xno ; then
HAVE_PGP=no
fi
OPS="$OPS \$(srcdir)/OPS.PGP"
fi
- AC_ARG_WITH(mixmaster, [ --with-mixmaster[=PATH] include Mixmaster support],
+ AC_ARG_WITH(mixmaster, [ --with-mixmaster[=PATH] Include Mixmaster support],
[if test -x "$withval" ; then
MIXMASTER="$withval"
else
AC_DEFINE_UNQUOTED(ISPELL, "$ISPELL")
fi
-AC_ARG_WITH(slang, [ --with-slang[=DIR] use S-Lang instead of ncurses],
+AC_ARG_WITH(slang, [ --with-slang[=DIR] Use S-Lang instead of ncurses],
[AC_CACHE_CHECK([if this is a BSD system], mutt_cv_bsdish,
[AC_TRY_RUN([#include <sys/param.h>
fi
fi
AC_MSG_RESULT($mutt_cv_slang)
- LIBS="$LIBS -lslang -lm"
if test $mutt_cv_bsdish = yes; then
AC_CHECK_LIB(termlib, main)
fi
AC_DEFINE(HAVE_COLOR)
MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS resize.o"
- dnl --- try to link a sample program to check if we're ok
+ dnl --- now that we've found it, check the link
- AC_MSG_CHECKING(if I can compile a test SLang program)
- AC_TRY_LINK([], [SLtt_get_terminfo ();],
- [AC_MSG_RESULT(yes)],
- [AC_MSG_ERROR(unable to compile. check config.log)])
+ AC_CHECK_LIB(slang, SLtt_get_terminfo,
+ [MUTTLIBS="$MUTTLIBS -lslang -lm"],
+ [AC_MSG_ERROR(unable to compile. check config.log)], -lm)
],
[mutt_cv_curses=/usr
- AC_ARG_WITH(curses, [ --with-curses=DIR ncurses is installed in ],
+ AC_ARG_WITH(curses, [ --with-curses=DIR Where ncurses is installed ],
[if test $withval != yes; then
mutt_cv_curses=$withval
fi
AC_CHECK_LIB(ncurses, initscr,
- [LIBS="$LIBS -lncurses"
+ [MUTTLIBS="$MUTTLIBS -lncurses"
if test x$mutt_cv_curses = x/usr -a -d /usr/include/ncurses; then
CPPFLAGS="$CPPFLAGS -I/usr/include/ncurses"
fi
AC_CHECK_HEADERS(ncurses.h)],
- [LIBS="$LIBS -lcurses"
+ [MUTTLIBS="$MUTTLIBS -lcurses"
if test -f /usr/ccs/lib/libcurses.a; then
LDFLAGS="$LDFLAGS -L/usr/ccs/lib"
else
fi
fi])
+ old_LIBS="$LIBS"
+ LIBS="$LIBS $MUTTLIBS"
AC_CHECK_FUNC(start_color, [AC_DEFINE(HAVE_COLOR)])
AC_CHECK_FUNCS(typeahead bkgdset curs_set meta use_default_colors)
- AC_CHECK_FUNCS(resizeterm, [MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS resize.o"])
+ AC_CHECK_FUNCS(resizeterm,
+ [MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS resize.o"])
+ LIBS="$old_LIBS"
])
AC_HEADER_STDC
dnl AIX may not have fchdir()
AC_CHECK_FUNCS(fchdir, [AC_DEFINE(HAVE_FCHDIR)], [mutt_cv_fchdir=no])
-AC_ARG_WITH(regex, [ --with-regex Use the GNU regex library ],
+AC_ARG_WITH(regex, [ --with-regex Use the GNU regex library ],
[mutt_cv_regex=yes],
[AC_CHECK_FUNCS(regcomp, mutt_cv_regex=no, mutt_cv_regex=yes)])
fi
-AC_ARG_WITH(homespool, [ --with-homespool[=FILE] file in user's directory where new mail is spooled], with_homespool=${withval})
+AC_ARG_WITH(homespool, [ --with-homespool[=FILE] File in user's directory where new mail is spooled], with_homespool=${withval})
if test x$with_homespool != x; then
if test $with_homespool = yes; then
with_homespool=mailbox
AC_DEFINE(USE_DOTLOCK)
mutt_cv_setgid=no
else
- AC_ARG_WITH(mailpath, [ --with-mailpath=DIR directory where spool mailboxes are located],
+ AC_ARG_WITH(mailpath, [ --with-mailpath=DIR Directory where spool mailboxes are located],
[mutt_cv_mailpath=$withval],
[ AC_CACHE_CHECK(where new mail is stored, mutt_cv_mailpath,
[mutt_cv_mailpath=no
fi
fi
-AC_ARG_ENABLE(external_dotlock, [ --enable-external-dotlock Force use of an external dotlock program],
+AC_ARG_ENABLE(external_dotlock, [ --enable-external-dotlock Force use of an external dotlock program],
[mutt_cv_external_dotlock="$enableval"])
if test "x$mutt_cv_setgid" = "xyes" || test "x$mutt_cv_fchdir" = "xno" \
AC_SUBST(DOTLOCK_TARGET)
-AC_ARG_WITH(libdir, [ --with-libdir=PATH specify where to put arch dependent files],
+AC_ARG_WITH(libdir, [ --with-libdir=PATH Specify where to put arch dependent files],
[mutt_cv_libdir=$withval],
[ AC_CACHE_CHECK(where to put architecture-dependent files,
mutt_cv_libdir,
libdir=$mutt_cv_libdir
AC_SUBST(libdir)
-AC_ARG_WITH(sharedir, [ --with-sharedir=PATH specify where to put arch independent files],
+AC_ARG_WITH(sharedir, [ --with-sharedir=PATH Specify where to put arch independent files],
[mutt_cv_sharedir=$withval],
[ AC_CACHE_CHECK(where to put architecture-independent data files,
mutt_cv_sharedir,
AC_SUBST(sharedir)
mutt_cv_charmaps=/usr/share/i18n/charmaps
-AC_ARG_WITH(charmaps, [--with-charmaps=PATH specify where on the system mutt can find character set definitions],
+AC_ARG_WITH(charmaps, [ --with-charmaps=PATH Where to find character set definitions],
[mutt_cv_charmaps=$withval])
mutt_cv_fake_charmaps=yes
AC_SUBST(charmaps)
AM_CONDITIONAL(BUILD_CHARMAPS, test x$need_charmaps = xyes)
-AC_ARG_WITH(docdir, [ --with-docdir=PATH specify where to put the documentation],
+AC_ARG_WITH(docdir, [ --with-docdir=PATH Specify where to put the documentation],
[mutt_cv_docdir=$withval],
[ AC_CACHE_CHECK(where to put the documentation,
mutt_cv_docdir,
AC_DEFINE_UNQUOTED(DOMAIN, "$withval")
fi])
-AC_ARG_ENABLE(pop, [ --enable-pop Enable POP3 support],
+AC_ARG_ENABLE(pop, [ --enable-pop Enable POP3 support],
[ if test x$enableval = xyes ; then
AC_DEFINE(USE_POP)
AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt))
])
AM_CONDITIONAL(BUILD_IMAP, test x$need_imap = xyes)
-AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authentication for IMAP],
+AC_ARG_WITH(gss, [ --with-gss[=DIR] Compile in GSSAPI authentication for IMAP],
[
+ if test "$need_imap" != "yes"
+ then
+ AC_MSG_ERROR([GSS support is only for IMAP, but IMAP is not enabled])
+ fi
+
if test "$with_gss" != "no"
then
if test "$with_gss" != "yes"
AC_CHECK_LIB(gssapi_krb5, gss_init_sec_context,,
AC_MSG_ERROR([could not find libgssapi_krb5 which is needed for GSS authentication]), -lkrb5)
LIBS="$saved_LIBS"
- GSSLIBS="-lgssapi_krb5 -lkrb5"
+ MUTTLIBS="$MUTTLIBS -lgssapi_krb5 -lkrb5"
AC_DEFINE(USE_GSS)
+ need_gss="yes"
fi
])
+AM_CONDITIONAL(USE_GSS, test x$need_gss = xyes)
-AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL support],
+AC_ARG_ENABLE(ssl, [ --enable-ssl Enable SSL support for IMAP],
[ if test "$need_imap" != "yes"; then
- AC_MSG_ERROR([Sorry, SSL support only for IMAP])
+ AC_MSG_ERROR([SSL support is only for IMAP, but IMAP is not enabled])
fi
+ saved_LIBS="$LIBS"
AC_CHECK_LIB(crypto, X509_new,, AC_MSG_ERROR([Unable to find SSL library]))
AC_CHECK_LIB(ssl, SSL_new,, AC_MSG_ERROR([Unable to find SSL library]), -lcrypto)
AC_DEFINE(USE_SSL)
+ LIBS="$saved_LIBS"
+ MUTTLIBS="$MUTTLIBS -lssl -lcrypto"
need_ssl=yes
])
AM_CONDITIONAL(USE_SSL, test x$need_ssl = xyes)
-AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging support],
+AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging support],
[ if test x$enableval = xyes ; then
AC_DEFINE(DEBUG)
fi
AC_DEFINE(EXACT_ADDRESS)
fi])
+AC_SUBST(MUTTLIBS)
AC_SUBST(MUTT_LIB_OBJECTS)
AC_SUBST(LIBIMAP)
AC_SUBST(LIBIMAPDEPS)
-AC_SUBST(GSSLIBS)
MUTT_AM_GNU_GETTEXT
CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/intl"
Mutt is designed to work with IMAP4rev1 servers, and was originally tested
with both the UWash IMAP server v11.241 and the Cyrus IMAP server v1.5.14.
-Nowadays it is primarily developed against UW-IMAP 12.250. It works
-more-or-less correctly against Cyrus 1.6.11, though there are a few minor
-quirks in the folder browser.
+Nowadays it is primarily developed against UW-IMAP 12.250. It appears
+to work more-or-less correctly against Cyrus 1.6.11 as well.
Note that if you are using mbox as the mail store on UW servers prior to
v12.250, the server has been reported to disconnect a client if another client
selects the same folder.
+<sect2>The Folder Browser
+<p>
+
+As of version 1.2, mutt supports browsing mailboxes on an IMAP
+server. This is mostly the same as the local file browser, with the
+following differences:
+<itemize>
+<item>In lieu of file permissions, mutt displays the string "IMAP",
+ possibly followed by the symbol "S", indicating that the entry is
+ selectable (contains messages), and/or the symbol "+", indicating
+ that the entry may contain subfolders. If your server is like the
+ UW-IMAP server, you won't see both symbols, but if you are using a
+ Cyrus-like server folders will often contain both messages and
+ subfolders.
+<item>For the case where an entry can contain both messages and
+ subfolders, the selection key (bound to <tt>enter</tt> by default)
+ will choose to descend into the subfolder view. If you wish to view
+ the messages in that folder, you must use <tt>view-file</tt> instead
+ (bound to <tt>space</tt> by default).
+<item>You can delete mailboxes with the <tt>delete-mailbox</tt>
+ command (bound to <tt>d</tt> by default. You may also
+ <tt>subscribe</tt> and <tt>unsubscribe</tt> to mailboxes (normally
+ these are bound to <tt>s</tt> and <tt>u</tt>, respectively).
+</itemize>
+
<sect2>Authentication
<p>
AUTOMAKE_OPTIONS = foreign
+if USE_GSS
+GSSSOURCES = auth_gss.c
+endif
+
if USE_SSL
SSLSOURCES = imap_ssl.c
SSLHEADERS = imap_ssl.h
noinst_HEADERS = imap_private.h imap_socket.h md5.h message.h $(SSLHEADERS)
libimap_a_SOURCES = auth.c browse.c command.c imap.c imap.h md5c.c message.c \
- socket.c util.c $(SSLSOURCES)
+ socket.c util.c $(GSSSOURCES) $(SSLSOURCES)
/*
* Copyright (C) 1996-8 Michael R. Elkins <me@cs.hmc.edu>
* Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
- * Copyright (C) 1999 Brendan Cully <brendan@kublai.com>
+ * Copyright (C) 1999-2000 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
#define MD5_BLOCK_LEN 64
#define MD5_DIGEST_LEN 16
+/* external authenticator prototypes */
#ifdef USE_GSS
-#include <netinet/in.h>
-#include <gssapi/gssapi.h>
-#include <gssapi/gssapi_generic.h>
-
-#define GSS_BUFSIZE 8192
-
-#define GSS_AUTH_P_NONE 1
-#define GSS_AUTH_P_INTEGRITY 2
-#define GSS_AUTH_P_PRIVACY 4
+int imap_auth_gss (IMAP_DATA* idata, const char* user);
#endif
/* forward declarations */
static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
const char* pass);
static int imap_auth_anon (IMAP_DATA *idata);
-#ifdef USE_GSS
-static int imap_auth_gss (IMAP_DATA* idata, const char* user);
-#endif
/* hmac_md5: produce CRAM-MD5 challenge response. */
static void hmac_md5 (const char* password, char* challenge,
MD5Final (response, &ctx);
}
-#ifdef USE_GSS
-/* imap_auth_gss: AUTH=GSSAPI support. Used unconditionally if the server
- * supports it */
-static int imap_auth_gss (IMAP_DATA* idata, const char* user)
-{
- gss_buffer_desc request_buf, send_token;
- gss_buffer_t sec_token;
- gss_name_t target_name;
- gss_ctx_id_t context;
- gss_OID mech_name;
- gss_qop_t quality;
- int cflags;
- OM_uint32 maj_stat, min_stat;
- char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
- unsigned long buf_size;
-
- char seq[16];
-
- dprint (2, (debugfile, "Attempting GSS login...\n"));
-
- /* get an IMAP service ticket for the server */
- snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->mx.host);
- request_buf.value = buf1;
- request_buf.length = strlen (buf1) + 1;
- maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name,
- &target_name);
- if (maj_stat != GSS_S_COMPLETE)
- {
- dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1));
- return -1;
- }
- else if (debuglevel >= 2)
- {
- maj_stat = gss_display_name (&min_stat, target_name, &request_buf,
- &mech_name);
- dprint (2, (debugfile, "Using service name [%s]\n",
- (char*) request_buf.value));
- maj_stat = gss_release_buffer (&min_stat, &request_buf);
- }
-
- /* now begin login */
- mutt_message _("Authenticating (GSSAPI)...");
- imap_make_sequence (seq, sizeof (seq));
- snprintf (buf1, sizeof (buf1), "%s AUTHENTICATE GSSAPI\r\n", seq);
- mutt_socket_write (idata->conn, buf1);
-
- /* expect a null continuation response ("+") */
- if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
- {
- dprint (1, (debugfile, "Error receiving server response.\n"));
- gss_release_name (&min_stat, &target_name);
-
- return -1;
- }
-
- if (buf1[0] != '+')
- {
- dprint (2, (debugfile, "Invalid response from server: %s\n", buf1));
- gss_release_name (&min_stat, &target_name);
-
- return -1;
- }
-
- /* now start the security context initialisation loop... */
- dprint (2, (debugfile, "Sending credentials\n"));
- sec_token = GSS_C_NO_BUFFER;
- context = GSS_C_NO_CONTEXT;
- do
- {
- /* build token */
- maj_stat = gss_init_sec_context (&min_stat,
- GSS_C_NO_CREDENTIAL,
- &context,
- target_name,
- GSS_C_NO_OID,
- GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
- 0,
- GSS_C_NO_CHANNEL_BINDINGS,
- sec_token,
- NULL,
- &send_token,
- (unsigned int*) &cflags,
- NULL);
- if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
- {
- dprint (1, (debugfile, "Error exchanging credentials\n"));
- gss_release_name (&min_stat, &target_name);
- /* end authentication attempt */
- mutt_socket_write (idata->conn, "*\r\n");
- mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn);
-
- return -1;
- }
-
- /* send token */
- mutt_to_base64 ((unsigned char*) buf1, send_token.value,
- send_token.length);
- gss_release_buffer (&min_stat, &send_token);
- strcpy (buf1 + strlen (buf1), "\r\n");
- mutt_socket_write (idata->conn, buf1);
-
- if (maj_stat == GSS_S_CONTINUE_NEEDED)
- {
- if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
- {
- dprint (1, (debugfile, "Error receiving server response.\n"));
- gss_release_name (&min_stat, &target_name);
-
- return -1;
- }
-
- request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
- request_buf.value = buf2;
- sec_token = &request_buf;
- }
- }
- while (maj_stat == GSS_S_CONTINUE_NEEDED);
-
- gss_release_name (&min_stat, &target_name);
-
- /* get security flags and buffer size */
- if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
- {
- dprint (1, (debugfile, "Error receiving server response.\n"));
-
- return -1;
- }
- request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
- request_buf.value = buf2;
-
- maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token,
- &cflags, &quality);
- if (maj_stat != GSS_S_COMPLETE)
- {
- dprint (2, (debugfile, "Couldn't unwrap security level data\n"));
- gss_release_buffer (&min_stat, &send_token);
-
- mutt_socket_write(idata->conn, "*\r\n");
- return -1;
- }
- dprint (2, (debugfile, "Credential exchange complete\n"));
-
- /* first octet is security levels supported. We want NONE */
- server_conf_flags = ((char*) send_token.value)[0];
- if ( !(((char*) send_token.value)[0] & GSS_AUTH_P_NONE) )
- {
- dprint (2, (debugfile, "Server requires integrity or privace\n"));
- gss_release_buffer (&min_stat, &send_token);
-
- mutt_socket_write(idata->conn, "*\r\n");
- return -1;
- }
-
- /* we don't care about buffer size if we don't wrap content. But here it is */
- ((char*) send_token.value)[0] = 0;
- buf_size = ntohl (*((long *) send_token.value));
- gss_release_buffer (&min_stat, &send_token);
- dprint (2, (debugfile, "Unwrapped security level flags: %c%c%c\n",
- server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-',
- server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
- server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-'));
- dprint (2, (debugfile, "Maximum GSS token size is %ld\n", buf_size));
-
- /* agree to terms (hack!) */
- buf_size = htonl (buf_size); /* not relevant without integrity/privacy */
- memcpy (buf1, &buf_size, 4);
- buf1[0] = GSS_AUTH_P_NONE;
- /* server decides if principal can log in as user */
- strncpy (buf1 + 4, user, sizeof (buf1) - 4);
- request_buf.value = buf1;
- request_buf.length = 4 + strlen (user) + 1;
- maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
- &cflags, &send_token);
- if (maj_stat != GSS_S_COMPLETE)
- {
- dprint (2, (debugfile, "Error creating login request\n"));
-
- mutt_socket_write(idata->conn, "*\r\n");
- return -1;
- }
-
- mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length);
- dprint (2, (debugfile, "Requesting authorisation as %s\n", user));
- strncat (buf1, "\r\n", sizeof (buf1));
- mutt_socket_write (idata->conn, buf1);
-
- /* Joy of victory or agony of defeat? */
- if (mutt_socket_read_line_d (buf1, GSS_BUFSIZE, idata->conn) < 0)
- {
- dprint (1, (debugfile, "Error receiving server response.\n"));
-
- mutt_socket_write(idata->conn, "*\r\n");
- return -1;
- }
- if (imap_code (buf1))
- {
- /* flush the security context */
- dprint (2, (debugfile, "Releasing GSS credentials\n"));
- maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token);
- if (maj_stat != GSS_S_COMPLETE)
- {
- dprint (1, (debugfile, "Error releasing credentials\n"));
-
- return -1;
- }
- /* send_token may contain a notification to the server to flush
- * credentials. RFC 1731 doesn't specify what to do, and since this
- * support is only for authentication, we'll assume the server knows
- * enough to flush its own credentials */
- gss_release_buffer (&min_stat, &send_token);
-
- dprint (2, (debugfile, "GSS login complete\n"));
-
- return 0;
- }
-
- /* logon failed */
- dprint (2, (debugfile, "GSS login failed.\n"));
-
- return -1;
-}
-#endif
-
/* imap_auth_cram_md5: AUTH=CRAM-MD5 support. Used unconditionally if the
* server supports it */
static int imap_auth_cram_md5 (IMAP_DATA* idata, const char* user,
--- /dev/null
+/*
+ * Copyright (C) 1999-2000 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* GSS login/authentication code */
+
+#include "mutt.h"
+#include "imap_private.h"
+
+#include <netinet/in.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+
+#define GSS_BUFSIZE 8192
+
+#define GSS_AUTH_P_NONE 1
+#define GSS_AUTH_P_INTEGRITY 2
+#define GSS_AUTH_P_PRIVACY 4
+
+/* imap_auth_gss: AUTH=GSSAPI support. Used unconditionally if the server
+ * supports it */
+int imap_auth_gss (IMAP_DATA* idata, const char* user)
+{
+ gss_buffer_desc request_buf, send_token;
+ gss_buffer_t sec_token;
+ gss_name_t target_name;
+ gss_ctx_id_t context;
+ gss_OID mech_name;
+ gss_qop_t quality;
+ int cflags;
+ OM_uint32 maj_stat, min_stat;
+ char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
+ unsigned long buf_size;
+
+ char seq[16];
+
+ dprint (2, (debugfile, "Attempting GSS login...\n"));
+
+ /* get an IMAP service ticket for the server */
+ snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->mx.host);
+ request_buf.value = buf1;
+ request_buf.length = strlen (buf1) + 1;
+ maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name,
+ &target_name);
+ if (maj_stat != GSS_S_COMPLETE)
+ {
+ dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1));
+ return -1;
+ }
+ else if (debuglevel >= 2)
+ {
+ maj_stat = gss_display_name (&min_stat, target_name, &request_buf,
+ &mech_name);
+ dprint (2, (debugfile, "Using service name [%s]\n",
+ (char*) request_buf.value));
+ maj_stat = gss_release_buffer (&min_stat, &request_buf);
+ }
+
+ /* now begin login */
+ mutt_message _("Authenticating (GSSAPI)...");
+ imap_make_sequence (seq, sizeof (seq));
+ snprintf (buf1, sizeof (buf1), "%s AUTHENTICATE GSSAPI\r\n", seq);
+ mutt_socket_write (idata->conn, buf1);
+
+ /* expect a null continuation response ("+") */
+ if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
+ {
+ dprint (1, (debugfile, "Error receiving server response.\n"));
+ gss_release_name (&min_stat, &target_name);
+
+ return -1;
+ }
+
+ if (buf1[0] != '+')
+ {
+ dprint (2, (debugfile, "Invalid response from server: %s\n", buf1));
+ gss_release_name (&min_stat, &target_name);
+
+ return -1;
+ }
+
+ /* now start the security context initialisation loop... */
+ dprint (2, (debugfile, "Sending credentials\n"));
+ sec_token = GSS_C_NO_BUFFER;
+ context = GSS_C_NO_CONTEXT;
+ do
+ {
+ /* build token */
+ maj_stat = gss_init_sec_context (&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &context,
+ target_name,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ sec_token,
+ NULL,
+ &send_token,
+ (unsigned int*) &cflags,
+ NULL);
+ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+ {
+ dprint (1, (debugfile, "Error exchanging credentials\n"));
+ gss_release_name (&min_stat, &target_name);
+ /* end authentication attempt */
+ mutt_socket_write (idata->conn, "*\r\n");
+ mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn);
+
+ return -1;
+ }
+
+ /* send token */
+ mutt_to_base64 ((unsigned char*) buf1, send_token.value,
+ send_token.length);
+ gss_release_buffer (&min_stat, &send_token);
+ strcpy (buf1 + strlen (buf1), "\r\n");
+ mutt_socket_write (idata->conn, buf1);
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED)
+ {
+ if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
+ {
+ dprint (1, (debugfile, "Error receiving server response.\n"));
+ gss_release_name (&min_stat, &target_name);
+
+ return -1;
+ }
+
+ request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
+ request_buf.value = buf2;
+ sec_token = &request_buf;
+ }
+ }
+ while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ gss_release_name (&min_stat, &target_name);
+
+ /* get security flags and buffer size */
+ if (mutt_socket_read_line_d (buf1, sizeof (buf1), idata->conn) < 0)
+ {
+ dprint (1, (debugfile, "Error receiving server response.\n"));
+
+ return -1;
+ }
+ request_buf.length = mutt_from_base64 (buf2, buf1 + 2);
+ request_buf.value = buf2;
+
+ maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token,
+ &cflags, &quality);
+ if (maj_stat != GSS_S_COMPLETE)
+ {
+ dprint (2, (debugfile, "Couldn't unwrap security level data\n"));
+ gss_release_buffer (&min_stat, &send_token);
+
+ mutt_socket_write(idata->conn, "*\r\n");
+ return -1;
+ }
+ dprint (2, (debugfile, "Credential exchange complete\n"));
+
+ /* first octet is security levels supported. We want NONE */
+ server_conf_flags = ((char*) send_token.value)[0];
+ if ( !(((char*) send_token.value)[0] & GSS_AUTH_P_NONE) )
+ {
+ dprint (2, (debugfile, "Server requires integrity or privace\n"));
+ gss_release_buffer (&min_stat, &send_token);
+
+ mutt_socket_write(idata->conn, "*\r\n");
+ return -1;
+ }
+
+ /* we don't care about buffer size if we don't wrap content. But here it is */
+ ((char*) send_token.value)[0] = 0;
+ buf_size = ntohl (*((long *) send_token.value));
+ gss_release_buffer (&min_stat, &send_token);
+ dprint (2, (debugfile, "Unwrapped security level flags: %c%c%c\n",
+ server_conf_flags & GSS_AUTH_P_NONE ? 'N' : '-',
+ server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
+ server_conf_flags & GSS_AUTH_P_PRIVACY ? 'P' : '-'));
+ dprint (2, (debugfile, "Maximum GSS token size is %ld\n", buf_size));
+
+ /* agree to terms (hack!) */
+ buf_size = htonl (buf_size); /* not relevant without integrity/privacy */
+ memcpy (buf1, &buf_size, 4);
+ buf1[0] = GSS_AUTH_P_NONE;
+ /* server decides if principal can log in as user */
+ strncpy (buf1 + 4, user, sizeof (buf1) - 4);
+ request_buf.value = buf1;
+ request_buf.length = 4 + strlen (user) + 1;
+ maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
+ &cflags, &send_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ {
+ dprint (2, (debugfile, "Error creating login request\n"));
+
+ mutt_socket_write(idata->conn, "*\r\n");
+ return -1;
+ }
+
+ mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length);
+ dprint (2, (debugfile, "Requesting authorisation as %s\n", user));
+ strncat (buf1, "\r\n", sizeof (buf1));
+ mutt_socket_write (idata->conn, buf1);
+
+ /* Joy of victory or agony of defeat? */
+ if (mutt_socket_read_line_d (buf1, GSS_BUFSIZE, idata->conn) < 0)
+ {
+ dprint (1, (debugfile, "Error receiving server response.\n"));
+
+ mutt_socket_write(idata->conn, "*\r\n");
+ return -1;
+ }
+ if (imap_code (buf1))
+ {
+ /* flush the security context */
+ dprint (2, (debugfile, "Releasing GSS credentials\n"));
+ maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ {
+ dprint (1, (debugfile, "Error releasing credentials\n"));
+
+ return -1;
+ }
+ /* send_token may contain a notification to the server to flush
+ * credentials. RFC 1731 doesn't specify what to do, and since this
+ * support is only for authentication, we'll assume the server knows
+ * enough to flush its own credentials */
+ gss_release_buffer (&min_stat, &send_token);
+
+ dprint (2, (debugfile, "GSS login complete\n"));
+
+ return 0;
+ }
+
+ /* logon failed */
+ dprint (2, (debugfile, "GSS login failed.\n"));
+
+ return -1;
+}
}
/* imap_add_folder: add a folder name to the browser list, formatting it as
- * necessary. NOTE: check for duplicate folders removed, believed to be
- * useless. Tell me if otherwise (brendan@kublai.com) */
+ * 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];
- int flen = strlen (folder);
IMAP_MBOX mx;
if (imap_parse_path (state->folder, &mx))
imap_unquote_string (folder);
- /* plus 2: folder may be selectable AND have inferiors */
- if (state->entrylen + 2 == state->entrymax)
+ if (state->entrylen + 1 == state->entrymax)
{
safe_realloc ((void **) &state->entry,
sizeof (struct folder_file) * (state->entrymax += 256));
- /* apparently linux+glibc2.1 was zeroing this for me? Jon Hellan reports
- * that the browser segfaults on more than 256 entries. I never had this
- * problem */
memset (state->entry + state->entrylen, 0,
(sizeof (struct folder_file) * (state->entrymax - state->entrylen)));
}
if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not))
return;
- if (!noselect)
- {
- imap_qualify_path (tmp, sizeof (tmp), &mx, folder, NULL);
- (state->entry)[state->entrylen].name = safe_strdup (tmp);
-
- (state->entry)[state->entrylen].desc = safe_strdup (relpath);
-
- (state->entry)[state->entrylen].notfolder = 0;
- (state->entrylen)++;
- }
- if (!noinferiors)
- {
- char trailing_delim[2];
-
- /* create trailing delimiter if necessary */
- trailing_delim[1] = '\0';
- trailing_delim[0] = (flen && folder[flen - 1] != delim) ? delim : '\0';
-
- imap_qualify_path (tmp, sizeof (tmp), &mx, folder, trailing_delim);
- (state->entry)[state->entrylen].name = safe_strdup (tmp);
+ imap_qualify_path (tmp, sizeof (tmp), &mx, folder, NULL);
+ (state->entry)[state->entrylen].name = safe_strdup (tmp);
- if (!isparent && (strlen (relpath) < sizeof (relpath) - 2))
- strcat (relpath, trailing_delim);
- (state->entry)[state->entrylen].desc = safe_strdup (relpath);
+ (state->entry)[state->entrylen].desc = safe_strdup (relpath);
- (state->entry)[state->entrylen].notfolder = 1;
- (state->entrylen)++;
- }
+ (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)++;
}
static int compare_names(struct folder_file *a, struct folder_file *b)
do
{
if (mutt_socket_read_line_d (buf, sizeof (buf), idata->conn) < 0)
- {
- return (-1);
- }
+ return -1;
if (buf[0] == '*')
{
return 0;
}
-/* Check which namespaces actually exist */
+/* Check which namespaces have contents */
static int verify_namespace (CONNECTION *conn, IMAP_NAMESPACE_INFO *nsi,
int nns)
{
for (i = 0; i < nns; i++, nsi++)
{
imap_make_sequence (seq, sizeof (seq));
- snprintf (buf, sizeof (buf), "%s %s \"\" \"%s\"\r\n", seq,
- option (OPTIMAPLSUB) ? "LSUB" : "LIST", nsi->prefix);
+ /* Cyrus gives back nothing if the % isn't added. This may return lots
+ * of data in some cases, I guess, but I currently feel that's better
+ * than invisible namespaces */
+ snprintf (buf, sizeof (buf), "%s %s \"\" \"%s%c%%\"\r\n", seq,
+ option (OPTIMAPLSUB) ? "LSUB" : "LIST", nsi->prefix, nsi->delim);
mutt_socket_write (conn, buf);
nsi->listable = 0;
do
{
if (imap_parse_list_response(conn, buf, sizeof(buf), &name,
- &(nsi->noselect), &(nsi->noinferiors),
- &delim) != 0)
- return (-1);
+ &(nsi->noselect), &(nsi->noinferiors), &delim) != 0)
+ return -1;
nsi->listable |= (name != NULL);
}
while ((mutt_strncmp (buf, seq, SEQLEN) != 0));
safe_free ((void **) &CTX_DATA->cache[i].path);
}
}
+
+#if 0
+ /* This is not the right place to logout, actually. There are two dangers:
+ * 1. status is set to IMAP_LOGOUT as soon as the user says q, even if she
+ * cancels a bit later.
+ * 2. We may get here when closing the $received folder, but before we sync
+ * the spool. So the sync will currently cause an abort. */
if (CTX_DATA->status == IMAP_BYE || CTX_DATA->status == IMAP_FATAL ||
CTX_DATA->status == IMAP_LOGOUT)
{
CTX_DATA->conn->data = NULL;
safe_free ((void **) &ctx->data);
}
+#endif
}
/* use the NOOP command to poll for new mail
}
mutt_expand_path (mbox, sizeof (mbox));
-#ifdef USE_IMAP
- if (!mx_is_imap (ctx->path))
-#endif
if (isSpool)
{
snprintf (buf, sizeof (buf), _("Move read messages to %s?"), mbox);
if (move_messages)
{
- if (mx_open_mailbox (mbox, M_APPEND, &f) == NULL)
- return (-1);
-
mutt_message (_("Moving read messages to %s..."), mbox);
- for (i = 0; i < ctx->msgcount; i++)
+#ifdef USE_IMAP
+ /* try to use server-side copy first */
+ i = 1;
+
+ if (ctx->magic == M_IMAP && mx_is_imap (mbox))
{
- if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted)
- {
- if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) == 0)
- {
- ctx->hdrs[i]->deleted = 1;
- ctx->deleted++;
- }
+ /* tag messages for moving, and clear old tags, if any */
+ for (i = 0; i < ctx->msgcount; i++)
+ if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted)
+ ctx->hdrs[i]->tagged = 1;
else
- {
- mx_close_mailbox (&f);
- return -1;
+ ctx->hdrs[i]->tagged = 0;
+
+ i = imap_copy_messages (ctx, NULL, mbox, 1);
+ }
+
+ if (i == 0) /* success */
+ mutt_clear_error ();
+ else if (i == -1) /* horrible error, bail */
+ return -1;
+ else /* use regular append-copy mode */
+#endif
+ {
+ if (mx_open_mailbox (mbox, M_APPEND, &f) == NULL)
+ return -1;
+
+ for (i = 0; i < ctx->msgcount; i++)
+ {
+ if (ctx->hdrs[i]->read && !ctx->hdrs[i]->deleted)
+ {
+ if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) == 0)
+ {
+ ctx->hdrs[i]->deleted = 1;
+ ctx->deleted++;
+ }
+ else
+ {
+ mx_close_mailbox (&f);
+ return -1;
+ }
}
}
+
+ mx_close_mailbox (&f);
}
-
- mx_close_mailbox (&f);
+
}
else if (!ctx->changed && ctx->deleted == 0)
{
{
if (imap_sync_mailbox (ctx, purge) == -1)
return -1;
- if (ctx->magic == M_IMAP && !purge)
- mutt_message (_("%d kept."), ctx->msgcount);
}
else
#endif
if (sync_mailbox (ctx) == -1)
return -1;
}
+ }
- if (move_messages)
- mutt_message (_("%d kept, %d moved, %d deleted."),
- ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
- else
- mutt_message (_("%d kept, %d deleted."),
- ctx->msgcount - ctx->deleted, ctx->deleted);
+ if (move_messages)
+ mutt_message (_("%d kept, %d moved, %d deleted."),
+ ctx->msgcount - ctx->deleted, read_msgs, ctx->deleted);
+ else
+ mutt_message (_("%d kept, %d deleted."),
+ ctx->msgcount - ctx->deleted, ctx->deleted);
- if (ctx->msgcount == ctx->deleted &&
- (ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
- !mutt_is_spool(ctx->path) && !option (OPTSAVEEMPTY))
- mx_unlink_empty (ctx->path);
- }
+ if (ctx->msgcount == ctx->deleted &&
+ (ctx->magic == M_MMDF || ctx->magic == M_MBOX) &&
+ !mutt_is_spool(ctx->path) && !option (OPTSAVEEMPTY))
+ mx_unlink_empty (ctx->path);
mx_fastclose_mailbox (ctx);