Commit 2b99aa95 2b99aa9530eb3001e8015df35993d245383ffcca by Alain Magloire

* imap4d/create.c: First implementation.

	* imap4d/delete.c: First implementation.
	* imap4d/list.c: First implementation.
	* imap4d/rename.c: First implementation.
	* imap4d/util.c: New functions.
	(util_tilde_expansion): expand ~.
	(util_unquote): Remove surrounding double quotes.
	(util_getfullpath): Expand the path to absolute.
	* imap4d/fetch.c:  Did not cycle throught the items.
	(fetch_send_address): Patch from Sam Roberts, did not
	use the index.

	* mailbox/attachement.c (message_unencapsulate): Little buglet
	strncasemp() passing the wrong size.
1 parent 29972003
2001-04-22 Alain Magloire
* imap4d/create.c: First implementation.
* imap4d/delete.c: First implementation.
* imap4d/list.c: First implementation.
* imap4d/rename.c: First implementation.
* imap4d/util.c: New functions.
(util_tilde_expansion): expand ~.
(util_unquote): Remove surrounding double quotes.
(util_getfullpath): Expand the path to absolute.
* imap4d/fetch.c: Did not cycle throught the items.
(fetch_send_address): Patch from Sam Roberts, did not
use the index.
* mailbox/attachement.c (message_unencapsulate): Little buglet
strncasemp() passing the wrong size.
2001-04-19 Alain Magloire
* mailbox/smtp.c(smtp_readline): Because of the buffering mechanism,
......@@ -332,6 +349,339 @@
This feature was put in to help the imap4d server, in this protocol
you can get a substring of the message starting from a certain offset.
To make the coding of imap4d simpler the complexity was move to the
* mailbox/smtp.c(smtp_readline): Because of the buffering mechanism,
we must maintain the offset.
* imap4d/list.c: partially implemented the LIST command.
* imap4d/imap4d.c: homedir new global variable.
* imap4d/login.c: same ${HOME} in homedir.
* imap4d/util.c (util_getword): Be aware of the quotes.
* lib/fnmatch.c: New replacement file.
* lib/fnmatch.h: New replacement file.
* configure.in: Check for fnmatch().
2001-04-18 Alain Magloire
Finally took time to put the code in from Jakob first draft, in
an email excerpt:
states is the valid states for a command (eg. LOGIN is only valid in
non-authenticated state, SELECT is valid in authenticated or
selected). success is the state to enter when the command completes
successfully (STATE_NONE means no change) and failure is the state to
enter when the command fails.
* imap4d/*.c: All the commands check there state.
* imap4d/fetch.c: Cleanup the fetch code, and broke
the fetch_operation() routine in multiple routines.
(fetch_message): New function.
(fetch_header): Likewised
(fetch_content): Likewised
(fetch_io): Likewised
(fetch_header_fields): Likewised
(fetch_header_fields_not): Likewised
(fetch_send_address): Likewised
* mailbox/header.c: Some functions were usefull while
programming the imap server, put them here.
(header_aget_value): New function.
(header_aget_field_name): New function.
(header_aget_field_value): New function.
* mailbox/memory_stream.c: New file, implemented a
malloc() buffer that you can access via the stream interface.
* mailbox/file_stream.c: Remove spurious checks.
* mailbox/mapfile_stream.c: Remove spurious checks.
(_mapfile_read): check if there is no newline left.
2001-04-16 Alain Magloire
To get things to compile on Solaris, change configure to check
for -lpthread since in libc the thread functions are defined
but all return ENOSYS, you need to explicitely link with -lphtread.
the ctype functions is*() arguments should be explicitely cast
since Solaris use them as indexes. __REENTRANT as to be define
if compile with support for threads. Never realize this but
setenv() is a BSD/GNU thing, so took a variant from libiberty
to cope.
* configure.in: Check for setenv. First check if -lphtread is ok.
* lib/setenv.c: New functions.
* lib/Makefile.am: add setenv.c in EXTRA_DIST.
* imap4d/imap4d.h: Include <alloca.h>
* mailbox/attachment.c: Include <alloca.h>
* (_header_get_param): isspace() cast argument.
* mail/mail.h: check if <paths.h> is available if not
define _PATH_SENDMAIL=/usr/lib/sendmail.
* mailbox/folder_imap.c: if pthread available, define _REENTRANT.
Include <alloca.h>.
(imap_bodystructure0): isdigit() cast argument.
* mailbox/mbx_mbox.c: if pthread available, define _REENTRANT.
Include <alloca.h>.
* mailbox/parse822.c: isdigit() cast argument unsigned.
2001-04-15 Alain Magloire
The FILE* stream "stdout" is not an lvalue so it is an error to
attempt to assign to it. To be able to redirect at will stdout
we need to assign it. In GNU lib C, it was not error since stdout
stderr, and stdin are variables, but to be portable we can not
assume this. The way out is to always use fprintf () and have
a global varialbe "ofile" pointing to stdout.
* mail/*: All the files under mail been change to use fprintf()
and ofile as the default stdout.
Copyright updated.
2001-04-15 Alain Magloire
Create a argp directory, it contains the necessary
files to build a standalone libargp.a
* argp: New directory.
argp-ba.c, argp-eexst.c, argp-fmtstream.c, argp-fmtstream.h
argp-fs-xinl.c, argp-help.c, argp-namefrob.h, argp-parse.c
argp-pv.c, argp-pvh.c, argp-xinl.c, argp.h, pin.c.
* mail/mail.c: Comment out the code that use readline
specifics WITH_READLINE.
* mail/mail.h: Likewised.
* mail/util.c: Likewised.
2001-04-15 Alain Magloire
* Makefile.am: Add argp in the list of subdirs.
* configure.am: Check for argp.h and look for
argp_parse().
* lib/strchrnul.c: New function.
* lib/strndup.c: New function.
* lib/strnlen.c: New function.
* lib/vasnprintf.c: New function.
* lib/getopt.c: Updated.
* lib/getopt1.c: Updated.
* lib/getopt.h: Updated.
* frm/Makefile.am: Remove -DTESTING in CFLAGS.
* from/Makefile.am: Remove -DTESTING in CFLAGS.
Add AUTOMAKE_OPTIONS.
* imapd/Makefile.am: Likewised.
* pop3d/Makefile.am: Likewised.
* mail/Makefile.am: Likewised. Added ARGPLIB macro
2001-04-14 Sam Roberts
* examples/{Makefile,Addrs,addr.c,Addrs.good}: address test f/w.
* include/mailutils/address.h,mailbox/{address.c,parse822.c}: now
stuff a group name into an _address, and added a function to do
a quick check if it is a group.
* mailbox/parse822.c: fixed bug where ",sam@foo.bar" wasn't valid.
2001-04-14 Alain Magloire
* mailbox/folder_imap.c: When calling imap_writeline () the
cookie for the tag should be unsigned %d --> %u.
(imap_send) : The number of bytes in memmove was wrong.
* mailbox/mbx_imap.c: Some duplicate degug calls MAILBOX_DEBUG0()
removed.
(attribute_string): IMAP does not have a \\Read flag it should be
the same as \\Seen so attribute_read() == attribute_seen().
(flag_string): New function.
(imap_attr_set_flags): Use flag_string(), instead.
* mailbox/include/imap0.h: CLEAR_STATE() should also deselect
the current mailbox.
* mailbox/mbx_pop.c (pop_write): The number of bytes in the memmove
was wrong.
* imap4d/imap4d.h: Add HAVE_SECURITY_PAM_APPL_H.
* imap4d/login.c: PAM_ERROR wrongly define.
* imap4d/expunge.c: Initialise variable sp.
* imap4d/logout.c: Initialise variable sp.
* imap4d/noop.c: Initialise variable sp.
* mailbox/attachement.c (message_create_attachment): Use base_name().
* mailbox/mbx_mbox.c (mbox_tmpfile): Use base_name().
* configure.in: AC_REP_FUNC(vasprintf strcasecmp strncasecmp).
* include/mailutils/Makefile.am: Add property.h, parse822.h.
* lib/vasprintf.c: Taken from libit.
* lib/basename.c: Taken from libit/fileutils.
* lib/Makefile.am.c: Always use our basename(base_name).
(AM_INIT_AUTOMAKE): Change version to 0.0.9
2001-04-13 Sam Roberts
QNX needed to include <strings.h> for many of the useful string
functions also to define _QNX_SOURCE so <sys/time.h> doesn't have
an internal warning.
* configure.in: Check for libgen.h.
* imap4d/imap4d.h: Define __QNX_SOURCE.
* mailbox/mime.c: wasn't including config.h
* mailbox/attachment.c: QNX needed libgen.h for basename(),
include <strings.h> if define.
* mailbox/filter.c: Include <strings.h>.
* mailbox/mbx_mbox.c: Include <strings.h>.
* mailbox/folder_imap.c: Include <strings.h>.
* mailbox/header.c: Include <strings.h>.
* mailbox/property.c: Include <strings.h>.
* mailbox/registrar.c: Include <strings.h>.
* mailbox/url.c: Include <strings.h>.
* mailbox/url_pop.c: Include <strings.h>.
* pop3d/user.c: QNX says getspnam(char* n), I cast away the error.
2001-04-13 Sam Roberts
* mailbox/parse822.c, include/mailutils/parse822.h: now can
quote the local-part of and addr-spec, and a string.
2001-04-13 Sam Roberts
* doc/address.texi: updated docs, now they match the parse822.
* mailbox/parse822.c: small tweaks to the new parser, the changes
made during the tidying over the last month were:
- use C comments only.
- don't use C++ reserved words.
- fix is_digit() to be like the other is functions
- Changed return codes to:
. no mem (ENOMEM)
. function wasn't called correctly, usually a missing
argument (EINVAL)
. invalid syntax found during parsing (ENOENT)
. success == 0
- const-corrected the APIs
- removed unnecessary (in C) casts.
- mailbox_t* removed in favor of address_t.
- fix handful of memory leaks detected by Alain.
2001-04-10 Alain Magloire
* pop3d/retr.c (pop3_retr): Typo.
* pop3d/top.c (pop3_top): No need to reallocate when sending the
headers.
* doc/mailbox.texi: Put the functions in alphabetic orders.
2001-04-10 Alain Magloire
* mailbox/Makefile.am: filter_trans.c added, trans_stream.c deleted.
* mailbox/attachment.c (message_create_attachment): use
filter_create().
(message_save_attachment): use filter_create().
* mailbox/body.c (body_set_lines): Wrong comparison for the owner.
* mailbox/mbx_mbox.c: Do not count the line separtor of part
of the mailbox.
* mailbox/url.c (url_is_same_sheme): New function.
(url_is_same_user): New function.
(url_is_same_path): New function.
(url_is_same_host): New function.
(url_is_same_port): New function.
* mailbox/folder.c : Moved the is_same_*() functions in url.c
they can be generally usefull.
(is_same_sheme): Removed.
(is_same_user): Removed.
(is_same_path): Removed.
(is_same_host): Removed.
(is_same_port): Removed.
* mailbox/folder_imap.c (folder_imap_create): New function,
CREATE a new mailbox.
(folder_imap_open): Calls folder_imap_create when the MU_STREAM_CREAT
flag is set.
* mailbox/mbx_imap.c: Appending messages implemented, if the message
comes from the same imap folder, it is COPY otherwise APPEND.
(is_same_folder): New function.
(imap_append_message): Implemented.
(attribute_string): New functions.
(imap_copy_message): New function.
* mailbox/include/imap0.h: New enum, IMAP_APPEND, IMAP_APPEND_ACK,
IMAP_APPEND_CONT, IMAP_APPEND_SEND, IMAP_COPY, IMAP_COPY_ACK,
IMAP_CREATE, IMAP_CREATE_ACK.
2001-04-06 Alain Magloire
* mailbox/parse822.c: New parser.
* include/mailutils/parse822.h: New file.
* mailbox/address.c (address_create): Remove the old parsing and use
parse822 as the underlying engine.
(address_parse): Removed.
(gettoken): Removed.
(quotes): Removed.
(address_get_personal): Remove the code to unquote, parse822 takes
care if it. Return value when no field is ENOENT.
(address_get_comments): Return value when no field ENOENT.
(address_get_local_part): Return value when no field ENOENT.
(address_get_domain): Return value when no field ENOENT.
(address_get_email): Return value when no field ENOENT.
(address_get_route): Return value when no field ENOENT.
* mailbox/message.c (message_sender): Use parse822 to retrieve
the email from the From: field.
(message_set_mailbox): New function.
* mailbox/misc.c : Removed the old parsing code.
(gettoken): Removed.
(parseaddr): Removed.
* mailbox/include/misc.h : Removed parseaddr() prototypes.
From Sam Roberts, the new parse822 parser..
2001-04-04 Alain Magloire
Introduction of the notion of filter_t object takes a stream and
perform some filtering on it. All the decoding streams will move
to this i.e. quoted-printable, base64 etc .. This scheme will also
permit users to add to the list new filters. Still work in progress.
* mailbox/Makefile.am : Add filter.c filter_rfc822.c.
* mailbox/body.c : When creating a floating body i.e creating
a temporary file, the stream was not "own" by the body_t.
(_body_get_fd): Likewised.
(_body_read):_ Likewised.
(_body_readline): Likewised.
(_body_write): Likewised.
(_body_truncate): Likewised.
(_body_size): Likewised.
(_body_flush): Likewised.
* mailbox/folder_imap.c (imap_literal_string): Check if the
callback.buffer is NULL.
(imap_body): Do no set the callback.type if "FIELDS" is part of the
string.
* mailbox/header.c: Remove the support for RFC822 it will be part
of the filter_t object.
* mailbox/mbx_mbox.c: Likewised.
* mailbox/mailbox.c (mailbox_size): Rename to mailbox_get_size().
* mailbox/stream.c (stream_is_seekable): New function.
(stream_set_property): New function.
(stream_get_property): New function.
* mailbox/trans_stream.c: Beautify.
* include/mailutils/filter.h: new file.
* doc/mailbox.texi: Updated.
More changes to come.
2001-03-20 Alain Magloire
* mailbox/folder_imap.c (imap_readline) : If the server goes away
suddenly return an error.
* mailbox/smtp.c (smtp_readline) : If the server goes away
suddenly return an error.
* mailbox/mbx_pop.c (pop_readline) : If the server goes away suddenly
return an error.
* mailbox/mbx_pop.c (pop_open) : The ticket comes from the folder.
2001-03-17 Vesselin Atanasov
* configure.in: AC_REQUIRE is not .. required. Just call
jm_FUNC_MALLOC and jm_FUNC_REALLOC.
2001-03-17 Alain Magloire
Clean the property object, mailbox and mailer have property object
that you can examine. Still a draft: the value of objects are ints.
This feature was put in to help the imap4d server, in this protocol
you can get a substring of the message starting from a certain offset.
To make the coding of imap4d simpler the complexity was move to the
library. It is possible now by setting a property of the mailbox
to make it output rfc822 stream i.e. "\r\n" terminated stream, the
offset and the size are also in term of rfc822.
......
......@@ -16,6 +16,7 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "imap4d.h"
#include <unistd.h>
/*
* must create a new mailbox
......@@ -24,7 +25,76 @@
int
imap4d_create (struct imap4d_command *command, char *arg)
{
char *name;
char *sp = NULL;
const char *delim = "/";
int rc = RESP_OK;
const char *msg = "Completed";
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
return util_finish (command, RESP_NO, "Command not supported");
name = util_getword (arg, &sp);
if (!name)
return util_finish (command, RESP_BAD, "Too few arguments");
util_unquote (&name);
if (*name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
/* Creating, "Inbox" should always fail. */
if (strcasecmp (name, "INBOX") == 0)
return util_finish (command, RESP_BAD, "Already exist");
/* Allocates memory. */
name = util_getfullpath (name, delim);
/* It will fail if the mailbx already exists. */
if (access (name, F_OK) != 0)
{
char *dir;
char *d = name + strlen (delim); /* Pass the root delimeter. */
if (chdir (delim) == 0) /* start on the root. */
for (; (dir = strchr (d, delim[0])); d = dir)
{
*dir++ = '\0';
if (chdir (d) != 0)
{
if (mkdir (d, 0700) == 0)
{
if (chdir (d) == 0)
{
continue;
}
else
{
rc = RESP_NO;
msg = "Can not create mailbox";
break;
}
}
}
}
/* If it ended with the delim they wanted to create a new folder. */
if (rc == RESP_OK && d && *d != '\0')
{
int fd = creat (d, 0600);
if (fd != -1)
close (fd);
else
{
rc = RESP_NO;
msg = "Can not create mailbox";
}
}
}
else
{
rc = RESP_NO;
msg = "already exists";
}
chdir (homedir);
free (name);
return util_finish (command, rc, msg);
}
......
......@@ -24,7 +24,36 @@
int
imap4d_delete (struct imap4d_command *command, char *arg)
{
char *sp = NULL;
int rc = RESP_OK;
const char *msg = "Completed";
const char *delim = "/";
char *name;
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
return util_finish (command, RESP_NO, "Command not supported");;
name = util_getword (arg, &sp);
if (!name)
return util_finish (command, RESP_BAD, "Too few arguments");
util_unquote (&name);
if (*name == '\0')
return util_finish (command, RESP_BAD, "Too few arguments");
/* Deleting, "Inbox" should always fail. */
if (strcasecmp (name, "INBOX") == 0)
return util_finish (command, RESP_BAD, "Already exist");
/* Allocates memory. */
name = util_getfullpath (name, delim);
if (remove (name) != 0)
{
rc = RESP_NO;
msg = "Can not remove";
}
free (name);
return util_finish (command, rc, msg);
}
......
......@@ -105,7 +105,6 @@ imap4d_fetch (struct imap4d_command *command, char *arg)
int status;
const char *errmsg = "Completed";
struct imap4d_command *fcmd;
char item[32];
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
......@@ -114,14 +113,8 @@ imap4d_fetch (struct imap4d_command *command, char *arg)
if (!msgset)
return util_finish (command, RESP_BAD, "Too few args");
/* Get the command name. */
item[0] = '\0';
util_token (item, sizeof (item), &sp);
/* Search in the table. */
fcmd = util_getcommand (item, fetch_command_table);
if (!fcmd)
return util_finish (command, RESP_BAD, "Command unknown");
if (sp == NULL || *sp == '\0')
return util_finish (command, RESP_BAD, "Too few args");
/* Get the message numbers in set[]. */
status = util_msgset (msgset, &set, &n, 0);
......@@ -130,10 +123,26 @@ imap4d_fetch (struct imap4d_command *command, char *arg)
for (i = 0; i < n; i++)
{
/* We use the states field to hold the msgno/uid. */
fcmd->states = set[i];
char item[32];
char *items = strdup (sp);
char *p = items;
util_send ("* FETCH %d (", set[i]);
fcmd->func (fcmd, sp);
item[0] = '\0';
/* Get the fetch command names. */
while (*items && *items != ')')
{
util_token (item, sizeof (item), &items);
/* Search in the table. */
fcmd = util_getcommand (item, fetch_command_table);
if (fcmd)
{
/* We use the states field to hold the msgno/uid. */
fcmd->states = set[i];
fcmd->func (fcmd, items);
util_send (" ");
}
}
free (p);
util_send (")\r\n");
}
free (set);
......@@ -358,19 +367,19 @@ fetch_rfc822 (struct imap4d_command *command, char *arg)
{
if (*arg == '.')
{
if (strcasecmp (arg, ".SIZE") == 0)
if (strncasecmp (arg, ".SIZE", 5) == 0)
{
struct imap4d_command c_rfc= fetch_command_table[F_RFC822_SIZE];
c_rfc.states = command->states;
fetch_rfc822_size (&c_rfc, arg);
}
else if (strcasecmp (arg, ".TEXT") == 0)
else if (strncasecmp (arg, ".TEXT", 5) == 0)
{
struct imap4d_command c_rfc = fetch_command_table[F_RFC822_TEXT];
c_rfc.states = command->states;
fetch_rfc822_text (&c_rfc, arg);
}
else if (strcasecmp (arg, ".HEADER") == 0)
else if (strncasecmp (arg, ".HEADER", 7) == 0)
{
struct imap4d_command c_rfc = fetch_command_table[F_RFC822_HEADER];
c_rfc.states = command->states;
......@@ -824,33 +833,46 @@ fetch_send_address (char *addr)
util_send ("(");
*buf = '\0';
address_get_personal (address, 1, buf, sizeof (buf), NULL);
address_get_personal (address, i, buf, sizeof (buf), NULL);
if (*buf == '\0')
util_send ("NIL ");
util_send ("NIL");
else
util_send ("\"%s\" ", buf);
util_send ("\"%s\"", buf);
util_send (" ");
*buf = '\0';
address_get_route (address, 1, buf, sizeof (buf), NULL);
address_get_route (address, i, buf, sizeof (buf), NULL);
if (*buf == '\0')
util_send ("NIL ");
util_send ("NIL");
else
util_send ("\"%s\" ", buf);
util_send ("NIL ", buf);
util_send ("\"%s\"", buf);
util_send (" ");
*buf = '\0';
address_get_local_part (address, 1, buf, sizeof (buf), NULL);
{
int is_group = 0;
address_is_group(address, i, &is_group);
if(is_group)
address_get_personal (address, i, buf, sizeof (buf), NULL);
else
address_get_local_part (address, i, buf, sizeof (buf), NULL);
}
if (*buf == '\0')
util_send ("NIL ");
util_send ("NIL");
else
util_send ("\"%s\" ", buf);
util_send ("\"%s\"", buf);
util_send (" ");
*buf = '\0';
address_get_domain (address, 1, buf, sizeof (buf), NULL);
address_get_domain (address, i, buf, sizeof (buf), NULL);
if (*buf == '\0')
util_send ("NIL ");
util_send ("NIL");
else
util_send ("\"%s\" ", buf);
util_send ("\"%s\"", buf);
util_send (")");
}
......
......@@ -47,6 +47,8 @@
#include <stdarg.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
......@@ -111,47 +113,50 @@ extern char *homedir;
extern int state;
/* Imap4 commands */
int imap4d_capability __P ((struct imap4d_command *, char *));
int imap4d_noop __P ((struct imap4d_command *, char *));
int imap4d_logout __P ((struct imap4d_command *, char *));
int imap4d_authenticate __P ((struct imap4d_command *, char *));
int imap4d_login __P ((struct imap4d_command *, char *));
int imap4d_select __P ((struct imap4d_command *, char *));
int imap4d_select0 __P ((struct imap4d_command *, char *, int));
int imap4d_examine __P ((struct imap4d_command *, char *));
int imap4d_create __P ((struct imap4d_command *, char *));
int imap4d_delete __P ((struct imap4d_command *, char *));
int imap4d_rename __P ((struct imap4d_command *, char *));
int imap4d_subscribe __P ((struct imap4d_command *, char *));
int imap4d_unsubscribe __P ((struct imap4d_command *, char *));
int imap4d_list __P ((struct imap4d_command *, char *));
int imap4d_lsub __P ((struct imap4d_command *, char *));
int imap4d_status __P ((struct imap4d_command *, char *));
int imap4d_append __P ((struct imap4d_command *, char *));
int imap4d_check __P ((struct imap4d_command *, char *));
int imap4d_close __P ((struct imap4d_command *, char *));
int imap4d_expunge __P ((struct imap4d_command *, char *));
int imap4d_search __P ((struct imap4d_command *, char *));
int imap4d_fetch __P ((struct imap4d_command *, char *));
int imap4d_store __P ((struct imap4d_command *, char *));
int imap4d_copy __P ((struct imap4d_command *, char *));
int imap4d_uid __P ((struct imap4d_command *, char *));
extern int imap4d_capability __P ((struct imap4d_command *, char *));
extern int imap4d_noop __P ((struct imap4d_command *, char *));
extern int imap4d_logout __P ((struct imap4d_command *, char *));
extern int imap4d_authenticate __P ((struct imap4d_command *, char *));
extern int imap4d_login __P ((struct imap4d_command *, char *));
extern int imap4d_select __P ((struct imap4d_command *, char *));
extern int imap4d_select0 __P ((struct imap4d_command *, char *, int));
extern int imap4d_examine __P ((struct imap4d_command *, char *));
extern int imap4d_create __P ((struct imap4d_command *, char *));
extern int imap4d_delete __P ((struct imap4d_command *, char *));
extern int imap4d_rename __P ((struct imap4d_command *, char *));
extern int imap4d_subscribe __P ((struct imap4d_command *, char *));
extern int imap4d_unsubscribe __P ((struct imap4d_command *, char *));
extern int imap4d_list __P ((struct imap4d_command *, char *));
extern int imap4d_lsub __P ((struct imap4d_command *, char *));
extern int imap4d_status __P ((struct imap4d_command *, char *));
extern int imap4d_append __P ((struct imap4d_command *, char *));
extern int imap4d_check __P ((struct imap4d_command *, char *));
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_store __P ((struct imap4d_command *, char *));
extern int imap4d_copy __P ((struct imap4d_command *, char *));
extern int imap4d_uid __P ((struct imap4d_command *, char *));
/* Helper functions. */
int util_out __P ((int rc, const char *f, ...));
int util_send __P ((const char *f, ...));
int util_start __P ((char *tag));
int util_finish __P ((struct imap4d_command *, int sc, const char *f, ...));
int util_getstate __P ((void));
int util_do_command __P ((char *prompt));
char *imap4d_readline __P ((int fd));
void util_quit __P ((int));
char *util_getword __P ((char *s, char **save_ptr));
int util_token __P ((char *s, size_t, char **save_ptr));
int util_msgset __P ((char *s, int **set, int *n, int isuid));
int util_upper __P ((char *));
struct imap4d_command *util_getcommand __P ((char *cmd,
struct imap4d_command []));
extern int util_out __P ((int, const char *, ...));
extern int util_send __P ((const char *, ...));
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 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_upper __P ((char *));
extern struct imap4d_command *util_getcommand __P ((char *,
struct imap4d_command []));
#ifdef __cplusplus
}
......
......@@ -16,168 +16,260 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "imap4d.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#define NOMATCH (0)
#define MATCH (1 << 0)
#define RECURSE_MATCH (1 << 1)
#define NOMATCH (1 << 2)
#define NOSELECT (1 << 3)
#define NOINFERIORS (1 << 4)
#define NOSELECT_RECURSE (1 << 5)
#define NOSELECT (1 << 2)
#define NOINFERIORS (1 << 3)
#define NOSELECT_RECURSE (1 << 4)
/*
*
*/
1- IMAP4 insists: the reference argument that is include in the
interpreted form SHOULD prefix the interpreted form. It SHOULD
also be in the same form as the reference name argument. This
rule permits the client to determine if the returned mailbox name
is in the context of the reference argument, or if something about
the mailbox argument overrode the reference argument.
ex:
Reference Mailbox --> Interpretation
~smith/Mail foo.* --> ~smith/Mail/foo.*
archive % --> archive/%
#news comp.mail.* --> #news.comp.mail.*
~smith/Mail /usr/doc/foo --> /usr/doc/foo
archive ~fred/Mail --> ~fred/Mail/ *
static void unquote (char **);
static int match (const char *, const char *);
static int imap_match (const char *, const char *);
static void list_file (const char *, const char *);
static char *expand (const char *);
static void print_file (const char *, const char *);
static void print_dir (const char *, const char *);
2- The character "*" is a wildcard, and matches zero or more characters
at this position. The charcater "%" is similar to "*",
but it does not match ahierarchy delimiter. */
static int match __P ((const char *, const char *, const char *));
static int imap_match __P ((const char *, const char *, const char *));
static void list_file __P ((const char *, const char *, char *, const char *));
static void print_file __P ((const char *, const char *, const char *));
static void print_dir __P ((const char *, const char *, const char *));
int
imap4d_list (struct imap4d_command *command, char *arg)
{
char *sp = NULL;
char *reference;
char *wildcard;
char *ref;
char *wcard;
const char *delim = "/";
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
reference = util_getword (arg, &sp);
wildcard = util_getword (NULL, &sp);
if (!reference || !wildcard)
ref = util_getword (arg, &sp);
wcard = util_getword (NULL, &sp);
if (!ref || !wcard)
return util_finish (command, RESP_BAD, "Too few arguments");
unquote (&reference);
unquote (&wildcard);
if (*wildcard == '\0')
/* Remove the double quotes. */
util_unquote (&ref);
util_unquote (&wcard);
/* If wildcard is empty, it is a special case: we have to
return the hierarchy. */
if (*wcard == '\0')
{
/* FIXME: How do we know the hierarchy delimeter. */
util_out (RESP_NONE, "LIST (\\NoSelect) \"/\" \"%s\"",
(*reference == '\0' || *reference != '/') ? "" : "/");
util_out (RESP_NONE, "LIST (\\NoSelect) \"%s\" \"%s\"", delim,
(*ref) ? delim : "");
}
/* There is only one mailbox in the "INBOX" hierarchy ... INBOX. */
else if (strcasecmp (ref, "INBOX") == 0)
{
util_out (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
}
else
{
char *p;
char *cwd;
char *dir;
switch (*wcard)
{
/* Absolute Path in wcard, dump the old ref. */
case '/':
{
ref = calloc (2, 1);
ref[0] = *wcard;
wcard++;
}
break;
if (*reference == '\0')
reference = homedir;
/* Absolute Path, but take care of things like ~guest/Mail,
ref becomes ref = ~guest. */
case '~':
{
char *s = strchr (wcard, '/');
if (s)
{
ref = calloc (s - wcard + 1, 1);
memcpy (ref, wcard, s - wcard);
ref [s - wcard] = '\0';
wcard = s + 1;
}
else
{
ref = strdup (wcard);
wcard += strlen (wcard);
}
}
break;
p = expand (reference);
if (chdir (p) == 0)
{
list_file (reference, wildcard);
chdir (homedir);
default:
ref = strdup (ref);
}
free (p);
}
return util_finish (command, RESP_OK, "Completed");
}
/* expand the '~' */
static char *
expand (const char *ref)
{
/* FIXME: note done. */
return strdup (ref);
}
/* Move any directory not containing a wildcard into the reference
So (ref = ~guest, wcard = Mail/folder1/%.vf) -->
(ref = ~guest/Mail/folder1, wcard = %.vf). */
for (; (dir = strpbrk (wcard, "/%*")); wcard = dir)
{
if (*dir == '/')
{
*dir = '\0';
ref = realloc (ref, strlen (ref) + 1 + (dir - wcard) + 1);
if (*ref && ref[strlen (ref) - 1] != '/')
strcat (ref, "/");
strcat (ref, wcard);
dir++;
}
else
break;
}
/* Remove the surrounding double quotes. */
static void
unquote (char **ptr)
{
char *s = *ptr;
if (*s == '"')
{
char *p = ++s;
while (*p && *p != '"')
p++;
if (*p == '"')
*p = '\0';
}
*ptr = s;
}
/* Allocates. */
cwd = util_getfullpath (ref, delim);
static void
print_file (const char *ref, const char *file)
{
if (strpbrk (file, "\"{}"))
{
util_out (RESP_NONE, "LIST (\\NoInferior \\UnMarked) \"/\" {%d}",
strlen (ref) + strlen ((*ref) ? "/" : "") + strlen (file));
util_send ("%s%s%s\r\n", ref, (*ref) ? "/" : "", file);
}
else
util_out (RESP_NONE, "LIST (\\NoInferior \\UnMarked) \"/\" %s%s%s",
ref, (*ref) ? "/" : "", file);
}
/* If wcard match inbox return it too, part of the list. */
if (!*ref && (match ("INBOX", wcard, delim)
|| match ("inbox", wcard, delim)))
util_out (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX");
static void
print_dir (const char *ref, const char *file)
{
if (strpbrk (file, "\"{}"))
{
util_out (RESP_NONE, "LIST (\\NoSelect) \"/\" {%d}",
strlen (ref) + strlen ((*ref) ? "/" : "") + strlen (file));
util_send ("%s%s%s\r\n", ref, (*ref) ? "/" : "", file);
if (chdir (cwd) == 0)
{
list_file (cwd, ref, dir, delim);
chdir (homedir);
}
free (cwd);
free (ref);
}
else
util_out (RESP_NONE, "LIST (\\NoSelect) \"/\" %s%s%s",
ref, (*ref) ? "/" : "", file);
return util_finish (command, RESP_OK, "Completed");
}
/* Recusively calling the files. */
static void
list_file (const char *ref, const char *pattern)
list_file (const char *cwd, const char *ref, char *pattern, const char *delim)
{
DIR *dirp;
struct dirent *dp;
char *next;
/* Shortcut no wildcards. */
if (!strpbrk (pattern, "%*"))
{
/* Equivalent to stat(). */
int status = match (pattern, pattern, delim);
if (status & NOSELECT)
print_dir (ref, pattern, delim);
else if (status & NOINFERIORS)
print_file (ref, pattern, delim);
return ;
}
dirp = opendir (".");
if (dirp == NULL)
return;
next = strchr (pattern, delim[0]);
if (next)
*next++ = '\0';
while ((dp = readdir (dirp)) != NULL)
{
int status = match (dp->d_name, pattern);
if (status & (MATCH | RECURSE_MATCH))
/* Skip "", ".", and "..". "" is returned by at least one buggy
implementation: Solaris 2.4 readdir on NFS filesystems. */
char const *entry = dp->d_name;
if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
{
if (status & NOSELECT)
int status = match (entry, pattern, delim);
if (status)
{
print_dir (ref, dp->d_name);
if (status & RECURSE_MATCH)
if (status & NOSELECT)
{
if (chdir (dp->d_name) == 0)
if (next || status & RECURSE_MATCH)
{
char *buffer = NULL;
asprintf (&buffer, "%s%s%s", ref,
(*ref) ? "/" : "", dp->d_name);
list_file (buffer, pattern);
free (buffer);
chdir ("..");
if (!next)
print_dir (ref, entry, delim);
if (chdir (entry) == 0)
{
char *rf;
char *cd;
rf = calloc (strlen (ref) + strlen (delim) +
strlen (entry) + 1, 1);
sprintf (rf, "%s%s%s", ref, delim, entry);
cd = calloc (strlen (cwd) + strlen (delim) +
strlen (entry) + 1, 1);
sprintf (cd, "%s%s%s", cwd, delim, entry);
list_file (cd, rf, (next) ? next : pattern, delim);
free (rf);
free (cd);
chdir (cwd);
}
}
else
print_dir (ref, entry, delim);
}
else if (status & NOINFERIORS)
{
print_file (ref, entry, delim);
}
}
else if (status & NOINFERIORS)
{
print_file (ref, dp->d_name);
}
}
}
closedir (dirp);
}
/* Make sure that the file name does not contain any undesirable
chars like "{}. If yes send it as a literal string. */
static void
print_file (const char *ref, const char *file, const char *delim)
{
if (strpbrk (file, "\"{}"))
{
util_out (RESP_NONE, "LIST (\\NoInferiors) \"%s\" {%d}", delim,
strlen (ref) + strlen ((*ref) ? delim : "") + strlen (file));
util_send ("%s%s%s\r\n", ref, (*ref) ? delim : "", file);
}
else
util_out (RESP_NONE, "LIST (\\NoInferiors) \"%s\" %s%s%s", delim,
ref, (*ref) ? delim : "", file);
}
/* Make sure that the file name does not contain any undesirable
chars like "{}. If yes send it as a literal string. */
static void
print_dir (const char *ref, const char *file, const char *delim)
{
if (strpbrk (file, "\"{}"))
{
util_out (RESP_NONE, "LIST (\\NoSelect) \"%s\" {%d}", delim,
strlen (ref) + strlen ((*ref) ? delim : "") + strlen (file));
util_send ("%s%s%s\r\n", ref, (*ref) ? delim : "", file);
}
else
util_out (RESP_NONE, "LIST (\\NoSelect) \"%s\" %s%s%s", delim,
ref, (*ref) ? delim : "", file);
}
/* Calls the imap_matcher if a match found out the attribute. */
static int
match (const char *entry, const char *pattern)
match (const char *entry, const char *pattern, const char *delim)
{
struct stat stats;
int status = imap_match (entry, pattern);
if (status & MATCH || status || RECURSE_MATCH)
int status = imap_match (entry, pattern, delim);
if (status)
{
if (stat (entry, &stats) == 0)
status |= (S_ISREG (stats.st_mode)) ? NOINFERIORS : NOSELECT;
......@@ -188,21 +280,27 @@ match (const char *entry, const char *pattern)
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, nonzero if not. */
static int
imap_match (const char *string, const char *pattern)
imap_match (const char *string, const char *pattern, const char *delim)
{
const char *p = pattern, *n = string;
char c;
for (;(c = *p++) != '\0'; n++)
for (;(c = *p++) != '\0' && *n; n++)
{
switch (c)
{
case '%':
if (*p == '\0')
return (*n == '/') ? NOMATCH : MATCH;
for (; *n != '\0'; ++n)
if (match (n, p) == MATCH)
return MATCH;
{
/* Matches everything except '/' */
for (; *n && *n != delim[0]; n++)
;
return (*n == '/') ? RECURSE_MATCH : MATCH;
}
else
for (; *n != '\0'; ++n)
if (imap_match (n, p, delim) == MATCH)
return MATCH;
break;
case '*':
......@@ -210,7 +308,7 @@ imap_match (const char *string, const char *pattern)
return RECURSE_MATCH;
for (; *n != '\0'; ++n)
{
int status = match (n, p);
int status = imap_match (n, p, delim);
if (status == MATCH || status == RECURSE_MATCH)
return status;
}
......@@ -222,10 +320,9 @@ imap_match (const char *string, const char *pattern)
}
}
if (*n == '\0')
if (!c && !*n)
return MATCH;
return NOMATCH;
}
......
......@@ -24,7 +24,109 @@
int
imap4d_rename (struct imap4d_command *command, char *arg)
{
char *oldname;
char *newname;
char *sp = NULL;
int rc = RESP_OK;
const char *msg = "Completed";
struct stat newst;
const char *delim = "/";
if (! (command->states & state))
return util_finish (command, RESP_BAD, "Wrong state");
return util_finish (command, RESP_NO, "Not Supported");
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 (strcasecmp (newname, "INBOX") == 0)
return util_finish (command, RESP_NO, "Name Inbox is reservered");
/* Allocates memory. */
newname = util_getfullpath (newname, delim);
/* It is an error to attempt to rename from a mailbox name that already
exist. */
if (stat (newname, &newst) == 0)
{
if (!S_ISDIR(newst.st_mode))
{
free (newname);
return util_finish (command, RESP_NO, "Already exist, delete first");
}
}
/* Renaming INBOX is permitted, and has special behavior. It moves
all messages in INBOX to a new mailbox with the given name,
leaving INBOX empty. */
if (strcasecmp (oldname, "INBOX") == 0)
{
mailbox_t newmbox = NULL;
char *name;
struct passwd *pw;
if (S_ISDIR(newst.st_mode))
{
free (newname);
return util_finish (command, RESP_NO, "Can not be a directory");
}
name = calloc (strlen ("mbox:") + strlen (newname) + 1, 1);
sprintf (name, "mbox:%s", newname);
if (mailbox_create (&newmbox, newname) != 0
|| mailbox_open (newmbox, MU_STREAM_CREAT | MU_STREAM_RDWR) != 0)
{
free (name);
free (newname);
return util_finish (command, RESP_NO, "Can not create new mailbox");
}
free (name);
free (newname);
pw = getpwuid (getuid ());
if (pw)
{
mailbox_t inbox = NULL;
if (mailbox_create_default (&inbox, pw->pw_name) == 0 &&
mailbox_open (inbox, MU_STREAM_RDWR) == 0)
{
size_t no;
size_t total = 0;
mailbox_messages_count (inbox, &total);
for (no = 1; no <= total; no++)
{
message_t message;
if (mailbox_get_message (inbox, no, &message) == 0)
{
attribute_t attr = NULL;
mailbox_append_message (newmbox, message);
message_get_attribute (message, &attr);
attribute_set_deleted (attr);
}
}
mailbox_expunge (inbox);
mailbox_close (inbox);
mailbox_destroy (&inbox);
}
}
mailbox_close (newmbox);
mailbox_destroy (&newmbox);
return util_finish (command, RESP_OK, "Already exist");
}
oldname = util_getfullpath (oldname, delim);
/* It must exist. */
if (rename (oldname, newname) != 0)
{
rc = RESP_NO;
msg = "Failed";
}
free (oldname);
free (newname);
return util_finish (command, rc, msg);
}
......
......@@ -58,6 +58,9 @@ util_token (char *buf, size_t len, char **ptr)
/* 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 == '.'
......@@ -76,7 +79,82 @@ util_token (char *buf, size_t len, char **ptr)
/* Skip trailing space. */
while (**ptr && **ptr == ' ')
(*ptr)++;
return *ptr - start;;
return *ptr - start;
}
/* Remove the surrounding double quotes. */
void
util_unquote (char **ptr)
{
char *s = *ptr;
if (*s == '"')
{
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 *
util_tilde_expansion (const char *ref, const char *delim)
{
char *p = strdup (ref);
if (*p == '~')
{
p++;
if (*p == delim[0] || *p == '\0')
{
char *s = calloc (strlen (homedir) + strlen (p) + 1, 1);
strcpy (s, homedir);
strcat (s, p);
free (--p);
p = s;
}
else
{
struct passwd *pw;
char *s = p;
char *name;
while (*s && *s != delim[0])
s++;
name = calloc (s - p + 1, 1);
memcpy (name, p, s - p);
name [s - p] = '\0';
pw = getpwnam (name);
free (name);
if (pw)
{
char *buf = calloc (strlen (pw->pw_dir) + strlen (s) + 1, 1);
strcpy (buf, pw->pw_dir);
strcat (buf, s);
free (--p);
p = buf;
}
else
p--;
}
}
return p;
}
/* Absolute path. */
char *
util_getfullpath (char *name, const char *delim)
{
char *p = util_tilde_expansion (name, delim);
if (*p != delim[0])
{
char *s = calloc (strlen (homedir) + strlen (delim) + strlen (p) + 1, 1);
sprintf (s, "%s%s%s", homedir, delim, p);
free (p);
p = s;
}
return p;
}
/* Return in set an allocated array contain (n) numbers, for imap messsage set
......
......@@ -346,7 +346,7 @@ int message_unencapsulate(message_t msg, message_t *newmsg, void **data)
if ( ( content_type = alloca(size+1) ) == NULL )
return ENOMEM;
header_get_value(hdr, "Content-Type", content_type, size+1, 0);
if ( strncasecmp(content_type, "message/rfc822", strlen(content_type)) != 0 )
if ( strncasecmp(content_type, "message/rfc822", strlen("message/rfc822")) != 0 )
return EINVAL;
} else
return EINVAL;
......
......@@ -1533,6 +1533,7 @@ imap_token (char *buf, size_t len, char **ptr)
/* Skip leading space. */
while (**ptr && **ptr == ' ')
(*ptr)++;
/* Break the string by token, when we recognise Atoms we stop. */
for (i = 1; **ptr && i < len; (*ptr)++, buf++, i++)
{
if (**ptr == ' ' || **ptr == '.'
......