I wanted to have a group-readable maildir store so that my agentic user could
read but not modify my email. To allow for existing behavior to continue (user
access only), while allowing group readable/writeable maildir stores, I'm
proposing the addition of a GroupAccess option at the Maildir Store level. If
it is enabled, new messages are created mode 0660 (subject to OS/umask rules)
and new directories 0770. The option should default to false. The proposed
patch does not attempt to address changes to the new GroupAccess configuration.
It is left to the user to repermission files/directories appropriately in the
even the Maildir needs to have its permissions adjusted.
Coding and testing was driven by the pi coding agent configured with GLM-5.3.
Regards,
-- Archit
diff --git a/src/common.h b/src/common.h
index 38521b6..0c3ffa7 100644
--- a/src/common.h
+++ b/src/common.h
@@ -300,7 +300,7 @@ void ATTR_NORETURN oom( void );
int map_name( const char *arg, int argl, char **result, uint reserve, const char *in, const char *out );
-int mkdir_p( char *path, int len );
+int mkdir_p( char *path, int len, int mode );
#define DEFINE_ARRAY_TYPE(T) \
typedef struct { \
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
index fe0e951..4bb2d62 100644
--- a/src/drv_maildir.c
+++ b/src/drv_maildir.c
@@ -42,6 +42,7 @@ typedef union maildir_store_conf {
#endif /* USE_DB */
char info_delimiter;
char sub_style;
+ char group_access; /* create new messages with mode 0660 (subject to umask) */
char failed;
char *info_prefix, *info_stop; /* precalculated from info_delimiter */
};
@@ -588,6 +589,7 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
int i, bl, ret;
struct stat st;
char buf[_POSIX_PATH_MAX];
+ int mode = ctx->conf->group_access ? 0770 : 0700;
bl = nfsnprintf( buf, sizeof(buf) - 4, "%s/", box );
if (stat( buf, &st )) {
@@ -597,7 +599,7 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
}
if (!create)
return DRV_BOX_BAD;
- if (mkdir_p( buf, bl - 1 )) {
+ if (mkdir_p( buf, bl - 1, mode )) {
sys_error( "Maildir error: cannot create mailbox '%s'", buf );
ctx->conf->failed = FAIL_FINAL;
maildir_invoke_bad_callback( ctx );
@@ -614,7 +616,7 @@ maildir_validate( const char *box, int create, maildir_store_t *ctx )
/* We always create new/ and tmp/ if they are missing. cur/ is the presence indicator. */
if (!i && !create)
return DRV_BOX_BAD;
- if (mkdir( buf, 0700 )) {
+ if (mkdir( buf, mode )) {
sys_error( "Maildir error: cannot create directory %s", buf );
return DRV_BOX_BAD;
}
@@ -1611,7 +1613,8 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
maildir_make_flags( ctx->conf->info_delimiter, data->flags, fbuf );
nfsnprintf( buf, sizeof(buf), "%s/tmp/%s%s", box, base, fbuf );
- if ((fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, 0600 )) < 0) {
+ int mode = ctx->conf->group_access ? 0660 : 0600;
+ if ((fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, mode )) < 0) {
if (errno != ENOENT || !to_trash) {
sys_error( "Maildir error: cannot create %s", buf );
free( data->data );
@@ -1623,7 +1626,7 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
cb( ret, 0, aux );
return;
}
- if ((fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, 0600 )) < 0) {
+ if ((fd = open( buf, O_WRONLY | O_CREAT | O_EXCL, mode )) < 0) {
sys_error( "Maildir error: cannot create %s", buf );
free( data->data );
cb( DRV_BOX_BAD, 0, aux );
@@ -1909,6 +1912,8 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
cfg->err = 1;
continue;
}
+ } else if (!strcasecmp( "GroupAccess", cfg->cmd )) {
+ store->group_access = parse_bool( cfg );
} else if (!strcasecmp( "SubFolders", cfg->cmd )) {
if (!strcasecmp( "Verbatim", cfg->val )) {
store->sub_style = SUB_VERBATIM;
diff --git a/src/mbsync.1.in b/src/mbsync.1.in
index 5169b31..eb05fe0 100644
--- a/src/mbsync.1.in
+++ b/src/mbsync.1.in
@@ -276,6 +276,14 @@ See \fBRECOMMENDATIONS\fR below.
(Default: \fBno\fR)
.
.TP
+\fBGroupAccess\fR \fByes\fR|\fBno\fR
+Whether message files and mailbox directories newly created by \fBmbsync\fR
+are created with modes 0600 and 0700, or with modes 0660 and 0770,
+respectively, subject to the process umask.
+.br
+(Default: \fBno\fR)
+.
+.TP
\fBInbox\fR \fIpath\fR
The location of the \fBINBOX\fR. This is \fInot\fR relative to \fBPath\fR,
but it is allowed to place the \fBINBOX\fR inside the \fBPath\fR.
diff --git a/src/sync_state.c b/src/sync_state.c
index d499f85..7480a45 100644
--- a/src/sync_state.c
+++ b/src/sync_state.c
@@ -60,7 +60,7 @@ prepare_state( sync_vars_t *svars )
// Note that this may be shorter than the configuration value,
// as that may contain a filename prefix.
*s = 0;
- if (mkdir_p( svars->dname, s - svars->dname )) {
+ if (mkdir_p( svars->dname, s - svars->dname, 0700 )) {
sys_error( "Error: cannot create SyncState directory '%s'", svars->dname );
return 0;
}
diff --git a/src/util.c b/src/util.c
index 22e639f..700a541 100644
--- a/src/util.c
+++ b/src/util.c
@@ -797,20 +797,20 @@ map_name( const char *arg, int l, char **result, uint reserve, const char *in, c
}
int
-mkdir_p( char *path, int len )
+mkdir_p( char *path, int len, int mode )
{
- if (!mkdir( path, 0700 ) || errno == EEXIST)
+ if (!mkdir( path, mode ) || errno == EEXIST)
return 0;
char *p = memrchr( path, '/', (size_t)len );
if (!p)
return -1; // mkdir() already set errno
*p = 0;
- if (mkdir_p( path, (int)(p - path) )) {
+ if (mkdir_p( path, (int)(p - path), mode )) {
*p = '/';
return -1;
}
*p = '/';
- return mkdir( path, 0700 );
+ return mkdir( path, mode );
}
static int
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel