Commit 720c9b75 720c9b75ad87d0e02a8a387ea74b1e9741facce7 by Sergey Poznyakoff

Allow to set mailbox permissions using fileinto Sieve action.

* mailbox/permstr.c: New file.
* mailbox/Makefile.am (libmailutils_la_SOURCES): Add permstr.c.
* imap4d/imap4d.c (parse_mode_spec): Remove function.
(cb_mailbox_mode): Use mu_parse_stream_perm_string instead of
parse_mode_spec.
* imap4d/imap4d.h (namespace_getfullpath)
(namespace_checkfullpath): First arg is const.
* imap4d/namespace.c (namespace_getfullpath)
(namespace_checkfullpath): First arg is const.
* include/mailutils/message.h (mu_message_save_to_mailbox): Add
5th argument (permissions).
* mailbox/message.c: Likewise.
* include/mailutils/mutil.h (mu_parse_stream_perm_string): New
proto.
(mu_stream_flags_to_mode): Add isdir argument.
* libsieve/actions.c (sieve_action_fileinto): New tag :permissions
allows to set permissions to the mailbox being created.
* mailbox/mutil.c (mu_stream_flags_to_mode): Add 2nd argument
(isdir). Any bits in [go] sets imply raising 'x' bit for
directories (i.e. if isdir != 0).

* libproto/maildir/mbox.c, mailbox/amd.c: Update calls to
mu_stream_flags_to_mode.

* NEWS, doc/texinfo/sieve.texi: Update.

