mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-21 18:38:08 +01:00
cli/insert: add --world-readable flag
In some cases (e.g. when building a publicly-visible e-mail archive) it doesn't make any sense to restrict visibility of the message to the current user account. This adds a --world-readable boolean option for "notmuch insert", so that those who want to archive their mail publicly can feed their archiver with: notmuch insert --world-readable Other local delivery agents (postfix's local, and dovecot's lda) all default to delivery in mode 0600 rather than relying on the user's umask, so this fix doesn't change the default. Also, this does not override the user's umask. if the umask is already set tight, it will not become looser as the result of passing --world-readable. Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
This commit is contained in:
parent
0cbe982bfd
commit
b6e3efde05
3 changed files with 65 additions and 11 deletions
|
@ -51,6 +51,12 @@ Supported options for **insert** include
|
||||||
``--no-hooks``
|
``--no-hooks``
|
||||||
Prevent hooks from being run.
|
Prevent hooks from being run.
|
||||||
|
|
||||||
|
``--world-readable``
|
||||||
|
When writing mail to the mailbox, allow it to be read by users
|
||||||
|
other than the current user. Note that this does not override
|
||||||
|
umask. By default, delivered mail is only readable by the current
|
||||||
|
user.
|
||||||
|
|
||||||
``--decrypt=(true|nostash|auto|false)``
|
``--decrypt=(true|nostash|auto|false)``
|
||||||
If ``true`` and the message is encrypted, try to decrypt the
|
If ``true`` and the message is encrypted, try to decrypt the
|
||||||
message while indexing, stashing any session keys discovered. If
|
message while indexing, stashing any session keys discovered. If
|
||||||
|
|
|
@ -159,10 +159,10 @@ mkdir_recursive (const void *ctx, const char *path, int mode)
|
||||||
* otherwise. Partial results are not cleaned up on errors.
|
* otherwise. Partial results are not cleaned up on errors.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
maildir_create_folder (const void *ctx, const char *maildir)
|
maildir_create_folder (const void *ctx, const char *maildir, bool world_readable)
|
||||||
{
|
{
|
||||||
const char *subdirs[] = { "cur", "new", "tmp" };
|
const char *subdirs[] = { "cur", "new", "tmp" };
|
||||||
const int mode = 0700;
|
const int mode = (world_readable ? 0755 : 0700);
|
||||||
char *subdir;
|
char *subdir;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
@ -211,10 +211,11 @@ tempfilename (const void *ctx)
|
||||||
* is not touched).
|
* is not touched).
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
maildir_mktemp (const void *ctx, const char *maildir, char **path_out)
|
maildir_mktemp (const void *ctx, const char *maildir, bool world_readable, char **path_out)
|
||||||
{
|
{
|
||||||
char *filename, *path;
|
char *filename, *path;
|
||||||
int fd;
|
int fd;
|
||||||
|
const int mode = (world_readable ? 0644 : 0600);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
filename = tempfilename (ctx);
|
filename = tempfilename (ctx);
|
||||||
|
@ -227,7 +228,7 @@ maildir_mktemp (const void *ctx, const char *maildir, char **path_out)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = open (path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600);
|
fd = open (path, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
|
||||||
} while (fd == -1 && errno == EEXIST);
|
} while (fd == -1 && errno == EEXIST);
|
||||||
|
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
|
@ -289,12 +290,12 @@ copy_fd (int fdout, int fdin)
|
||||||
* the file, or NULL on errors.
|
* the file, or NULL on errors.
|
||||||
*/
|
*/
|
||||||
static char *
|
static char *
|
||||||
maildir_write_tmp (const void *ctx, int fdin, const char *maildir)
|
maildir_write_tmp (const void *ctx, int fdin, const char *maildir, bool world_readable)
|
||||||
{
|
{
|
||||||
char *path;
|
char *path;
|
||||||
int fdout;
|
int fdout;
|
||||||
|
|
||||||
fdout = maildir_mktemp (ctx, maildir, &path);
|
fdout = maildir_mktemp (ctx, maildir, world_readable, &path);
|
||||||
if (fdout < 0)
|
if (fdout < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -323,11 +324,11 @@ FAIL:
|
||||||
* errors.
|
* errors.
|
||||||
*/
|
*/
|
||||||
static char *
|
static char *
|
||||||
maildir_write_new (const void *ctx, int fdin, const char *maildir)
|
maildir_write_new (const void *ctx, int fdin, const char *maildir, bool world_readable)
|
||||||
{
|
{
|
||||||
char *cleanpath, *tmppath, *newpath, *newdir;
|
char *cleanpath, *tmppath, *newpath, *newdir;
|
||||||
|
|
||||||
tmppath = maildir_write_tmp (ctx, fdin, maildir);
|
tmppath = maildir_write_tmp (ctx, fdin, maildir, world_readable);
|
||||||
if (! tmppath)
|
if (! tmppath)
|
||||||
return NULL;
|
return NULL;
|
||||||
cleanpath = tmppath;
|
cleanpath = tmppath;
|
||||||
|
@ -457,6 +458,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
|
||||||
bool create_folder = false;
|
bool create_folder = false;
|
||||||
bool keep = false;
|
bool keep = false;
|
||||||
bool hooks = true;
|
bool hooks = true;
|
||||||
|
bool world_readable = false;
|
||||||
bool synchronize_flags;
|
bool synchronize_flags;
|
||||||
char *maildir;
|
char *maildir;
|
||||||
char *newpath;
|
char *newpath;
|
||||||
|
@ -467,7 +469,8 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
|
||||||
{ .opt_string = &folder, .name = "folder", .allow_empty = true },
|
{ .opt_string = &folder, .name = "folder", .allow_empty = true },
|
||||||
{ .opt_bool = &create_folder, .name = "create-folder" },
|
{ .opt_bool = &create_folder, .name = "create-folder" },
|
||||||
{ .opt_bool = &keep, .name = "keep" },
|
{ .opt_bool = &keep, .name = "keep" },
|
||||||
{ .opt_bool = &hooks, .name = "hooks" },
|
{ .opt_bool = &hooks, .name = "hooks" },
|
||||||
|
{ .opt_bool = &world_readable, .name = "world-readable" },
|
||||||
{ .opt_inherit = notmuch_shared_indexing_options },
|
{ .opt_inherit = notmuch_shared_indexing_options },
|
||||||
{ .opt_inherit = notmuch_shared_options },
|
{ .opt_inherit = notmuch_shared_options },
|
||||||
{ }
|
{ }
|
||||||
|
@ -523,7 +526,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
strip_trailing (maildir, '/');
|
strip_trailing (maildir, '/');
|
||||||
if (create_folder && ! maildir_create_folder (config, maildir))
|
if (create_folder && ! maildir_create_folder (config, maildir, world_readable))
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
/* Set up our handler for SIGINT. We do not set SA_RESTART so that copying
|
/* Set up our handler for SIGINT. We do not set SA_RESTART so that copying
|
||||||
|
@ -535,7 +538,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
|
||||||
sigaction (SIGINT, &action, NULL);
|
sigaction (SIGINT, &action, NULL);
|
||||||
|
|
||||||
/* Write the message to the Maildir new directory. */
|
/* Write the message to the Maildir new directory. */
|
||||||
newpath = maildir_write_new (config, STDIN_FILENO, maildir);
|
newpath = maildir_write_new (config, STDIN_FILENO, maildir, world_readable);
|
||||||
if (! newpath) {
|
if (! newpath) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ test_description='"notmuch insert"'
|
||||||
|
|
||||||
test_require_external_prereq gdb
|
test_require_external_prereq gdb
|
||||||
|
|
||||||
|
# subtests about file permissions assume that we're working with umask
|
||||||
|
# 022 by default.
|
||||||
|
umask 022
|
||||||
|
|
||||||
# Create directories and database before inserting.
|
# Create directories and database before inserting.
|
||||||
mkdir -p "$MAIL_DIR"/{cur,new,tmp}
|
mkdir -p "$MAIL_DIR"/{cur,new,tmp}
|
||||||
mkdir -p "$MAIL_DIR"/Drafts/{cur,new,tmp}
|
mkdir -p "$MAIL_DIR"/Drafts/{cur,new,tmp}
|
||||||
|
@ -37,6 +41,9 @@ notmuch insert < "$gen_msg_filename"
|
||||||
cur_msg_filename=$(notmuch search --output=files "subject:insert-subject")
|
cur_msg_filename=$(notmuch search --output=files "subject:insert-subject")
|
||||||
test_expect_equal_file "$cur_msg_filename" "$gen_msg_filename"
|
test_expect_equal_file "$cur_msg_filename" "$gen_msg_filename"
|
||||||
|
|
||||||
|
test_begin_subtest "Permissions on inserted message should be 0600"
|
||||||
|
test_expect_equal "600" "$(stat -c %a "$cur_msg_filename")"
|
||||||
|
|
||||||
test_begin_subtest "Insert message adds default tags"
|
test_begin_subtest "Insert message adds default tags"
|
||||||
output=$(notmuch show --format=json "subject:insert-subject")
|
output=$(notmuch show --format=json "subject:insert-subject")
|
||||||
expected='[[[{
|
expected='[[[{
|
||||||
|
@ -73,6 +80,27 @@ notmuch insert +custom < "$gen_msg_filename"
|
||||||
output=$(notmuch search --output=messages tag:custom)
|
output=$(notmuch search --output=messages tag:custom)
|
||||||
test_expect_equal "$output" "id:$gen_msg_id"
|
test_expect_equal "$output" "id:$gen_msg_id"
|
||||||
|
|
||||||
|
test_begin_subtest "Insert tagged world-readable message"
|
||||||
|
gen_insert_msg
|
||||||
|
notmuch insert --world-readable +world-readable-test < "$gen_msg_filename"
|
||||||
|
cur_msg_filename=$(notmuch search --output=files "tag:world-readable-test")
|
||||||
|
test_expect_equal_file "$cur_msg_filename" "$gen_msg_filename"
|
||||||
|
|
||||||
|
test_begin_subtest "Permissions on inserted world-readable message should be 0644"
|
||||||
|
test_expect_equal "644" "$(stat -c %a "$cur_msg_filename")"
|
||||||
|
|
||||||
|
test_begin_subtest "Insert tagged world-readable message with group-only umask"
|
||||||
|
oldumask=$(umask)
|
||||||
|
umask 027
|
||||||
|
gen_insert_msg
|
||||||
|
notmuch insert --world-readable +world-readable-umask-test < "$gen_msg_filename"
|
||||||
|
cur_msg_filename=$(notmuch search --output=files "tag:world-readable-umask-test")
|
||||||
|
umask "$oldumask"
|
||||||
|
test_expect_equal_file "$cur_msg_filename" "$gen_msg_filename"
|
||||||
|
|
||||||
|
test_begin_subtest "Permissions on inserted world-readable message with funny umask should be 0640"
|
||||||
|
test_expect_equal "640" "$(stat -c %a "$cur_msg_filename")"
|
||||||
|
|
||||||
test_begin_subtest "Insert message, add/remove tags"
|
test_begin_subtest "Insert message, add/remove tags"
|
||||||
gen_insert_msg
|
gen_insert_msg
|
||||||
notmuch insert +custom -unread < "$gen_msg_filename"
|
notmuch insert +custom -unread < "$gen_msg_filename"
|
||||||
|
@ -170,6 +198,23 @@ output=$(notmuch search --output=files path:F/G/H/I/J/new tag:folder)
|
||||||
basename=$(basename "$output")
|
basename=$(basename "$output")
|
||||||
test_expect_equal_file "$gen_msg_filename" "${MAIL_DIR}/F/G/H/I/J/new/${basename}"
|
test_expect_equal_file "$gen_msg_filename" "${MAIL_DIR}/F/G/H/I/J/new/${basename}"
|
||||||
|
|
||||||
|
test_begin_subtest "Created subfolder should have permissions 0700"
|
||||||
|
test_expect_equal "700" "$(stat -c %a "${MAIL_DIR}/F/G/H/I/J")"
|
||||||
|
test_begin_subtest "Created subfolder new/ should also have permissions 0700"
|
||||||
|
test_expect_equal "700" "$(stat -c %a "${MAIL_DIR}/F/G/H/I/J/new")"
|
||||||
|
|
||||||
|
test_begin_subtest "Insert message, create world-readable subfolder"
|
||||||
|
gen_insert_msg
|
||||||
|
notmuch insert --folder=F/G/H/I/J/K --create-folder --world-readable +folder-world-readable < "$gen_msg_filename"
|
||||||
|
output=$(notmuch search --output=files path:F/G/H/I/J/K/new tag:folder-world-readable)
|
||||||
|
basename=$(basename "$output")
|
||||||
|
test_expect_equal_file "$gen_msg_filename" "${MAIL_DIR}/F/G/H/I/J/K/new/${basename}"
|
||||||
|
|
||||||
|
test_begin_subtest "Created world-readable subfolder should have permissions 0755"
|
||||||
|
test_expect_equal "755" "$(stat -c %a "${MAIL_DIR}/F/G/H/I/J/K")"
|
||||||
|
test_begin_subtest "Created world-readable subfolder new/ should also have permissions 0755"
|
||||||
|
test_expect_equal "755" "$(stat -c %a "${MAIL_DIR}/F/G/H/I/J/K/new")"
|
||||||
|
|
||||||
test_begin_subtest "Insert message, create existing subfolder"
|
test_begin_subtest "Insert message, create existing subfolder"
|
||||||
gen_insert_msg
|
gen_insert_msg
|
||||||
notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename"
|
notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename"
|
||||||
|
|
Loading…
Reference in a new issue