2177 - IMAP4 IDLE command
2221 - IMAP4 Login Referrals
2342 - IMAP4 Namespace
+ 2971 - IMAP4 ID extension
3348 - IMAP4 Child Mailbox Extension
3502 - IMAP4 MULTIAPPEND Extension
3691 - IMAP4 UNSELECT command
sys/uio.h sys/sysmacros.h sys/resource.h sys/select.h libgen.h \
sys/quota.h sys/fs/ufs_quota.h ufs/ufs/quota.h jfs/quota.h sys/fs/quota_common.h \
mntent.h sys/mnttab.h sys/event.h sys/time.h sys/mkdev.h linux/dqblk_xfs.h \
- xfs/xqm.h sasl.h sasl/sasl.h execinfo.h ucontext.h malloc_np.h)
+ xfs/xqm.h sasl.h sasl/sasl.h execinfo.h ucontext.h malloc_np.h sys/utsname.h)
AC_ARG_ENABLE(ipv6,
[ --enable-ipv6 Enable IPv6 support (auto)],
])
AC_SUBST(LIBCAP)
+AC_DEFINE(PACKAGE_WEBPAGE, "http://www.dovecot.org/", Support URL)
+
dnl * after -lsocket and -lnsl tests, inet_aton() may be in them
AC_CHECK_FUNCS(fcntl flock lockf inet_aton sigaction getpagesize madvise \
- strcasecmp stricmp vsyslog writev pread \
+ strcasecmp stricmp vsyslog writev pread uname \
setrlimit setproctitle seteuid setreuid setegid setresgid \
strtoull strtoll strtouq strtoq \
setpriority quotactl getmntent kqueue kevent backtrace_symbols \
dnl ** capabilities
dnl **
-capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS UIDPLUS LIST-EXTENDED I18NLEVEL=1 ENABLE CONDSTORE QRESYNC ESEARCH SEARCHRES WITHIN CONTEXT=SEARCH"
+capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS UIDPLUS LIST-EXTENDED I18NLEVEL=1 ENABLE CONDSTORE QRESYNC ESEARCH SEARCHRES WITHIN CONTEXT=SEARCH ID"
AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
CFLAGS="$CFLAGS $EXTRA_CFLAGS"
# Override the IMAP CAPABILITY response.
#imap_capability =
+ # ID field names and values to send to clients. Using * as the value makes
+ # Dovecot use the default value. The following fields have default values
+ # currently: name, version, os, os-version, support-url, support-email.
+ #imap_id_send =
+
+ # ID fields sent by client to log. * means everything.
+ #imap_id_log =
+
# Workarounds for various client bugs:
# delay-newmail:
# Send EXISTS/RECENT new mail notifications only when replying to NOOP
#include "str.h"
#include "strescape.h"
#include "imap-parser.h"
+#include "imap-id.h"
#include "client.h"
#include "client-authenticate.h"
#include "auth-client.h"
#include "ssl-proxy.h"
#include "imap-proxy.h"
+#include <stdlib.h>
+
/* max. size of one parameter in line, or max reply length in SASL
authentication */
#define MAX_INBUF_SIZE 4096
return 1;
}
+static int cmd_id(struct imap_client *client, const struct imap_arg *args)
+{
+ const char *env, *value;
+
+ if (!client->id_logged) {
+ client->id_logged = TRUE;
+ env = getenv("IMAP_ID_LOG");
+ value = imap_id_args_get_log_reply(args, env);
+ if (value != NULL) {
+ client_syslog(&client->common,
+ t_strdup_printf("ID sent: %s", value));
+ }
+ }
+
+ env = getenv("IMAP_ID_SEND");
+ client_send_line(client, t_strdup_printf("* ID %s",
+ imap_id_reply_generate(env)));
+ client_send_tagline(client, "OK ID completed.");
+ return 1;
+}
+
static int cmd_noop(struct imap_client *client)
{
client_send_tagline(client, "OK NOOP completed.");
return cmd_capability(client);
if (strcmp(cmd, "STARTTLS") == 0)
return cmd_starttls(client);
+ if (strcmp(cmd, "ID") == 0)
+ return cmd_id(client, args);
if (strcmp(cmd, "NOOP") == 0)
return cmd_noop(client);
if (strcmp(cmd, "LOGOUT") == 0)
unsigned int input_blocked:1;
unsigned int destroyed:1;
unsigned int greeting_sent:1;
+ unsigned int id_logged:1;
};
void client_destroy(struct imap_client *client, const char *reason);
cmd-examine.c \
cmd-expunge.c \
cmd-fetch.c \
+ cmd-id.c \
cmd-idle.c \
cmd-list.c \
cmd-logout.c \
unsigned int destroyed:1;
unsigned int handling_input:1;
unsigned int syncing:1;
+ unsigned int id_logged:1;
unsigned int input_skip_line:1; /* skip all the data until we've
found a new line */
unsigned int modseqs_sent_since_sync:1;
--- /dev/null
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "imap-id.h"
+
+bool cmd_id(struct client_command_context *cmd)
+{
+ const struct imap_arg *args;
+ const char *value;
+
+ if (!client_read_args(cmd, 0, 0, &args))
+ return FALSE;
+
+ if (!cmd->client->id_logged) {
+ cmd->client->id_logged = TRUE;
+ value = imap_id_args_get_log_reply(args, imap_id_log);
+ if (value != NULL)
+ i_info("ID sent: %s", value);
+ }
+
+ client_send_line(cmd->client, t_strdup_printf(
+ "* ID %s", imap_id_reply_generate(imap_id_send)));
+ client_send_tagline(cmd, "OK ID completed.");
+ return TRUE;
+}
+
const struct command imap_ext_commands[] = {
{ "CANCELUPDATE", cmd_cancelupdate,0 },
{ "ENABLE", cmd_enable, 0 },
+ { "ID", cmd_id, 0 },
{ "IDLE", cmd_idle, COMMAND_FLAG_BREAKS_SEQS },
{ "NAMESPACE", cmd_namespace, 0 },
{ "SORT", cmd_sort, COMMAND_FLAG_USES_SEQS },
/* IMAP extensions: */
bool cmd_cancelupdate(struct client_command_context *cmd);
bool cmd_enable(struct client_command_context *cmd);
+bool cmd_id(struct client_command_context *cmd);
bool cmd_idle(struct client_command_context *cmd);
bool cmd_namespace(struct client_command_context *cmd);
bool cmd_sort(struct client_command_context *cmd);
extern unsigned int imap_max_line_length;
extern enum client_workarounds client_workarounds;
extern const char *logout_format;
+extern const char *imap_id_send, *imap_id_log;
extern string_t *capability_string;
unsigned int imap_max_line_length;
enum client_workarounds client_workarounds = 0;
const char *logout_format;
+const char *imap_id_send, *imap_id_log;
static struct io *log_io = NULL;
static struct module *modules = NULL;
if (logout_format == NULL)
logout_format = "bytes=%i/%o";
+ imap_id_send = getenv("IMAP_ID_SEND");
+ imap_id_log = getenv("IMAP_ID_LOG");
+
parse_workarounds();
namespace_pool = pool_alloconly_create("namespaces", 1024);
imap-bodystructure.c \
imap-date.c \
imap-envelope.c \
+ imap-id.c \
imap-match.c \
imap-parser.c \
imap-quote.c \
imap-bodystructure.h \
imap-date.h \
imap-envelope.h \
+ imap-id.h \
imap-match.h \
imap-parser.h \
imap-quote.h \
--- /dev/null
+/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "istream.h"
+#include "imap-parser.h"
+#include "imap-quote.h"
+#include "imap-id.h"
+
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_UNAME
+static struct utsname utsname_result;
+static bool utsname_set = FALSE;
+
+static const char *imap_id_get_uname(const char *key)
+{
+ if (!utsname_set) {
+ utsname_set = TRUE;
+ if (uname(&utsname_result) < 0) {
+ i_error("uname() failed: %m");
+ memset(&utsname_result, 0, sizeof(utsname_result));
+ }
+ }
+
+ if (strcmp(key, "os") == 0)
+ return utsname_result.sysname;
+ if (strcmp(key, "os-version") == 0)
+ return utsname_result.release;
+ return NULL;
+}
+#endif
+
+static const char *imap_id_get_default(const char *key)
+{
+ if (strcasecmp(key, "name") == 0)
+ return PACKAGE_NAME;
+ if (strcasecmp(key, "version") == 0)
+ return PACKAGE_VERSION;
+ if (strcasecmp(key, "support-url") == 0)
+ return PACKAGE_WEBPAGE;
+ if (strcasecmp(key, "support-email") == 0)
+ return PACKAGE_BUGREPORT;
+#ifdef HAVE_UNAME
+ return imap_id_get_uname(key);
+#endif
+}
+
+static const char *
+imap_id_reply_generate_from_imap_args(const struct imap_arg *args)
+{
+ string_t *str;
+ const char *key, *value;
+
+ if (args->type == IMAP_ARG_EOL)
+ return "NIL";
+
+ str = t_str_new(256);
+ str_append_c(str, '(');
+ for (; args->type != IMAP_ARG_EOL; args++) {
+ if (!IMAP_ARG_TYPE_IS_STRING(args->type)) {
+ /* broken input */
+ if (args[1].type == IMAP_ARG_EOL)
+ break;
+ args++;
+ } else {
+ /* key */
+ if (str_len(str) > 1)
+ str_append_c(str, ' ');
+ key = IMAP_ARG_STR_NONULL(args);
+ imap_dquote_append(str, key);
+ str_append_c(str, ' ');
+ /* value */
+ if (args[1].type == IMAP_ARG_EOL) {
+ str_append(str, "NIL");
+ break;
+ }
+ args++;
+ if (!IMAP_ARG_TYPE_IS_STRING(args->type))
+ value = NULL;
+ else {
+ value = IMAP_ARG_STR_NONULL(args);
+ if (strcmp(value, "*") == 0)
+ value = imap_id_get_default(key);
+ }
+
+ if (value == NULL)
+ str_append(str, "NIL");
+ else
+ imap_quote_append_string(str, value, FALSE);
+ }
+ }
+ if (str_len(str) == 1) {
+ /* broken */
+ return "NIL";
+ }
+ str_append_c(str, ')');
+ return str_c(str);
+}
+
+const char *imap_id_reply_generate(const char *settings)
+{
+ struct istream *input;
+ struct imap_parser *parser;
+ const struct imap_arg *args;
+ const char *ret;
+
+ if (settings == NULL)
+ return "NIL";
+
+ input = i_stream_create_from_data(settings, strlen(settings));
+ (void)i_stream_read(input);
+
+ parser = imap_parser_create(input, NULL, (size_t)-1);
+ if (imap_parser_finish_line(parser, 0, 0, &args) <= 0)
+ ret = "NIL";
+ else
+ ret = imap_id_reply_generate_from_imap_args(args);
+
+ imap_parser_destroy(&parser);
+ i_stream_destroy(&input);
+ return ret;
+}
+
+const char *imap_id_args_get_log_reply(const struct imap_arg *args,
+ const char *settings)
+{
+ const char *const *keys, *key, *value;
+ string_t *reply;
+ bool log_all;
+
+ if (settings == NULL || *settings == '\0' ||
+ args->type != IMAP_ARG_LIST)
+ return NULL;
+
+ args = IMAP_ARG_LIST_ARGS(args);
+
+ log_all = strcmp(settings, "*") == 0;
+ reply = t_str_new(256);
+ keys = t_strsplit_spaces(settings, " ");
+ while (args->type != IMAP_ARG_EOL && args[1].type != IMAP_ARG_EOL) {
+ if (args->type != IMAP_ARG_STRING) {
+ /* broken input */
+ args += 2;
+ continue;
+ }
+ key = IMAP_ARG_STR_NONULL(args);
+ args++;
+ if (strlen(key) > 30) {
+ /* broken: ID spec requires fields to be max. 30
+ octets */
+ args++;
+ continue;
+ }
+
+ if (log_all || str_array_icase_find(keys, key)) {
+ if (IMAP_ARG_TYPE_IS_STRING(args->type))
+ value = IMAP_ARG_STR_NONULL(args);
+ else if (args->type == IMAP_ARG_NIL)
+ value = "NIL";
+ else
+ value = "";
+ if (str_len(reply) > 0)
+ str_append(reply, ", ");
+ str_append(reply, str_sanitize(key, 30));
+ str_append_c(reply, '=');
+ str_append(reply, str_sanitize(value, 80));
+ }
+ }
+ return str_len(reply) == 0 ? NULL : str_c(reply);
+}
--- /dev/null
+#ifndef IMAP_ID_H
+#define IMAP_ID_H
+
+/* Return ID reply based on given settings. */
+const char *imap_id_reply_generate(const char *settings);
+/* Return a line that should be logged based on given args and settings.
+ Returns NULL if nothing should be logged. */
+const char *imap_id_args_get_log_reply(const struct imap_arg *args,
+ const char *settings);
+
+#endif
env_put(t_strconcat("LOG_FORMAT_ELEMENTS=",
set->login_log_format_elements, NULL));
env_put(t_strconcat("LOG_FORMAT=", set->login_log_format, NULL));
+ env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL));
+ env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL));
if (set->login_greeting_capability)
env_put("GREETING_CAPABILITY=1");
set->imap_client_workarounds, NULL));
env_put(t_strconcat("IMAP_LOGOUT_FORMAT=",
set->imap_logout_format, NULL));
+ env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL));
+ env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL));
}
if (set->protocol == MAIL_PROTOCOL_POP3) {
env_put(t_strconcat("POP3_CLIENT_WORKAROUNDS=",
DEF_STR(imap_capability),
DEF_STR(imap_client_workarounds),
DEF_STR(imap_logout_format),
+ DEF_STR(imap_id_send),
+ DEF_STR(imap_id_log),
/* pop3 */
DEF_BOOL(pop3_no_flag_updates),
MEMBER(imap_capability) "",
MEMBER(imap_client_workarounds) "",
MEMBER(imap_logout_format) "bytes=%i/%o",
+ MEMBER(imap_id_send) "",
+ MEMBER(imap_id_log) "",
/* pop3 */
MEMBER(pop3_no_flag_updates) FALSE,
const char *imap_capability;
const char *imap_client_workarounds;
const char *imap_logout_format;
+ const char *imap_id_send;
+ const char *imap_id_log;
/* pop3 */
bool pop3_no_flag_updates;