Commit 43ba0c68 43ba0c68726d41135916aaaf9f4dfffdb56af872 by Alain Magloire

Implemented UID fetch BODYSTRUCTURE and multiple access to mailbox via IMAP

1 parent 33825193
2001-05-07 Alain Magloire
Now we can have multiple access to the mailbox and the server
will update the mailbox and send the unsollicited responses.
This is implemented in imap4d/sync.c.
* imap4d/copy.c: Move the code into imap4d_copy0() to allow
the use of UID COPY.
(imaprd_copy0): New function.
* imap4d/fetch.c: Define a new structure command for FETCH.
(fetch_getcommand): New function.
(imap4d_fetch0): New function to allow the use of UID COPY.
(fetch_envelope0): New function to permit to reuse the function
in bodystructure.
(fetch_bodystructure): Implemented.
(fetch_bodystructure0): The implementation.
(bodystructure): The helper function for fetch_bodystructure.
* imap4d/imap4d.c (imap4d_mainloop): call imap4d_sync() in the
mainloop. Now use FILE *ifile.
* imap4d/imap4d.h: Update prototypes. Define ERR_NO_MEM and
ERR_NO_OFILE.
* imap4d/select.c (imap4d_select0): Call imap4d_sync() to update.
* imap4d/store.c (ima4d_store): Cal imap4d_sync_flags() to
update the flags in the uid_table.
* imap4d/uid.c: Implemented.
* imap4d/util.c (imap4d_readline): Use fgets() and deal
with literals send from the client.
* imap4d/sync.c: New file.
* include/mailutils/header.c: Added some new headers.
* mailbox/attribute.c (attribute_copy): Do a shalow copy.
* mailbox/file_stream.c: Check if FILE * is null in all functions.
* mailbox/mailbox.c (mailbox_is_updated): Should not return ENOSYS
but rather 1.
2001-05-07 Sam Roberts
* mailbox/parse822.c: Now allow a unix mailbox in an address.
* include/mailutils/parse822.h: Declare the function to parse them.
* doc/address.texi: Document the fact.
* doc/Makefile.am: Automatically build the example code from addr.c.
* examples/addr.c: And update the parser test.
* examples/Addrs.good: Update the parser test.
2001-05-03 Sam Roberts
* mail/mail.c: Typo
......
......@@ -23,3 +23,6 @@ EXTRA_DIST = \
url.texi \
version.texi
ex-address.texi: ../examples/addr.c
sed -es/{/@{/g -e s/}/@}/g < $< > $@
......
@code{#include <mailutils/mailbox.h>}
@code{#include <mailutils/address.h>}
The internet address format is defined in RFC 822. RFC 822 is in the
process of being updated, and will soon be superceeded by a new RFC
that makes some corrections and clarifications. References to RFC 822
here apply equally to the new RFC.
The internet address format is defined in RFC 822. RFC 822 has been
updated, and is now superceeded by RFC 2822, which
makes some corrections and clarifications. References to RFC 822
here apply equally to RFC 2822.
The RFC 822 format is more flexible than many people realize, here
is a quick summary of the syntax this parser implements, see
......@@ -16,10 +16,15 @@ mailbox = addr-spec ["(" display-name ")"] /
[display-name] "<" [route] addr-spec ">"
mailbox-list = mailbox ["," mailbox-list]
group = display-name ":" [mailbox-list] ";"
address = mailbox / group
address = mailbox / group / unix-mbox
address-list = address ["," address-list]
@end example
unix-mbox is a non-standard extention meant to deal with the common
practice of using user names as addresses in mail utilities. It allows
addresses such as "root" to be parsed correctly. These are NOT valid
internet email addresses, they must be qualified before use.
Several address functions have a set of common arguments with consistent
semantics, these are described here to avoid repetition.
......@@ -133,7 +138,8 @@ The return value is @code{0} on success and a code number on error conditions:
@deftypefun int address_get_email (address_t *@var{addr}, size_t @var{no}, char* @var{buf}, size_t @var{len}, size_t* @var{n})
Acesses the email addr-spec extracted while
parsing the @var{no}th email address.
parsing the @var{no}th email address. This will be @code{0}
length for a unix-mbox.
The return value is @code{0} on success and a code number on error conditions:
@table @code
......@@ -157,7 +163,8 @@ The return value is @code{0} on success and a code number on error conditions:
@deftypefun int address_get_domain (address_t *@var{addr}, size_t @var{no}, char* @var{buf}, size_t @var{len}, size_t* @var{n})
Acesses the domain of an email addr-spec extracted while
parsing the @var{no}th email address.
parsing the @var{no}th email address. This will be @code{0}
length for a unix-mbox.
The return value is @code{0} on success and a code number on error conditions:
@table @code
......@@ -223,54 +230,6 @@ The return value is @code{0}.
@section Example
@example
#include <stdio.h>
#include <mailutils/address.h>
int
main(int argc, const char *argv[])
@{
for(argc = 1; argv[argc]; argc++)
@{
const char* str = argv[argc];
address_t address = NULL;
address_create(&address, str);
printf("'%s' ->\n", str);
@{
size_t no = 0;
size_t pcount;
address_get_count(address, &pcount);
printf(" pcount %d\n", pcount);
for(no = 1; no <= pcount; no++)
@{
char buf[BUFSIZ];
address_get_personal(address, no, buf, sizeof(buf), 0);
printf(" personal '%s'\n", buf);
address_get_local_part(address, no, buf, sizeof(buf), 0);
printf(" local_part '%s'\n", buf);
address_get_domain(address, no, buf, sizeof(buf), 0);
printf(" domain '%s'\n", buf);
address_get_email(address, no, buf, sizeof(buf), 0);
printf(" email '%s'\n", buf);
@}
@}
address_destroy(&address);
@}
return 0;
@}
@include ex-address.texi
@end example
......
......@@ -228,7 +228,7 @@ list-ietf-wg-apps-drums@faerber.muc.de (=?ISO-8859-1?Q?Claus_F=E4rber?=)=> pcoun
personal <'sroberts@certicom.ca'>
local-part <sroberts> domain <certicom.ca>
"=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" <jcmarcos@datavoicees>=> pcount 1
"=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" <jcmarcos@datavoice.es>=> pcount 1
1 email <jcmarcos@datavoice.es>
personal <=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=>
local-part <jcmarcos> domain <datavoice.es>
......@@ -432,17 +432,17 @@ list-ietf-wg-apps-drums@faerber.muc.de (=?ISO-8859-1?Q?Claus_F=E4rber?=)=> pcoun
personal <=?US-ASCII?Q?gary=5Fc?=>
local-part <gary_c> domain <cunningham-lee.com>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidusnet>=> pcount 1
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidus.net>=> pcount 1
1 email <0@pervalidus.net>
personal <=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier>
local-part <0> domain <pervalidus.net>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidusnet>,=> pcount 1
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidus.net>,=> pcount 1
1 email <0@pervalidus.net>
personal <=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier>
local-part <0> domain <pervalidus.net>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= <0@pervalidusnet>=> pcount 1
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= <0@pervalidus.net>=> pcount 1
1 email <0@pervalidus.net>
personal <=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?=>
local-part <0> domain <pervalidus.net>
......@@ -554,14 +554,22 @@ No_Reply-To@mapquest.com=> pcount 1
1 email <No_Reply-To@mapquest.com>
local-part <No_Reply-To> domain <mapquest.com>
OSULLIE@rte.ie, skidswam@hotmail.com, boot=> error ENOENT
OSULLIE@rte.ie, skidswam@hotmail.com, boot=> pcount 3
1 email <OSULLIE@rte.ie>
local-part <OSULLIE> domain <rte.ie>
2 email <skidswam@hotmail.com>
local-part <skidswam> domain <hotmail.com>
3 email <>
local-part <boot>
Paul Hoffman / IMC <phoffman@imc.org>=> pcount 1
1 email <phoffman@imc.org>
personal <Paul Hoffman / IMC>
local-part <phoffman> domain <imc.org>
Sam=> error ENOENT
Sam=> pcount 1
1 email <>
local-part <Sam>
Sam Roberts <sam@cogent.ca>=> pcount 1
1 email <sam@cogent.ca>
......@@ -605,7 +613,9 @@ mcaustin@eudoramail.com, aposner@usaid.gov, Kieran.O'Leary@anpost.ie,=> pcount 3
rfunk@wks.uts.ohio-state.eduofflinemailer-bounce@dikke.penguin.nl=> error ENOENT
root=> error ENOENT
root=> pcount 1
1 email <>
local-part <root>
srr <sam@localhost>=> error ENOENT
......
......@@ -78,11 +78,15 @@ static int parse(const char* str)
address_get_local_part(address, no, buf, sizeof(buf), &got);
if(got) printf(" local-part <%s>", buf);
if(got) {
printf(" local-part <%s>", buf);
address_get_domain(address, no, buf, sizeof(buf), &got);
if(got) printf(" domain <%s>\n", buf);
if(got) printf(" domain <%s>", buf);
printf("\n");
}
address_get_route(address, no, buf, sizeof(buf), &got);
......
......@@ -21,34 +21,57 @@
* copy messages in argv[2] to mailbox in argv[3]
*/
/* FIXME if the mailbox is the one selecte we should send notif. */
int
imap4d_copy (struct imap4d_command *command, char *arg)
{
int rc;
char buffer[64];
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
rc = imap4d_copy0 (arg, 0, buffer, sizeof buffer);
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_finish (command, rc, buffer);
}
int
imap4d_copy0 (char *arg, int isuid, char *resp, size_t resplen)
{
int status;
char *msgset;
char *name;
char *mailbox_name;
const char *delim = "/";
char *sp = NULL;
int *set = NULL;
size_t *set = NULL;
size_t n = 0;
mailbox_t cmbox = NULL;
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
msgset = util_getword (arg, &sp);
name = util_getword (NULL, &sp);
util_unquote (&name);
if (!msgset || !name || *name == '\0')
return util_finish (command, RESP_BAD, "Too few args");
{
snprintf (resp, resplen, "Too few args");
return RESP_BAD;
}
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, 0);
status = util_msgset (msgset, &set, &n, isuid);
if (status != 0)
return util_finish (command, RESP_BAD, "Bogus number set");
{
snprintf (resp, resplen, "Bogus number set");
return RESP_BAD;
}
if (strcasecmp (name, "INBOX") == 0)
{
......@@ -71,7 +94,8 @@ imap4d_copy (struct imap4d_command *command, char *arg)
for (i = 0; i < n; i++)
{
message_t msg = NULL;
mailbox_get_message (mbox, set[i], &msg);
size_t msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
mailbox_get_message (mbox, msgno, &msg);
mailbox_append_message (cmbox, msg);
}
mailbox_close (cmbox);
......@@ -82,16 +106,16 @@ imap4d_copy (struct imap4d_command *command, char *arg)
free (mailbox_name);
if (status == 0)
return util_finish (command, RESP_OK, "Completed");
/* Since we do not call util_finish, reset the state ourself. */
if (command->failure != STATE_NONE)
state = command->failure;
{
snprintf (resp, resplen, "Completed");
return RESP_OK;
}
/* Unless it is certain that the destination mailbix can not be created,
the server MUST send the response code "[TRYCREATE]" as the prefix
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. */
return util_send ("%s NO [TRYCREATE] failed\r\n", command->tag);
snprintf (resp, resplen, "NO [TRYCREATE] failed");
return RESP_NONE;
}
......
......@@ -17,14 +17,13 @@
#include "imap4d.h"
int *ifile;
FILE *ofile;
unsigned int timeout = 1800; /* RFC2060: 30 minutes, if enable. */
mailbox_t mbox;
char *homedir;
int state = STATE_NONAUTH;
static int imap4_mainloop __P ((int, int));
static int imap4d_mainloop __P ((int, int));
int
main (int argc, char **argv)
......@@ -39,18 +38,19 @@ main (int argc, char **argv)
list_append (bookie, path_record);
}
/* FIXME: Incomplete, make it work for standalone, see pop3d. */
imap4_mainloop (fileno (stdin), fileno (stdout));
imap4d_mainloop (fileno (stdin), fileno (stdout));
return 0;
}
static int
imap4_mainloop (int infile, int outfile)
imap4d_mainloop (int infile, int outfile)
{
const char *remote_host = "";
FILE *ifile;
ifile = fdopen (infile, "r");
ofile = fdopen (outfile, "w");
if (ofile == NULL)
util_quit (1);
if (!ofile || !ifile)
util_quit (ERR_NO_OFILE);
/* FIXME: Retreive hostname with getpeername() and log. */
syslog (LOG_INFO, "Incoming connection from %s", remote_host);
......@@ -61,9 +61,11 @@ imap4_mainloop (int infile, int outfile)
while (1)
{
char *cmd = imap4d_readline (infile);
char *cmd = imap4d_readline (ifile);
/* check for updates */
imap4d_sync ();
util_do_command (cmd);
imap4d_sync ();
free (cmd);
fflush (ofile);
}
......
......@@ -99,12 +99,17 @@ struct imap4d_command
#define STATE_ALL (STATE_NONE | STATE_NONAUTH | STATE_AUTH | STATE_SEL \
| STATE_LOGOUT)
/* Response code. */
#define RESP_OK 0
#define RESP_BAD 1
#define RESP_NO 2
#define RESP_BYE 3
#define RESP_NONE 4
/* Error values. */
#define ERR_NO_MEM 1
#define ERR_NO_OFILE 2
extern struct imap4d_command imap4d_command_table[];
extern FILE *ofile;
extern unsigned int timeout;
......@@ -135,10 +140,18 @@ extern int imap4d_close __P ((struct imap4d_command *, char *));
extern int imap4d_expunge __P ((struct imap4d_command *, char *));
extern int imap4d_search __P ((struct imap4d_command *, char *));
extern int imap4d_fetch __P ((struct imap4d_command *, char *));
extern int imap4d_fetch0 __P ((char *, int, char *, size_t));
extern int imap4d_store __P ((struct imap4d_command *, char *));
extern int imap4d_store0 __P ((char *, int, char *, size_t));
extern int imap4d_copy __P ((struct imap4d_command *, char *));
extern int imap4d_copy0 __P ((char *, int, char *, size_t));
extern int imap4d_uid __P ((struct imap4d_command *, char *));
/* Synchronisation on simultenous access. */
extern int imap4d_sync __P ((void));
extern int imap4d_sync_flags __P ((size_t));
extern size_t uid_to_msgno __P ((size_t));
/* Helper functions. */
extern int util_out __P ((int, const char *, ...));
extern int util_send __P ((const char *, ...));
......@@ -146,14 +159,14 @@ extern int util_start __P ((char *));
extern int util_finish __P ((struct imap4d_command *, int, const char *, ...));
extern int util_getstate __P ((void));
extern int util_do_command __P ((char *));
extern char *imap4d_readline __P ((int));
extern char *imap4d_readline __P ((FILE*));
extern void util_quit __P ((int));
extern char *util_getword __P ((char *, char **));
extern int util_token __P ((char *, size_t, char **));
extern void util_unquote __P ((char **));
extern char *util_tilde_expansion __P ((const char *, const char *));
extern char *util_getfullpath __P ((char *, const char *));
extern int util_msgset __P ((char *, int **, int *, int));
extern int util_msgset __P ((char *, size_t **, int *, int));
extern int util_upper __P ((char *));
extern struct imap4d_command *util_getcommand __P ((char *,
struct imap4d_command []));
......
......@@ -97,6 +97,7 @@ imap4d_login (struct imap4d_command *command, char *arg)
return util_finish (command, RESP_NO, "Too many args");
pw = getpwnam (arg);
#ifndef USE_LIBPAM
if (pw == NULL || pw->pw_uid < 1)
return util_finish (command, RESP_NO, "User name or passwd rejected");
......@@ -105,7 +106,7 @@ imap4d_login (struct imap4d_command *command, char *arg)
#ifdef HAVE_SHADOW_H
struct spwd *spw;
spw = getspnam (arg);
if (spw == NULL || strcmp (spw->sp_pwdp, crypt (pass, spw->sp_pwdp)))
if (spw == NULL || strcmp (spw->sp_pwdp, (char *)crypt (pass, spw->sp_pwdp)))
#endif /* HAVE_SHADOW_H */
return util_finish (command, RESP_NO, "User name or passwd rejected");
}
......
......@@ -50,6 +50,8 @@ imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
{
mailbox_close (mbox);
mailbox_destroy (&mbox);
/* Destroy the old uid table. */
imap4d_sync ();
}
if (strcasecmp (mailbox_name, "INBOX") == 0)
......@@ -63,7 +65,7 @@ imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
&& mailbox_open (mbox, flags) == 0)
{
const char *mflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
const char *pflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
const char *pflags = "\\Answered \\Deleted \\Seen";
unsigned long uidvalidity = 0;
size_t count = 0, recent = 0, unseen = 0, uidnext = 0;
......@@ -83,10 +85,13 @@ imap4d_select0 (struct imap4d_command *command, char *arg, int flags)
/* FIXME:
- '\*' can be supported if we use the attribute_set userflag()
- Answered is still not set in the mailbox code. */
if (flags == MU_STREAM_READ)
util_out (RESP_OK, "[PERMANENTFLAGS ()] No Permanent flags");
else
util_out (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags", pflags);
/* Need to set the state explicitely for select. */
state = STATE_SEL;
return util_send ("%s OK [%s] %s Complete\r\n", command->tag,
return util_send ("%s OK [%s] %s Completed\r\n", command->tag,
(MU_STREAM_READ == flags) ?
"READ-ONLY" : "READ-WRITE", command->name);
}
......
......@@ -144,7 +144,7 @@ status_unseen (mailbox_t smbox)
attribute_t attr = NULL;
mailbox_get_message (smbox, i, &msg);
message_get_attribute (msg, &attr);
if (!attribute_is_seen (attr))
if (!attribute_is_seen (attr) && !attribute_is_read (attr))
unseen++;
}
util_send ("UNSEEN %d", unseen);
......
......@@ -25,23 +25,36 @@ static int get_attribute_type __P ((const char *, int *));
int
imap4d_store (struct imap4d_command *command, char *arg)
{
int rc;
char buffer[64];
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
rc = imap4d_store0 (arg, 0, buffer, sizeof buffer);
return util_finish (command, rc, buffer);
}
int
imap4d_store0 (char *arg, int isuid, char *resp, size_t resplen)
{
char *msgset;
char *data;
char *sp = NULL;
int status;
int ack = 0;
size_t i, n = 0;
int *set = NULL;
size_t *set = NULL;
enum value_type { STORE_SET, STORE_ADD, STORE_UNSET } how;
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
msgset = util_getword (arg, &sp);
data = util_getword (NULL, &sp);
if (!msgset || !data || !sp || *sp == '\0')
return util_finish (command, RESP_BAD, "Too few args");
{
snprintf (resp, resplen, "Too few args");
return RESP_BAD;
}
/* The parsing of the data-item is a little slugish. */
if (strcasecmp (data, "FLAGS") == 0)
......@@ -75,12 +88,18 @@ imap4d_store (struct imap4d_command *command, char *arg)
how = STORE_UNSET;
}
else
return util_finish (command, RESP_BAD, "Bogus data item");
{
snprintf (resp, resplen, "Bogus data item");
return RESP_BAD;
}
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, 0);
status = util_msgset (msgset, &set, &n, isuid);
if (status != 0)
return util_finish (command, RESP_BAD, "Bogus number set");
{
snprintf (resp, resplen, "Bogus number set");
return RESP_BAD;
}
for (i = 0; i < n; i++)
{
......@@ -89,8 +108,10 @@ imap4d_store (struct imap4d_command *command, char *arg)
char *items = strdup (sp); /* Don't use the orignal list. */
char *flags = strdup ("");
int first = 1;
size_t msgno;
mailbox_get_message (mbox, set[i], &msg);
msgno = (isuid) ? uid_to_msgno (set[i]) : set[i];
mailbox_get_message (mbox, msgno, &msg);
message_get_attribute (msg, &attr);
/* Get the fetch command names. */
......@@ -124,9 +145,12 @@ imap4d_store (struct imap4d_command *command, char *arg)
util_out (RESP_NONE, "%d FETCH FLAGS (%s)", set[i], flags);
free (items);
free (flags);
/* Update the flags of uid table. */
imap4d_sync_flags (set[i]);
}
free (set);
return util_finish (command, RESP_OK, "Completed");
snprintf (resp, resplen, "Completed");
return RESP_OK;
}
static int
......
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "imap4d.h"
/*
*/
struct _uid_table
{
size_t uid;
size_t msgno;
int notify;
attribute_t attr;
};
static struct _uid_table *uid_table;
static size_t uid_table_count;
static void
add_flag (char **pbuf, const char *f)
{
char *abuf = *pbuf;
abuf = realloc (abuf, strlen (abuf) + strlen (f) + 2);
if (abuf == NULL)
util_quit (ERR_NO_MEM);
if (*abuf)
strcat (abuf, " ");
strcat (abuf, "\\Seen");
*pbuf = abuf;
}
static void
notify_flag (size_t msgno, attribute_t oattr)
{
message_t msg = NULL;
attribute_t nattr = NULL;
int status ;
mailbox_get_message (mbox, msgno, &msg);
message_get_attribute (msg, &nattr);
status = attribute_is_equal (oattr, nattr);
//if (!attribute_is_equal (oattr, nattr))
if (status == 0)
{
char *abuf = malloc (1);;
if (!abuf)
util_quit (ERR_NO_MEM);
*abuf = '\0';
if (attribute_is_seen (nattr) && attribute_is_read (nattr))
if (!attribute_is_seen (oattr) && !attribute_is_read (oattr))
{
attribute_set_seen (oattr);
attribute_set_read (oattr);
add_flag (&abuf, "\\Seen");
}
if (attribute_is_answered (nattr))
if (!attribute_is_answered (oattr))
{
attribute_set_answered (oattr);
add_flag (&abuf, "\\Answered");
}
if (attribute_is_flagged (nattr))
if (!attribute_is_flagged (oattr))
{
attribute_set_flagged (oattr);
add_flag (&abuf, "\\Flagged");
}
if (attribute_is_deleted (nattr))
if (!attribute_is_deleted (oattr))
{
attribute_set_deleted (oattr);
add_flag (&abuf, "\\Deleted");
}
if (attribute_is_draft (nattr))
if (!attribute_is_draft (oattr))
{
attribute_set_draft (oattr);
add_flag (&abuf, "\\Draft");
}
if (attribute_is_recent (nattr))
if (!attribute_is_recent (oattr))
{
attribute_set_recent (oattr);
add_flag (&abuf, "\\Recent");
}
if (*abuf)
util_out (RESP_NONE, "%d FETCH FLAGS (%s)", msgno, abuf);
free (abuf);
}
}
static void
notify_deleted (void)
{
if (uid_table)
{
size_t i;
for (i = 0; i < uid_table_count; i++)
{
if (!(uid_table[i].notify))
{
util_out (RESP_NONE, "%d EXPUNGE", uid_table[i].msgno);
uid_table[i].notify = 1;
}
}
}
}
static int
notify_uid (size_t uid)
{
if (uid_table)
{
size_t i;
for (i = 0; i < uid_table_count; i++)
{
if (uid_table[i].uid == uid)
{
notify_flag (uid_table[i].msgno, uid_table[i].attr);
uid_table[i].notify = 1;
return 1;
}
}
}
return 0;
}
static void
notify (void)
{
if (uid_table)
{
size_t total = 0;
size_t i;
size_t recent = 0;
mailbox_messages_count (mbox, &total);
for (i = 1; i <= total; i++)
{
message_t msg = NULL;
size_t uid = 0;
mailbox_get_message (mbox, i, &msg);
message_get_uid (msg, &uid);
if (!notify_uid (uid))
recent++;
}
util_out (RESP_NONE, "%d EXISTS", total);
if (recent)
util_out (RESP_NONE, "%d RECENT", recent);
notify_deleted ();
}
}
static void
free_uids (void)
{
if (uid_table)
{
size_t i;
for (i = 0; i < uid_table_count; i++)
attribute_destroy (&(uid_table[i].attr), NULL);
free (uid_table);
uid_table = NULL;
uid_table_count = 0;
}
}
static void
reset_uids (void)
{
size_t total = 0;
size_t i;
notify ();
free_uids ();
mailbox_messages_count (mbox, &total);
for (i = 1; i <= total; i++)
{
message_t msg = NULL;
attribute_t attr = NULL;
size_t uid = 0;
uid_table = realloc (uid_table, sizeof (*uid_table) *
(uid_table_count + 1));
if (!uid_table)
util_quit (ERR_NO_MEM);
mailbox_get_message (mbox, i, &msg);
message_get_attribute (msg, &attr);
message_get_uid (msg, &uid);
uid_table[uid_table_count].uid = uid;
uid_table[uid_table_count].msgno = i;
uid_table[uid_table_count].notify = 0;
attribute_create (&(uid_table[uid_table_count].attr), NULL);
attribute_copy (uid_table[uid_table_count].attr, attr);
uid_table_count++;
}
}
size_t
uid_to_msgno (size_t uid)
{
size_t i;
for (i = 0; i < uid_table_count; i++)
if (uid_table[i].uid == uid)
return uid_table[i].msgno;
return 0;
}
int
imap4d_sync_flags (size_t msgno)
{
size_t i;
for (i = 0; i < uid_table_count; i++)
if (uid_table[i].msgno == msgno)
{
message_t msg = NULL;
attribute_t attr = NULL;
mailbox_get_message (mbox, msgno, &msg);
message_get_attribute (msg, &attr);
attribute_copy (uid_table[i].attr, attr);
break;
}
return 0;
}
int
imap4d_sync (void)
{
/* if mbox --> NULL, it means to free all the ressources.
it may be because of close or before select/examine a new mailbox. */
if (mbox == NULL)
free_uids ();
else if (uid_table == NULL || !mailbox_is_updated (mbox))
reset_uids ();
return 0;
}
......@@ -25,7 +25,33 @@
int
imap4d_uid (struct imap4d_command *command, char *arg)
{
char *cmd;
char *sp = NULL;
int rc = RESP_NO;
char buffer[64];
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
return util_finish (command, RESP_NO, "Not supported");
cmd = util_getword (arg, &sp);
if (!cmd)
util_finish (command, RESP_BAD, "Too few args");
if (strcasecmp (cmd, "FETCH") == 0)
{
rc = imap4d_fetch0 (sp, 1, buffer, sizeof buffer);
}
else if (strcasecmp (cmd, "COPY") == 0)
{
rc = imap4d_copy0 (sp, 1, buffer, sizeof buffer);
}
else if (strcasecmp (cmd, "STORE") == 0)
{
rc = imap4d_store0 (sp, 1, buffer, sizeof buffer);
}
else
{
snprintf (buffer, sizeof buffer, "Error uknown uid command");
rc = RESP_BAD;
}
return util_finish (command, rc, "%s %s", cmd, buffer);
}
......
......@@ -18,7 +18,7 @@
#include "imap4d.h"
#include <ctype.h>
static int add2set __P ((int **, int *, unsigned long, size_t));
static int add2set __P ((size_t **, int *, unsigned long, size_t));
static const char *sc2string __P ((int));
/* Get the next space/CR/NL separated word, some words are between double
......@@ -67,7 +67,8 @@ util_token (char *buf, size_t len, char **ptr)
if (**ptr == ' ' || **ptr == '.'
|| **ptr == '(' || **ptr == ')'
|| **ptr == '[' || **ptr == ']'
|| **ptr == '<' || **ptr == '>')
|| **ptr == '<' || **ptr == '>'
|| **ptr == '\r' || **ptr == '\n')
{
/* Advance. */
if (start == (*ptr))
......@@ -172,7 +173,7 @@ util_getfullpath (char *name, const char *delim)
FIXME: The algo below is to relaxe, things like <,,,> or <:12> or <20:10>
will not generate an error. */
int
util_msgset (char *s, int **set, int *n, int isuid)
util_msgset (char *s, size_t **set, int *n, int isuid)
{
unsigned long val = 0;
unsigned long low = 0;
......@@ -340,94 +341,80 @@ util_finish (struct imap4d_command *command, int rc, const char *format, ...)
return status;
}
#if 0
/* Need a replacement for readline that can support literals. */
/* Clients are allowed to send literal string to the servers. this
mean that it can me 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 (FILE *fp)
{
char buffer[512];
char *line;
size_t len;
long number = 0;
size_t total = 0;
char *line = malloc (1);
if (!line)
util_quit (ERR_NO_MEM);
line[0] = '\0'; /* start with a empty string. */
do
{
alarm (timeout);
line = fgets (buffer, sizeof (buffer), fp);
if (fgets (buffer, sizeof (buffer), fp) == NULL)
util_quit (0); /* Logout. */
alarm (0);
if (!line)
util_quit (1);
line = strdup (buffer);
len = strlen (buffer);
if (len > 2)
/* If we were in a litteral substract. We have to do it since the CR
is part of the count in a literal. */
if (number)
number -= len;
/* Remove CR. */
if (len > 2 && buffer[len - 1] == '\n')
{
if (buffer[len - 2] == '\r')
{
len--; /* C arrays are 0-based. */
if (line[len] == '\n' && line[len - 1] == '}')
buffer[len - 2] = '\n';
buffer[len - 1] = '\0';
}
}
line = realloc (line, total + len + 1);
if (!line)
util_quit (ERR_NO_MEM);
strcat (line, buffer);
total = strlen (line);
/* Check if the client try to send a literal and we are not already
retrieving a litera. */
if (number <= 0 && len > 2)
{
size_t n = total - 1; /* C arrays are 0-based. */
if (line[n] == '\n' && line[n - 1] == '}')
{
while (len && line[len] != '{') len--;
if (line [len] == '{')
while (n && line[n] != '{') n--;
if (line [n] == '{')
{
char *sp = NULL;
long number = strtoul (line + len + 1, &sp, 10);
/* 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");
line[len] = '\0';
while (number > 0)
{
char *literal = imap4d_readline (fd);
size_t n = strlen (literal);
line = realloc (line, strlen (line) + n + 1);
strcat (line, literal);
number -= n;
free (literal);
}
}
}
}
while (number > 0);
return line;
}
#endif
char *
imap4d_readline (int fd)
{
fd_set rfds;
struct timeval tv;
char buf[512], *ret = NULL;
int nread;
int total = 0;
int available;
FD_ZERO (&rfds);
FD_SET (fd, &rfds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
do
{
if (timeout)
{
available = select (fd + 1, &rfds, NULL, NULL, &tv);
if (!available)
util_quit (1);
}
nread = read (fd, buf, sizeof (buf) - 1);
if (nread < 1)
util_quit (1);
buf[nread] = '\0';
ret = realloc (ret, (total + nread + 1) * sizeof (char));
if (ret == NULL)
util_quit (1);
memcpy (ret + total, buf, nread + 1);
total += nread;
}
while (memchr (buf, '\n', nread) == NULL);
/* Nuke CR'\r' */
for (nread = total; nread > 0; nread--)
if (ret[nread] == '\r' || ret[nread] == '\n')
ret[nread] = '\0';
return ret;
}
int
util_do_command (char *prompt)
......@@ -435,6 +422,7 @@ util_do_command (char *prompt)
char *sp = NULL, *tag, *cmd;
struct imap4d_command *command;
static struct imap4d_command nullcommand;
size_t len;
tag = util_getword (prompt, &sp);
cmd = util_getword (NULL, &sp);
......@@ -462,6 +450,9 @@ util_do_command (char *prompt)
}
command->tag = tag;
len = strlen (sp);
if (len && sp[len - 1] == '\n')
sp[len - 1] = '\0';
return command->func (command, sp);
}
......@@ -487,8 +478,17 @@ util_start (char *tag)
void
util_quit (int err)
{
if (err)
switch (err)
{
case ERR_NO_OFILE:
/*util_out (RESP_BYE, "Server terminating dead socket."); */
break;
case ERR_NO_MEM:
util_out (RESP_BYE, "Server terminating no more ressources.");
break;
default:
util_out (RESP_BYE, "Server terminating");
}
exit (err);
}
......@@ -535,7 +535,7 @@ sc2string (int rc)
}
static int
add2set (int **set, int *n, unsigned long val, size_t max)
add2set (size_t **set, int *n, unsigned long val, size_t max)
{
int *tmp;
if (val == 0 || val > max
......
......@@ -61,8 +61,12 @@ extern "C" {
#define MU_HEADER_STATUS "Status"
#define MU_HEADER_CONTENT_LENGTH "Content-Length"
#define MU_HEADER_CONTENT_LANGUAGE "Content-Language"
#define MU_HEADER_CONTENT_ENCODING "Content-transfer-encoding"
#define MU_HEADER_CONTENT_TRANSFER_ENCODING "Content-transfer-encoding"
#define MU_HEADER_CONTENT_ID "Content-ID"
#define MU_HEADER_CONTENT_TYPE "Content-Type"
#define MU_HEADER_CONTENT_DESCRIPTION "Content-Description"
#define MU_HEADER_CONTENT_DISPOSITION "Content-Disposition"
#define MU_HEADER_CONTENT_MD5 "Content-MD5"
#define MU_HEADER_MIME_VERSION "MIME-Version"
#define MU_HEADER_X_UIDL "X-UIDL"
#define MU_HEADER_X_UID "X-UID"
......
......@@ -78,6 +78,7 @@ extern int parse822_address __P ((const char** p, const char* e, address_
extern int parse822_route_addr __P ((const char** p, const char* e, address_t* a));
extern int parse822_route __P ((const char** p, const char* e, char** route));
extern int parse822_addr_spec __P ((const char** p, const char* e, address_t* a));
extern int parse822_unix_mbox __P ((const char** p, const char* e, address_t* a));
extern int parse822_local_part __P ((const char** p, const char* e, char** local_part));
extern int parse822_domain __P ((const char** p, const char* e, char** domain));
extern int parse822_sub_domain __P ((const char** p, const char* e, char** sub_domain));
......
......@@ -441,7 +441,9 @@ attribute_copy (attribute_t dest, attribute_t src)
{
if (dest == NULL || src == NULL)
return EINVAL;
memcpy (dest, src, sizeof (*dest));
/* Can not be a deep copy. */
/* memcpy (dest, src, sizeof (*dest)); */
dest->flags = src->flags;
return 0;
}
......
......@@ -55,6 +55,13 @@ _file_read (stream_t stream, char *optr, size_t osize,
size_t n;
int err = 0;
if (!fs->file)
{
if (nbytes)
*nbytes = 0;
return 0;
}
if (fs->offset != offset)
{
if (fseek (fs->file, offset, SEEK_SET) != 0)
......@@ -84,6 +91,13 @@ _file_readline (stream_t stream, char *optr, size_t osize,
size_t n = 0;
int err = 0;
if (!fs->file)
{
if (nbytes)
*nbytes = 0;
return 0;
}
if (fs->offset != offset)
{
if (fseek (fs->file, offset, SEEK_SET) != 0)
......@@ -117,6 +131,13 @@ _file_write (stream_t stream, const char *iptr, size_t isize,
size_t n;
int err = 0;
if (!fs->file)
{
if (nbytes)
*nbytes = 0;
return 0;
}
if (fs->offset != offset)
{
if (fseek (fs->file, offset, SEEK_SET) != 0)
......@@ -144,7 +165,7 @@ static int
_file_truncate (stream_t stream, off_t len)
{
struct _file_stream *fs = stream_get_owner (stream);
if (ftruncate (fileno(fs->file), len) != 0)
if (fs->file && ftruncate (fileno(fs->file), len) != 0)
return errno;
return 0;
}
......@@ -154,6 +175,12 @@ _file_size (stream_t stream, off_t *psize)
{
struct _file_stream *fs = stream_get_owner (stream);
struct stat stbuf;
if (!fs->file)
{
if (psize)
*psize = 0;
return 0;
}
fflush (fs->file);
if (fstat(fileno(fs->file), &stbuf) == -1)
return errno;
......@@ -166,16 +193,24 @@ static int
_file_flush (stream_t stream)
{
struct _file_stream *fs = stream_get_owner (stream);
if (fs->file)
return fflush (fs->file);
return 0;
}
static int
_file_get_fd (stream_t stream, int *pfd)
{
struct _file_stream *fs = stream_get_owner (stream);
int status = 0;
if (pfd)
{
if (fs->file)
*pfd = fileno (fs->file);
return 0;
else
status = EINVAL;
}
return status;
}
static int
......
......@@ -261,7 +261,7 @@ int
mailbox_is_updated (mailbox_t mbox)
{
if (mbox == NULL || mbox->_is_updated == NULL)
return ENOSYS;
return 1;
return mbox->_is_updated (mbox);
}
......
......@@ -18,20 +18,9 @@
/*
Things to consider:
- A group should create an address node for a group, accessable
with address_get_personal(). Perhaps an is_group() would be
useful? Test that a zero-length phrase is rejected! So these
are invalid:
: a@b ;
"" : ;
- When parsing phrase, should I ignore non-ascii, or replace with a
'?' character? Right now parsing fails.
- Make domain optional in addr-spec, for parsing address lists
provided to local mail utilities, but NOT in the addr-spec of a
route-addr.
- Are comments allowed in domain-literals?
- Need a way to mark the *end* of a group. Maybe add a field to _address,
......@@ -53,14 +42,12 @@ Things to consider:
gets one address, or just say it is or it isn't in RFC format?
Right now we're strict, we'll see how it goes.
- parse field names and bodies?
- parse dates?
- parse Received: field?
- test for memory leaks on malloc failure
- fix the realloc, try a struct _string { char* b, size_t sz };
- get example mail from drums, and from the perl code.
- get example addresses from rfc2822, and from the perl code.
*/
#ifdef HAVE_CONFIG_H
......@@ -610,12 +597,15 @@ int parse822_address_list(address_t* a, const char* s)
int parse822_address(const char** p, const char* e, address_t* a)
{
/* address = mailbox / group */
/* address = mailbox / group / unix-mbox */
int rc;
if((rc = parse822_mail_box(p, e, a)) == EPARSE)
rc = parse822_group(p, e, a);
if((rc = parse822_mail_box(p, e, a)) == EPARSE) {
if((rc = parse822_group(p, e, a)) == EPARSE) {
rc = parse822_unix_mbox(p, e, a);
}
}
return rc;
}
......@@ -690,7 +680,9 @@ int parse822_group(const char** p, const char* e, address_t* a)
int parse822_mail_box(const char** p, const char* e, address_t* a)
{
/* mailbox = addr-spec [ "(" comment ")" ] / [phrase] route-addr
/* mailbox =
* addr-spec [ "(" comment ")" ] /
* [phrase] route-addr
*
* Note: we parse the ancient comment on the right since
* it's such "common practice". :-(
......@@ -717,10 +709,6 @@ int parse822_mail_box(const char** p, const char* e, address_t* a)
return rc;
}
if(rc != EPARSE) {
*p = save;
return rc;
}
/* -> phrase route-addr */
{
......@@ -732,17 +720,23 @@ int parse822_mail_box(const char** p, const char* e, address_t* a)
return rc;
}
if((rc = parse822_route_addr(p, e, a))) {
if((rc = parse822_route_addr(p, e, a)) == EOK) {
/* add the phrase */
(*a)->personal = phrase;
return EOK;
} else if(rc != EPARSE) {
/* some internal error, fail out */
*p = save;
str_free(&phrase);
return rc;
}
*p = save;
/* add the phrase */
(*a)->personal = phrase;
return rc;
}
return EOK;
return rc;
}
int parse822_route_addr(const char** p, const char* e, address_t* a)
......@@ -874,6 +868,29 @@ int parse822_addr_spec(const char** p, const char* e, address_t* a)
return rc;
}
int parse822_unix_mbox(const char** p, const char* e, address_t* a)
{
/* unix-mbox = atom */
const char* save = *p;
char* mbox = 0;
int rc;
parse822_skip_comments(p, e);
rc = parse822_atom(p, e, &mbox);
if(!rc) {
rc = fill_mb(a, 0, 0, mbox, 0);
}
if(rc) {
*p = save;
str_free(&mbox);
}
return rc;
}
int parse822_local_part(const char** p, const char* e, char** local_part)
{
/* local-part = word *("." word)
......