Commit 927c27f8 927c27f8e4bc2c8ae5699addfbcd833d8fec6e08 by Sergey Poznyakoff

Fix input operations and RFC-compliance in imap4d.

* imap4d/util.c (util_getword, util_getitem, util_token)
(util_unquote): Remove.
(imap4d_readline): Rewrite to reduce memory reallocation.  Add
support for non-synchronizing literals (RFC 2088).
(imap4d_readline_ex): Remove.
(util_do_command): Rewrite using new imap4d_readline.
(util_parse_internal_date0): Remove.
(imap4d_tokbuf_init, imap4d_tokbuf_destroy, imap4d_tokbuf_argc)
(imap4d_tokbuf_getarg, util_isdelim)
(imap4d_tokbuf_from_string): New functions.
* imap4d/append.c, imap4d/auth_gss.c, imap4d/authenticate.c,
imap4d/check.c, imap4d/close.c, imap4d/commands.c, imap4d/copy.c,
imap4d/create.c, imap4d/delete.c, imap4d/examine.c,
imap4d/imap4d.h, imap4d/list.c, imap4d/login.c, imap4d/logout.c,
imap4d/lsub.c, imap4d/expunge.c, imap4d/idle.c, imap4d/noop.c,
imap4d/rename.c, imap4d/search.c, imap4d/select.c,
imap4d/starttls.c, imap4d/status.c, imap4d/store.c,
imap4d/subscribe.c, imap4d/uid.c, imap4d/unsubscribe.c,
imap4d/version.c: Rewrite using new imap4d_readline.
* imap4d/namespace.c: Use new imap4d_readline.  Ensure that
each reported prefix ends with a hierarchy delimiter.
* imap4d/imap4d.c (imap4d_mainloop): Use new imap4d_readline.
* imap4d/fetch.c: Rewrite parser from scratch.
* imap4d/capability.c (imap4d_capability_init): Report LITERAL+
capability.