Allow to set file permissions of the local mailboxes during
creation.
1 parent 35baeb22
2008-11-07 Sergey Poznyakoff <gray@gnu.org.ua>
Allow to set mailbox permissions using fileinto Sieve action.
* mailbox/permstr.c: New file.
* mailbox/Makefile.am (libmailutils_la_SOURCES): Add permstr.c.
* imap4d/imap4d.c (parse_mode_spec): Remove function.
(cb_mailbox_mode): Use mu_parse_stream_perm_string instead of
parse_mode_spec.
* imap4d/imap4d.h (namespace_getfullpath)
(namespace_checkfullpath): First arg is const.
* imap4d/namespace.c (namespace_getfullpath)
(namespace_checkfullpath): First arg is const.
* include/mailutils/message.h (mu_message_save_to_mailbox): Add
5th argument (permissions).
* mailbox/message.c: Likewise.
* include/mailutils/mutil.h (mu_parse_stream_perm_string): New
proto.
(mu_stream_flags_to_mode): Add isdir argument.
* libsieve/actions.c (sieve_action_fileinto): New tag :permissions
allows to set permissions to the mailbox being created.
* mailbox/mutil.c (mu_stream_flags_to_mode): Add 2nd argument
(isdir). Any bits in [go] sets imply raising 'x' bit for
directories (i.e. if isdir != 0).
* libproto/maildir/mbox.c, mailbox/amd.c: Update calls to
mu_stream_flags_to_mode.
* NEWS, doc/texinfo/sieve.texi: Update.
Allow to set file permissions of the local mailboxes during
creation.
......
GNU mailutils NEWS -- history of user-visible changes. 2008-10-23
GNU mailutils NEWS -- history of user-visible changes. 2008-11-07
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
2008 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -61,7 +61,9 @@ message', below.
The communication protocol has been modified to make this possible.
The traditional comsat protocol is supported as well.
** New Sieve action `pipe'
** Sieve
*** New Sieve action `pipe'
Syntax: pipe [:envelope] <command line: string>
......@@ -69,6 +71,18 @@ This action executes the given <command line> and pipes the message to
its standard input. If the :envelope tag is given, the envelope of the
message is piped as well.
*** Fileinto :permissions
The `fileinto' action takes a tag :permissions that allows to set
permissions on the mailbox, in case it is created. Its argument is a
string, similar to that used in chmod(1):
[go](+|=)[rw]
For example:
fileinto :permissions "g=rw,o=r" "/shared/mailbox"
** Client SMTP STARTTLS support
** Support for new protocols: POPS (pops://) and IMAPS (imaps://),
......
@c This is part of the GNU Mailutils manual.
@c Copyright (C) 1999,2000,2001,2002,2003,2004,
@c 2007 Free Software Foundation, Inc.
@c 2007, 2008 Free Software Foundation, Inc.
@c See file mailutils.texi for copying conditions.
@comment *******************************************************************
......@@ -929,7 +929,7 @@ if header :contains ["from"] ["idiot@@example.edu"]
@end smallexample
@end deffn
@deffn Action fileinto @var{folder}
@deffn Action fileinto [:permissions @var{mode}] @var{folder}
@noindent
Required arguments:
......@@ -939,7 +939,74 @@ Required arguments:
A string representing the folder name
@end table
The @code{fileinto} action delivers the message into the specified folder.
@noindent
Tagged arguments:
@table @code
@item :permissions @var{mode}
Specifies the permissions to use, if the mailbox is created.
@end table
The @code{fileinto} action delivers the message into the specified
folder. If the folder is local, it is created using permissions
@samp{0600}, for regular files, and @samp{0700} for directories. This
default can be changed by using the @code{:permissions} tag. Its
argument is a mode specification, similar to that used by
@command{chmod} shell utility. It is a list of permissions settings
separated by commas. Each setting begins with one of the following
letters:
@table @asis
@item g
Set permissions for the users in the file group.
@item o
Set permissions for users not in the file's group.
@end table
This letter must be followed by either @samp{+} or @samp{=} and the
list of permissions to be set. This latter list is a string
containing any one or both of the following characters:
@table @asis
@item r
Grant permission to read.
@item w
Grant permission to write.
@end table
For example, the following instruction creates the mailbox
@file{~/shared} which will be world readable and writable
for the group:
@smallexample
fileinto :permissions "g=rw,o=r" "~/shared"
@end smallexample
Notice that:
@enumerate 1
@item
The @code{:permissions} setting are affected by the current umask
value.
@item
Only @code{r} and @code{w} permissions can be set, since other
permissions do not seem to be useful for mailboxes. However, for
mailboxes that have a directory structure (such as maildir and MH),
any settings in @samp{g} and @samp{o} sets imply setting the
executable bit.
@item
Owner's permissions cannot be set. The owner always has all
permissions on the mailbox he created.
@item
The @code{:permissions} settings apply only to local mailboxes. They
are ignored for remote mailboxes.
@end enumerate
@end deffn
@deffn Action reject @var{reason}
......
......@@ -284,101 +284,13 @@ cb_preauth (mu_debug_t debug, void *data, mu_config_value_t *val)
return 0;
}
#define FILE_MODE_READ 0x1
#define FILE_MODE_WRITE 0x2
static int
parse_mode_bits (int *pmode, const char *str, const char **endp)
{
switch (*str)
{
case '+':
case '=':
str++;
break;
default:
*endp = str;
return 1;
}
for (; *str; str++)
{
switch (*str)
{
case 'r':
*pmode |= FILE_MODE_READ;
break;
case 'w':
*pmode |= FILE_MODE_WRITE;
break;
case ',':
*endp = str;
return 0;
default:
*endp = str;
return 1;
}
}
*endp = str;
return 0;
}
static int
parse_mode_spec (int *pmode, const char *str, const char **endp)
{
int mode = 0;
int f;
while (*str)
{
switch (*str)
{
case 'g':
if (parse_mode_bits (&f, str + 1, &str))
{
*endp = str;
return 1;
}
if (f & FILE_MODE_READ)
mode |= MU_STREAM_IRGRP;
if (f & FILE_MODE_WRITE)
mode |= MU_STREAM_IWGRP;
break;
case 'o':
if (parse_mode_bits (&f, str + 1, &str))
{
*endp = str;
return 1;
}
if (f & FILE_MODE_READ)
mode |= MU_STREAM_IROTH;
if (f & FILE_MODE_WRITE)
mode |= MU_STREAM_IWOTH;
break;
default:
*endp = str;
return 1;
}
if (*str == ',')
str++;
}
*pmode = mode;
*endp = str;
return 0;
}
static int
cb_mailbox_mode (mu_debug_t debug, void *data, mu_config_value_t *val)
{
char *p;
const char *p;
if (mu_cfg_assert_value_type (val, MU_CFG_STRING, debug))
return 1;
if (parse_mode_spec ((int *)data, val->v.string, &p))
if (mu_parse_stream_perm_string ((int *)data, val->v.string, &p))
mu_cfg_format_error (debug, MU_DEBUG_ERROR,
_("invalid mode string near %s"), p);
return 0;
......
......@@ -283,8 +283,9 @@ extern mu_list_t namespace[NS_MAX];
extern int namespace_init_session (char *path);
extern void namespace_init (void);
extern char *namespace_getfullpath (char *name, const char *delim, int *pns);
extern char *namespace_checkfullpath (char *name, const char *pattern,
extern char *namespace_getfullpath (const char *name, const char *delim,
int *pns);
extern char *namespace_checkfullpath (const char *name, const char *pattern,
const char *delim, int *pns);
int imap4d_session_setup (char *username);
int imap4d_session_setup0 (void);
......
......@@ -166,11 +166,12 @@ risky_pattern (const char *pattern, int delim)
}
char *
namespace_checkfullpath (char *name, const char *pattern, const char *delim,
int *nspace)
namespace_checkfullpath (const char *name, const char *pattern,
const char *delim, int *nspace)
{
struct namespace_info info;
char *p, *path = NULL;
const char *p;
char *path = NULL;
char *scheme = NULL;
p = strchr (name, ':');
......@@ -226,17 +227,18 @@ namespace_checkfullpath (char *name, const char *pattern, const char *delim,
}
char *
namespace_getfullpath (char *name, const char *delim, int *nspace)
namespace_getfullpath (const char *name, const char *delim, int *nspace)
{
char *ret;
if (strcasecmp (name, "INBOX") == 0 && auth_data->change_uid)
{
name = strdup (auth_data->mailbox);
ret = strdup (auth_data->mailbox);
if (nspace)
*nspace = NS_PRIVATE;
}
else
name = namespace_checkfullpath (name, NULL, delim, nspace);
return name;
ret = namespace_checkfullpath (name, NULL, delim, nspace);
return ret;
}
int
......
......@@ -124,7 +124,8 @@ extern int mu_message_get_attachment_name (mu_message_t, char *name,
extern int mu_message_aget_attachment_name (mu_message_t, char **name);
extern int mu_message_save_to_mailbox (mu_message_t msg, mu_ticket_t ticket,
mu_debug_t debug, const char *toname);
mu_debug_t debug, const char *toname,
int perms);
extern int mu_stream_to_message (mu_stream_t instream, mu_message_t *pmsg);
......
......@@ -155,7 +155,10 @@ extern size_t mu_strftime (char *s, size_t max, const char *format,
extern int mutil_parse_field_map (const char *map, mu_assoc_t *passoc_tab,
int *perr);
extern int mu_stream_flags_to_mode (int flags);
extern int mu_stream_flags_to_mode (int flags, int isdir);
extern int mu_parse_stream_perm_string (int *pmode, const char *str,
const char **endp);
#ifdef __cplusplus
......
......@@ -435,7 +435,8 @@ maildir_create (struct _amd_data *amd, int flags)
char *tmpname = maildir_mkfilename (amd->name, dirs[i], NULL);
int rc = maildir_opendir (&dir, tmpname,
PERMS |
mu_stream_flags_to_mode (amd->mailbox->flags));
mu_stream_flags_to_mode (amd->mailbox->flags,
1));
if (rc)
return rc;
closedir (dir);
......@@ -528,7 +529,8 @@ maildir_flush (struct _amd_data *amd)
char *tmpname = maildir_mkfilename (amd->name, TMPSUF, NULL);
rc = maildir_opendir (&dir, tmpname,
PERMS | mu_stream_flags_to_mode (amd->mailbox->flags));
PERMS |
mu_stream_flags_to_mode (amd->mailbox->flags, 1));
if (rc)
{
free (tmpname);
......@@ -668,7 +670,8 @@ maildir_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED,
name = maildir_mkfilename (amd->name, NEWSUF, NULL);
status = maildir_opendir (&dir, name,
PERMS | mu_stream_flags_to_mode (mailbox->flags));
PERMS |
mu_stream_flags_to_mode (mailbox->flags, 1));
if (status == 0)
{
maildir_deliver_new (amd, dir);
......@@ -679,7 +682,8 @@ maildir_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED,
name = maildir_mkfilename (amd->name, CURSUF, NULL);
/* 3rd phase: Scan cur/ */
status = maildir_opendir (&dir, name,
PERMS | mu_stream_flags_to_mode (mailbox->flags));
PERMS |
mu_stream_flags_to_mode (mailbox->flags, 1));
if (status == 0)
{
status = maildir_scan_dir (amd, dir, CURSUF);
......
......@@ -79,19 +79,34 @@ static int
sieve_action_fileinto (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
{
int rc;
int mbflags = 0;
mu_sieve_value_t *opt;
mu_sieve_value_t *val = mu_sieve_value_get (args, 0);
if (!val)
{
mu_sieve_error (mach, _("cannot get filename!"));
mu_sieve_abort (mach);
}
if (mu_sieve_tag_lookup (tags, "permissions", &opt))
{
const char *p;
if (mu_parse_stream_perm_string (&mbflags, opt->v.string, &p))
{
/* Should not happen, but anyway... */
mu_sieve_error (mach, _("invalid permissions (near %s)"), p);
return 1;
}
}
mu_sieve_log_action (mach, "FILEINTO",
_("delivering into %s"), val->v.string);
if (mu_sieve_is_dry_run (mach))
return 0;
rc = mu_message_save_to_mailbox (mach->msg, mach->ticket, mach->debug,
val->v.string);
val->v.string, mbflags);
if (rc)
mu_sieve_error (mach, _("cannot save to mailbox: %s"),
mu_strerror (rc));
......@@ -506,14 +521,54 @@ mu_sieve_data_type fileinto_args[] = {
SVT_VOID
};
static int
perms_tag_checker (const char *name, mu_list_t tags, mu_list_t args)
{
mu_iterator_t itr;
int err = 0;
if (!tags || mu_list_get_iterator (tags, &itr))
return 0;
for (mu_iterator_first (itr); !err && !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
int flag;
char *p;
mu_sieve_runtime_tag_t *t;
mu_iterator_current (itr, (void **)&t);
if (strcmp (t->tag, "permissions") == 0)
{
if (mu_parse_stream_perm_string (&flag, t->arg->v.string, &p))
{
mu_sv_compile_error (&mu_sieve_locus,
_("invalid permissions (near %s)"), p);
err = 1;
}
}
}
mu_iterator_destroy (&itr);
return err;
}
static mu_sieve_tag_def_t perms_tags[] = {
{ "permissions", SVT_STRING },
{ NULL }
};
static mu_sieve_tag_group_t fileinto_tag_groups[] = {
{ perms_tags, perms_tag_checker },
{ NULL }
};
void
mu_sv_register_standard_actions (mu_sieve_machine_t mach)
{
mu_sieve_register_action (mach, "stop", sieve_action_stop, NULL, NULL, 1);
mu_sieve_register_action (mach, "keep", sieve_action_keep, NULL, NULL, 1);
mu_sieve_register_action (mach, "discard", sieve_action_discard, NULL, NULL, 1);
mu_sieve_register_action (mach, "discard", sieve_action_discard,
NULL, NULL, 1);
mu_sieve_register_action (mach, "fileinto", sieve_action_fileinto,
fileinto_args, NULL, 0);
fileinto_args, fileinto_tag_groups, 0);
mu_sieve_register_action (mach, "reject", sieve_action_reject, fileinto_args,
NULL, 0);
mu_sieve_register_action (mach, "redirect", sieve_action_redirect,
......
......@@ -108,6 +108,7 @@ libmailutils_la_SOURCES = \
opool.c\
parse822.c\
parsedate.c\
permstr.c\
progmailer.c\
property.c\
registrar.c\
......
......@@ -336,9 +336,8 @@ amd_open (mu_mailbox_t mailbox, int flags)
if ((flags & MU_STREAM_CREAT) && errno == ENOENT)
{
int rc;
if (mkdir (amd->name,
S_IRUSR|S_IWUSR|S_IXUSR|mu_stream_flags_to_mode (flags)))
int perms = mu_stream_flags_to_mode (flags, 1);
if (mkdir (amd->name, S_IRUSR|S_IWUSR|S_IXUSR|perms))
return errno;
if (stat (amd->name, &st) < 0)
return errno;
......
......@@ -488,7 +488,7 @@ _file_open (mu_stream_t stream)
return errno;
/* Race condition here when creating the file ??. */
fd = open (filename, flg|O_CREAT|O_EXCL,
0600 | mu_stream_flags_to_mode (flags));
0600 | mu_stream_flags_to_mode (flags, 0));
if (fd < 0)
return errno;
}
......
......@@ -1112,7 +1112,7 @@ message_body_read (mu_stream_t stream, char *buffer, size_t n, mu_off_t off,
int
mu_message_save_to_mailbox (mu_message_t msg, mu_ticket_t ticket,
mu_debug_t debug,
const char *toname)
const char *toname, int perms)
{
int rc = 0;
mu_mailbox_t to = 0;
......@@ -1148,7 +1148,9 @@ mu_message_save_to_mailbox (mu_message_t msg, mu_ticket_t ticket,
}
}
if ((rc = mu_mailbox_open (to, MU_STREAM_WRITE | MU_STREAM_CREAT)))
if ((rc = mu_mailbox_open (to,
MU_STREAM_WRITE | MU_STREAM_CREAT
| (perms & MU_STREAM_IMASK))))
{
MU_DEBUG2 (debug, MU_DEBUG_ERROR,
"mu_mailbox_open (%s) failed: %s\n", toname,
......
......@@ -1491,7 +1491,7 @@ mu_sql_decode_password_type (const char *arg, enum mu_password_type *t)
}
int
mu_stream_flags_to_mode (int flags)
mu_stream_flags_to_mode (int flags, int isdir)
{
int mode = 0;
if (flags & MU_STREAM_IRGRP)
......@@ -1502,5 +1502,14 @@ mu_stream_flags_to_mode (int flags)
mode |= S_IROTH;
if (flags & MU_STREAM_IWOTH)
mode |= S_IWOTH;
if (isdir)
{
if (mode & (S_IRGRP|S_IWGRP))
mode |= S_IXGRP;
if (mode & (S_IROTH|S_IWOTH))
mode |= S_IXOTH;
}
return mode;
}
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2008 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA */
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/stream.h>
#include <mailutils/mutil.h>
#include <mailutils/errno.h>
#define FILE_PERM_READ 0x1
#define FILE_PERM_WRITE 0x2
static int
parse_perm_bits (int *pmode, const char *str, const char **endp)
{
switch (*str)
{
case '+':
case '=':
str++;
break;
default:
if (endp)
*endp = str;
return 1;
}
for (; *str; str++)
{
switch (*str)
{
case 'r':
*pmode |= FILE_PERM_READ;
break;
case 'w':
*pmode |= FILE_PERM_WRITE;
break;
case ',':
if (endp)
*endp = str;
return 0;
default:
if (endp)
*endp = str;
return 1;
}
}
if (endp)
*endp = str;
return 0;
}
/* Parse a MU stream permission specification in form:
g(+|=)[wr]+,o(+|=)[wr]+
Return 0 on success.
On failure, return MU_ERR_FAILURE and point endp to the offending character.
*/
int
mu_parse_stream_perm_string (int *pmode, const char *str, const char **endp)
{
int mode = 0;
int f;
while (*str)
{
switch (*str)
{
case 'g':
if (parse_perm_bits (&f, str + 1, &str))
{
if (endp)
*endp = str;
return MU_ERR_FAILURE;
}
if (f & FILE_PERM_READ)
mode |= MU_STREAM_IRGRP;
if (f & FILE_PERM_WRITE)
mode |= MU_STREAM_IWGRP;
break;
case 'o':
if (parse_perm_bits (&f, str + 1, &str))
{
if (endp)
*endp = str;
return MU_ERR_FAILURE;
}
if (f & FILE_PERM_READ)
mode |= MU_STREAM_IROTH;
if (f & FILE_PERM_WRITE)
mode |= MU_STREAM_IWOTH;
break;
default:
if (endp)
*endp = str;
return MU_ERR_FAILURE;
}
if (*str == ',')
str++;
}
*pmode = mode;
if (endp)
*endp = str;
return 0;
}