diff --git a/lib/mu-msg.cc b/lib/mu-msg.cc index 2615aea7..796abaad 100644 --- a/lib/mu-msg.cc +++ b/lib/mu-msg.cc @@ -413,11 +413,11 @@ Mu::mu_msg_get_date(MuMsg* self) return (time_t)get_num_field(self, MU_MSG_FIELD_ID_DATE); } -MuFlags +MessageFlags Mu::mu_msg_get_flags(MuMsg* self) { - g_return_val_if_fail(self, MU_FLAG_NONE); - return (MuFlags)get_num_field(self, MU_MSG_FIELD_ID_FLAGS); + g_return_val_if_fail(self, MessageFlags::None); + return static_cast(get_num_field(self, MU_MSG_FIELD_ID_FLAGS)); } size_t @@ -742,140 +742,47 @@ Mu::mu_msg_is_readable(MuMsg* self) return access(mu_msg_get_path(self), R_OK) == 0 ? TRUE : FALSE; } -/* we need do to determine the - * /home/foo/Maildir/bar - * from the /bar - * that we got - */ -static char* -get_target_mdir(MuMsg* msg, const char* target_maildir, GError** err) -{ - char * rootmaildir, *rv; - const char* maildir; - gboolean not_top_level; - - /* maildir is the maildir stored in the message, e.g. '/foo' */ - maildir = mu_msg_get_maildir(msg); - if (!maildir) { - mu_util_g_set_error(err, MU_ERROR_GMIME, "message without maildir"); - return NULL; - } - - /* the 'rootmaildir' is the filesystem path from root to - * maildir, ie. /home/user/Maildir/foo */ - rootmaildir = mu_maildir_get_maildir_from_path(mu_msg_get_path(msg)); - if (!rootmaildir) { - mu_util_g_set_error(err, MU_ERROR_GMIME, "cannot determine maildir"); - return NULL; - } - - /* we do a sanity check: verify that that maildir is a suffix of - * rootmaildir;*/ - not_top_level = TRUE; - if (!g_str_has_suffix(rootmaildir, maildir) && - /* special case for the top-level '/' maildir, and - * remember not_top_level */ - (not_top_level = (g_strcmp0(maildir, "/") != 0))) { - g_set_error(err, - MU_ERROR_DOMAIN, - MU_ERROR_FILE, - "path is '%s', but maildir is '%s' ('%s')", - rootmaildir, - mu_msg_get_maildir(msg), - mu_msg_get_path(msg)); - g_free(rootmaildir); - return NULL; - } - - /* if we're not at the top-level, remove the final '/' from - * the rootmaildir */ - if (not_top_level) - rootmaildir[strlen(rootmaildir) - strlen(mu_msg_get_maildir(msg))] = '\0'; - - rv = g_strconcat(rootmaildir, target_maildir, NULL); - g_free(rootmaildir); - - return rv; -} /* * move a msg to another maildir, trying to maintain 'integrity', * ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be * super-paranoid here... */ -gboolean -Mu::mu_msg_move_to_maildir(MuMsg* self, - const char* maildir, - MuFlags flags, - gboolean ignore_dups, - gboolean new_name, - GError** err) +bool +Mu::mu_msg_move_to_maildir(MuMsg* self, + const std::string& root_maildir_path, + const std::string& target_maildir, + MessageFlags flags, + bool ignore_dups, + bool new_name, + GError** err) { - char* newfullpath; - char* targetmdir; + g_return_val_if_fail(self, false); - g_return_val_if_fail(self, FALSE); - g_return_val_if_fail(maildir, FALSE); /* i.e. "/inbox" */ - /* targetmdir is the full path to maildir, i.e., - * /home/foo/Maildir/inbox */ - targetmdir = get_target_mdir(self, maildir, err); - if (!targetmdir) - return FALSE; + const auto srcpath{mu_msg_get_path(self)}; + const auto dstpath{mu_maildir_determine_target(srcpath, + root_maildir_path, + target_maildir, + flags, + new_name)}; + if (!dstpath) + return false; - newfullpath = mu_maildir_move_message(mu_msg_get_path(self), - targetmdir, - flags, - ignore_dups, - new_name, - err); - if (!newfullpath) { - g_free(targetmdir); - return FALSE; - } + if (!mu_maildir_move_message(srcpath, *dstpath, ignore_dups)) + return false; /* clear the old backends */ mu_msg_doc_destroy(self->_doc); self->_doc = NULL; - mu_msg_file_destroy(self->_file); /* and create a new one */ - self->_file = mu_msg_file_new(newfullpath, maildir, err); - g_free(targetmdir); - g_free(newfullpath); + self->_file = mu_msg_file_new(dstpath->c_str(), target_maildir.c_str(), err); - return self->_file ? TRUE : FALSE; + return !!self->_file; } -/* - * Rename a message-file, keeping the same flags. This is useful for tricking - * some 3rd party progs such as mbsync - */ -gboolean -Mu::mu_msg_tickle(MuMsg* self, GError** err) -{ - g_return_val_if_fail(self, FALSE); - - return mu_msg_move_to_maildir(self, - mu_msg_get_maildir(self), - mu_msg_get_flags(self), - FALSE, - TRUE, - err); -} - -const char* -Mu::mu_str_flags_s(MuFlags flags) -{ - return mu_flags_to_str_s(flags, MU_FLAG_TYPE_ANY); -} - -char* -Mu::mu_str_flags(MuFlags flags) -{ - return g_strdup(mu_str_flags_s(flags)); -} static void cleanup_contact(char* contact) diff --git a/lib/mu-server.cc b/lib/mu-server.cc index d76945b6..82cd527e 100644 --- a/lib/mu-server.cc +++ b/lib/mu-server.cc @@ -19,6 +19,7 @@ #include "config.h" +#include "mu-message-flags.hh" #include "mu-msg-fields.h" #include "mu-msg.hh" #include "mu-server.hh" @@ -120,13 +121,15 @@ private: const Option qm, MuMsgOptions opts) const; - Sexp::List move_docid(Store::Id docid, const std::string& flagstr, bool new_name, bool no_view); - Sexp::List perform_move(Store::Id docid, - MuMsg* msg, - const std::string& maildirarg, - MuFlags flags, - bool new_name, - bool no_view); + Sexp::List move_docid(Store::Id docid, std::optional flagstr, + bool new_name, bool no_view); + + Sexp::List perform_move(Store::Id docid, + MuMsg* msg, + const std::string& maildirarg, + MessageFlags flags, + bool new_name, + bool no_view); bool maybe_mark_as_read(MuMsg* msg, Store::Id docid, bool rename); bool maybe_mark_msgid_as_read(const char* msgid, bool rename); @@ -799,10 +802,8 @@ void Server::Private::mkdir_handler(const Parameters& params) { const auto path{get_string_or(params, ":path")}; - - GError* gerr{}; - if (!mu_maildir_mkdir(path.c_str(), 0755, FALSE, &gerr)) - throw Error{Error::Code::File, &gerr, "failed to create maildir"}; + if (auto&& res = mu_maildir_mkdir(path, 0755, FALSE); !res) + throw res.error(); Sexp::List lst; lst.add_prop(":info", Sexp::make_string("mkdir")); @@ -811,31 +812,13 @@ Server::Private::mkdir_handler(const Parameters& params) output_sexp(std::move(lst)); } -static MuFlags -get_flags(const std::string& path, const std::string& flagstr) -{ - if (flagstr.empty()) - return MU_FLAG_NONE; /* ie., ignore flags */ - else { - /* if there's a '+' or '-' sign in the string, it must - * be a flag-delta */ - if (strstr(flagstr.c_str(), "+") || strstr(flagstr.c_str(), "-")) { - auto oldflags = mu_maildir_get_flags_from_path(path.c_str()); - return mu_flags_from_str_delta(flagstr.c_str(), oldflags, MU_FLAG_TYPE_ANY); - } else - return mu_flags_from_str(flagstr.c_str(), - MU_FLAG_TYPE_ANY, - TRUE /*ignore invalid*/); - } -} - Sexp::List -Server::Private::perform_move(Store::Id docid, - MuMsg* msg, - const std::string& maildirarg, - MuFlags flags, - bool new_name, - bool no_view) +Server::Private::perform_move(Store::Id docid, + MuMsg* msg, + const std::string& maildirarg, + MessageFlags flags, + bool new_name, + bool no_view) { bool different_mdir{}; auto maildir{maildirarg}; @@ -846,7 +829,10 @@ Server::Private::perform_move(Store::Id docid, different_mdir = maildir != mu_msg_get_maildir(msg); GError* gerr{}; - if (!mu_msg_move_to_maildir(msg, maildir.c_str(), flags, TRUE, new_name, &gerr)) + + if (!mu_msg_move_to_maildir(msg, + store().properties().root_maildir, + maildir, flags, true, new_name, &gerr)) throw Error{Error::Code::File, &gerr, "failed to move message"}; /* after mu_msg_move_to_maildir, path will be the *new* path, and flags and maildir @@ -866,11 +852,30 @@ Server::Private::perform_move(Store::Id docid, return seq; } + +static MessageFlags +calculate_message_flags(MuMsg* msg, std::optional flagopt) +{ + const auto flags = std::invoke([&]()->std::optional{ + auto msgflags{mu_msg_get_flags(msg)}; + if (!flagopt) + return mu_msg_get_flags(msg); + else + return message_flags_from_expr(*flagopt, msgflags); + }); + + if (!flags) + throw Error{Error::Code::InvalidArgument, + "invalid flags '%s'", flagopt.value_or("").c_str()}; + else + return flags.value(); +} + Sexp::List -Server::Private::move_docid(Store::Id docid, - const std::string& flagstr, - bool new_name, - bool no_view) +Server::Private::move_docid(Store::Id docid, + std::optional flagopt, + bool new_name, + bool no_view) { if (docid == Store::InvalidId) throw Error{Error::Code::InvalidArgument, "invalid docid"}; @@ -880,13 +885,7 @@ Server::Private::move_docid(Store::Id docid, if (!msg) throw Error{Error::Code::Store, "failed to get message from store"}; - const auto flags = flagstr.empty() ? mu_msg_get_flags(msg) - : get_flags(mu_msg_get_path(msg), flagstr); - if (flags == MU_FLAG_INVALID) - throw Error{Error::Code::InvalidArgument, - "invalid flags '%s'", - flagstr.c_str()}; - + const auto flags = calculate_message_flags(msg, flagopt); auto lst = perform_move(docid, msg, "", flags, new_name, no_view); mu_msg_unref(msg); return lst; @@ -911,7 +910,7 @@ void Server::Private::move_handler(const Parameters& params) { auto maildir{get_string_or(params, ":maildir")}; - const auto flagstr{get_string_or(params, ":flags")}; + const auto flagopt{get_string(params, ":flags")}; const auto rename{get_bool_or(params, ":rename")}; const auto no_view{get_bool_or(params, ":noupdate")}; const auto docids{determine_docids(store_, params)}; @@ -922,7 +921,8 @@ Server::Private::move_handler(const Parameters& params) "can't move multiple messages at the same time"}; // multi. for (auto&& docid : docids) - output_sexp(move_docid(docid, flagstr, rename, no_view)); + output_sexp(move_docid(docid, flagopt, + rename, no_view)); return; } auto docid{docids.at(0)}; @@ -939,17 +939,7 @@ Server::Private::move_handler(const Parameters& params) /* determine the real target flags, which come from the flags-parameter * we received (ie., flagstr), if any, plus the existing message * flags. */ - MuFlags flags{}; - if (!flagstr.empty()) - flags = get_flags(mu_msg_get_path(msg), flagstr.c_str()); - else - flags = mu_msg_get_flags(msg); - - if (flags == MU_FLAG_INVALID) { - mu_msg_unref(msg); - throw Error{Error::Code::InvalidArgument, "invalid flags"}; - } - + const auto flags = calculate_message_flags(msg, flagopt); try { output_sexp(perform_move(docid, msg, maildir, flags, rename, no_view)); } catch (...) { @@ -967,7 +957,7 @@ Server::Private::ping_handler(const Parameters& params) if (storecount == (unsigned)-1) throw Error{Error::Code::Store, "failed to read store"}; - const auto queries = get_string_vec(params, ":queries"); + const auto queries{get_string_vec(params, ":queries")}; Sexp::List qresults; for (auto&& q : queries) { const auto count{store_.count_query(q)}; @@ -1053,16 +1043,17 @@ Server::Private::maybe_mark_as_read(MuMsg* msg, Store::Id docid, bool rename) throw Error{Error::Code::Store, "missing message"}; const auto oldflags{mu_msg_get_flags(msg)}; - const auto newflags{get_flags(mu_msg_get_path(msg), "+S-u-N")}; - if (oldflags == newflags) + const auto newflags{message_flags_from_delta_expr("+S-u-N", oldflags)}; + if (!newflags || oldflags == *newflags) return false; // nothing to do. GError* gerr{}; if (!mu_msg_move_to_maildir(msg, + store().properties().root_maildir, mu_msg_get_maildir(msg), - newflags, - TRUE, - rename ? TRUE : FALSE, + *newflags, + true, + rename, &gerr)) throw Error{Error::Code::File, &gerr, "failed to move message"}; diff --git a/lib/tests/test-mu-maildir.cc b/lib/tests/test-mu-maildir.cc index da1669f0..31491551 100644 --- a/lib/tests/test-mu-maildir.cc +++ b/lib/tests/test-mu-maildir.cc @@ -1,5 +1,5 @@ /* -** Copyright (C) 2008-2020 Dirk-Jan C. Binnema +** Copyright (C) 2008-2022 Dirk-Jan C. Binnema ** ** 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 @@ -17,16 +17,13 @@ ** */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /*HAVE_CONFIG_H*/ - #include #include #include #include #include +#include #include "test-mu-common.hh" #include "mu-maildir.hh" @@ -44,7 +41,7 @@ test_mu_maildir_mkdir_01(void) tmpdir = test_mu_common_get_random_tmpdir(); mdir = g_strdup_printf("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); - g_assert_cmpuint(mu_maildir_mkdir(mdir, 0755, FALSE, NULL), ==, TRUE); + g_assert_true(!!mu_maildir_mkdir(mdir, 0755, FALSE)); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; @@ -73,7 +70,7 @@ test_mu_maildir_mkdir_02(void) tmpdir = test_mu_common_get_random_tmpdir(); mdir = g_strdup_printf("%s%c%s", tmpdir, G_DIR_SEPARATOR, "cuux"); - g_assert_cmpuint(mu_maildir_mkdir(mdir, 0755, TRUE, NULL), ==, TRUE); + g_assert_true(!!mu_maildir_mkdir(mdir, 0755, TRUE)); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; @@ -112,7 +109,7 @@ test_mu_maildir_mkdir_03(void) } /* this should still work */ - g_assert_cmpuint(mu_maildir_mkdir(mdir, 0755, FALSE, NULL), ==, TRUE); + g_assert_true(!!mu_maildir_mkdir(mdir, 0755, FALSE)); for (i = 0; i != G_N_ELEMENTS(subs); ++i) { gchar* dir; @@ -149,9 +146,9 @@ test_mu_maildir_mkdir_04(void) } /* this should fail now, because cur is not read/writable */ - g_assert_cmpuint(mu_maildir_mkdir(mdir, 0755, FALSE, NULL), - ==, - (geteuid() == 0 ? TRUE : FALSE)); + if (geteuid() != 0) + g_assert_false(!!mu_maildir_mkdir(mdir, 0755, false)); + g_free(tmpdir); g_free(mdir); } @@ -168,30 +165,34 @@ test_mu_maildir_mkdir_05(void) /* this must fail */ g_test_log_set_fatal_handler((GTestLogFatalFunc)ignore_error, NULL); - g_assert_cmpuint(mu_maildir_mkdir(NULL, 0755, TRUE, NULL), ==, FALSE); + g_assert_false(!!mu_maildir_mkdir({}, 0755, true)); } static void -test_mu_maildir_get_flags_from_path(void) +test_mu_maildir_flags_from_path(void) { int i; struct { - const char* path; - MuFlags flags; - } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FSR", - (MuFlags)(MU_FLAG_REPLIED | MU_FLAG_SEEN | MU_FLAG_FLAGGED)}, - {"/home/foo/Maildir/test/new/123456", MU_FLAG_NEW}, - {/* NOTE: when in new/, the :2,.. stuff is ignored */ - "/home/foo/Maildir/test/new/123456:2,FR", - MU_FLAG_NEW}, - {"/home/foo/Maildir/test/cur/123456:2,DTP", - (MuFlags)(MU_FLAG_DRAFT | MU_FLAG_TRASHED | MU_FLAG_PASSED)}, - {"/home/foo/Maildir/test/cur/123456:2,S", MU_FLAG_SEEN}}; + const char* path; + MessageFlags flags; + } paths[] = { + {"/home/foo/Maildir/test/cur/123456:2,FSR", + (MessageFlags::Replied | MessageFlags::Seen | MessageFlags::Flagged)}, + {"/home/foo/Maildir/test/new/123456", MessageFlags::New}, + {/* NOTE: when in new/, the :2,.. stuff is ignored */ + "/home/foo/Maildir/test/new/123456:2,FR", + MessageFlags::New}, + {"/home/foo/Maildir/test/cur/123456:2,DTP", + (MessageFlags::Draft | MessageFlags::Trashed | MessageFlags::Passed)}, + {"/home/foo/Maildir/test/cur/123456:2,S", MessageFlags::Seen}}; for (i = 0; i != G_N_ELEMENTS(paths); ++i) { - MuFlags flags; - flags = mu_maildir_get_flags_from_path(paths[i].path); - g_assert_cmpuint(flags, ==, paths[i].flags); + auto res{mu_maildir_flags_from_path(paths[i].path)}; + g_assert_true(!!res); + if (g_test_verbose()) + g_print("%s -> <%s>\n", paths[i].path, + message_flags_to_string(res.value()).c_str()); + g_assert_true(res.value() == paths[i].flags); } } @@ -205,166 +206,242 @@ assert_matches_regexp(const char* str, const char* rx) } } + static void -test_mu_maildir_get_new_path_new(void) +test_determine_target_ok(void) { - int i; + struct TestCase { + std::string old_path; + std::string root_maildir; + std::string target_maildir; + MessageFlags new_flags; + bool new_name; + std::string expected; + }; + const std::vector testcases = { + TestCase{ /* change some flags */ + "/home/foo/Maildir/test/cur/123456:2,FR", + "/home/foo/Maildir", + {}, + MessageFlags::Seen | MessageFlags::Passed, + false, + "/home/foo/Maildir/test/cur/123456:2,PS" + }, - struct { - const char* oldpath; - MuFlags flags; - const char* newpath; - } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_REPLIED, - "/home/foo/Maildir/test/cur/123456:2,R"}, - {"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_NEW, - "/home/foo/Maildir/test/new/123456"}, - {"/home/foo/Maildir/test/new/123456:2,FR", - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_REPLIED), - "/home/foo/Maildir/test/cur/123456:2,RS"}, - {"/home/foo/Maildir/test/new/1313038887_0.697:2,", - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED), - "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS"}, - {"/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", - MU_FLAG_SEEN, - "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S"}}; + TestCase{ /* from cur -> new */ + "/home/foo/Maildir/test/cur/123456:2,FR", + "/home/foo/Maildir", + {}, + MessageFlags::New, + false, + "/home/foo/Maildir/test/new/123456" + }, - for (i = 0; i != G_N_ELEMENTS(paths); ++i) { - char *str, *newbase; - str = mu_maildir_get_new_path(paths[i].oldpath, NULL, paths[i].flags, TRUE); - newbase = g_path_get_basename(str); - assert_matches_regexp(newbase, - "\\d+\\." - "[[:xdigit:]]{16}\\." - "[[:alnum:]][[:alnum:]-]+(:2,.*)?"); - g_free(newbase); - g_free(str); + TestCase{ /* from new->cur */ + "/home/foo/Maildir/test/cur/123456", + "/home/foo/Maildir", + {}, + MessageFlags::Seen | MessageFlags::Flagged, + false, + "/home/foo/Maildir/test/cur/123456:2,FS" + }, + + TestCase{ /* change maildir */ + "/home/foo/Maildir/test/cur/123456:2,FR", + "/home/foo/Maildir", + "/test2", + MessageFlags::Flagged | MessageFlags::Replied, + false, + "/home/foo/Maildir/test2/cur/123456:2,FR" + }, + TestCase{ /* remove all flags */ + "/home/foo/Maildir/test/new/123456", + "/home/foo/Maildir", + {}, + MessageFlags::None, + false, + "/home/foo/Maildir/test/cur/123456:2," + }, + }; + + for (auto&& testcase: testcases) { + const auto res = mu_maildir_determine_target( + testcase.old_path, + testcase.root_maildir, + testcase.target_maildir, + testcase.new_flags, + testcase.new_name); + g_assert_true(!!res); + g_assert_cmpstr(testcase.expected.c_str(), ==, + res.value().c_str()); } } -static void -test_mu_maildir_get_new_path_01(void) -{ - int i; - struct { - const char* oldpath; - MuFlags flags; - const char* newpath; - } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_REPLIED, - "/home/foo/Maildir/test/cur/123456:2,R"}, - {"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_NEW, - "/home/foo/Maildir/test/new/123456"}, - {"/home/foo/Maildir/test/new/123456:2,FR", - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_REPLIED), - "/home/foo/Maildir/test/cur/123456:2,RS"}, - {"/home/foo/Maildir/test/new/1313038887_0.697:2,", - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED), - "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS"}, - {"/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", - MU_FLAG_SEEN, - "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S"}}; - for (i = 0; i != G_N_ELEMENTS(paths); ++i) { - gchar* str; - str = mu_maildir_get_new_path(paths[i].oldpath, NULL, paths[i].flags, FALSE); - g_assert_cmpstr(str, ==, paths[i].newpath); - g_free(str); - } -} -static void -test_mu_maildir_get_new_path_02(void) -{ - int i; - struct { - const char* oldpath; - MuFlags flags; - const char* targetdir; - const char* newpath; - } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_REPLIED, - "/home/foo/Maildir/blabla", - "/home/foo/Maildir/blabla/cur/123456:2,R"}, - {"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_NEW, - "/home/bar/Maildir/coffee", - "/home/bar/Maildir/coffee/new/123456"}, - {"/home/foo/Maildir/test/new/123456", - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_REPLIED), - "/home/cuux/Maildir/tea", - "/home/cuux/Maildir/tea/cur/123456:2,RS"}, - {"/home/foo/Maildir/test/new/1313038887_0.697:2,", - (MuFlags)(MU_FLAG_SEEN | MU_FLAG_FLAGGED | MU_FLAG_PASSED), - "/home/boy/Maildir/stuff", - "/home/boy/Maildir/stuff/cur/1313038887_0.697:2,FPS"}}; - for (i = 0; i != G_N_ELEMENTS(paths); ++i) { - gchar* str; - str = mu_maildir_get_new_path(paths[i].oldpath, - paths[i].targetdir, - paths[i].flags, - FALSE); - g_assert_cmpstr(str, ==, paths[i].newpath); - g_free(str); - } -} -static void -test_mu_maildir_get_new_path_custom(void) -{ - int i; - struct { - const char* oldpath; - MuFlags flags; - const char* targetdir; - const char* newpath; - } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", - MU_FLAG_REPLIED, - "/home/foo/Maildir/blabla", - "/home/foo/Maildir/blabla/cur/123456:2,R"}, - {"/home/foo/Maildir/test/cur/123456:2,hFeRllo123", - MU_FLAG_FLAGGED, - "/home/foo/Maildir/blabla", - "/home/foo/Maildir/blabla/cur/123456:2,Fhello123"}, - {"/home/foo/Maildir/test/cur/123456:2,abc", - MU_FLAG_PASSED, - "/home/foo/Maildir/blabla", - "/home/foo/Maildir/blabla/cur/123456:2,Pabc"}}; - for (i = 0; i != G_N_ELEMENTS(paths); ++i) { - gchar* str; - str = mu_maildir_get_new_path(paths[i].oldpath, - paths[i].targetdir, - paths[i].flags, - FALSE); - g_assert_cmpstr(str, ==, paths[i].newpath); - g_free(str); - } -} -static void -test_mu_maildir_get_maildir_from_path(void) -{ - unsigned u; - struct { - const char *path, *exp; - } cases[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", "/home/foo/Maildir/test"}, - {"/home/foo/Maildir/lala/new/1313038887_0.697:2,", "/home/foo/Maildir/lala"}}; +// static void +// test_mu_maildir_determine_target(void) +// { +// int i; - for (u = 0; u != G_N_ELEMENTS(cases); ++u) { - gchar* mdir; - mdir = mu_maildir_get_maildir_from_path(cases[u].path); - g_assert_cmpstr(mdir, ==, cases[u].exp); - g_free(mdir); - } -} +// struct { +// std::string oldpath; +// MessageFlags flags; +// std::string newpath; +// } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::Replied, +// "/home/foo/Maildir/test/cur/123456:2,R"}, +// {"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::New, +// "/home/foo/Maildir/test/new/123456"}, +// {"/home/foo/Maildir/test/new/123456:2,FR", +// (MessageFlags::Seen | MessageFlags::Replied), +// "/home/foo/Maildir/test/cur/123456:2,RS"}, +// {"/home/foo/Maildir/test/new/1313038887_0.697:2,", +// (MessageFlags::Seen | MessageFlags::Flagged | MessageFlags::Passed), +// "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS"}, +// {"/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", +// MessageFlags::Seen, +// "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S"}}; + +// for (i = 0; i != G_N_ELEMENTS(paths); ++i) { +// const auto res{mu_maildir_determine_target(paths[i].oldpath, +// "/home/foo/Maildir", +// {}, +// paths[i].flags, false)}; +// g_assert_true(res && res.value() == paths[i].newpath); +// char *newbase = g_path_get_basename(newpath->c_str()); +// assert_matches_regexp(newbase, +// "\\d+\\." +// "[[:xdigit:]]{16}\\." +// "[[:alnum:]][[:alnum:]-]+(:2,.*)?"); +// g_free(newbase); +// } +// } + +// static void +// test_mu_maildir_get_new_path_01(void) +// { +// struct { +// std::string oldpath; +// MessageFlags flags; +// std::string newpath; +// } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::Replied, +// "/home/foo/Maildir/test/cur/123456:2,R"}, +// {"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::New, +// "/home/foo/Maildir/test/new/123456"}, +// {"/home/foo/Maildir/test/new/123456:2,FR", +// (MessageFlags::Seen | MessageFlags::Replied), +// "/home/foo/Maildir/test/cur/123456:2,RS"}, +// {"/home/foo/Maildir/test/new/1313038887_0.697:2,", +// (MessageFlags::Seen | MessageFlags::Flagged | MessageFlags::Passed), +// "/home/foo/Maildir/test/cur/1313038887_0.697:2,FPS"}, +// {"/home/djcb/Maildir/trash/new/1312920597.2206_16.cthulhu", +// MessageFlags::Seen, +// "/home/djcb/Maildir/trash/cur/1312920597.2206_16.cthulhu:2,S"}}; + +// for (int i = 0; i != G_N_ELEMENTS(paths); ++i) { +// const auto newpath{mu_maildir_determine_target( +// paths[i].oldpath, +// "/home/foo/maildir", +// {}, paths[i].flags, false)}; +// g_assert_true(newpath.has_value()); +// g_assert_true(*newpath == paths[i].newpath); +// } +// } + +// static void +// test_mu_maildir_get_new_path_02(void) +// { +// struct { +// std::string oldpath; +// MessageFlags flags; +// std::string targetdir; +// std::string newpath; +// } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::Replied, +// "/home/foo/Maildir/blabla", +// "/home/foo/Maildir/blabla/cur/123456:2,R"}, +// {"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::New, +// "/home/bar/Maildir/coffee", +// "/home/bar/Maildir/coffee/new/123456"}, +// {"/home/foo/Maildir/test/new/123456", +// (MessageFlags::Seen | MessageFlags::Replied), +// "/home/cuux/Maildir/tea", +// "/home/cuux/Maildir/tea/cur/123456:2,RS"}, +// {"/home/foo/Maildir/test/new/1313038887_0.697:2,", +// (MessageFlags::Seen | MessageFlags::Flagged | MessageFlags::Passed), +// "/home/boy/Maildir/stuff", +// "/home/boy/Maildir/stuff/cur/1313038887_0.697:2,FPS"}}; + +// for (int i = 0; i != G_N_ELEMENTS(paths); ++i) { +// auto newpath{mu_maildir_determine_target(paths[i].oldpath, +// paths[i].targetdir, +// paths[i].flags, +// false)}; +// g_assert_true(newpath.has_value()); +// g_assert_true(*newpath == paths[i].newpath); +// } +// } + +// static void +// test_mu_maildir_get_new_path_custom(void) +// { +// struct { +// std::string oldpath; +// MessageFlags flags; +// std::string targetdir; +// std::string newpath; +// } paths[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", +// MessageFlags::Replied, +// "/home/foo/Maildir/blabla", +// "/home/foo/Maildir/blabla/cur/123456:2,R"}, +// {"/home/foo/Maildir/test/cur/123456:2,hFeRllo123", +// MessageFlags::Flagged, +// "/home/foo/Maildir/blabla", +// "/home/foo/Maildir/blabla/cur/123456:2,Fhello123"}, +// {"/home/foo/Maildir/test/cur/123456:2,abc", +// MessageFlags::Passed, +// "/home/foo/Maildir/blabla", +// "/home/foo/Maildir/blabla/cur/123456:2,Pabc"}}; + +// for (int i = 0; i != G_N_ELEMENTS(paths); ++i) { +// auto newpath{mu_maildir_get_new_path(paths[i].oldpath, +// paths[i].targetdir, +// paths[i].flags, +// FALSE)}; +// g_assert_true(newpath); +// g_assert_true(*newpath == paths[i].newpath); +// } +// } + +// static void +// test_mu_maildir_from_path(void) +// { +// unsigned u; + +// struct { +// std::string path, exp; +// } cases[] = {{"/home/foo/Maildir/test/cur/123456:2,FR", "/home/foo/Maildir/test"}, +// {"/home/foo/Maildir/lala/new/1313038887_0.697:2,", "/home/foo/Maildir/lala"}}; + +// for (u = 0; u != G_N_ELEMENTS(cases); ++u) { +// auto mdir{mu_maildir_from_path(cases[u].path)}; +// g_assert_true(mdir.has_value()); +// g_assert_true(*mdir == cases[u].exp); +// } +// } int main(int argc, char* argv[]) @@ -378,19 +455,27 @@ main(int argc, char* argv[]) g_test_add_func("/mu-maildir/mu-maildir-mkdir-04", test_mu_maildir_mkdir_04); g_test_add_func("/mu-maildir/mu-maildir-mkdir-05", test_mu_maildir_mkdir_05); - /* get/set flags */ - g_test_add_func("/mu-maildir/mu-maildir-get-new-path-new", - test_mu_maildir_get_new_path_new); + g_test_add_func("/mu-maildir/mu-maildir-flags-from-path", + test_mu_maildir_flags_from_path); - g_test_add_func("/mu-maildir/mu-maildir-get-new-path-01", test_mu_maildir_get_new_path_01); - g_test_add_func("/mu-maildir/mu-maildir-get-new-path-02", test_mu_maildir_get_new_path_02); - g_test_add_func("/mu-maildir/mu-maildir-get-new-path-custom", - test_mu_maildir_get_new_path_custom); - g_test_add_func("/mu-maildir/mu-maildir-get-flags-from-path", - test_mu_maildir_get_flags_from_path); - g_test_add_func("/mu-maildir/mu-maildir-get-maildir-from-path", - test_mu_maildir_get_maildir_from_path); + g_test_add_func("/mu-maildir/mu-maildir-determine-target-ok", + test_determine_target_ok); + + + // /* get/set flags */ + // g_test_add_func("/mu-maildir/mu-maildir-get-new-path-new", + // test_mu_maildir_get_new_path_new); + + // g_test_add_func("/mu-maildir/mu-maildir-get-new-path-01", test_mu_maildir_get_new_path_01); + // g_test_add_func("/mu-maildir/mu-maildir-get-new-path-02", test_mu_maildir_get_new_path_02); + // g_test_add_func("/mu-maildir/mu-maildir-get-new-path-custom", + // test_mu_maildir_get_new_path_custom); + // g_test_add_func("/mu-maildir/mu-maildir-get-flags-from-path", + // test_mu_maildir_get_flags_from_path); + + // g_test_add_func("/mu-maildir/mu-maildir-from-path", + // test_mu_maildir_from_path); g_log_set_handler( NULL, diff --git a/lib/utils/mu-command-parser.cc b/lib/utils/mu-command-parser.cc index 97271741..552d0364 100644 --- a/lib/utils/mu-command-parser.cc +++ b/lib/utils/mu-command-parser.cc @@ -126,48 +126,48 @@ wrong_type(Sexp::Type expected, Sexp::Type got) to_string(got).c_str()); } -const std::string& -Command::get_string_or(const Parameters& params, const std::string& argname, const std::string& alt) +std::optional +Command::get_string(const Parameters& params, const std::string& argname) { const auto it = find_param_node(params, argname); if (it == params.end() || it->is_nil()) - return alt; + return std::nullopt; else if (!it->is_string()) throw wrong_type(Sexp::Type::String, it->type()); else return it->value(); } -const std::string& -Command::get_symbol_or(const Parameters& params, const std::string& argname, const std::string& alt) +std::optional +Command::get_symbol(const Parameters& params, const std::string& argname) { const auto it = find_param_node(params, argname); if (it == params.end() || it->is_nil()) - return alt; + return std::nullopt; else if (!it->is_symbol()) throw wrong_type(Sexp::Type::Symbol, it->type()); else return it->value(); } -int -Command::get_int_or(const Parameters& params, const std::string& argname, int alt) +std::optional +Command::get_int(const Parameters& params, const std::string& argname) { const auto it = find_param_node(params, argname); if (it == params.end() || it->is_nil()) - return alt; + return std::nullopt; else if (!it->is_number()) throw wrong_type(Sexp::Type::Number, it->type()); else return ::atoi(it->value().c_str()); } -bool -Command::get_bool_or(const Parameters& params, const std::string& argname, bool alt) +std::optional +Command::get_bool(const Parameters& params, const std::string& argname) { const auto it = find_param_node(params, argname); if (it == params.end()) - return alt; + return std::nullopt; else if (!it->is_symbol()) throw wrong_type(Sexp::Type::Symbol, it->type()); else diff --git a/lib/utils/mu-command-parser.hh b/lib/utils/mu-command-parser.hh index a2ecebcb..cc592045 100644 --- a/lib/utils/mu-command-parser.hh +++ b/lib/utils/mu-command-parser.hh @@ -26,6 +26,7 @@ #include #include #include +#include #include "utils/mu-error.hh" #include "utils/mu-sexp.hh" @@ -61,15 +62,37 @@ using ArgMap = std::unordered_map; // The parameters to a Handler. using Parameters = Sexp::Seq; -int get_int_or(const Parameters& parms, const std::string& argname, int alt = 0); -bool get_bool_or(const Parameters& parms, const std::string& argname, bool alt = false); -const std::string& -get_string_or(const Parameters& parms, const std::string& argname, const std::string& alt = ""); -const std::string& -get_symbol_or(const Parameters& parms, const std::string& argname, const std::string& alt = "nil"); +std::optional get_int(const Parameters& parms, const std::string& argname); +std::optional get_bool(const Parameters& parms, const std::string& argname); +std::optional get_string(const Parameters& parms, const std::string& argname); +std::optional get_symbol(const Parameters& parms, const std::string& argname); std::vector get_string_vec(const Parameters& params, const std::string& argname); +/* + * backward compat + */ +static inline int +get_int_or(const Parameters& parms, const std::string& arg, int alt = 0) { + return get_int(parms, arg).value_or(alt); +} +static inline bool +get_bool_or(const Parameters& parms, const std::string& arg, bool alt = false) { + return get_bool(parms, arg).value_or(alt); +} +static inline std::string +get_string_or(const Parameters& parms, const std::string& arg, const std::string& alt = ""){ + return get_string(parms, arg).value_or(alt); +} + +static inline std::string +get_symbol_or(const Parameters& parms, const std::string& arg, const std::string& alt = "nil") { + return get_symbol(parms, arg).value_or(alt); +} + + + + // A handler function using Handler = std::function; diff --git a/mu/mu-cmd-find.cc b/mu/mu-cmd-find.cc index cbca78ac..e0b34d52 100644 --- a/mu/mu-cmd-find.cc +++ b/mu/mu-cmd-find.cc @@ -202,20 +202,16 @@ prepare_links(const MuConfig* opts, GError** err) { /* note, mu_maildir_mkdir simply ignores whatever part of the * mail dir already exists */ - - if (!mu_maildir_mkdir(opts->linksdir, 0700, TRUE, err)) { - mu_util_g_set_error(err, - MU_ERROR_FILE_CANNOT_MKDIR, - "error creating %s", - opts->linksdir); + if (auto&& res = mu_maildir_mkdir(opts->linksdir, 0700, true); !res) { + res.error().fill_g_error(err); return FALSE; } - if (opts->clearlinks && !mu_maildir_clear_links(opts->linksdir, err)) { - mu_util_g_set_error(err, - MU_ERROR_FILE, - "error clearing links under %s", - opts->linksdir); + if (!opts->clearlinks) + return TRUE; + + if (auto&& res = mu_maildir_clear_links(opts->linksdir); !res) { + res.error().fill_g_error(err); return FALSE; } @@ -229,8 +225,12 @@ output_link(MuMsg* msg, const OutputInfo& info, const MuConfig* opts, GError** e return prepare_links(opts, err); else if (info.footer) return true; - - return mu_maildir_link(mu_msg_get_path(msg), opts->linksdir, err); + if (auto&& res = mu_maildir_link( + mu_msg_get_path(msg), opts->linksdir); !res) { + res.error().fill_g_error(err); + return FALSE; + } + return TRUE; } static void @@ -293,6 +293,18 @@ field_string_list(MuMsg* msg, MuMsgFieldId mfid) return NULL; } +/* ugly... for backward compat */ +static const char* +flags_s(MessageFlags flags) +{ + static char buf[64]; + const auto flagstr{message_flags_to_string(flags)}; + + ::strncpy(buf, flagstr.c_str(), sizeof(buf) - 1); + + return buf; +} + static const char* display_field(MuMsg* msg, MuMsgFieldId mfid) { @@ -305,14 +317,13 @@ display_field(MuMsg* msg, MuMsgFieldId mfid) return str ? str : ""; } case MU_MSG_FIELD_TYPE_INT: - if (mfid == MU_MSG_FIELD_ID_PRIO) { const auto val = static_cast(mu_msg_get_field_numeric(msg, mfid)); const auto prio = message_priority_from_char(val); return message_priority_name_c_str(prio); } else if (mfid == MU_MSG_FIELD_ID_FLAGS) { val = mu_msg_get_field_numeric(msg, mfid); - return mu_str_flags_s((MuFlags)val); + return flags_s(static_cast(val)); } else /* as string */ return mu_msg_get_field_string(msg, mfid); diff --git a/mu/mu-cmd.cc b/mu/mu-cmd.cc index 6629a849..6544e221 100644 --- a/mu/mu-cmd.cc +++ b/mu/mu-cmd.cc @@ -34,7 +34,7 @@ #include "mu-maildir.hh" #include "mu-contacts.hh" #include "mu-runtime.hh" -#include "mu-flags.hh" +#include "mu-message-flags.hh" #include "utils/mu-util.h" #include "utils/mu-str.h" @@ -122,7 +122,7 @@ body_or_summary(MuMsg* msg, const MuConfig* opts) color = !opts->nocolor; body = mu_msg_get_body_text(msg, (MuMsgOptions)my_opts); if (!body) { - if (mu_msg_get_flags(msg) & MU_FLAG_ENCRYPTED) { + if (any_of(mu_msg_get_flags(msg) & MessageFlags::Encrypted)) { color_maybe(MU_COLOR_CYAN); g_print("[No body found; " "message has encrypted parts]\n"); @@ -268,9 +268,15 @@ cmd_mkdir(const MuConfig* opts, GError** err) return MU_ERROR_IN_PARAMETERS; } - for (i = 1; opts->params[i]; ++i) - if (!mu_maildir_mkdir(opts->params[i], opts->dirmode, FALSE, err)) - return err && *err ? (MuError)(*err)->code : MU_ERROR_FILE_CANNOT_MKDIR; + for (i = 1; opts->params[i]; ++i) { + if (auto&& res{mu_maildir_mkdir(opts->params[i], + opts->dirmode, FALSE)}; !res) { + g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR_FILE, + "%s", res.error().what()); + return MU_ERROR_FILE_CANNOT_MKDIR; + } + } + return MU_OK; }