* imap4d/testsuite/imap4d/anystate.exp: Account for the LITERAL+
capability.
* imap4d/testsuite/imap4d/append.exp: Fix APPEND arguments (imap4d
requires exactly three arguments, as per RFC3501.
Fix octet count in literals returned by fetch (previous versions
failed to include the trailing CRLF).
* imap4d/testsuite/imap4d/create.exp: Likewise.
* imap4d/testsuite/imap4d/fetch.exp: Fix FETCH arguments (previous
versions incorrectly accepted header-list without parentheses.
Fix result of FETCH 4 BODY[2.2.1]: it returns entire part, in the
contrast to previous versions, which treated it as
BODY[2.2.1.TEXT].

* maidag/mailtmp.c (mail_tmp_finish): Ensure /dev/null is treated
as mailbox.
1 parent 7317a04a
2008-08-11 Sergey Poznyakoff <gray@gnu.org.ua>
Fix input operations and RFC-compliance in imap4d.
* imap4d/util.c (util_getword, util_getitem, util_token)
(util_unquote): Remove.
(imap4d_readline): Rewrite to reduce memory reallocation. Add
support for non-synchronizing literals (RFC 2088).
(imap4d_readline_ex): Remove.
(util_do_command): Rewrite using new imap4d_readline.
(util_parse_internal_date0): Remove.
(imap4d_tokbuf_init, imap4d_tokbuf_destroy, imap4d_tokbuf_argc)
(imap4d_tokbuf_getarg, util_isdelim)
(imap4d_tokbuf_from_string): New functions.
* imap4d/append.c, imap4d/auth_gss.c, imap4d/authenticate.c,
imap4d/check.c, imap4d/close.c, imap4d/commands.c, imap4d/copy.c,
imap4d/create.c, imap4d/delete.c, imap4d/examine.c,
imap4d/imap4d.h, imap4d/list.c, imap4d/login.c, imap4d/logout.c,
imap4d/lsub.c, imap4d/expunge.c, imap4d/idle.c, imap4d/noop.c,
imap4d/rename.c, imap4d/search.c, imap4d/select.c,
imap4d/starttls.c, imap4d/status.c, imap4d/store.c,
imap4d/subscribe.c, imap4d/uid.c, imap4d/unsubscribe.c,
imap4d/version.c: Rewrite using new imap4d_readline.
* imap4d/namespace.c: Use new imap4d_readline. Ensure that
each reported prefix ends with a hierarchy delimiter.
* imap4d/imap4d.c (imap4d_mainloop): Use new imap4d_readline.
* imap4d/fetch.c: Rewrite parser from scratch.
* imap4d/capability.c (imap4d_capability_init): Report LITERAL+
capability.
* imap4d/testsuite/imap4d/anystate.exp: Account for the LITERAL+
capability.
* imap4d/testsuite/imap4d/append.exp: Fix APPEND arguments (imap4d
requires exactly three arguments, as per RFC3501.
Fix octet count in literals returned by fetch (previous versions
failed to include the trailing CRLF).
* imap4d/testsuite/imap4d/create.exp: Likewise.
* imap4d/testsuite/imap4d/fetch.exp: Fix FETCH arguments (previous
versions incorrectly accepted header-list without parentheses.
Fix result of FETCH 4 BODY[2.2.1]: it returns entire part, in the
contrast to previous versions, which treated it as
BODY[2.2.1.TEXT].
* maidag/mailtmp.c (mail_tmp_finish): Ensure /dev/null is treated
as mailbox.
2008-08-08 Sergey Poznyakoff <gray@gnu.org.ua>
Fix imap appends for AMD mailboxes.
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2006, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2006, 2007,
2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -18,49 +19,6 @@
#include "imap4d.h"
/* APPEND mbox [(flags)] [date_time] message_literal */
int
imap4d_append (struct imap4d_command *command, char *arg)
{
char *sp;
char *mboxname;
int flags = 0;
mu_mailbox_t dest_mbox = NULL;
int status;
mboxname = util_getword (arg, &sp);
if (!mboxname)
return util_finish (command, RESP_BAD, "Too few arguments");
util_unquote (&mboxname);
if (*sp == '(' && util_parse_attributes (sp+1, &sp, &flags))
return util_finish (command, RESP_BAD, "Missing closing parenthesis");
mboxname = namespace_getfullpath (mboxname, "/");
if (!mboxname)
return util_finish (command, RESP_NO, "Couldn't open mailbox");
status = mu_mailbox_create_default (&dest_mbox, mboxname);
if (status == 0)
{
/* It SHOULD NOT automatifcllly create the mailbox. */
status = mu_mailbox_open (dest_mbox, MU_STREAM_RDWR);
if (status == 0)
{
status = imap4d_append0 (dest_mbox, flags, sp);
mu_mailbox_close (dest_mbox);
}
mu_mailbox_destroy (&dest_mbox);
}
free (mboxname);
if (status == 0)
return util_finish (command, RESP_OK, "Completed");
return util_finish (command, RESP_NO, "[TRYCREATE] failed");
}
static int
_append_date (mu_envelope_t envelope, char *buf, size_t len, size_t *pnwrite)
{
......@@ -93,12 +51,18 @@ _append_size (mu_message_t msg, size_t *psize)
mu_stream_t str;
int status = mu_message_get_stream (msg, &str);
if (status == 0)
status = mu_stream_size (str, psize);
{
mu_off_t size;
status = mu_stream_size (str, &size);
if (status == 0 && psize)
*psize = size;
}
return status;
}
int
imap4d_append0 (mu_mailbox_t mbox, int flags, char *text)
imap4d_append0 (mu_mailbox_t mbox, int flags, char *date_time, char *text,
char **err_text)
{
mu_stream_t stream;
int rc = 0;
......@@ -118,23 +82,25 @@ imap4d_append0 (mu_mailbox_t mbox, int flags, char *text)
return 1;
}
while (*text && isspace (*text))
text++;
/* If a date_time is specified, the internal date SHOULD be set in the
resulting message; otherwise, the internal date of the resulting
message is set to the current date and time by default. */
if (util_parse_internal_date0 (text, &t, &text) == 0)
if (date_time)
{
while (*text && isspace(*text))
text++;
if (util_parse_internal_date (date_time, &t))
{
*err_text = "Invalid date/time format";
return 1;
}
}
else
{
time(&t);
}
time(&t);
tm = gmtime(&t);
while (*text && isspace (*text))
text++;
mu_stream_write (stream, text, strlen (text), len, &len);
mu_message_set_stream (msg, stream, &tm);
mu_message_set_size (msg, _append_size, &tm);
......@@ -158,4 +124,87 @@ imap4d_append0 (mu_mailbox_t mbox, int flags, char *text)
return rc;
}
/* APPEND mbox [(flags)] [date_time] message_literal */
int
imap4d_append (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
int i;
char *mboxname;
int flags = 0;
mu_mailbox_t dest_mbox = NULL;
int status;
int argc = imap4d_tokbuf_argc (tok);
char *date_time;
char *msg_text;
char *err_text = "[TRYCREATE] failed";
if (argc < 4)
return util_finish (command, RESP_BAD, "Too few arguments");
mboxname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
if (!mboxname)
return util_finish (command, RESP_BAD, "Too few arguments");
i = IMAP4_ARG_2;
if (imap4d_tokbuf_getarg (tok, i)[0] == '(')
{
while (++i < argc)
{
int type;
char *arg = imap4d_tokbuf_getarg (tok, i);
if (!util_attribute_to_type (arg, &type))
flags |= type;
else if (arg[0] == ')')
break;
}
if (i == argc)
return util_finish (command, RESP_BAD, "Missing closing parenthesis");
i++;
}
switch (argc - i)
{
case 2:
/* Date/time is present */
date_time = imap4d_tokbuf_getarg (tok, i);
i++;
break;
case 1:
date_time = NULL;
break;
default:
return util_finish (command, RESP_BAD, "Too many arguments");
}
msg_text = imap4d_tokbuf_getarg (tok, i);
mboxname = namespace_getfullpath (mboxname, "/");
if (!mboxname)
return util_finish (command, RESP_NO, "Couldn't open mailbox");
status = mu_mailbox_create_default (&dest_mbox, mboxname);
if (status == 0)
{
/* It SHOULD NOT automatically create the mailbox. */
status = mu_mailbox_open (dest_mbox, MU_STREAM_RDWR);
if (status == 0)
{
status = imap4d_append0 (dest_mbox, flags, date_time, msg_text,
&err_text);
mu_mailbox_close (dest_mbox);
}
mu_mailbox_destroy (&dest_mbox);
}
free (mboxname);
if (status == 0)
return util_finish (command, RESP_OK, "Completed");
return util_finish (command, RESP_NO, err_text);
}
......
......@@ -119,7 +119,9 @@ auth_gssapi (struct imap4d_command *command,
gss_ctx_id_t context;
gss_cred_id_t cred_handle, server_creds;
gss_OID mech_type;
char *token_str;
char *token_str = NULL;
size_t token_size = 0;
size_t token_len;
unsigned char *tmp = NULL;
size_t size;
gss_name_t server_name;
......@@ -168,11 +170,10 @@ auth_gssapi (struct imap4d_command *command,
for (;;)
{
token_str = imap4d_readline_ex ();
util_base64_decode (token_str, strlen (token_str), &tmp, &size);
imap4d_getline (&token_str, &token_size, &token_len);
util_base64_decode (token_str, token_len, &tmp, &size);
tokbuf.value = tmp;
tokbuf.length = size;
free (token_str);
maj_stat = gss_accept_sec_context (&min_stat,
&context,
......@@ -202,6 +203,7 @@ auth_gssapi (struct imap4d_command *command,
display_status ("accept context", maj_stat, min_stat);
maj_stat = gss_delete_sec_context (&min_stat, &context, &outbuf);
gss_release_buffer (&min_stat, &outbuf);
free (token_str);
return RESP_NO;
}
......@@ -211,8 +213,7 @@ auth_gssapi (struct imap4d_command *command,
util_send ("+ %*.*s\r\n", size, size, tmp);
free (tmp);
gss_release_buffer (&min_stat, &outbuf);
token_str = imap4d_readline_ex ();
free (token_str);
imap4d_getline (&token_str, &token_size, &token_len);
}
/* Construct security-level data */
......@@ -224,6 +225,7 @@ auth_gssapi (struct imap4d_command *command,
if (maj_stat != GSS_S_COMPLETE)
{
display_status ("wrap", maj_stat, min_stat);
free (token_str);
return RESP_NO;
}
......@@ -231,8 +233,8 @@ auth_gssapi (struct imap4d_command *command,
util_send ("+ %*.*s\r\n", size, size, tmp);
free (tmp);
token_str = imap4d_readline_ex ();
util_base64_decode (token_str, strlen (token_str),
imap4d_getline (&token_str, &token_size, &token_len);
util_base64_decode (token_str, token_len,
(unsigned char **) &tokbuf.value, &tokbuf.length);
free (token_str);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2006, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2006, 2007,
2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -93,24 +94,30 @@ imap4d_auth_capability ()
mu_list_do (imap_auth_list, _auth_capa, NULL);
}
/*
6.2.1. AUTHENTICATE Command
Arguments: authentication mechanism name
*/
int
imap4d_authenticate (struct imap4d_command *command, char *arg)
imap4d_authenticate (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
char *auth_type;
struct auth_data adata;
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
auth_type = util_getword (arg, &sp);
util_unquote (&auth_type);
if (!auth_type)
return util_finish (command, RESP_BAD, "Too few arguments");
auth_type = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
if (tls_required)
return util_finish (command, RESP_NO, "Command disabled: Use STARTTLS first");
return util_finish (command, RESP_NO,
"Command disabled: Use STARTTLS first");
adata.command = command;
adata.auth_type = auth_type;
adata.arg = sp;
adata.arg = NULL;
adata.username = NULL;
if (mu_list_do (imap_auth_list, _auth_try, &adata) == 0)
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2003, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2003, 2005, 2007,
2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -50,6 +51,7 @@ imap4d_capability_init ()
"IMAP4rev1",
"NAMESPACE",
"IDLE",
"LITERAL+",
"X-VERSION",
NULL
};
......@@ -67,8 +69,11 @@ print_capa (void *item, void *data)
}
int
imap4d_capability (struct imap4d_command *command, char *arg MU_ARG_UNUSED)
imap4d_capability (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
util_send ("* CAPABILITY");
mu_list_do (capa_list, print_capa, NULL);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,13 +19,20 @@
#include "imap4d.h"
/*
* Do we need to do anything here?
* FIXME: This is like noop we need to notify the client of
* new mails etc ... and do housekeeping.
*/
6.4.1. CHECK Command
Arguments: none
Responses: no specific responses for this command
Result: OK - check completed
BAD - command unknown or arguments invalid
*/
int
imap4d_check (struct imap4d_command *command, char *arg MU_ARG_UNUSED)
imap4d_check (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
return util_finish (command, RESP_OK, "Completed");
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2004, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2004, 2005, 2007,
2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,16 +20,27 @@
#include "imap4d.h"
/*
*/
6.4.2. CLOSE Command
/* The CLOSE command permanently removes from the currently selected
Arguments: none
Responses: no specific responses for this command
Result: OK - close completed, now in authenticated state
NO - close failure: no mailbox selected
BAD - command unknown or arguments invalid
The CLOSE command permanently removes from the currently selected
mailbox all messages that have the \\Deleted flag set, and returns
to authenticated state from selected state. */
int
imap4d_close (struct imap4d_command *command, char *arg MU_ARG_UNUSED)
imap4d_close (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
const char *msg = NULL;
int status, flags;
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
mu_mailbox_get_flags (mbox, &flags);
if ((flags & MU_STREAM_READ) == 0)
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2003, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2003, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,55 +19,69 @@
#include "imap4d.h"
/*
* copy messages in argv[2] to mailbox in argv[3]
6.4.7. COPY Command
Arguments: message set
mailbox name
Responses: no specific responses for this command
Result: OK - copy completed
NO - copy error: can't copy those messages or to that
name
BAD - command unknown or arguments invalid
copy messages in argv[2] to mailbox in argv[3]
*/
int
imap4d_copy (struct imap4d_command *command, char *arg)
imap4d_copy (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
int rc;
char buffer[64];
char *text;
rc = imap4d_copy0 (arg, 0, buffer, sizeof buffer);
if (imap4d_tokbuf_argc (tok) != 4)
return util_finish (command, RESP_BAD, "Invalid arguments");
rc = imap4d_copy0 (tok, 0, &text);
if (rc == RESP_NONE)
{
/* Reset the state ourself. */
int new_state = (rc == RESP_OK) ? command->success : command->failure;
if (new_state != STATE_NONE)
state = new_state;
return util_send ("%s %s\r\n", command->tag, buffer);
return util_send ("%s %s\r\n", command->tag, text);
}
return util_finish (command, rc, "%s", buffer);
return util_finish (command, rc, "%s", text);
}
int
imap4d_copy0 (char *arg, int isuid, char *resp, size_t resplen)
imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
{
int status;
char *msgset;
char *name;
char *mailbox_name;
const char *delim = "/";
char *sp = NULL;
size_t *set = NULL;
int n = 0;
mu_mailbox_t cmbox = NULL;
int arg = IMAP4_ARG_1 + !!isuid;
msgset = util_getword (arg, &sp);
name = util_getword (NULL, &sp);
util_unquote (&name);
if (!msgset || !name || *name == '\0')
if (imap4d_tokbuf_argc (tok) != arg + 2)
{
snprintf (resp, resplen, "Too few args");
return RESP_BAD;
*err_text = "Invalid arguments";
return 1;
}
msgset = imap4d_tokbuf_getarg (tok, arg);
name = imap4d_tokbuf_getarg (tok, arg + 1);
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, isuid);
if (status != 0)
{
snprintf (resp, resplen, "Bogus number set");
*err_text = "Bogus number set";
return RESP_BAD;
}
......@@ -75,7 +89,7 @@ imap4d_copy0 (char *arg, int isuid, char *resp, size_t resplen)
if (!mailbox_name)
{
snprintf (resp, resplen, "NO Create failed.");
*err_text = "NO Create failed.";
return RESP_NO;
}
......@@ -105,7 +119,7 @@ imap4d_copy0 (char *arg, int isuid, char *resp, size_t resplen)
if (status == 0)
{
snprintf (resp, resplen, "Completed");
*err_text = "Completed";
return RESP_OK;
}
......@@ -114,6 +128,6 @@ imap4d_copy0 (char *arg, int isuid, char *resp, size_t resplen)
of the text of the tagged NO response. This gives a hint to the
client that it can attempt a CREATE command and retry the copy if
the CREATE is successful. */
snprintf (resp, resplen, "NO [TRYCREATE] failed");
*err_text = "NO [TRYCREATE] failed";
return RESP_NONE;
}
......
......@@ -63,25 +63,34 @@ mkdir_p (char *name, int delim)
return 0;
}
/*
6.3.3. CREATE Command
Arguments: mailbox name
Responses: no specific responses for this command
Result: OK - create completed
NO - create failure: can't create mailbox with that name
BAD - command unknown or arguments invalid
*/
/* FIXME: How do we do this ??????:
IF a new mailbox is created with the same name as a mailbox which was
deleted, its unique identifiers MUST be greater than any unique identifiers
used in the previous incarnation of the mailbox. */
int
imap4d_create (struct imap4d_command *command, char *arg)
imap4d_create (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *name;
char *sp = NULL;
const char *delim = "/";
int isdir = 0;
int rc = RESP_OK;
const char *msg = "Completed";
name = util_getword (arg, &sp);
if (!name)
return util_finish (command, RESP_BAD, "Too few arguments");
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
util_unquote (&name);
name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
if (*name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
......@@ -152,6 +161,5 @@ imap4d_create (struct imap4d_command *command, char *arg)
msg = "already exists";
}
free (name);
return util_finish (command, rc, msg);
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,20 +19,27 @@
#include "imap4d.h"
/*
*
*/
6.3.4. DELETE Command
Arguments: mailbox name
Responses: no specific responses for this command
Result: OK - delete completed
NO - delete failure: can't delete mailbox with that name
BAD - command unknown or arguments invalid
*/
int
imap4d_delete (struct imap4d_command *command, char *arg)
imap4d_delete (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
int rc = RESP_OK;
const char *msg = "Completed";
const char *delim = "/";
char *name;
name = util_getword (arg, &sp);
util_unquote (&name);
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
if (!name || *name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
......@@ -51,6 +58,5 @@ imap4d_delete (struct imap4d_command *command, char *arg)
rc = RESP_NO;
msg = "Cannot remove";
}
free (name);
return util_finish (command, rc, msg);
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,11 +19,23 @@
#include "imap4d.h"
/*
* copy things from select.c
*/
6.3.2. EXAMINE Command
Arguments: mailbox name
Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT
OPTIONAL OK untagged responses: UNSEEN, PERMANENTFLAGS
Result: OK - examine completed, now in selected state
NO - examine failure, now in authenticated state: no
such mailbox, can't access mailbox
BAD - command unknown or arguments invalid
*/
int
imap4d_examine (struct imap4d_command *command, char *arg)
imap4d_examine (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
return imap4d_select0 (command, arg, MU_STREAM_READ);
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
return imap4d_select0 (command, imap4d_tokbuf_getarg (tok, IMAP4_ARG_1),
MU_STREAM_READ);
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,16 +19,23 @@
#include "imap4d.h"
/*
*
*/
6.4.3. EXPUNGE Command
Arguments: none
Responses: untagged responses: EXPUNGE
Result: OK - expunge completed
NO - expunge failure: can't expunge (e.g. permission
denied)
BAD - command unknown or arguments invalid
*/
int
imap4d_expunge (struct imap4d_command *command, char *arg)
imap4d_expunge (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
if (util_getword (arg, &sp))
return util_finish (command, RESP_NO, "Too many args");
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
/* FIXME: check for errors. */
mu_mailbox_expunge (mbox);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2006, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2006, 2007,
2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -20,9 +21,6 @@
#include <ctype.h>
#include <mailutils/argcv.h>
/* This will suck, too.
Alain: Yes it does. */
/* Taken from RFC2060
fetch ::= "FETCH" SPACE set SPACE ("ALL" / "FULL" /
"FAST" / fetch_att / "(" 1#fetch_att ")")
......@@ -34,495 +32,93 @@
["<" number "." nz_number ">"]
*/
struct fetch_command;
static int fetch_all (struct fetch_command *, char**);
static int fetch_full (struct fetch_command *, char**);
static int fetch_fast (struct fetch_command *, char**);
static int fetch_envelope (struct fetch_command *, char**);
static int fetch_flags (struct fetch_command *, char**);
static int fetch_internaldate (struct fetch_command *, char**);
static int fetch_rfc822_header (struct fetch_command *, char**);
static int fetch_rfc822_size (struct fetch_command *, char**);
static int fetch_rfc822_text (struct fetch_command *, char**);
static int fetch_rfc822 (struct fetch_command *, char**);
static int fetch_bodystructure (struct fetch_command *, char**);
static int fetch_body (struct fetch_command *, char**);
static int fetch_uid (struct fetch_command *, char**);
/* Helper functions. */
static int fetch_envelope0 (mu_message_t);
static int fetch_bodystructure0 (mu_message_t, int);
static int bodystructure (mu_message_t, int);
static void send_parameter_list (const char *);
static int fetch_operation (mu_message_t, char **, int);
static int fetch_message (mu_message_t, unsigned long, unsigned long);
static int fetch_header (mu_message_t, unsigned long, unsigned long);
static int fetch_body_content (mu_message_t, unsigned long, unsigned long);
static int fetch_io (mu_stream_t, unsigned long, unsigned long, unsigned long);
static int fetch_header_fields (mu_message_t, char **, unsigned long, unsigned long);
static int fetch_header_fields_not (mu_message_t, char **, unsigned long, unsigned long);
static int fetch_send_address (const char *);
static struct fetch_command* fetch_getcommand (char *, struct fetch_command*);
struct fetch_command
struct fetch_runtime_closure
{
const char *name;
int (*func) (struct fetch_command *, char **);
int eltno;
size_t msgno;
mu_message_t msg;
} fetch_command_table [] =
{
#define F_ALL 0
{"ALL", fetch_all, 0},
#define F_FULL 1
{"FULL", fetch_full, 0},
#define F_FAST 2
{"FAST", fetch_fast, 0},
#define F_ENVELOPE 3
{"ENVELOPE", fetch_envelope, 0},
#define F_FLAGS 4
{"FLAGS", fetch_flags, 0},
#define F_INTERNALDATE 5
{"INTERNALDATE", fetch_internaldate, 0},
#define F_RFC822_HEADER 6
{"RFC822.HEADER", fetch_rfc822_header, 0},
#define F_RFC822_SIZE 7
{"RFC822.SIZE", fetch_rfc822_size, 0},
#define F_RFC822_TEXT 8
{"RFC822.TEXT", fetch_rfc822_text, 0},
#define F_RFC822 9
{"RFC822", fetch_rfc822, 0},
#define F_BODYSTRUCTURE 10
{"BODYSTRUCTURE", fetch_bodystructure, 0},
#define F_BODY 11
{"BODY", fetch_body, 0},
#define F_UID 12
{"UID", fetch_uid, 0},
{ NULL, 0, 0}
char *err_text;
};
/* Go through the fetch array sub command and returns the the structure. */
static struct fetch_command *
fetch_getcommand (char *cmd, struct fetch_command *command_table)
{
size_t i, len = strlen (cmd);
struct fetch_function_closure;
for (i = 0; command_table[i].name != 0; i++)
{
if (strlen (command_table[i].name) == len &&
!strcasecmp (command_table[i].name, cmd))
return &command_table[i];
}
return NULL;
}
typedef int (*fetch_function_t) (struct fetch_function_closure *,
struct fetch_runtime_closure *);
/* The FETCH command retrieves data associated with a message in the
mailbox. The data items to be fetched can be either a single atom
or a parenthesized list. */
int
imap4d_fetch (struct imap4d_command *command, char *arg)
struct fetch_function_closure
{
int rc;
char buffer[64];
rc = imap4d_fetch0 (arg, 0, buffer, sizeof buffer);
return util_finish (command, rc, "%s", buffer);
}
fetch_function_t fun; /* Handler function */
const char *name; /* Response tag */
size_t *section_part; /* Section-part */
size_t nset; /* Number of elements in section_part */
int peek;
int not; /* Negate header set */
mu_list_t headers; /* Headers */
size_t start; /* Substring start */
size_t size; /* Substring length */
};
/* Where the real implementation is. It is here since UID command also
calls FETCH. */
int
imap4d_fetch0 (char *arg, int isuid, char *resp, size_t resplen)
static int
fetch_send_address (const char *addr)
{
struct fetch_command *fcmd = NULL;
int rc = RESP_OK;
char *sp = NULL;
char *msgset;
size_t *set = NULL;
int n = 0;
int i;
int status;
mu_address_t address;
size_t i, count = 0;
msgset = util_getword (arg, &sp);
if (!msgset || !sp || *sp == '\0')
/* Short circuit. */
if (addr == NULL || *addr == '\0')
{
snprintf (resp, resplen, "Too few args");
return RESP_BAD;
util_send ("NIL");
return RESP_OK;
}
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, isuid);
if (status != 0)
{
snprintf (resp, resplen, "Bogus number set");
return RESP_BAD;
}
mu_address_create (&address, addr);
mu_address_get_count (address, &count);
/* Prepare status code. It will be replaced if an error occurs in the
loop below */
snprintf (resp, resplen, "Completed");
for (i = 0; i < n && rc == RESP_OK; i++)
/* We failed: can't parse. */
if (count == 0)
{
size_t msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
mu_message_t msg = NULL;
if (msgno && mu_mailbox_get_message (mbox, msgno, &msg) == 0)
{
char item[32];
char *items = strdup (sp);
char *p = items;
int space = 0;
fcmd = NULL;
util_send ("* %s FETCH (", mu_umaxtostr (0, msgno));
item[0] = '\0';
/* Server implementations MUST implicitly
include the UID message data item as part of any FETCH
response caused by a UID command, regardless of whether
a UID was specified as a message data item to the FETCH. */
if (isuid)
{
fcmd = &fetch_command_table[F_UID];
fcmd->msg = msg;
rc = fetch_uid (fcmd, &items);
}
/* Get the fetch command names. */
while (*items && *items != ')')
{
util_token (item, sizeof (item), &items);
/* Do not send the UID again. */
if (isuid && strcasecmp (item, "UID") == 0)
continue;
if (fcmd)
space = 1;
/* Search in the table. */
fcmd = fetch_getcommand (item, fetch_command_table);
if (fcmd)
{
if (space)
{
util_send (" ");
space = 0;
}
fcmd->msg = msg;
rc = fcmd->func (fcmd, &items);
}
}
util_send (")\r\n");
free (p);
}
else if (!isuid)
/* According to RFC 3501, "A non-existent unique identifier is
ignored without any error message generated." */
{
snprintf (resp, resplen,
"Bogus message set: message number out of range");
rc = RESP_BAD;
break;
}
util_send ("NIL");
return RESP_OK;
}
free (set);
return rc;
}
/* ALL:
Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
Combination of FAST and ENVELOPE. */
static int
fetch_all (struct fetch_command *command, char **arg)
{
struct fetch_command c_env = fetch_command_table[F_ENVELOPE];
fetch_fast (command, arg);
util_send (" ");
c_env.msg = command->msg;
fetch_envelope (&c_env, arg);
return RESP_OK;
}
/* FULL:
Macro equivalent to: (FLAGS INTERNALDATE
RFC822.SIZE ENVELOPE BODY).
Combination of (ALL BODY). */
static int
fetch_full (struct fetch_command *command, char **arg)
{
struct fetch_command c_body = fetch_command_table[F_BODY];
fetch_all (command, arg);
util_send (" ");
c_body.msg = command->msg;
return fetch_body (&c_body, arg);
}
/* FAST:
Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE)
Combination of (FLAGS INTERNALDATE RFC822.SIZE). */
static int
fetch_fast (struct fetch_command *command, char **arg)
{
struct fetch_command c_idate = fetch_command_table[F_INTERNALDATE];
struct fetch_command c_rfc = fetch_command_table[F_RFC822_SIZE];
struct fetch_command c_flags = fetch_command_table[F_FLAGS];
c_flags.msg = command->msg;
fetch_flags (&c_flags, arg);
util_send (" ");
c_idate.msg = command->msg;
fetch_internaldate (&c_idate, arg);
util_send (" ");
c_rfc.msg = command->msg;
fetch_rfc822_size (&c_rfc, arg);
return RESP_OK;
}
/* ENVELOPE:
Header: Date, Subject, From, Sender, Reply-To, To, Cc, Bcc, In-Reply-To,
and Message-Id. */
static int
fetch_envelope (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
int status;
util_send ("%s (", command->name);
status = fetch_envelope0 (command->msg);
util_send (")");
return status;
}
/* FLAGS: The flags that are set for this message. */
/* FIXME: User flags not done. If enable change the PERMANENTFLAGS in SELECT */
void
fetch_flags0 (const char *prefix, mu_message_t msg, int isuid)
{
mu_attribute_t attr = NULL;
mu_message_get_attribute (msg, &attr);
if (isuid)
util_send ("(");
for (i = 1; i <= count; i++)
{
struct fetch_command *fcmd = &fetch_command_table[F_UID];
fcmd->msg = msg;
fetch_uid (fcmd, NULL);
util_send (" ");
}
util_send ("%s (", prefix);
util_print_flags(attr);
util_send (")");
}
static int
fetch_flags (struct fetch_command *command, char **arg)
{
fetch_flags0 (command->name, command->msg, 0);
return RESP_OK;
}
/* INTERNALDATE The internal date of the message.
Format:
date_time ::= <"> date_day_fixed "-" date_month "-" date_year
SPACE time SPACE zone <">
date_day ::= 1*2digit
;; Day of month
date_day_fixed ::= (SPACE digit) / 2digit
;; Fixed-format version of date_day
date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
"Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
date_text ::= date_day "-" date_month "-" date_year
date_year ::= 4digit
const char *str;
int is_group = 0;
time ::= 2digit ":" 2digit ":" 2digit
;; Hours minutes seconds
util_send ("(");
zone ::= ("+" / "-") 4digit
;; Signed four-digit value of hhmm representing
;; hours and minutes west of Greenwich (that is,
;; (the amount that the given time differs from
;; Universal Time). Subtracting the timezone
;; from the given time will give the UT form.
;; The Universal Time zone is "+0000". */
static int
fetch_internaldate (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
const char *date;
mu_envelope_t env = NULL;
struct tm tm, *tmp = NULL;
mu_timezone tz;
char datebuf[sizeof ("13-Jul-2002 00:00:00")];
mu_address_sget_personal (address, i, &str);
util_send_qstring (str);
util_send (" ");
mu_message_get_envelope (command->msg, &env);
if (mu_envelope_sget_date (env, &date) == 0
&& mu_parse_ctime_date_time (&date, &tm, &tz) == 0)
tmp = &tm;
else
{
time_t t = time (NULL);
tmp = localtime (&t);
}
mu_strftime (datebuf, sizeof (datebuf), "%d-%b-%Y %H:%M:%S", tmp);
util_send ("%s", command->name);
util_send (" \"%s +0000\"", datebuf);
return RESP_OK;
}
mu_address_sget_route (address, i, &str);
util_send_qstring (str);
/*
RFC822.HEADER:
Functionally equivalent to BODY.PEEK[HEADER], differing in the syntax of
the resulting untagged FETCH data (RFC822.HEADER is returned). */
static int
fetch_rfc822_header (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
char buffer[32];
char *p = buffer;
util_send (" ");
strcpy (buffer, ".PEEK[HEADER]");
return fetch_body (command, &p);
}
mu_address_is_group (address, i, &is_group);
str = NULL;
if (is_group)
mu_address_sget_personal (address, i, &str);
else
mu_address_sget_local_part (address, i, &str);
/* RFC822.TEXT:
Functionally equivalent to BODY[TEXT], differing in the syntax of the
resulting untagged FETCH data (RFC822.TEXT is returned). */
static int
fetch_rfc822_text (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
char buffer[16];
char *p = buffer;
util_send_qstring (str);
strcpy (buffer, "[TEXT]");
return fetch_body (command, &p);
}
util_send (" ");
/* The [RFC-822] size of the message. */
static int
fetch_rfc822_size (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
size_t size = 0;
size_t lines = 0;
mu_message_size (command->msg, &size);
mu_message_lines (command->msg, &lines);
util_send ("%s %u", command->name, size + lines);
return RESP_OK;
}
mu_address_sget_domain (address, i, &str);
util_send_qstring (str);
/* RFC822:
Functionally equivalent to BODY[], differing in the syntax of the
resulting untagged FETCH data (RFC822 is returned). */
static int
fetch_rfc822 (struct fetch_command *command, char **arg)
{
if (**arg == '.')
{
/* We have to catch the other RFC822.XXX commands here. This is because
util_token() in imap4d_fetch0 will return the RFC822 token only. */
if (strncasecmp (*arg, ".SIZE", 5) == 0)
{
struct fetch_command c_rfc= fetch_command_table[F_RFC822_SIZE];
c_rfc.msg = command->msg;
(*arg) += 5;
fetch_rfc822_size (&c_rfc, arg);
}
else if (strncasecmp (*arg, ".TEXT", 5) == 0)
{
struct fetch_command c_rfc = fetch_command_table[F_RFC822_TEXT];
c_rfc.msg = command->msg;
(*arg) += 5;
fetch_rfc822_text (&c_rfc, arg);
}
else if (strncasecmp (*arg, ".HEADER", 7) == 0)
{
struct fetch_command c_rfc = fetch_command_table[F_RFC822_HEADER];
c_rfc.msg = command->msg;
(*arg) += 7;
fetch_rfc822_header (&c_rfc, arg);
}
}
else
{
char buffer[16];
char *p = buffer;
strcpy (buffer, "[]");
fetch_body (command, &p);
util_send (")");
}
return RESP_OK;
}
/* UID: The unique identifier for the message. */
static int
fetch_uid (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
size_t uid = 0;
mu_message_get_uid (command->msg, &uid);
util_send ("%s %s", command->name, mu_umaxtostr (0, uid));
return RESP_OK;
}
/* BODYSTRUCTURE:
The [MIME-IMB] body structure of the message. This is computed by the
server by parsing the [MIME-IMB] header fields in the [RFC-822] header and
[MIME-IMB] headers. */
static int
fetch_bodystructure (struct fetch_command *command, char **arg MU_ARG_UNUSED)
{
util_send ("%s (", command->name);
fetch_bodystructure0 (command->msg, 1); /* 1 means with extension data. */
util_send (")");
return RESP_OK;
}
/* BODY: Non-extensible form of BODYSTRUCTURE.
BODY[<section>]<<partial>> :
The text of a particular body section. The section specification is a set
of zero or more part specifiers delimited by periods. A part specifier
is either a part number or one of the following: HEADER, HEADER.FIELDS,
HEADER.FIELDS.NOT, MIME, and TEXT. An empty section specification refers
to the entire message, including the header.
Note: for body section, the \Seen flag is implicitly set;
if this causes the flags to change they SHOULD be
included as part of the FETCH responses. */
static int
fetch_body (struct fetch_command *command, char **arg)
{
/* It's body section, set the message as seen */
if (**arg == '[')
{
mu_attribute_t attr = NULL;
mu_message_get_attribute (command->msg, &attr);
if (!mu_attribute_is_read (attr))
{
util_send ("FLAGS (\\Seen) ");
mu_attribute_set_read (attr);
}
}
else if (strncasecmp (*arg,".PEEK", 5) == 0)
{
/* Move pass the .peek */
(*arg) += 5;
while (isspace ((unsigned)**arg))
(*arg)++;
}
else if (**arg != '[' && **arg != '.')
{
/* Call body structure without the extension. */
util_send ("%s (", command->name);
fetch_bodystructure0 (command->msg, 0);
util_send (")");
return RESP_OK;
}
util_send ("%s", command->name);
return fetch_operation (command->msg, arg,
strcasecmp (command->name, "BODY"));
}
/* Helper Functions: Where the Beef is. */
static void
fetch_send_header_value (mu_header_t header, const char *name,
const char *defval, int space)
......@@ -543,25 +139,6 @@ fetch_send_header_value (mu_header_t header, const char *name,
}
static void
fetch_send_header_list (mu_header_t header, const char *name,
const char *defval, int space)
{
char *buffer;
if (space)
util_send (" ");
if (mu_header_aget_value (header, name, &buffer) == 0)
{
send_parameter_list (buffer);
free (buffer);
}
else if (defval)
send_parameter_list (defval);
else
util_send ("NIL");
}
static void
fetch_send_header_address (mu_header_t header, const char *name,
const char *defval, int space)
{
......@@ -578,6 +155,101 @@ fetch_send_header_address (mu_header_t header, const char *name,
fetch_send_address (defval);
}
/* Send parameter list for the bodystructure. */
static void
send_parameter_list (const char *buffer)
{
int argc = 0;
char **argv;
if (!buffer)
{
util_send ("NIL");
return;
}
mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
if (argc == 0)
util_send ("NIL");
else
{
char *p;
util_send ("(");
p = argv[0];
util_send_qstring (p);
if (argc > 1)
{
int i, space = 0;
char *lvalue = NULL;
util_send ("(");
for (i = 1; i < argc; i++)
{
if (lvalue)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
lvalue = NULL;
space = 1;
}
switch (argv[i][0])
{
case ';':
continue;
case '=':
if (++i < argc)
{
char *p = argv[i];
util_send (" ");
util_send_qstring (p);
}
break;
default:
lvalue = argv[i];
}
}
if (lvalue)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
}
util_send (")");
}
else
util_send (" NIL");
util_send (")");
}
mu_argcv_free (argc, argv);
}
static void
fetch_send_header_list (mu_header_t header, const char *name,
const char *defval, int space)
{
char *buffer;
if (space)
util_send (" ");
if (mu_header_aget_value (header, name, &buffer) == 0)
{
send_parameter_list (buffer);
free (buffer);
}
else if (defval)
send_parameter_list (defval);
else
util_send ("NIL");
}
/* ENVELOPE:
The envelope structure of the message. This is computed by the server by
parsing the [RFC-822] header into the component parts, defaulting various
......@@ -616,139 +288,6 @@ fetch_envelope0 (mu_message_t msg)
return RESP_OK;
}
/* The beef BODYSTRUCTURE.
A parenthesized list that describes the [MIME-IMB] body structure of a
message. Multiple parts are indicated by parenthesis nesting. Instead of
a body type as the first element of the parenthesized list there is a nested
body. The second element of the parenthesized list is the multipart
subtype (mixed, digest, parallel, alternative, etc.).
The extension data of a multipart body part are in the following order:
body parameter parenthesized list:
A parenthesized list of attribute/value pairs [e.g. ("foo" "bar" "baz"
"rag") where "bar" is the value of "foo" and "rag" is the value of
"baz"] as defined in [MIME-IMB].
body disposition:
A parenthesized list, consisting of a disposition type string followed by a
parenthesized list of disposition attribute/value pairs. The disposition
type and attribute names will be defined in a future standards-track
revision to [DISPOSITION].
body language:
A string or parenthesized list giving the body language value as defined
in [LANGUAGE-TAGS]. */
static int
fetch_bodystructure0 (mu_message_t message, int extension)
{
size_t nparts = 1;
size_t i;
int is_multipart = 0;
mu_message_is_multipart (message, &is_multipart);
if (is_multipart)
{
char *buffer = NULL;
mu_header_t header = NULL;
mu_message_get_num_parts (message, &nparts);
/* Get all the sub messages. */
for (i = 1; i <= nparts; i++)
{
mu_message_t msg = NULL;
mu_message_get_part (message, i, &msg);
util_send ("(");
fetch_bodystructure0 (msg, extension);
util_send (")");
} /* for () */
mu_message_get_header (message, &header);
/* The subtype. */
if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
{
int argc = 0;
char **argv;
char *s;
mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
s = strchr (argv[0], '/');
if (s)
s++;
util_send (" ");
util_send_qstring (s);
/* The extension data for multipart. */
if (extension)
{
int space = 0;
char *lvalue = NULL;
util_send (" (");
for (i = 1; i < argc; i++)
{
/* body parameter parenthesized list:
Content-type parameter list. */
if (lvalue)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
lvalue = NULL;
space = 1;
}
switch (argv[i][0])
{
case ';':
continue;
case '=':
if (++i < argc)
{
char *p = argv[i];
util_send (" ");
util_unquote (&p);
util_send_qstring (p);
}
break;
default:
lvalue = argv[i];
}
}
if (lvalue)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
}
util_send (")");
}
else
util_send (" NIL");
mu_argcv_free (argc, argv);
free (buffer);
}
else
/* No content-type header */
util_send (" NIL");
/* body disposition: Content-Disposition. */
fetch_send_header_list (header, MU_HEADER_CONTENT_DISPOSITION,
NULL, 1);
/* body language: Content-Language. */
fetch_send_header_list (header, MU_HEADER_CONTENT_LANGUAGE,
NULL, 1);
}
else
bodystructure (message, extension);
return RESP_OK;
}
/* The basic fields of a non-multipart body part are in the following order:
body type:
A string giving the content media type name as defined in [MIME-IMB].
......@@ -857,7 +396,6 @@ bodystructure (mu_message_t msg, int extension)
{
char *p = argv[i];
util_send (" ");
util_unquote (&p);
util_send_qstring (p);
}
break;
......@@ -955,195 +493,187 @@ bodystructure (mu_message_t msg, int extension)
return RESP_OK;
}
static int
fetch_operation (mu_message_t msg, char **arg, int silent)
{
unsigned long start = ULONG_MAX; /* No starting offset. */
unsigned long end = ULONG_MAX; /* No limit. */
char *section; /* Hold the section number string. */
char *partial = strchr (*arg, '<');
int rc;
/* Check for section specific offset. */
if (partial)
{
/* NOTE: should this should be move in imap4d_fetch() and have a more
draconian check? */
*partial = '\0';
partial++;
start = strtoul (partial, &partial, 10);
if (*partial == '.')
{
partial++;
end = strtoul (partial, NULL, 10);
}
}
/* Pass the first bracket '[' */
(*arg)++;
section = *arg;
/* The beef BODYSTRUCTURE.
A parenthesized list that describes the [MIME-IMB] body structure of a
message. Multiple parts are indicated by parenthesis nesting. Instead of
a body type as the first element of the parenthesized list there is a nested
body. The second element of the parenthesized list is the multipart
subtype (mixed, digest, parallel, alternative, etc.).
/* Retreive the section message. */
while (isdigit ((unsigned)**arg))
{
unsigned long j = strtoul (*arg, arg, 10);
int status;
The extension data of a multipart body part are in the following order:
body parameter parenthesized list:
A parenthesized list of attribute/value pairs [e.g. ("foo" "bar" "baz"
"rag") where "bar" is the value of "foo" and "rag" is the value of
"baz"] as defined in [MIME-IMB].
/* Wrong section message number bail out. */
if (j == 0 || j == ULONG_MAX) /* Technical: I should check errno too. */
break;
body disposition:
A parenthesized list, consisting of a disposition type string followed by a
parenthesized list of disposition attribute/value pairs. The disposition
type and attribute names will be defined in a future standards-track
revision to [DISPOSITION].
/* If the section message did not exist bail out here. */
status = mu_message_get_part (msg, j, &msg);
if (status != 0)
{
util_send (" \"\"");
return RESP_OK;
}
if (**arg == '.')
(*arg)++;
else
break;
}
body language:
A string or parenthesized list giving the body language value as defined
in [LANGUAGE-TAGS]. */
static int
fetch_bodystructure0 (mu_message_t message, int extension)
{
size_t nparts = 1;
size_t i;
int is_multipart = 0;
/* Did we have a section message? */
if (((*arg) - section) > 0)
mu_message_is_multipart (message, &is_multipart);
if (is_multipart)
{
char *p = section;
section = calloc ((*arg) - p + 1, 1);
if (section)
memcpy (section, p, (*arg) - p);
}
else
section = calloc (1, 1);
char *buffer = NULL;
mu_header_t header = NULL;
rc = RESP_OK;
/* Choose the right fetch attribute. */
if (*section == '\0' && **arg == ']')
{
if (!silent)
util_send ("[]");
(*arg)++;
rc = fetch_message (msg, start, end);
}
else if (strncasecmp (*arg, "HEADER]", 7) == 0)
{
if (!silent)
{
/* NOTE: We violate the RFC here: Header cannot take a prefix for
section messages it only referes to the RFC822 header .. ok
see it as an extension. But according to IMAP4 we should
have send an empty string: util_send (" \"\"");
*/
util_send ("[%sHEADER]", section);
}
(*arg) += 7;
rc = fetch_header (msg, start, end);
}
else if (strncasecmp (*arg, "MIME]", 5) == 0)
{
if (!silent)
mu_message_get_num_parts (message, &nparts);
/* Get all the sub messages. */
for (i = 1; i <= nparts; i++)
{
mu_message_t msg = NULL;
mu_message_get_part (message, i, &msg);
util_send ("(");
fetch_bodystructure0 (msg, extension);
util_send (")");
} /* for () */
mu_message_get_header (message, &header);
/* The subtype. */
if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0)
{
if (*section)
util_send ("[%sMIME]", section);
int argc = 0;
char **argv;
char *s;
mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
s = strchr (argv[0], '/');
if (s)
s++;
util_send (" ");
util_send_qstring (s);
/* The extension data for multipart. */
if (extension)
{
int space = 0;
char *lvalue = NULL;
util_send (" (");
for (i = 1; i < argc; i++)
{
/* body parameter parenthesized list:
Content-type parameter list. */
if (lvalue)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
lvalue = NULL;
space = 1;
}
switch (argv[i][0])
{
case ';':
continue;
case '=':
if (++i < argc)
{
char *p = argv[i];
util_send (" ");
util_send_qstring (p);
}
break;
default:
lvalue = argv[i];
}
}
if (lvalue)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
}
util_send (")");
}
else
util_send ("[%s", *arg);
util_send (" NIL");
mu_argcv_free (argc, argv);
free (buffer);
}
(*arg) += 5;
rc = fetch_header (msg, start, end);
}
else if (strncasecmp (*arg, "HEADER.FIELDS.NOT", 17) == 0)
{
/* NOTE: we should flag an error if section is not empty: accept
as an extension for now. */
if (*section)
util_send ("[%s", section);
else
util_send ("[");
(*arg) += 17;
rc = fetch_header_fields_not (msg, arg, start, end);
}
else if (strncasecmp (*arg, "HEADER.FIELDS", 13) == 0)
{
/* NOTE: we should flag an error if section is not empty: accept
as an extension for now. */
if (*section)
util_send ("[%s", section);
else
util_send ("[");
(*arg) += 13;
rc = fetch_header_fields (msg, arg, start, end);
/* No content-type header */
util_send (" NIL");
/* body disposition: Content-Disposition. */
fetch_send_header_list (header, MU_HEADER_CONTENT_DISPOSITION,
NULL, 1);
/* body language: Content-Language. */
fetch_send_header_list (header, MU_HEADER_CONTENT_LANGUAGE,
NULL, 1);
}
else
bodystructure (message, extension);
return RESP_OK;
}
else if (strncasecmp (*arg, "TEXT]", 5) == 0)
static void
set_seen (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
if (!ffc->peek)
{
if (!silent)
mu_attribute_t attr = NULL;
mu_message_get_attribute (frt->msg, &attr);
if (!mu_attribute_is_read (attr))
{
if (*section)
util_send ("[%sTEXT]", section);
else
util_send ("[TEXT]");
util_send ("FLAGS (\\Seen) ");
mu_attribute_set_read (attr);
}
(*arg) += 5;
rc = fetch_body_content (msg, start, end);
}
else if (**arg == ']')
{
if (!silent)
util_send ("[%s]", section);
(*arg)++;
rc = fetch_body_content (msg, start, end);
}
else
{
util_send (" \"\"");/*FIXME: ERROR Message!*/
rc = RESP_BAD;
}
free (section);
return rc;
}
static int
fetch_message (mu_message_t msg, unsigned long start, unsigned long end)
static mu_message_t
fetch_get_part (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
mu_stream_t stream = NULL;
size_t size = 0, lines = 0;
mu_message_get_stream (msg, &stream);
mu_message_size (msg, &size);
mu_message_lines (msg, &lines);
return fetch_io (stream, start, end, size + lines);
}
mu_message_t msg = frt->msg;
size_t i;
static int
fetch_header (mu_message_t msg, unsigned long start, unsigned long end)
{
mu_header_t header = NULL;
mu_stream_t stream = NULL;
size_t size = 0, lines = 0;
mu_message_get_header (msg, &header);
mu_header_size (header, &size);
mu_header_lines (header, &lines);
mu_header_get_stream (header, &stream);
return fetch_io (stream, start, end, size + lines);
for (i = 0; i < ffc->nset; i++)
if (mu_message_get_part (msg, ffc->section_part[i], &msg))
return NULL;
return msg;
}
static int
fetch_body_content (mu_message_t msg, unsigned long start, unsigned long end)
static void
fetch_send_section_part (struct fetch_function_closure *ffc,
const char *prefix, const char *suffix)
{
mu_body_t body = NULL;
mu_stream_t stream = NULL;
size_t size = 0, lines = 0;
mu_message_get_body (msg, &body);
mu_body_size (body, &size);
mu_body_lines (body, &lines);
mu_body_get_stream (body, &stream);
return fetch_io (stream, start, end, size + lines);
int i;
util_send ("%s", prefix);
for (i = 0; i < ffc->nset; i++)
{
if (i)
util_send (".");
util_send ("%lu", (unsigned long) ffc->section_part[i]);
}
if (i && suffix[0] != ']')
util_send (".");
util_send ("%s", suffix);
}
static int
fetch_io (mu_stream_t stream, unsigned long start, unsigned long end,
unsigned long max)
fetch_io (mu_stream_t stream, size_t start, size_t size, size_t max)
{
mu_stream_t rfc = NULL;
size_t n = 0;
......@@ -1151,25 +681,26 @@ fetch_io (mu_stream_t stream, unsigned long start, unsigned long end,
mu_filter_create (&rfc, stream, "rfc822", MU_FILTER_ENCODE, MU_STREAM_READ);
if (start == ULONG_MAX || end == ULONG_MAX)
if (start == 0 && size == (size_t) -1)
{
char buffer[512];
offset = 0;
if (max)
{
util_send (" {%lu}\r\n", max);
util_send (" {%lu}\r\n", (unsigned long) max);
while (mu_stream_read (rfc, buffer, sizeof (buffer) - 1, offset,
&n) == 0 && n > 0)
&n) == 0 && n > 0)
{
buffer[n] = '\0';
util_send ("%s", buffer);
offset += n;
}
/* FIXME: Make sure exactly max bytes were sent */
}
else
util_send (" \"\"");
}
else if (end + 2 < end) /* Check for integer overflow */
else if (size + 2 < size) /* Check for integer overflow */
{
return RESP_BAD;
}
......@@ -1178,393 +709,944 @@ fetch_io (mu_stream_t stream, unsigned long start, unsigned long end,
char *buffer, *p;
size_t total = 0;
offset = start;
p = buffer = calloc (end + 2, 1);
while (end > 0
&& mu_stream_read (rfc, buffer, end + 1, offset, &n) == 0 && n > 0)
p = buffer = malloc (size + 1);
if (!p)
imap4d_bye (ERR_NO_MEM);
while (total < size
&& mu_stream_read (rfc, p, size - total + 1, offset, &n) == 0
&& n > 0)
{
offset += n;
total += n;
end -= n;
buffer += n;
p += n;
}
/* Make sure we null terminate. */
*buffer = '\0';
util_send ("<%lu>", start);
*p = 0;
util_send ("<%lu>", (unsigned long) start);
if (total)
{
util_send (" {%s}\r\n", mu_umaxtostr (0, total));
util_send ("%s", p);
util_send (" {%lu}\r\n", (unsigned long) total);
util_send ("%s", buffer);
}
else
util_send (" \"\"");
free (p);
free (buffer);
}
return RESP_OK;
}
/* Runtime functions */
static int
fetch_header_fields (mu_message_t msg, char **arg, unsigned long start,
unsigned long end)
_frt_uid (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
char *buffer = NULL;
char **array = NULL;
size_t array_len = 0;
size_t off = 0;
size_t lines = 0;
mu_stream_t stream = NULL;
int status;
status = mu_memory_stream_create (&stream, 0, 0);
if (status != 0)
imap4d_bye (ERR_NO_MEM);
/* Save the fields in an array. */
{
char *field;
char *sp = NULL;
char *f = *arg;
/* Find the end of the header.field tag. */
field = strchr (f, ']');
if (field)
{
*field++ = '\0';
*arg = field;
}
else
*arg += strlen (*arg);
for (;(field = util_getitem (f, " ()]\r\n", &sp)); f = NULL, array_len++)
{
array = realloc (array, (array_len + 1) * sizeof (*array));
if (!array)
imap4d_bye (ERR_NO_MEM);
array[array_len] = field;
}
}
/* Get the header values. */
{
size_t j;
mu_header_t header = NULL;
mu_message_get_header (msg, &header);
for (j = 0; j < array_len; j++)
{
char *value = NULL;
size_t n = 0;
if (mu_header_aget_value (header, array[j], &value))
continue;
n = asprintf (&buffer, "%s: %s\n", array[j], value);
status = mu_stream_write (stream, buffer, n, off, &n);
off += n;
/* count the lines. */
{
char *nl = buffer;
for (;(nl = strchr (nl, '\n')); nl++)
lines++;
}
free (value);
free (buffer);
buffer = NULL;
if (status != 0)
{
free (array);
imap4d_bye (ERR_NO_MEM);
}
}
}
/* Headers are always sent with the NL separator. */
mu_stream_write (stream, "\n", 1, off, NULL);
off++;
lines++;
/* Send the command back. The first braket was already sent. */
util_send ("HEADER.FIELDS");
{
size_t j;
util_send (" (");
for (j = 0; j < array_len; j++)
{
util_upper (array[j]);
if (j)
util_send (" ");
util_send_qstring (array[j]);
}
util_send (")");
util_send ("]");
}
size_t uid = 0;
fetch_io (stream, start, end, off + lines);
if (array)
free (array);
mu_message_get_uid (frt->msg, &uid);
util_send ("%s %s", ffc->name, mu_umaxtostr (0, uid));
return RESP_OK;
}
static int
fetch_header_fields_not (mu_message_t msg, char **arg, unsigned long start,
unsigned long end)
_frt_envelope (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
char **array = NULL;
size_t array_len = 0;
char *buffer = NULL;
size_t off = 0;
size_t lines = 0;
mu_stream_t stream = NULL;
int status;
status = mu_memory_stream_create (&stream, 0, 0);
if (status)
imap4d_bye (ERR_NO_MEM);
/* Save the field we want to ignore. */
{
char *field;
char *sp = NULL;
char *f = *arg;
/* Find the end of the header.field.no tag. */
field = strchr (f, ']');
if (field)
{
*field++ = '\0';
*arg = field;
}
else
*arg += strlen (*arg);
for (;(field = strtok_r (f, " ()\r\n", &sp)); f = NULL, array_len++)
{
array = realloc (array, (array_len + 1) * sizeof (*array));
if (!array)
imap4d_bye (ERR_NO_MEM);
array[array_len] = field;
}
}
/* Build the memory buffer. */
{
size_t i;
mu_header_t header = NULL;
size_t count = 0;
mu_message_get_header (msg, &header);
mu_header_get_field_count (header, &count);
for (i = 1; i <= count; i++)
{
char *name = NULL;
char *value ;
size_t n = 0;
size_t ignore = 0;
/* Get the field name. */
status = mu_header_aget_field_name (header, i, &name);
if (*name == '\0')
{
free (name);
continue;
}
/* Should we ignore the field? */
{
size_t j;
for (j = 0; j < array_len; j++)
{
if (strcasecmp (array[j], name) == 0)
{
ignore = 1;
break;
}
}
if (ignore)
{
free (name);
continue;
}
}
if (mu_header_aget_field_value (header, i, &value) == 0)
{
char *nl;
/* Save the field. */
n = asprintf (&buffer, "%s: %s\n", name, value);
status = mu_stream_write (stream, buffer, n, off, &n);
off += n;
/* count the lines. */
for (nl = buffer;(nl = strchr (nl, '\n')); nl++)
lines++;
free (value);
}
free (name);
free (buffer);
buffer = NULL;
if (status != 0)
{
free (array);
imap4d_bye (ERR_NO_MEM);
}
}
}
/* Headers are always sent with a NL separator. */
mu_stream_write (stream, "\n", 1, off, NULL);
off++;
lines++;
util_send ("HEADER.FIELDS.NOT");
{
size_t j;
util_send (" (");
for (j = 0; j < array_len; j++)
{
util_upper (array[j]);
if (j)
util_send (" ");
util_send_qstring (array[j]);
}
util_send (")");
util_send ("]");
}
fetch_io (stream, start, end, off + lines);
if (array)
free (array);
util_send ("%s (", ffc->name);
fetch_envelope0 (frt->msg);
util_send (")");
return RESP_OK;
}
static int
fetch_send_address (const char *addr)
_frt_flags (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
mu_address_t address;
size_t i, count = 0;
mu_attribute_t attr = NULL;
/* Short circuit. */
if (addr == NULL || *addr == '\0')
{
util_send ("NIL");
return RESP_OK;
}
mu_message_get_attribute (frt->msg, &attr);
util_send ("%s (", ffc->name);
util_print_flags (attr);
util_send (")");
return 0;
}
mu_address_create (&address, addr);
mu_address_get_count (address, &count);
/* INTERNALDATE The internal date of the message.
Format:
/* We failed: can't parse. */
if (count == 0)
{
util_send ("NIL");
return RESP_OK;
}
date_time ::= <"> date_day_fixed "-" date_month "-" date_year
SPACE time SPACE zone <">
util_send ("(");
for (i = 1; i <= count; i++)
{
const char *str;
int is_group = 0;
date_day ::= 1*2digit
;; Day of month
util_send ("(");
date_day_fixed ::= (SPACE digit) / 2digit
;; Fixed-format version of date_day
mu_address_sget_personal (address, i, &str);
util_send_qstring (str);
util_send (" ");
date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
"Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
mu_address_sget_route (address, i, &str);
util_send_qstring (str);
date_text ::= date_day "-" date_month "-" date_year
util_send (" ");
date_year ::= 4digit
mu_address_is_group (address, i, &is_group);
str = NULL;
if (is_group)
mu_address_sget_personal (address, i, &str);
else
mu_address_sget_local_part (address, i, &str);
time ::= 2digit ":" 2digit ":" 2digit
;; Hours minutes seconds
util_send_qstring (str);
zone ::= ("+" / "-") 4digit
;; Signed four-digit value of hhmm representing
;; hours and minutes west of Greenwich (that is,
;; (the amount that the given time differs from
;; Universal Time). Subtracting the timezone
;; from the given time will give the UT form.
;; The Universal Time zone is "+0000". */
static int
_frt_internaldate (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
const char *date;
mu_envelope_t env = NULL;
struct tm tm, *tmp = NULL;
mu_timezone tz;
char datebuf[sizeof ("13-Jul-2002 00:00:00")];
util_send (" ");
mu_message_get_envelope (frt->msg, &env);
if (mu_envelope_sget_date (env, &date) == 0
&& mu_parse_ctime_date_time (&date, &tm, &tz) == 0)
tmp = &tm;
else
{
time_t t = time (NULL);
tmp = localtime (&t);
}
mu_strftime (datebuf, sizeof (datebuf), "%d-%b-%Y %H:%M:%S", tmp);
util_send ("%s", ffc->name);
util_send (" \"%s +0000\"", datebuf);
return 0;
}
mu_address_sget_domain (address, i, &str);
util_send_qstring (str);
static int
_frt_bodystructure (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
util_send ("%s (", ffc->name);
fetch_bodystructure0 (frt->msg, 1); /* 1 means with extension data. */
util_send (")");
return RESP_OK;
}
util_send (")");
}
static int
_frt_bodystructure0 (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
util_send ("%s (", ffc->name);
fetch_bodystructure0 (frt->msg, 0);
util_send (")");
return RESP_OK;
}
/* Send parameter list for the bodystructure. */
static void
send_parameter_list (const char *buffer)
/* BODY[] */
static int
_frt_body (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
int argc = 0;
char **argv;
if (!buffer)
mu_message_t msg;
mu_stream_t stream = NULL;
size_t size = 0, lines = 0;
set_seen (ffc, frt);
if (ffc->name)
util_send ("%s", ffc->name);
else
fetch_send_section_part (ffc, "BODY[", "]");
msg = fetch_get_part (ffc, frt);
if (!msg)
{
util_send ("NIL");
return;
util_send (" \"\"");
return RESP_OK;
}
mu_message_get_stream (msg, &stream);
mu_message_size (msg, &size);
mu_message_lines (msg, &lines);
return fetch_io (stream, ffc->start, ffc->size, size + lines);
}
mu_argcv_get (buffer, " \t\r\n;=", NULL, &argc, &argv);
static int
_frt_body_text (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
mu_message_t msg;
mu_body_t body = NULL;
mu_stream_t stream = NULL;
size_t size = 0, lines = 0;
set_seen (ffc, frt);
if (ffc->name)
util_send ("%s", ffc->name);
else
fetch_send_section_part (ffc, "BODY[", "TEXT]");
msg = fetch_get_part (ffc, frt);
if (!msg)
{
util_send (" \"\"");
return RESP_OK;
}
mu_message_get_body (msg, &body);
mu_body_size (body, &size);
mu_body_lines (body, &lines);
mu_body_get_stream (body, &stream);
return fetch_io (stream, ffc->start, ffc->size, size + lines);
}
static int
_frt_size (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
size_t size = 0;
size_t lines = 0;
if (argc == 0)
util_send ("NIL");
mu_message_size (frt->msg, &size);
mu_message_lines (frt->msg, &lines);
util_send ("%s %u", ffc->name, size + lines);
return RESP_OK;
}
static int
_frt_header0 (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt,
const char *suffix)
{
mu_message_t msg;
mu_header_t header = NULL;
mu_stream_t stream = NULL;
size_t size = 0, lines = 0;
set_seen (ffc, frt);
if (ffc->name)
util_send ("%s", ffc->name);
else
fetch_send_section_part (ffc, "BODY[", suffix);
msg = fetch_get_part (ffc, frt);
if (!msg)
{
char *p;
util_send (" \"\"");
return RESP_OK;
}
mu_message_get_header (msg, &header);
mu_header_size (header, &size);
mu_header_lines (header, &lines);
mu_header_get_stream (header, &stream);
return fetch_io (stream, ffc->start, ffc->size, size + lines);
}
static int
_frt_header (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
return _frt_header0 (ffc, frt, "HEADER]");
}
static int
_frt_mime (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
return _frt_header0 (ffc, frt, "MIME]");
}
static int
_send_header_name (void *item, void *data)
{
int *pf = data;
if (*pf)
util_send (" ");
else
*pf = 1;
util_send ("\"%s\"", (char*) item);
return 0;
}
static int
count_nl (const char *str)
{
int n = 0;
for (;(str = strchr (str, '\n')); str++)
n++;
return n;
}
static int
_frt_header_fields (struct fetch_function_closure *ffc,
struct fetch_runtime_closure *frt)
{
int status;
mu_message_t msg;
mu_off_t size = 0;
size_t lines = 0;
mu_stream_t stream;
mu_header_t header;
mu_iterator_t itr;
set_seen (ffc, frt);
fetch_send_section_part (ffc, "BODY[", "HEADER.FIELDS");
if (ffc->not)
util_send (".NOT");
util_send (" (");
status = 0;
mu_list_do (ffc->headers, _send_header_name, &status);
util_send (")]");
msg = fetch_get_part (ffc, frt);
if (!msg)
{
util_send (" \"\"");
return RESP_OK;
}
/* Collect headers: */
if (mu_message_get_header (msg, &header)
|| mu_header_get_iterator (header, &itr))
{
util_send (" \"\"");
return RESP_OK;
}
status = mu_memory_stream_create (&stream, NULL, MU_STREAM_NO_CHECK);
if (status != 0)
imap4d_bye (ERR_NO_MEM);
for (mu_iterator_first (itr);
!mu_iterator_is_done (itr); mu_iterator_next (itr))
{
const char *hf;
char *hv;
const char *item;
util_send ("(");
p = argv[0];
util_send_qstring (p);
mu_iterator_current_kv (itr, (const void **)&hf, (void **)&hv);
status = mu_list_locate (ffc->headers, (void *)hf, (void**) &item) == 0;
if (ffc->not)
{
status = !status;
item = hf;
}
if (status)
{
mu_stream_sequential_printf (stream, "%s: %s\n", item, hv);
lines += 1 + count_nl (hv);
}
}
mu_stream_sequential_write (stream, "\n", 1);
lines++;
/* Output collected data */
mu_stream_size (stream, &size);
status = fetch_io (stream, ffc->start, ffc->size, size + lines);
mu_stream_destroy (&stream, NULL);
return status;
}
if (argc > 1)
static void
ffc_init (struct fetch_function_closure *ffc)
{
memset(ffc, 0, sizeof *ffc);
ffc->start = 0;
ffc->size = (size_t) -1;
}
static void
_free_ffc (void *item)
{
struct fetch_function_closure *ffc = item;
mu_list_destroy (&ffc->headers);
free (ffc);
}
static int
_do_fetch (void *item, void *data)
{
struct fetch_function_closure *ffc = item;
struct fetch_runtime_closure *frt = data;
if (frt->eltno++)
util_send (" ");
return ffc->fun (ffc, frt);
}
struct parsebuf
{
imap4d_tokbuf_t tok;
int arg;
char *token;
int isuid;
mu_list_t fnlist;
jmp_buf errjmp;
char *err_text;
};
static void
parsebuf_exit (struct parsebuf *p, char *text)
{
p->err_text = text;
longjmp (p->errjmp, 1);
}
static char *
parsebuf_peek (struct parsebuf *p)
{
return imap4d_tokbuf_getarg (p->tok, p->arg);
}
static char *
parsebuf_next (struct parsebuf *p, int req)
{
p->token = imap4d_tokbuf_getarg (p->tok, p->arg++);
if (!p->token && req)
parsebuf_exit (p, "Too few arguments");
return p->token;
}
static void
append_ffc (struct parsebuf *p, struct fetch_function_closure *ffc)
{
struct fetch_function_closure *new_ffc = malloc (sizeof (*new_ffc));
if (!new_ffc)
imap4d_bye (ERR_NO_MEM);
*new_ffc = *ffc;
mu_list_append (p->fnlist, new_ffc);
}
static void
append_simple_function (struct parsebuf *p, const char *name,
fetch_function_t fun)
{
struct fetch_function_closure ffc;
ffc_init (&ffc);
ffc.fun = fun;
ffc.name = name;
append_ffc (p, &ffc);
}
static struct fetch_macro
{
char *macro;
char *exp;
} fetch_macro_tab[] = {
{ "ALL", "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE" },
{ "FULL", "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY" },
{ "FAST", "FLAGS INTERNALDATE RFC822.SIZE" },
{ NULL }
};
static char *
find_macro (const char *name)
{
int i;
for (i = 0; fetch_macro_tab[i].macro; i++)
if (strcasecmp (fetch_macro_tab[i].macro, name) == 0)
return fetch_macro_tab[i].exp;
return NULL;
}
struct fetch_att_tab
{
char *name;
fetch_function_t fun;
};
static struct fetch_att_tab fetch_att_tab[] = {
{ "ENVELOPE", _frt_envelope },
{ "FLAGS", _frt_flags },
{ "INTERNALDATE", _frt_internaldate },
{ "UID", _frt_uid },
{ NULL }
};
static struct fetch_att_tab *
find_fetch_att_tab (char *name)
{
struct fetch_att_tab *p;
for (p = fetch_att_tab; p->name; p++)
if (strcasecmp (p->name, name) == 0)
return p;
return NULL;
}
/*
fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
"RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
"BODY" ["STRUCTURE"] / "UID" /
"BODY" section ["<" number "." nz-number ">"] /
"BODY.PEEK" section ["<" number "." nz-number ">"]
*/
/* "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] */
static void
parse_fetch_rfc822 (struct parsebuf *p)
{
struct fetch_function_closure ffc;
ffc_init (&ffc);
ffc.name = "RFC822";
parsebuf_next (p, 0);
if (p->token == NULL || p->token[0] == ')')
{
/* Equivalent to BODY[]. */
ffc.fun = _frt_body;
}
else if (p->token[0] == '.')
{
parsebuf_next (p, 1);
if (strcasecmp (p->token, "HEADER") == 0)
{
int i, space = 0;
char *lvalue = NULL;
/* RFC822.HEADER
Equivalent to BODY[HEADER]. Note that this did not result in
\Seen being set, because RFC822.HEADER response data occurs as
a result of a FETCH of RFC822.HEADER. BODY[HEADER] response
data occurs as a result of a FETCH of BODY[HEADER] (which sets
\Seen) or BODY.PEEK[HEADER] (which does not set \Seen). */
ffc.name = "RFC822.HEADER";
ffc.fun = _frt_header;
ffc.peek = 1;
parsebuf_next (p, 0);
}
else if (strcasecmp (p->token, "SIZE") == 0)
{
/* A number expressing the [RFC-2822] size of the message. */
ffc.name = "RFC822.SIZE";
ffc.fun = _frt_size;
parsebuf_next (p, 0);
}
else if (strcasecmp (p->token, "TEXT") == 0)
{
/* RFC822.TEXT
Equivalent to BODY[TEXT]. */
ffc.name = "RFC822.TEXT";
ffc.fun = _frt_body_text;
parsebuf_next (p, 0);
}
else
parsebuf_exit (p, "Syntax error after RFC822.");
}
else
parsebuf_exit (p, "Syntax error after RFC822");
append_ffc (p, &ffc);
}
util_send ("(");
for (i = 1; i < argc; i++)
static int
_header_cmp (const void *a, const void *b)
{
return strcasecmp ((char*)a, (char*)b);
}
/*
header-fld-name = astring
header-list = "(" header-fld-name *(SP header-fld-name) ")"
*/
static void
parse_header_list (struct parsebuf *p, struct fetch_function_closure *ffc)
{
if (p->token[0] != '(')
parsebuf_exit (p, "Syntax error: expected (");
mu_list_create (&ffc->headers);
mu_list_set_comparator (ffc->headers, _header_cmp);
for (parsebuf_next (p, 1); p->token[0] != ')'; parsebuf_next (p, 1))
{
if (util_isdelim (p->token))
parsebuf_exit (p, "Syntax error: unexpected delimiter");
mu_list_append (ffc->headers, p->token);
}
parsebuf_next (p, 1);
}
/*
section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list /
"TEXT"
; top-level or MESSAGE/RFC822 part
section-text = section-msgtext / "MIME"
; text other than actual body part (headers, etc.)
*/
static int
parse_section_text (struct parsebuf *p, struct fetch_function_closure *ffc,
int allow_mime)
{
if (strcasecmp (p->token, "HEADER") == 0)
{
/* "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list */
parsebuf_next (p, 1);
if (p->token[0] == '.')
{
parsebuf_next (p, 1);
if (strcasecmp (p->token, "FIELDS"))
parsebuf_exit (p, "Expected FIELDS");
ffc->fun = _frt_header_fields;
parsebuf_next (p, 1);
if (p->token[0] == '.')
{
if (lvalue)
parsebuf_next (p, 1);
if (strcasecmp (p->token, "NOT") == 0)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
lvalue = NULL;
space = 1;
}
switch (argv[i][0])
{
case ';':
continue;
case '=':
if (++i < argc)
{
char *p = argv[i];
util_send (" ");
util_unquote (&p);
util_send_qstring (p);
}
break;
default:
lvalue = argv[i];
ffc->not = 1;
parsebuf_next (p, 1);
}
else
parsebuf_exit (p, "Expected NOT");
}
if (lvalue)
parse_header_list (p, ffc);
}
else
ffc->fun = _frt_header;
}
else if (strcasecmp (p->token, "TEXT") == 0)
{
parsebuf_next (p, 1);
ffc->fun = _frt_body_text;
}
else if (allow_mime && strcasecmp (p->token, "MIME") == 0)
{
parsebuf_next (p, 1);
ffc->fun = _frt_mime;
}
else
return 1;
return 0;
}
static size_t
parsebuf_get_number (struct parsebuf *p)
{
char *cp;
unsigned n = strtoul (p->token, &cp, 10);
if (*cp)
parsebuf_exit (p, "Syntax error: expected number");
return n;
}
/*
section-part = nz-number *("." nz-number)
; body part nesting
*/
static void
parse_section_part (struct parsebuf *p, struct fetch_function_closure *ffc)
{
size_t *parts;
size_t nmax = 0;
size_t ncur = 0;
for (;;)
{
char *cp;
size_t n = parsebuf_get_number (p);
if (ncur == nmax)
{
if (nmax == 0)
{
if (space)
util_send (" ");
util_send_qstring (lvalue);
nmax = 16;
parts = calloc (nmax, sizeof (parts[0]));
}
util_send (")");
else
{
nmax *= 2;
parts = realloc (parts, nmax * sizeof (parts[0]));
}
if (!parts)
imap4d_bye (ERR_NO_MEM);
}
parts[ncur++] = n;
parsebuf_next (p, 1);
if (p->token[0] == '.'
&& (cp = parsebuf_peek (p)) && isascii (*cp) && isdigit (cp[0]))
parsebuf_next (p, 1);
else
util_send (" NIL");
util_send (")");
break;
}
mu_argcv_free (argc, argv);
ffc->section_part = parts;
ffc->nset = ncur;
}
/*
section = "[" [section-spec] "]"
section-spec = section-msgtext / (section-part ["." section-text])
*/
static int
parse_section (struct parsebuf *p, struct fetch_function_closure *ffc)
{
if (p->token[0] != '[')
return 1;
ffc_init (ffc);
ffc->name = NULL;
ffc->fun = _frt_body;
parsebuf_next (p, 1);
if (parse_section_text (p, ffc, 0))
{
if (p->token[0] == ']')
/* OK */;
else if (isascii (p->token[0]) && isdigit (p->token[0]))
{
parse_section_part (p, ffc);
if (p->token[0] == '.')
{
parsebuf_next (p, 1);
parse_section_text (p, ffc, 1);
}
}
else
parsebuf_exit (p, "Syntax error");
}
if (p->token[0] != ']')
parsebuf_exit (p, "Syntax error: missing ]");
parsebuf_next (p, 0);
return 0;
}
static void
parse_substring (struct parsebuf *p, struct fetch_function_closure *ffc)
{
if (p->token && p->token[0] == '<')
{
parsebuf_next (p, 1);
ffc->start = parsebuf_get_number (p);
parsebuf_next (p, 1);
if (p->token[0] != '.')
parsebuf_exit (p, "Syntax error: expected .");
parsebuf_next (p, 1);
ffc->size = parsebuf_get_number (p);
parsebuf_next (p, 1);
if (p->token[0] != '>')
parsebuf_exit (p, "Syntax error: expected >");
parsebuf_next (p, 0);
}
}
/* section ["<" number "." nz-number ">"] */
static int
parse_body_args (struct parsebuf *p, int peek)
{
struct fetch_function_closure ffc;
if (parse_section (p, &ffc) == 0)
{
parse_substring (p, &ffc);
ffc.peek = peek;
append_ffc (p, &ffc);
return 0;
}
return 1;
}
static void
parse_body_peek (struct parsebuf *p)
{
parsebuf_next (p, 1);
if (strcasecmp (p->token, "PEEK") == 0)
{
parsebuf_next (p, 1);
if (parse_body_args (p, 1))
parsebuf_exit (p, "Syntax error");
}
else
parsebuf_exit (p, "Syntax error: expected PEEK");
}
/* "BODY" ["STRUCTURE"] /
"BODY" section ["<" number "." nz-number ">"] /
"BODY.PEEK" section ["<" number "." nz-number ">"] */
static void
parse_fetch_body (struct parsebuf *p)
{
if (parsebuf_next (p, 0) == NULL || p->token[0] == ')')
append_simple_function (p, "BODY", _frt_bodystructure0);
else if (p->token[0] == '.')
parse_body_peek (p);
else if (strcasecmp (p->token, "STRUCTURE") == 0)
{
/* For compatibility with previous versions */
append_simple_function (p, "BODYSTRUCTURE", _frt_bodystructure);
parsebuf_next (p, 0);
}
else if (parse_body_args (p, 0))
parsebuf_exit (p, "Syntax error");
}
static int
parse_fetch_att (struct parsebuf *p)
{
struct fetch_att_tab *ent;
ent = find_fetch_att_tab (p->token);
if (ent)
{
if (!(ent->fun == _frt_uid && p->isuid))
append_simple_function (p, ent->name, ent->fun);
parsebuf_next (p, 0);
}
else if (strcasecmp (p->token, "RFC822") == 0)
parse_fetch_rfc822 (p);
else if (strcasecmp (p->token, "BODY") == 0)
parse_fetch_body (p);
else if (strcasecmp (p->token, "BODYSTRUCTURE") == 0)
{
append_simple_function (p, "BODYSTRUCTURE", _frt_bodystructure);
parsebuf_next (p, 0);
}
else
return 1;
return 0;
}
/* fetch-att *(SP fetch-att) */
static void
parse_fetch_att_list (struct parsebuf *p)
{
while (p->token && parse_fetch_att (p) == 0)
;
}
/* "ALL" / "FULL" / "FAST" / fetch-att / "(" */
static void
parse_macro (struct parsebuf *p)
{
char *exp;
parsebuf_next (p, 1);
if (p->token[0] == '(')
{
parsebuf_next (p, 1);
parse_fetch_att_list (p);
if (p->token[0] != ')')
parsebuf_exit (p, "Missing closing parenthesis");
}
else if ((exp = find_macro (p->token)))
{
imap4d_tokbuf_t save_tok = p->tok;
int save_arg = p->arg;
p->tok = imap4d_tokbuf_from_string (exp);
p->arg = 0;
parsebuf_next (p, 1);
parse_fetch_att_list (p);
imap4d_tokbuf_destroy (&p->tok);
p->arg = save_arg;
p->tok = save_tok;
if (parsebuf_peek (p))
parsebuf_exit (p, "Too many arguments");
}
else
{
parse_fetch_att (p);
if (p->token)
parsebuf_exit (p, "Too many arguments");
}
}
/* Where the real implementation is. It is here since UID command also
calls FETCH. */
int
imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
{
int rc = RESP_OK;
char *msgset;
size_t *set = NULL;
int n = 0;
int i;
int status;
struct fetch_runtime_closure frc;
struct parsebuf pb;
if (imap4d_tokbuf_argc (tok) - (IMAP4_ARG_1 + isuid) < 2)
{
*err_text = "Invalid arguments";
return 1;
}
pb.tok = tok;
pb.arg = IMAP4_ARG_1 + isuid;
pb.isuid = isuid;
pb.err_text = "Syntax error";
mu_list_create (&pb.fnlist);
mu_list_set_destroy_item (pb.fnlist, _free_ffc);
if (setjmp (pb.errjmp))
{
*err_text = pb.err_text;
mu_list_destroy (&pb.fnlist);
return RESP_BAD;
}
msgset = parsebuf_next (&pb, 1);
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, isuid);
if (status != 0)
{
*err_text = "Bogus number set";
return RESP_BAD;
}
/* Compile the expression */
/* Server implementations MUST implicitly
include the UID message data item as part of any FETCH
response caused by a UID command, regardless of whether
a UID was specified as a message data item to the FETCH. */
if (isuid)
append_simple_function (&pb, "UID", _frt_uid);
parse_macro (&pb);
/* Prepare status code. It will be replaced if an error occurs in the
loop below */
frc.err_text = "Completed";
for (i = 0; i < n && rc == RESP_OK; i++)
{
frc.msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
if (frc.msgno && mu_mailbox_get_message (mbox, frc.msgno, &frc.msg) == 0)
{
util_send ("* %lu FETCH (", (unsigned long) frc.msgno);
frc.eltno = 0;
rc = mu_list_do (pb.fnlist, _do_fetch, &frc);
util_send (")\r\n");
}
}
mu_list_destroy (&pb.fnlist);
free (set);
return rc;
}
/*
6.4.5. FETCH Command
Arguments: message set
message data item names
Responses: untagged responses: FETCH
Result: OK - fetch completed
NO - fetch error: can't fetch that data
BAD - command unknown or arguments invalid
*/
/* The FETCH command retrieves data associated with a message in the
mailbox. The data items to be fetched can be either a single atom
or a parenthesized list. */
int
imap4d_fetch (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
int rc;
char *err_text = "Completed";
rc = imap4d_fetch0 (tok, 0, &err_text);
return util_finish (command, rc, "%s", err_text);
}
......
......@@ -19,13 +19,12 @@
#include "imap4d.h"
int
imap4d_idle (struct imap4d_command *command, char *arg)
imap4d_idle (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp;
time_t start;
if (util_getword (arg, &sp))
return util_finish (command, RESP_BAD, "Too many args");
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
if (util_wait_input (0) == -1)
return util_finish (command, RESP_NO, "Cannot idle");
......@@ -38,15 +37,10 @@ imap4d_idle (struct imap4d_command *command, char *arg)
{
if (util_wait_input (5))
{
int rc;
char *p;
char *cmd = imap4d_readline ();
p = util_getword (cmd, &sp);
rc = strcasecmp (p, "done") == 0;
free (cmd);
if (rc)
imap4d_readline (tok);
if (imap4d_tokbuf_argc (tok) == 1
&& strcasecmp (imap4d_tokbuf_getarg (tok, IMAP4_ARG_TAG),
"done") == 0)
break;
}
else if (time (NULL) - start > idle_timeout)
......
......@@ -370,6 +370,7 @@ get_client_address (int fd, struct sockaddr_in *pcs)
static int
imap4d_mainloop (int fd, FILE *infile, FILE *outfile)
{
imap4d_tokbuf_t tokp;
char *text;
int debug_mode = isatty (fd);
static int sigtab[] = { SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGSTOP, SIGPIPE,
......@@ -400,14 +401,14 @@ imap4d_mainloop (int fd, FILE *infile, FILE *outfile)
util_out ((state == STATE_AUTH) ? RESP_PREAUTH : RESP_OK, "%s", text);
util_flush_output ();
tokp = imap4d_tokbuf_init ();
while (1)
{
char *cmd = imap4d_readline ();
imap4d_readline (tokp);
/* check for updates */
imap4d_sync ();
util_do_command (cmd);
util_do_command (tokp);
imap4d_sync ();
free (cmd);
util_flush_output ();
}
......
......@@ -41,6 +41,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <setjmp.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <syslog.h>
......@@ -108,10 +109,12 @@
extern "C" {
#endif
typedef struct imap4d_tokbuf *imap4d_tokbuf_t;
struct imap4d_command
{
const char *name;
int (*func) (struct imap4d_command *, char *);
int (*func) (struct imap4d_command *, imap4d_tokbuf_t);
int states;
int failure;
int success;
......@@ -191,48 +194,63 @@ extern int imap4d_transcript;
#ifndef HAVE_STRTOK_R
extern char *strtok_r (char *s, const char *delim, char **save_ptr);
#endif
/* Input functions */
imap4d_tokbuf_t imap4d_tokbuf_init (void);
void imap4d_tokbuf_destroy (imap4d_tokbuf_t *tok);
int imap4d_tokbuf_argc (imap4d_tokbuf_t tok);
char *imap4d_tokbuf_getarg (imap4d_tokbuf_t tok, int n);
void imap4d_readline (imap4d_tokbuf_t tok);
struct imap4d_tokbuf *imap4d_tokbuf_from_string (char *str);
int imap4d_getline (char **pbuf, size_t *psize, size_t *pnbytes);
#define IMAP4_ARG_TAG 0
#define IMAP4_ARG_COMMAND 1
#define IMAP4_ARG_1 2
#define IMAP4_ARG_2 3
#define IMAP4_ARG_3 4
#define IMAP4_ARG_4 5
/* Imap4 commands */
extern int imap4d_append (struct imap4d_command *, char *);
extern int imap4d_append0 (mu_mailbox_t mbox, int flags, char *text);
extern int imap4d_authenticate (struct imap4d_command *, char *);
/* Imap4 commands */
extern int imap4d_append (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_authenticate (struct imap4d_command *, imap4d_tokbuf_t);
extern void imap4d_auth_capability (void);
extern int imap4d_capability (struct imap4d_command *, char *);
extern int imap4d_check (struct imap4d_command *, char *);
extern int imap4d_close (struct imap4d_command *, char *);
extern int imap4d_copy (struct imap4d_command *, char *);
extern int imap4d_copy0 (char *, int, char *, size_t);
extern int imap4d_create (struct imap4d_command *, char *);
extern int imap4d_delete (struct imap4d_command *, char *);
extern int imap4d_examine (struct imap4d_command *, char *);
extern int imap4d_expunge (struct imap4d_command *, char *);
extern int imap4d_fetch (struct imap4d_command *, char *);
extern int imap4d_fetch0 (char *, int, char *, size_t);
extern int imap4d_list (struct imap4d_command *, char *);
extern int imap4d_lsub (struct imap4d_command *, char *);
extern int imap4d_login (struct imap4d_command *, char *);
extern int imap4d_logout (struct imap4d_command *, char *);
extern int imap4d_noop (struct imap4d_command *, char *);
extern int imap4d_rename (struct imap4d_command *, char *);
extern int imap4d_capability (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_check (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_close (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_copy (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_copy0 (imap4d_tokbuf_t, int isuid, char **err_text);
extern int imap4d_create (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_delete (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_examine (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_expunge (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_fetch (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text);
extern int imap4d_list (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_lsub (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_login (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_logout (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_noop (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_rename (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_preauth_setup (int fd);
extern int imap4d_search (struct imap4d_command *, char *);
extern int imap4d_search0 (char *arg, int isuid, char *replybuf, size_t replysize);
extern int imap4d_select (struct imap4d_command *, char *);
extern int imap4d_search (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_search0 (imap4d_tokbuf_t, int isuid, char **repyptr);
extern int imap4d_select (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_select0 (struct imap4d_command *, char *, int);
extern int imap4d_select_status (void);
#ifdef WITH_TLS
extern int imap4d_starttls (struct imap4d_command *, char *);
extern int imap4d_starttls (struct imap4d_command *, imap4d_tokbuf_t);
extern void starttls_init (void);
#endif /* WITH_TLS */
extern int imap4d_status (struct imap4d_command *, char *);
extern int imap4d_store (struct imap4d_command *, char *);
extern int imap4d_store0 (char *, int, char *, size_t);
extern int imap4d_subscribe (struct imap4d_command *, char *);
extern int imap4d_uid (struct imap4d_command *, char *);
extern int imap4d_unsubscribe (struct imap4d_command *, char *);
extern int imap4d_namespace (struct imap4d_command *, char *);
extern int imap4d_version (struct imap4d_command *, char *);
extern int imap4d_idle (struct imap4d_command *, char *);
extern int imap4d_status (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_store (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_store0 (imap4d_tokbuf_t, int, char **);
extern int imap4d_subscribe (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_uid (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_unsubscribe (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_namespace (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_version (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_idle (struct imap4d_command *, imap4d_tokbuf_t);
extern int imap4d_check_home_dir (const char *dir, uid_t uid, gid_t gid);
......@@ -274,25 +292,17 @@ extern int util_start (char *);
extern int util_finish (struct imap4d_command *, int, const char *, ...)
MU_PRINTFLIKE(3,4);
extern int util_getstate (void);
extern int util_do_command (char *);
extern char *imap4d_readline (void);
extern char *imap4d_readline_ex (void);
extern char *util_getword (char *, char **);
extern char *util_getitem (char *, const char *, char **);
extern int util_token (char *, size_t, char **);
extern void util_unquote (char **);
extern int util_do_command (imap4d_tokbuf_t);
extern char *util_tilde_expansion (const char *, const char *);
extern char *util_getfullpath (char *, const char *);
extern int util_msgset (char *, size_t **, int *, int);
extern int util_upper (char *);
extern struct imap4d_command *util_getcommand (char *,
struct imap4d_command []);
extern int util_parse_internal_date0 (char *date, time_t *timep, char **endp);
extern int util_parse_internal_date (char *date, time_t *timep);
extern int util_parse_822_date (const char *date, time_t *timep);
extern int util_parse_ctime_date (const char *date, time_t *timep);
extern char *util_strcasestr (const char *haystack, const char *needle);
extern int util_parse_attributes (char *items, char **save, int *flags);
extern int util_base64_encode (const unsigned char *input,
size_t input_len,
......@@ -330,6 +340,7 @@ void util_bye (void);
void util_atexit (void (*fp) (void));
void util_chdir (const char *homedir);
int is_atom (const char *s);
int util_isdelim (const char *str);
#ifdef WITH_TLS
int imap4d_init_tls_server (void);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2002, 2005, 2006,
2007 Free Software Foundation, Inc.
2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -106,6 +106,19 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
}
/*
6.3.8. LIST Command
Arguments: reference name
mailbox name with possible wildcards
Responses: untagged responses: LIST
Result: OK - list completed
NO - list failure: can't list that reference or name
BAD - command unknown or arguments invalid
*/
/*
1- IMAP4 insists: the reference argument present in the
interpreted form SHOULD prefix the interpreted form. It SHOULD
also be in the same form as the reference name argument. This
......@@ -126,21 +139,17 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
but it does not match the hierarchy delimiter. */
int
imap4d_list (struct imap4d_command *command, char *arg)
imap4d_list (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
char *ref;
char *wcard;
const char *delim = "/";
ref = util_getword (arg, &sp);
wcard = util_getword (NULL, &sp);
if (!ref || !wcard)
return util_finish (command, RESP_BAD, "Too few arguments");
/* Remove the double quotes. */
util_unquote (&ref);
util_unquote (&wcard);
if (imap4d_tokbuf_argc (tok) != 4)
return util_finish (command, RESP_BAD, "Invalid arguments");
ref = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
wcard = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
/* If wildcard is empty, it is a special case: we have to
return the hierarchy. */
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2002, 2006,
2007 Free Software Foundation, Inc.
2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,26 +19,32 @@
#include "imap4d.h"
/*
6.2.2. LOGIN Command
Arguments: user name
password
Responses: no specific responses for this command
Result: OK - login completed, now in authenticated state
NO - login failure: user name or password rejected
BAD - command unknown or arguments invalid
*/
int
imap4d_login (struct imap4d_command *command, char *arg)
imap4d_login (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL, *username, *pass;
char *username, *pass;
int rc;
if (login_disabled || tls_required)
return util_finish (command, RESP_NO, "Command disabled");
username = util_getword (arg, &sp);
pass = util_getword (NULL, &sp);
/* Remove the double quotes. */
util_unquote (&username);
util_unquote (&pass);
if (username == NULL || *username == '\0' || pass == NULL)
return util_finish (command, RESP_NO, "Too few args");
else if (util_getword (NULL, &sp))
return util_finish (command, RESP_NO, "Too many args");
if (imap4d_tokbuf_argc (tok) != 4)
return util_finish (command, RESP_BAD, "Invalid arguments");
username = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
pass = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
auth_data = mu_get_auth_by_name (username);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,15 +19,21 @@
#include "imap4d.h"
/*
* This needs to properly close the mailbox
*/
6.1.3. LOGOUT Command
Arguments: none
Responses: REQUIRED untagged response: BYE
Result: OK - logout completed
BAD - command unknown or arguments invalid
*/
int
imap4d_logout (struct imap4d_command *command, char *arg)
imap4d_logout (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
if (util_getword (arg, &sp))
return util_finish (command, RESP_BAD, "Too many args");
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
imap4d_bye0 (OK, command);
return 0;
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,13 +19,20 @@
#include "imap4d.h"
/*
*
*/
6.3.9. LSUB Command
Arguments: reference name
mailbox name with possible wildcards
Responses: untagged responses: LSUB
Result: OK - lsub completed
NO - lsub failure: can't list that reference or name
BAD - command unknown or arguments invalid
*/
int
imap4d_lsub (struct imap4d_command *command, char *arg)
imap4d_lsub (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp;
char *ref;
char *wcard;
char *file = NULL;
......@@ -33,14 +40,11 @@ imap4d_lsub (struct imap4d_command *command, char *arg)
const char *delim = "/";
FILE *fp;
ref = util_getword (arg, &sp);
wcard = util_getword (NULL, &sp);
if (!ref || !wcard)
return util_finish (command, RESP_BAD, "Too few arguments");
/* Remove the double quotes. */
util_unquote (&ref);
util_unquote (&wcard);
if (imap4d_tokbuf_argc (tok) != 4)
return util_finish (command, RESP_BAD, "Invalid arguments");
ref = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
wcard = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
asprintf (&pattern, "%s%s", ref, wcard);
if (!pattern)
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -89,8 +89,9 @@ print_namespace (int n)
util_send ("(");
for (i = 0; i < namespace[n].subdir_c; i++)
{
util_send ("(\"%s\" \"/\")",
printable_pathname (namespace[n].subdir_v[i]));
char *dir = printable_pathname (namespace[n].subdir_v[i]);
char *suf = (dir[0] && dir[strlen (dir) - 1] != '/') ? "/" : "";
util_send ("(\"%s%s\" \"/\")", dir, suf);
}
util_send (")");
}
......@@ -114,11 +115,27 @@ namespace_enumerate_all (nsfp_t f, void *closure)
|| namespace_enumerate (NS_SHARED, f, closure);
}
/*
5. NAMESPACE Command
Arguments: none
Response: an untagged NAMESPACE response that contains the prefix
and hierarchy delimiter to the server's Personal
Namespace(s), Other Users' Namespace(s), and Shared
Namespace(s) that the server wishes to expose. The
response will contain a NIL for any namespace class
that is not available. Namespace_Response_Extensions
MAY be included in the response.
Namespace_Response_Extensions which are not on the IETF
standards track, MUST be prefixed with an "X-".
*/
int
imap4d_namespace (struct imap4d_command *command, char *arg)
imap4d_namespace (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
if (*arg)
return util_finish (command, RESP_BAD, "Too many arguments");
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
util_send ("* NAMESPACE ");
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,13 +19,10 @@
#include "imap4d.h"
int
imap4d_noop (struct imap4d_command *command, char *arg)
imap4d_noop (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
if (util_getword (arg, &sp))
return util_finish (command, RESP_BAD, "Too many args");
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
imap4d_sync ();
return util_finish (command, RESP_OK, "Completed");
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,30 +19,37 @@
#include "imap4d.h"
/*
6.3.5. RENAME Command
Arguments: existing mailbox name
new mailbox name
Responses: no specific responses for this command
Result: OK - rename completed
NO - rename failure: can't rename mailbox with that name,
can't rename to mailbox with that name
BAD - command unknown or arguments invalid
*/
/*
FIXME: Renaming a mailbox we must change the UIDVALIDITY
of the mailbox. */
int
imap4d_rename (struct imap4d_command *command, char *arg)
imap4d_rename (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *oldname;
char *newname;
char *sp = NULL;
int rc = RESP_OK;
const char *msg = "Completed";
struct stat newst;
const char *delim = "/";
oldname = util_getword (arg, &sp);
newname = util_getword (NULL, &sp);
if (!newname || !oldname)
return util_finish (command, RESP_BAD, "Too few arguments");
util_unquote (&newname);
util_unquote (&oldname);
if (*newname == '\0' || *oldname == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
if (imap4d_tokbuf_argc (tok) != 4)
return util_finish (command, RESP_BAD, "Invalid arguments");
oldname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
newname = imap4d_tokbuf_getarg (tok, IMAP4_ARG_2);
if (strcasecmp (newname, "INBOX") == 0)
return util_finish (command, RESP_NO, "Name Inbox is reservered");
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2002, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2002, 2005, 2007,
2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -223,11 +224,9 @@ struct mem_chain
/* Parse buffer structure */
struct parsebuf
{
char *token; /* Current token. Either points to tokbuf
or is allocated within `alloc' chain */
char tokbuf[MAXTOKEN+1]; /* Token buffer for short tokens */
char *arg; /* Rest of command line to be parsed */
imap4d_tokbuf_t tok; /* Token buffer */
int arg; /* Argument number */
char *token; /* Current token */
int isuid; /* UIDs instead of msgnos are required */
char *err_mesg; /* Error message if a parse error occured */
struct mem_chain *alloc; /* Chain of objects allocated during parsing */
......@@ -249,30 +248,45 @@ static int parse_gettoken (struct parsebuf *pb, int req);
static int search_run (struct parsebuf *pb);
static void do_search (struct parsebuf *pb);
/*
6.4.4. SEARCH Command
Arguments: OPTIONAL [CHARSET] specification
searching criteria (one or more)
Responses: REQUIRED untagged response: SEARCH
Result: OK - search completed
NO - search error: can't search that [CHARSET] or
criteria
BAD - command unknown or arguments invalid
*/
int
imap4d_search (struct imap4d_command *command, char *arg)
imap4d_search (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
int rc;
char buffer[64];
char *err_text= "";
rc = imap4d_search0 (arg, 0, buffer, sizeof buffer);
return util_finish (command, rc, "%s", buffer);
rc = imap4d_search0 (tok, 0, &err_text);
return util_finish (command, rc, "%s", err_text);
}
int
imap4d_search0 (char *arg, int isuid, char *replybuf, size_t replysize)
imap4d_search0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
{
struct parsebuf parsebuf;
memset (&parsebuf, 0, sizeof(parsebuf));
parsebuf.arg = arg;
parsebuf.tok = tok;
parsebuf.arg = IMAP4_ARG_1 + !!isuid;
parsebuf.err_mesg = NULL;
parsebuf.alloc = NULL;
parsebuf.isuid = isuid;
if (!parse_gettoken (&parsebuf, 0))
{
snprintf (replybuf, replysize, "Too few args");
*err_text = "Too few args";
return RESP_BAD;
}
......@@ -280,20 +294,20 @@ imap4d_search0 (char *arg, int isuid, char *replybuf, size_t replysize)
{
if (!parse_gettoken (&parsebuf, 0))
{
snprintf (replybuf, replysize, "Too few args");
*err_text = "Too few args";
return RESP_BAD;
}
/* Currently only ASCII is supported */
if (strcasecmp (parsebuf.token, "US-ASCII"))
{
snprintf (replybuf, replysize, "Charset not supported");
*err_text = "Charset not supported";
return RESP_NO;
}
if (!parse_gettoken (&parsebuf, 0))
{
snprintf (replybuf, replysize, "Too few args");
*err_text = "Too few args";
return RESP_BAD;
}
......@@ -304,16 +318,14 @@ imap4d_search0 (char *arg, int isuid, char *replybuf, size_t replysize)
if (!parsebuf.tree)
{
parse_free_mem (&parsebuf);
snprintf (replybuf, replysize, "%s (near %s)",
parsebuf.err_mesg,
*parsebuf.arg ? parsebuf.arg : "end");
*err_text = "Parse error";
return RESP_BAD;
}
if (parsebuf.token[0] != 0)
if (parsebuf.token)
{
parse_free_mem (&parsebuf);
snprintf (replybuf, replysize, "Junk at the end of statement");
*err_text = "Junk at the end of statement";
return RESP_BAD;
}
......@@ -322,7 +334,7 @@ imap4d_search0 (char *arg, int isuid, char *replybuf, size_t replysize)
parse_free_mem (&parsebuf);
snprintf (replybuf, replysize, "Completed");
*err_text = "Completed";
return RESP_OK;
}
......@@ -359,41 +371,13 @@ do_search (struct parsebuf *pb)
int
parse_gettoken (struct parsebuf *pb, int req)
{
int rc;
char *s;
pb->token = pb->tokbuf;
while (*pb->arg && *pb->arg == ' ')
pb->arg++;
switch (*pb->arg)
if (req && pb->arg >= imap4d_tokbuf_argc (pb->tok))
{
case '(':
case ')':
pb->token[0] = *pb->arg++;
pb->token[1] = 0;
rc = 1;
break;
case '"':
s = ++pb->arg;
while (*pb->arg && *pb->arg != '"')
pb->arg++;
rc = pb->arg - s;
if (*pb->arg)
pb->arg++;
if (rc >= sizeof(pb->tokbuf))
pb->token = parse_alloc (pb, rc+1);
memcpy (pb->token, s, rc);
pb->token[rc] = 0;
break;
default:
rc = util_token (pb->token, sizeof(pb->tokbuf), &pb->arg);
break;
pb->err_mesg = "Unexpected end of statement";
return 0;
}
if (req && rc == 0)
pb->err_mesg = "Unexpected end of statement";
return rc;
pb->token = imap4d_tokbuf_getarg (pb->tok, pb->arg++);
return 1;
}
/* Memory handling */
......@@ -468,7 +452,7 @@ parse_search_key_list (struct parsebuf *pb)
{
struct search_node *leftarg = NULL;
while (pb->token[0] && pb->token[0] != ')')
while (pb->token && pb->token[0] != ')')
{
struct search_node *rightarg = parse_search_key (pb);
if (!rightarg)
......@@ -563,8 +547,8 @@ parse_equiv_key (struct parsebuf *pb)
{
struct search_node *node;
struct cond_equiv *condp;
char *arg;
char *save_arg;
int save_arg;
imap4d_tokbuf_t save_tok;
for (condp = equiv_list; condp->name && strcasecmp (condp->name, pb->token);
condp++)
......@@ -574,8 +558,9 @@ parse_equiv_key (struct parsebuf *pb)
return parse_simple_key (pb);
save_arg = pb->arg;
arg = parse_strdup (pb, condp->equiv);
pb->arg = arg;
save_tok = pb->tok;
pb->tok = imap4d_tokbuf_from_string (condp->equiv);
pb->arg = 0;
parse_gettoken (pb, 0);
......@@ -584,11 +569,13 @@ parse_equiv_key (struct parsebuf *pb)
{
/* shouldn't happen? */
mu_diag_output (MU_DIAG_CRIT, _("%s:%d: INTERNAL ERROR (please report)"),
__FILE__, __LINE__);
__FILE__, __LINE__);
abort ();
}
imap4d_tokbuf_destroy (&pb->tok);
pb->arg = save_arg;
pb->tok = save_tok;
parse_gettoken (pb, 0);
return node;
}
......@@ -658,7 +645,7 @@ parse_simple_key (struct parsebuf *pb)
return NULL;
}
if (!pb->token[0])
if (!pb->token)
{
pb->err_mesg = "Not enough arguments for criterion";
return NULL;
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2003, 2005, 2006,
2007 Free Software Foundation, Inc.
2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -24,25 +24,22 @@ static int select_flags;
/* select ::= "SELECT" SPACE mailbox */
int
imap4d_select (struct imap4d_command *command, char *arg)
imap4d_select (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
return imap4d_select0 (command, arg, MU_STREAM_RDWR);
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
return imap4d_select0 (command, imap4d_tokbuf_getarg (tok, IMAP4_ARG_1),
MU_STREAM_RDWR);
}
/* This code is share with EXAMINE. */
int
imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
imap4d_select0 (struct imap4d_command *command, char *mailbox_name, int flags)
{
char *mailbox_name, *sp = NULL;
int status;
/* FIXME: Check state. */
mailbox_name = util_getword (arg, &sp);
util_unquote (&mailbox_name);
if (mailbox_name == NULL || *mailbox_name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
/* Even if a mailbox is selected, a SELECT EXAMINE or LOGOUT
command MAY be issued without previously issuing a CLOSE command.
The SELECT, EXAMINE, and LOGUT commands implictly close the
......
......@@ -23,17 +23,26 @@
static int tls_available;
static int tls_done;
/*
6.2.1. STARTTLS Command
Arguments: none
Responses: no specific response for this command
Result: OK - starttls completed, begin TLS negotiation
BAD - command unknown or arguments invalid
*/
int
imap4d_starttls (struct imap4d_command *command, char *arg)
imap4d_starttls (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
int status;
char *sp = NULL;
if (!tls_available || tls_done)
return util_finish (command, RESP_BAD, "Invalid command");
if (util_getword (arg, &sp))
return util_finish (command, RESP_BAD, "Too many args");
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
util_atexit (mu_deinit_tls_libs);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -52,11 +52,22 @@ status_get_handler (const char *name)
return p->fun;
return NULL;
}
/*
6.3.10. STATUS Command
Arguments: mailbox name
status data item names
Responses: untagged responses: STATUS
Result: OK - status completed
NO - status failure: no status for that name
BAD - command unknown or arguments invalid
*/
int
imap4d_status (struct imap4d_command *command, char *arg)
imap4d_status (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
char *name;
char *mailbox_name;
const char *delim = "/";
......@@ -64,11 +75,12 @@ imap4d_status (struct imap4d_command *command, char *arg)
int status;
int count = 0;
char *err_msg = NULL;
int argc = imap4d_tokbuf_argc (tok);
if (argc < 4)
return util_finish (command, RESP_BAD, "Invalid arguments");
name = util_getword (arg, &sp);
util_unquote (&name);
if (!name || *name == '\0' || !sp || *sp == '\0')
return util_finish (command, RESP_BAD, "Too few args");
name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
mailbox_name = namespace_getfullpath (name, delim);
......@@ -85,20 +97,22 @@ imap4d_status (struct imap4d_command *command, char *arg)
status = mu_mailbox_open (smbox, MU_STREAM_READ);
if (status == 0)
{
char item[32];
item[0] = '\0';
if (*sp == '(')
sp++;
else
*sp = 0;
int i = IMAP4_ARG_2;
char *item = imap4d_tokbuf_getarg (tok, i);
/* Get the status item names. */
while (*sp && *sp != ')')
if (item[0] == '(')
{
if (imap4d_tokbuf_getarg (tok, argc - 1)[0] != ')')
return util_finish (command, RESP_BAD, "Invalid arguments");
argc--;
i++;
}
for (; i < argc; i++)
{
status_funcp fun;
util_token (item, sizeof (item), &sp);
item = imap4d_tokbuf_getarg (tok, i);
fun = status_get_handler (item);
if (!fun)
{
......@@ -112,6 +126,8 @@ imap4d_status (struct imap4d_command *command, char *arg)
if (!fun (smbox))
util_send (" ");
}
if (count > 0)
util_send (")\r\n");
mu_mailbox_close (smbox);
......@@ -193,5 +209,4 @@ status_unseen (mu_mailbox_t smbox)
}
util_send ("UNSEEN %d", unseen);
return 0;
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2005, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2005, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -23,134 +23,155 @@
*/
int
imap4d_store (struct imap4d_command *command, char *arg)
imap4d_store (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
int rc;
char buffer[64];
char *err_text;
rc = imap4d_store0 (tok, 0, &err_text);
return util_finish (command, rc, "%s", err_text);
}
struct parsebuf
{
char *token;
imap4d_tokbuf_t tok;
int arg;
jmp_buf errjmp;
char *err_text;
};
static void
parsebuf_exit (struct parsebuf *p, char *text)
{
p->err_text = text;
longjmp (p->errjmp, 1);
}
rc = imap4d_store0 (arg, 0, buffer, sizeof buffer);
return util_finish (command, rc, "%s", buffer);
static char *
parsebuf_next (struct parsebuf *p, int req)
{
p->token = imap4d_tokbuf_getarg (p->tok, p->arg++);
if (!p->token && req)
parsebuf_exit (p, "Too few arguments");
return p->token;
}
int
imap4d_store0 (char *arg, int isuid, char *resp, size_t resplen)
imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext)
{
char *msgset;
char *data;
char *sp = NULL;
int status;
int ack = 0;
int ack = 1;
size_t i;
int n = 0;
size_t *set = NULL;
enum value_type { STORE_SET, STORE_ADD, STORE_UNSET } how;
msgset = util_getword (arg, &sp);
data = util_getword (NULL, &sp);
if (!msgset || !data || !sp || *sp == '\0')
struct parsebuf pb;
char *data;
int type = 0;
pb.tok = tok;
pb.arg = IMAP4_ARG_1;
pb.err_text = NULL;
if (setjmp (pb.errjmp))
{
snprintf (resp, resplen, "Too few args");
*ptext = pb.err_text;
free (set);
return RESP_BAD;
}
msgset = parsebuf_next (&pb, 1);
data = parsebuf_next (&pb, 1);
/* The parsing of the data-item is a little slugish. */
if (strcasecmp (data, "FLAGS") == 0)
{
ack = 1;
how = STORE_SET;
}
else if (strcasecmp (data, "+FLAGS") == 0)
if (*data == '+')
{
ack = 1;
how = STORE_ADD;
data++;
}
else if (strcasecmp (data, "-FLAGS") == 0)
else if (*data == '-')
{
ack = 1;
how = STORE_UNSET;
}
else if (strcasecmp (data, "FLAGS.SILENT") == 0)
{
ack = 0;
how = STORE_SET;
}
else if (strcasecmp (data, "+FLAGS.SILENT") == 0)
{
ack = 0;
how = STORE_ADD;
}
else if (strcasecmp (data, "-FLAGS.SILENT") == 0)
{
ack = 0;
how = STORE_UNSET;
data++;
}
else
how = STORE_SET;
if (strcasecmp (data, "FLAGS"))
parsebuf_exit (&pb, "Bogus data item");
data = parsebuf_next (&pb, 1);
if (*data == '.')
{
snprintf (resp, resplen, "Bogus data item");
return RESP_BAD;
data = parsebuf_next (&pb, 1);
if (strcasecmp (data, "SILENT") == 0)
{
ack = 0;
parsebuf_next (&pb, 1);
}
else
parsebuf_exit (&pb, "Bogus data suffix");
}
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, isuid);
if (status != 0)
parsebuf_exit (&pb, "Bogus number set");
if (pb.token[0] != '(')
parsebuf_exit (&pb, "Syntax error");
parsebuf_next (&pb, 1);
do
{
snprintf (resp, resplen, "Bogus number set");
return RESP_BAD;
int t;
if (!util_attribute_to_type (pb.token, &t))
type |= t;
}
while (parsebuf_next (&pb, 1) && pb.token[0] != ')');
for (i = 0; i < n; i++)
{
mu_message_t msg = NULL;
mu_attribute_t attr = NULL;
char *items = strdup (sp); /* Don't use the orignal list. */
int first = 1;
size_t msgno;
char *p = items;
msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
size_t msgno = isuid ? uid_to_msgno (set[i]) : set[i];
if (msgno)
{
mu_mailbox_get_message (mbox, msgno, &msg);
mu_message_get_attribute (msg, &attr);
/* Get the fetch command names. */
while (*items && *items != ')')
switch (how)
{
int type = 0;
char item[64] = "";
util_token (item, sizeof (item), &items);
if (!util_attribute_to_type (item, &type))
{
if (how == STORE_ADD )
mu_attribute_set_flags (attr, type);
else if (how == STORE_UNSET )
mu_attribute_unset_flags (attr, type);
else if (how == STORE_SET )
{
if (first)
{
mu_attribute_set_flags (attr, 0);
first = 0;
}
mu_attribute_set_flags (attr, type);
}
mu_attribute_set_flags (attr, MU_ATTRIBUTE_MODIFIED);
}
case STORE_ADD:
mu_attribute_set_flags (attr, type);
break;
case STORE_UNSET:
mu_attribute_unset_flags (attr, type);
break;
case STORE_SET:
mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */
mu_attribute_set_flags (attr, type);
}
}
if (ack)
{
util_send ("* %d FETCH (", msgno);
fetch_flags0 ("FLAGS", msg, isuid);
util_send (")\r\n");
if (isuid)
util_send ("UID %lu ", (unsigned long) msgno);
util_send ("FLAGS (");
util_print_flags (attr);
util_send ("))\r\n");
}
free (p);
/* Update the flags of uid table. */
imap4d_sync_flags (set[i]);
}
free (set);
snprintf (resp, resplen, "Completed");
*ptext = "Completed";
return RESP_OK;
}
......
......@@ -19,21 +19,27 @@
#include "imap4d.h"
/*
FIXME: We need to lock the file to prevent simultaneous access.
*/
6.3.6. SUBSCRIBE Command
Arguments: mailbox
Responses: no specific responses for this command
Result: OK - subscribe completed
NO - subscribe failure: can't subscribe to that name
BAD - command unknown or arguments invalid
*/
int
imap4d_subscribe (struct imap4d_command *command, char *arg)
imap4d_subscribe (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
char *name;
char *file;
FILE *fp;
name = util_getword (arg, &sp);
util_unquote (&name);
if (!name || *name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
asprintf (&file, "%s/.mailboxlist", homedir);
fp = fopen (file, "a");
......
# -*- tcl -*-
# This file is part of Mailutils testsuite.
# Copyright (C) 2002, 2007 Free Software Foundation
# Copyright (C) 2002, 2007, 2008 Free Software Foundation
#
# 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
......@@ -20,7 +20,7 @@
imap4d_start
imap4d_test "CAPABILITY" \
"CAPABILITY IMAP4rev1 NAMESPACE IDLE X-VERSION" \
"CAPABILITY IMAP4rev1 NAMESPACE IDLE LITERAL+ X-VERSION" \
"OK"
imap4d_test "NOOP"
......
......@@ -48,7 +48,7 @@ Hello Joe, do you think we can meet at 3:30 tomorrow?
"
imap4d_test -long "APPEND mbox 25-Aug-2002 18:00:00 +0200"\
imap4d_test -long "APPEND mbox \"25-Aug-2002 18:00:00 +0200\""\
"Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
From: Fred Foobar <foobar@Blurdybloop.COM>
Subject: afternoon meeting again
......@@ -72,7 +72,7 @@ imap4d_test "SELECT mbox"\
"OK"
imap4d_test "FETCH 2:3 BODY\[\]"\
"2 FETCH (FLAGS (\\Seen) BODY\[\] {310}"\
"2 FETCH (FLAGS (\\Seen) BODY\[\] {312}"\
-literal\
"Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)"\
"From: Fred Foobar <foobar@Blurdybloop.COM>"\
......@@ -83,9 +83,10 @@ imap4d_test "FETCH 2:3 BODY\[\]"\
"Content-Type: TEXT/PLAIN; CHARSET=US-ASCII"\
""\
"Hello Joe, do you think we can meet at 3:30 tomorrow?"\
""\
")"\
-noliteral\
"3 FETCH (FLAGS (\\Seen) BODY\[\] {283}"\
"3 FETCH (FLAGS (\\Seen) BODY\[\] {285}"\
-literal\
"Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)"\
"From: Fred Foobar <foobar@Blurdybloop.COM>"\
......
......@@ -37,7 +37,7 @@ imap4d_test "CREATE flat"
imap4d_test "CREATE en/to/tre"
imap4d_test -long "APPEND en/to/tre 25-Aug-2002 18:00:00 +0200"\
imap4d_test -long "APPEND en/to/tre \"25-Aug-2002 18:00:00 +0200\""\
"Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
From: Fred Foobar <foobar@Blurdybloop.COM>
Subject: afternoon meeting again
......@@ -65,7 +65,7 @@ imap4d_test "SELECT en/to/tre"\
"OK \[READ-WRITE\] SELECT Completed"
imap4d_test "FETCH 1 ALL"\
"1 FETCH (FLAGS (\\Recent) INTERNALDATE \"25-Aug-2002 16:00:00 +0000\" RFC822.SIZE 283 ENVELOPE (\"Mon, 7 Feb 1994 21:52:25 -0800 (PST)\" \"afternoon meeting again\" ((\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\")) ((\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\")) ((\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\")) ((NIL NIL \"mooch\" \"owatagu.siam.edu\")) NIL NIL NIL \"<B27397-0200000@Blurdybloop.COM>\"))"\
"1 FETCH (FLAGS (\\Recent) INTERNALDATE \"25-Aug-2002 16:00:00 +0000\" RFC822.SIZE 285 ENVELOPE (\"Mon, 7 Feb 1994 21:52:25 -0800 (PST)\" \"afternoon meeting again\" ((\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\")) ((\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\")) ((\"Fred Foobar\" NIL \"foobar\" \"Blurdybloop.COM\")) ((NIL NIL \"mooch\" \"owatagu.siam.edu\")) NIL NIL NIL \"<B27397-0200000@Blurdybloop.COM>\"))"\
"OK"
......
......@@ -165,7 +165,7 @@ imap4d_test "FETCH 1 BODY\[HEADER\]"\
# The subset returned by HEADER.FIELDS contains only those header fields
# with a field-name that matches one of the names in the list;
imap4d_test "FETCH 1 BODY\[HEADER.FIELDS FROM TO SUBJECT\]"\
imap4d_test "FETCH 1 BODY\[HEADER.FIELDS (FROM TO SUBJECT)\]"\
"1 FETCH (BODY\[HEADER.FIELDS (\"FROM\" \"TO\" \"SUBJECT\")\] {94}"\
-literal\
"FROM: Foo Bar <foobar@nonexistent.net>"\
......@@ -178,7 +178,7 @@ imap4d_test "FETCH 1 BODY\[HEADER.FIELDS FROM TO SUBJECT\]"\
# similarly, the subset returned by HEADER.FIELDS.NOT contains only
# the header fields with a non-matching field-name.
imap4d_test "FETCH 1 BODY\[HEADER.FIELDS.NOT FROM TO SUBJECT\]"\
imap4d_test "FETCH 1 BODY\[HEADER.FIELDS.NOT (FROM TO SUBJECT)\]"\
"1 FETCH (BODY\[HEADER.FIELDS.NOT (\"FROM\" \"TO\" \"SUBJECT\")\] {235}"\
-literal\
"Received: (from foobar@nonexistent.net) "\
......@@ -330,8 +330,13 @@ imap4d_test "FETCH 3 BODY\[1.MIME\]"\
"OK"
imap4d_test "FETCH 4 BODY\[2.2.1\]"\
"4 FETCH (FLAGS (\\Seen) BODY\[2.2.1\] {490}"\
"4 FETCH (FLAGS (\\Seen) BODY\[2.2.1\] {680}"\
-literal\
"Content-Type: application/octet-stream; name=\"msg.23\""\
"Content-ID: <5122.1026510654.6@Mirddin.farlep.net>"\
"Content-Description: Father William Part III"\
"Content-Transfer-Encoding: base64"\
""\
"YFlvdSBhcmUgb2xkLCcgc2FpZCB0aGUgeW91dGgsIGBhbmQgeW91ciBqYXdzIGFyZSB0b28gd2Vh"\
"awpGb3IgYW55dGhpbmcgdG91Z2hlciB0aGFuIHN1ZXQ7CllldCB5b3UgZmluaXNoZWQgdGhlIGdv"\
"b3NlLCB3aXRoIHRoZSBib25lcyBhbmQgdGhlIGJlYWstLQpQcmF5IGhvdyBkaWQgeW91IG1hbmFn"\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -24,36 +24,29 @@
*/
int
imap4d_uid (struct imap4d_command *command, char *arg)
imap4d_uid (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *cmd;
char *sp = NULL;
int rc = RESP_NO;
char buffer[64];
char *err_text = "Completed";
cmd = util_getword (arg, &sp);
if (!cmd)
util_finish (command, RESP_BAD, "Too few args");
if (imap4d_tokbuf_argc (tok) < 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
cmd = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
if (strcasecmp (cmd, "FETCH") == 0)
{
rc = imap4d_fetch0 (sp, 1, buffer, sizeof buffer);
}
rc = imap4d_fetch0 (tok, 1, &err_text);
else if (strcasecmp (cmd, "COPY") == 0)
{
rc = imap4d_copy0 (sp, 1, buffer, sizeof buffer);
}
rc = imap4d_copy0 (tok, 1, &err_text);
else if (strcasecmp (cmd, "STORE") == 0)
{
rc = imap4d_store0 (sp, 1, buffer, sizeof buffer);
}
rc = imap4d_store0 (tok, 1, &err_text);
else if (strcasecmp (cmd, "SEARCH") == 0)
{
rc = imap4d_search0 (sp, 1, buffer, sizeof buffer);
}
rc = imap4d_search0 (tok, 1, &err_text);
else
{
snprintf (buffer, sizeof buffer, "Error uknown uid command");
err_text = "Uknown uid command";
rc = RESP_BAD;
}
return util_finish (command, rc, "%s %s", cmd, buffer);
return util_finish (command, rc, "%s %s", cmd, err_text);
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -71,19 +71,34 @@ unsubscribe (struct scan_data *data, char *name)
return 0;
}
/*
6.3.7. UNSUBSCRIBE Command
Arguments: mailbox name
Responses: no specific responses for this command
Result: OK - unsubscribe completed
NO - unsubscribe failure: can't unsubscribe that name
BAD - command unknown or arguments invalid
The UNSUBSCRIBE command removes the specified mailbox name from
the server's set of "active" or "subscribed" mailboxes as returned
by the LSUB command. This command returns a tagged OK response
only if the unsubscription is successful.
*/
int
imap4d_unsubscribe (struct imap4d_command *command, char *arg)
imap4d_unsubscribe (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
char *sp = NULL;
char *name;
char *file;
struct scan_data sd;
int rc;
name = util_getword (arg, &sp);
util_unquote (&name);
if (!name || *name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
if (imap4d_tokbuf_argc (tok) != 3)
return util_finish (command, RESP_BAD, "Invalid arguments");
name = imap4d_tokbuf_getarg (tok, IMAP4_ARG_1);
asprintf (&file, "%s/.mailboxlist", homedir);
sd.result = 0;
......
......@@ -25,92 +25,6 @@ static mu_stream_t ostream;
static int add2set (size_t **, int *, unsigned long);
static const char *sc2string (int);
/* Get the next space/CR/NL separated word, some words are between double
quotes, strtok() cannot handle it. */
char *
util_getword (char *s, char **save)
{
return util_getitem (s, " \r\n", save);
}
/* Take care of words between double quotes. */
char *
util_getitem (char *s, const char *delim, char **save)
{
{
char *p;
if ((p = s) || (p = *save))
{
while (isspace ((unsigned) *p))
p++;
if (*p == '"')
{
s = p;
p++;
while (*p && *p != '"')
p++;
if (*p == '"')
p++;
*save = (*p) ? p + 1 : p;
*p = '\0';
return s;
}
}
}
return strtok_r (s, delim, save);
}
/* Stop at the first char that represents an IMAP4 special character. */
int
util_token (char *buf, size_t len, char **ptr)
{
char *start = *ptr;
size_t i;
/* Skip leading space. */
while (**ptr && **ptr == ' ')
(*ptr)++;
/* Break the string by token, i.e when we reconize IMAP special
atoms we stop and send it. */
for (i = 1; **ptr && i < len; (*ptr)++, buf++, i++)
{
if (**ptr == ' ' || **ptr == '.'
|| **ptr == '(' || **ptr == ')'
|| **ptr == '[' || **ptr == ']'
|| **ptr == '<' || **ptr == '>' || **ptr == '\r' || **ptr == '\n')
{
/* Advance. */
if (start == (*ptr))
(*ptr)++;
break;
}
*buf = **ptr;
}
*buf = '\0';
/* Skip trailing space. */
while (**ptr && **ptr == ' ')
(*ptr)++;
return *ptr - start;
}
/* Remove the surrounding double quotes. */
/* FIXME: Check if the quote was escaped and ignore it. */
void
util_unquote (char **ptr)
{
char *s = *ptr;
size_t len;
if (s && *s == '"' && (len = strlen (s)) > 1 && s[len - 1] == '"')
{
char *p = ++s;
while (*p && *p != '"')
p++;
if (*p == '"')
*p = '\0';
}
*ptr = s;
}
/* NOTE: Allocates Memory. */
/* Expand: ~ --> /home/user and to ~guest --> /home/guest. */
char *
......@@ -266,8 +180,8 @@ util_msgset (char *s, size_t ** set, int *n, int isuid)
s++;
break;
/* As a convenience. '*' is provided to refer to the highest message
number int the mailbox:
/* As a convenience. '*' is provided to refer to the highest
message number int the mailbox:
5:* --> 5 6 7 8
*/
case '*':
......@@ -479,143 +393,30 @@ util_finish (struct imap4d_command *command, int rc, const char *format, ...)
return status;
}
/* Clients are allowed to send literal string to the servers. this
means that it can occur everywhere where a string is allowed.
A literal is a sequence of zero or more octets (including CR and LF)
prefix-quoted with an octet count in the form of an open brace ("{"),
the number of octets, close brace ("}"), and CRLF.
*/
char *
imap4d_readline (void)
{
char buffer[512];
size_t len;
long number = 0;
size_t total = 0;
char *line = malloc (1);
if (!line)
imap4d_bye (ERR_NO_MEM);
line[0] = '\0'; /* start with a empty string. */
do
{
size_t sz;
int rc;
alarm (idle_timeout);
rc = mu_stream_sequential_readline (istream, buffer, sizeof (buffer), &sz);
if (sz == 0)
{
mu_diag_output (MU_DIAG_INFO, _("Unexpected eof on input"));
imap4d_bye (ERR_NO_OFILE);
}
else if (rc)
{
const char *p;
if (mu_stream_strerror (istream, &p))
p = strerror (errno);
mu_diag_output (MU_DIAG_INFO, _("Error reading from input file: %s"), p);
imap4d_bye (ERR_NO_OFILE);
}
alarm (0);
len = strlen (buffer);
/* If we were in a litteral substract. We have to do it here since the CR
is part of the count in a literal. */
if (number)
number -= len;
/* Remove CR. */
if (len > 1 && buffer[len - 1] == '\n')
{
if (buffer[len - 2] == '\r')
{
buffer[len - 2] = '\n';
buffer[len - 1] = '\0';
}
}
line = realloc (line, total + len + 1);
if (!line)
imap4d_bye (ERR_NO_MEM);
strcat (line, buffer);
total = strlen (line);
/* I observe some client requesting long FETCH operations since, I did
not see any limit in the command length in the RFC, catch things
here. */
/* Check that we do have a terminated NL line and we are not retrieving
a literal. If we don't continue the read. */
if (number <= 0 && total && line[total - 1] != '\n')
continue;
/* Check if the client try to send a literal and make sure we are not
already retrieving a literal. */
if (number <= 0 && len > 2)
{
size_t n = total - 1; /* C arrays are 0-based. */
/* A literal is this "{number}\n". The CR is already strip. */
if (line[n] == '\n' && line[n - 1] == '}')
{
/* Search for the matching bracket. */
while (n && line[n] != '{')
n--;
if (line[n] == '{')
{
char *sp = NULL;
/* Truncate where the literal number was. */
line[n] = '\0';
number = strtoul (line + n + 1, &sp, 10);
/* Client can ask for non synchronise literal,
if a '+' is append to the octet count. */
if (*sp != '+')
util_send ("+ GO AHEAD\r\n");
}
}
}
}
while (number > 0 || (total && line[total - 1] != '\n'));
if (imap4d_transcript)
mu_diag_output (MU_DIAG_DEBUG, "recv: %s", line);
return line;
}
char *
imap4d_readline_ex (void)
{
int len;
char *s = imap4d_readline ();
if (s && (len = strlen (s)) > 0 && s[len - 1] == '\n')
s[len - 1] = 0;
return s;
}
int
util_do_command (char *prompt)
util_do_command (imap4d_tokbuf_t tok)
{
char *sp = NULL, *tag, *cmd;
char *tag, *cmd;
struct imap4d_command *command;
static struct imap4d_command nullcommand;
size_t len;
tag = util_getword (prompt, &sp);
cmd = util_getword (NULL, &sp);
if (!tag)
int argc = imap4d_tokbuf_argc (tok);
if (argc == 0)
{
nullcommand.name = "";
nullcommand.tag = (char *) "*";
return util_finish (&nullcommand, RESP_BAD, "Null command");
}
else if (!cmd)
else if (argc == 1)
{
nullcommand.name = "";
nullcommand.tag = tag;
return util_finish (&nullcommand, RESP_BAD, "Missing arguments");
return util_finish (&nullcommand, RESP_BAD, "Missing command");
}
tag = imap4d_tokbuf_getarg (tok, 0);
cmd = imap4d_tokbuf_getarg (tok, 1);
command = util_getcommand (cmd, imap4d_command_table);
if (command == NULL)
{
......@@ -629,10 +430,7 @@ util_do_command (char *prompt)
if (command->states && (command->states & state) == 0)
return util_finish (command, RESP_BAD, "Wrong state");
len = strlen (sp);
if (len && sp[len - 1] == '\n')
sp[len - 1] = '\0';
return command->func (command, sp);
return command->func (command, tok);
}
int
......@@ -702,7 +500,7 @@ add2set (size_t ** set, int *n, unsigned long val)
}
int
util_parse_internal_date0 (char *date, time_t * timep, char **endp)
util_parse_internal_date (char *date, time_t * timep)
{
struct tm tm;
mu_timezone tz;
......@@ -717,19 +515,10 @@ util_parse_internal_date0 (char *date, time_t * timep, char **endp)
return 2;
*timep = time;
if (endp)
*endp = *datep;
return 0;
}
int
util_parse_internal_date (char *date, time_t * timep)
{
return util_parse_internal_date0 (date, timep, NULL);
}
int
util_parse_822_date (const char *date, time_t * timep)
{
struct tm tm;
......@@ -875,35 +664,6 @@ util_attribute_matches_flag (mu_attribute_t attr, const char *item)
return flags & mask;
}
int
util_parse_attributes (char *items, char **save, int *flags)
{
int rc = 0;
*flags = 0;
while (*items)
{
int type = 0;
char item[64] = "";
util_token (item, sizeof (item), &items);
if (!util_attribute_to_type (item, &type))
*flags |= type;
/*FIXME: else? */
if (*items == ')')
{
items++;
rc = 0;
break;
}
}
*save = items;
return rc;
}
int
util_base64_encode (const unsigned char *input, size_t input_len,
unsigned char **output, size_t * output_len)
......@@ -1324,3 +1084,327 @@ is_atom (const char *s)
return 1;
}
static size_t
remove_cr (char *line, size_t len)
{
char *prev = NULL;
size_t rlen = len;
char *p;
while ((p = memchr (line, '\r', len)))
{
if (prev)
{
memmove (prev, line, p - line);
prev += p - line;
}
else
prev = p;
rlen--;
len -= p - line + 1;
line = p + 1;
}
if (prev)
memmove (prev, line, len);
return rlen;
}
static size_t
unquote (char *line, size_t len)
{
char *prev = NULL;
size_t rlen = len;
char *p;
int off = 0;
while ((p = memchr (line + off, '\\', len - off)))
{
if (p[1] == '\\' || p[1] == '"')
{
if (prev)
{
memmove (prev, line, p - line);
prev += p - line;
}
else
prev = p;
off = p[1] == '\\';
rlen--;
len -= p - line + 1;
line = p + 1;
}
}
if (prev)
memmove (prev, line, len);
return rlen;
}
struct imap4d_tokbuf {
char *buffer;
size_t size;
size_t level;
int argc;
int argmax;
size_t *argp;
};
struct imap4d_tokbuf *
imap4d_tokbuf_init ()
{
struct imap4d_tokbuf *tok = malloc (sizeof (tok[0]));
if (!tok)
imap4d_bye (ERR_NO_MEM);
memset (tok, 0, sizeof (*tok));
return tok;
}
void
imap4d_tokbuf_destroy (struct imap4d_tokbuf **ptok)
{
struct imap4d_tokbuf *tok = *ptok;
free (tok->buffer);
free (tok->argp);
free (tok);
*ptok = NULL;
}
int
imap4d_tokbuf_argc (struct imap4d_tokbuf *tok)
{
return tok->argc;
}
char *
imap4d_tokbuf_getarg (struct imap4d_tokbuf *tok, int n)
{
if (n < tok->argc)
return tok->buffer + tok->argp[n];
return NULL;
}
static void
imap4d_tokbuf_unquote (struct imap4d_tokbuf *tok, size_t *poff, size_t *plen)
{
char *buf = tok->buffer + *poff;
if (buf[0] == '"' && buf[*plen - 1] == '"')
{
++*poff;
*plen = unquote (buf + 1, *plen - 1);
}
}
static void
imap4d_tokbuf_expand (struct imap4d_tokbuf *tok, size_t size)
{
if (tok->size - tok->level < size)
{
tok->size = tok->level + size;
tok->buffer = realloc (tok->buffer, tok->size);
if (!tok->buffer)
imap4d_bye (ERR_NO_MEM);
}
}
#define ISDELIM(c) (strchr (".()[]<>", (c)) != NULL)
#define ISWS(c) (c == ' ' || c == '\t')
int
util_isdelim (const char *str)
{
return str[1] == 0 && ISDELIM (str[0]);
}
static size_t
insert_nul (struct imap4d_tokbuf *tok, size_t off)
{
imap4d_tokbuf_expand (tok, 1);
if (off < tok->level)
{
memmove (tok->buffer + off + 1, tok->buffer + off, tok->level - off);
tok->level++;
}
tok->buffer[off] = 0;
return off + 1;
}
static size_t
gettok (struct imap4d_tokbuf *tok, size_t off)
{
char *buf = tok->buffer;
while (off < tok->level && ISWS (buf[off]))
off++;
if (tok->argc == tok->argmax)
{
if (tok->argmax == 0)
tok->argmax = 16;
else
tok->argmax *= 2;
tok->argp = realloc(tok->argp, tok->argmax * sizeof (tok->argp[0]));
if (!tok->argp)
imap4d_bye (ERR_NO_MEM);
}
if (buf[off] == '"')
{
char *start = buf + off + 1;
char *p = NULL;
while (*start && (p = strchr (start, '"')))
{
if (p == start || p[-1] != '\\')
break;
start = p + 1;
}
if (p)
{
size_t len;
off++;
len = unquote(buf + off, p - (buf + off));
buf[off + len] = 0;
tok->argp[tok->argc++] = off;
return p - buf + 1;
}
}
tok->argp[tok->argc++] = off;
if (ISDELIM (buf[off]))
return insert_nul (tok, off + 1);
while (off < tok->level && !ISWS (buf[off]))
{
if (ISDELIM (buf[off]))
return insert_nul (tok, off);
off++;
}
buf[off++] = 0;
return off;
}
static void
imap4d_tokbuf_tokenize (struct imap4d_tokbuf *tok, size_t off)
{
while (off < tok->level)
off = gettok (tok, off);
}
static void
check_input_err (int rc, size_t sz)
{
if (rc)
{
const char *p;
if (mu_stream_strerror (istream, &p))
p = mu_strerror (rc);
mu_diag_output (MU_DIAG_INFO,
_("Error reading from input file: %s"), p);
imap4d_bye (ERR_NO_OFILE);
}
else if (sz == 0)
{
mu_diag_output (MU_DIAG_INFO, _("Unexpected eof on input"));
imap4d_bye (ERR_NO_OFILE);
}
}
static size_t
imap4d_tokbuf_getline (struct imap4d_tokbuf *tok)
{
char buffer[512];
size_t level = tok->level;
do
{
size_t len;
int rc;
rc = mu_stream_sequential_readline (istream,
buffer, sizeof (buffer), &len);
check_input_err (rc, len);
imap4d_tokbuf_expand (tok, len);
memcpy (tok->buffer + tok->level, buffer, len);
tok->level += len;
}
while (tok->level && tok->buffer[tok->level - 1] != '\n');
tok->buffer[--tok->level] = 0;
if (tok->buffer[tok->level - 1] == '\r')
tok->buffer[--tok->level] = 0;
return level;
}
void
imap4d_readline (struct imap4d_tokbuf *tok)
{
tok->argc = 0;
for (;;)
{
char *last_arg;
size_t off = imap4d_tokbuf_getline (tok);
imap4d_tokbuf_tokenize (tok, off);
last_arg = tok->buffer + tok->argp[tok->argc - 1];
if (last_arg[0] == '{' && last_arg[strlen(last_arg)-1] == '}')
{
int rc;
unsigned long number;
char *sp = NULL;
char *buf;
size_t len;
number = strtoul (last_arg + 1, &sp, 10);
/* Client can ask for non-synchronised literal,
if a '+' is appended to the octet count. */
if (*sp == '}')
util_send ("+ GO AHEAD\r\n");
else if (*sp != '+')
break;
imap4d_tokbuf_expand (tok, number + 1);
off = tok->level;
buf = tok->buffer + off;
rc = mu_stream_sequential_read (istream, buf, number, &len);
check_input_err (rc, len);
len = remove_cr (buf, len);
imap4d_tokbuf_unquote (tok, &off, &len);
tok->level += len;
tok->buffer[tok->level++] = 0;
tok->argp[tok->argc - 1] = off;
if (buf[len-1] != '\n')
continue;
}
break;
}
}
struct imap4d_tokbuf *
imap4d_tokbuf_from_string (char *str)
{
struct imap4d_tokbuf *tok = imap4d_tokbuf_init ();
tok->buffer = strdup (str);
if (!tok->buffer)
imap4d_bye (ERR_NO_MEM);
tok->level = strlen (str);
tok->size = tok->level + 1;
imap4d_tokbuf_tokenize (tok, 0);
return tok;
}
int
imap4d_getline (char **pbuf, size_t *psize, size_t *pnbytes)
{
size_t len;
int rc = mu_stream_sequential_getline (istream, pbuf, psize, &len);
if (rc == 0)
{
char *s = *pbuf;
if (s && len > 0 && s[len - 1] == '\n')
s[--len] = 0;
if (s && len > 0 && s[len - 1] == '\r')
s[--len] = 0;
if (pnbytes)
*pnbytes = len;
}
return rc;
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2001, 2007 Free Software Foundation, Inc.
Copyright (C) 1999, 2001, 2007, 2008 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -19,8 +19,10 @@
#include "imap4d.h"
int
imap4d_version (struct imap4d_command *command, char *arg)
imap4d_version (struct imap4d_command *command, imap4d_tokbuf_t tok)
{
if (imap4d_tokbuf_argc (tok) != 2)
return util_finish (command, RESP_BAD, "Invalid arguments");
util_send ("* %s GNU %s\r\n", command->name, program_version);
return util_finish (command, RESP_OK, "Completed");
}
......
......@@ -145,7 +145,7 @@ mail_tmp_finish (struct mail_tmp *mtmp, mu_mailbox_t *mbox)
}
mu_stream_flush (mtmp->stream);
if ((status = mu_mailbox_create (mbox, "/dev/null"))
if ((status = mu_mailbox_create (mbox, "mbox:/dev/null"))
|| (status = mu_mailbox_open (*mbox, MU_STREAM_READ))
|| (status = mu_mailbox_set_stream (*mbox, mtmp->stream)))
{
......
......@@ -51,7 +51,7 @@ _memory_destroy (mu_stream_t stream)
struct _memory_stream *mfs = mu_stream_get_owner (stream);
if (mfs && mfs->ptr != NULL)
free (mfs->ptr);
if(mfs->filename)
if (mfs->filename)
free (mfs->filename);
free (mfs);
}
......