server: rework for updated Sexp/CommandHandler
Rework for the new APIs.
This commit is contained in:
504
lib/mu-server.cc
504
lib/mu-server.cc
@ -42,16 +42,16 @@
|
|||||||
|
|
||||||
#include "utils/mu-utils.hh"
|
#include "utils/mu-utils.hh"
|
||||||
#include "utils/mu-option.hh"
|
#include "utils/mu-option.hh"
|
||||||
#include "utils/mu-command-parser.hh"
|
#include "utils/mu-command-handler.hh"
|
||||||
#include "utils/mu-readline.hh"
|
#include "utils/mu-readline.hh"
|
||||||
|
|
||||||
using namespace Mu;
|
using namespace Mu;
|
||||||
using namespace Command;
|
|
||||||
|
|
||||||
/// @brief object to manage the server-context for all commands.
|
/// @brief object to manage the server-context for all commands.
|
||||||
struct Server::Private {
|
struct Server::Private {
|
||||||
Private(Store& store, Output output)
|
Private(Store& store, Output output)
|
||||||
: store_{store}, output_{output}, command_map_{make_command_map()},
|
: store_{store}, output_{output},
|
||||||
|
command_handler_{make_command_map()},
|
||||||
keep_going_{true}
|
keep_going_{true}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -63,14 +63,14 @@ struct Server::Private {
|
|||||||
//
|
//
|
||||||
// construction helpers
|
// construction helpers
|
||||||
//
|
//
|
||||||
CommandMap make_command_map();
|
CommandHandler::CommandInfoMap make_command_map();
|
||||||
|
|
||||||
//
|
//
|
||||||
// acccessors
|
// acccessors
|
||||||
Store& store() { return store_; }
|
Store& store() { return store_; }
|
||||||
const Store& store() const { return store_; }
|
const Store& store() const { return store_; }
|
||||||
Indexer& indexer() { return store().indexer(); }
|
Indexer& indexer() { return store().indexer(); }
|
||||||
const CommandMap& command_map() const { return command_map_; }
|
//CommandMap& command_map() const { return command_map_; }
|
||||||
|
|
||||||
//
|
//
|
||||||
// invoke
|
// invoke
|
||||||
@ -80,32 +80,29 @@ struct Server::Private {
|
|||||||
//
|
//
|
||||||
// output
|
// output
|
||||||
//
|
//
|
||||||
void output_sexp(Sexp&& sexp,Server::OutputFlags flags = {}) const {
|
void output_sexp(const Sexp& sexp, Server::OutputFlags flags = {}) const {
|
||||||
if (output_)
|
if (output_)
|
||||||
output_(std::move(sexp), flags);
|
output_(sexp, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_sexp(Sexp::List&& lst, Server::OutputFlags flags = {}) const {
|
|
||||||
output_sexp(Sexp::make_list(std::move(lst)), flags);
|
|
||||||
}
|
|
||||||
size_t output_results(const QueryResults& qres, size_t batch_size) const;
|
size_t output_results(const QueryResults& qres, size_t batch_size) const;
|
||||||
|
|
||||||
//
|
//
|
||||||
// handlers for various commands.
|
// handlers for various commands.
|
||||||
//
|
//
|
||||||
void add_handler(const Parameters& params);
|
void add_handler(const Command& cmd);
|
||||||
void compose_handler(const Parameters& params);
|
void compose_handler(const Command& cmd);
|
||||||
void contacts_handler(const Parameters& params);
|
void contacts_handler(const Command& cmd);
|
||||||
void find_handler(const Parameters& params);
|
void find_handler(const Command& cmd);
|
||||||
void help_handler(const Parameters& params);
|
void help_handler(const Command& cmd);
|
||||||
void index_handler(const Parameters& params);
|
void index_handler(const Command& cmd);
|
||||||
void move_handler(const Parameters& params);
|
void move_handler(const Command& cmd);
|
||||||
void mkdir_handler(const Parameters& params);
|
void mkdir_handler(const Command& cmd);
|
||||||
void ping_handler(const Parameters& params);
|
void ping_handler(const Command& cmd);
|
||||||
void quit_handler(const Parameters& params);
|
void quit_handler(const Command& cmd);
|
||||||
void remove_handler(const Parameters& params);
|
void remove_handler(const Command& cmd);
|
||||||
void sent_handler(const Parameters& params);
|
void sent_handler(const Command& cmd);
|
||||||
void view_handler(const Parameters& params);
|
void view_handler(const Command& cmd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// helpers
|
// helpers
|
||||||
@ -113,88 +110,83 @@ private:
|
|||||||
Store::Id docid,
|
Store::Id docid,
|
||||||
const Option<QueryMatch&> qm) const;
|
const Option<QueryMatch&> qm) const;
|
||||||
|
|
||||||
Sexp::List move_docid(Store::Id docid, Option<std::string> flagstr,
|
Sexp move_docid(Store::Id docid, Option<std::string> flagstr,
|
||||||
bool new_name, bool no_view);
|
bool new_name, bool no_view);
|
||||||
|
|
||||||
Sexp::List perform_move(Store::Id docid,
|
Sexp perform_move(Store::Id docid,
|
||||||
const Message& msg,
|
const Message& msg,
|
||||||
const std::string& maildirarg,
|
const std::string& maildirarg,
|
||||||
Flags flags,
|
Flags flags,
|
||||||
bool new_name,
|
bool new_name,
|
||||||
bool no_view);
|
bool no_view);
|
||||||
|
|
||||||
bool maybe_mark_as_read(Store::Id docid, Flags old_flags, bool rename);
|
bool view_mark_as_read(Store::Id docid, const Message& msg, bool rename);
|
||||||
bool maybe_mark_msgid_as_read(const std::string& msgid, bool rename);
|
|
||||||
|
|
||||||
Store& store_;
|
Store& store_;
|
||||||
Server::Output output_;
|
Server::Output output_;
|
||||||
const CommandMap command_map_;
|
const CommandHandler command_handler_;
|
||||||
std::atomic<bool> keep_going_{};
|
std::atomic<bool> keep_going_{};
|
||||||
std::thread index_thread_;
|
std::thread index_thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
static Sexp
|
static Sexp
|
||||||
build_metadata(const QueryMatch& qmatch)
|
build_metadata(const QueryMatch& qmatch)
|
||||||
{
|
{
|
||||||
Sexp::List mdata;
|
|
||||||
|
|
||||||
auto symbol_t = [] { return Sexp::make_symbol("t"); };
|
|
||||||
|
|
||||||
mdata.add_prop(":path", Sexp::make_string(qmatch.thread_path));
|
|
||||||
mdata.add_prop(":level", Sexp::make_number(qmatch.thread_level));
|
|
||||||
mdata.add_prop(":date", Sexp::make_string(qmatch.thread_date));
|
|
||||||
|
|
||||||
Sexp::List dlist;
|
|
||||||
const auto td{::atoi(qmatch.thread_date.c_str())};
|
const auto td{::atoi(qmatch.thread_date.c_str())};
|
||||||
dlist.add(Sexp::make_number((unsigned)(td >> 16)));
|
auto mdata = Sexp().put_props(":path", qmatch.thread_path,
|
||||||
dlist.add(Sexp::make_number((unsigned)(td & 0xffff)));
|
":level", qmatch.thread_level,
|
||||||
dlist.add(Sexp::make_number(0));
|
":date", qmatch.thread_date,
|
||||||
mdata.add_prop(":date-tstamp", Sexp::make_list(std::move(dlist)));
|
":data-tstamp", Sexp().add(static_cast<unsigned>(td >> 16),
|
||||||
|
static_cast<unsigned>(td & 0xffff),
|
||||||
|
0));
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::Root))
|
if (qmatch.has_flag(QueryMatch::Flags::Root))
|
||||||
mdata.add_prop(":root", symbol_t());
|
mdata.put_props(":root", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::Related))
|
if (qmatch.has_flag(QueryMatch::Flags::Related))
|
||||||
mdata.add_prop(":related", symbol_t());
|
mdata.put_props(":related", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::First))
|
if (qmatch.has_flag(QueryMatch::Flags::First))
|
||||||
mdata.add_prop(":first-child", symbol_t());
|
mdata.put_props(":first-child", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::Last))
|
if (qmatch.has_flag(QueryMatch::Flags::Last))
|
||||||
mdata.add_prop(":last-child", symbol_t());
|
mdata.put_props(":last-child", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::Orphan))
|
if (qmatch.has_flag(QueryMatch::Flags::Orphan))
|
||||||
mdata.add_prop(":orphan", symbol_t());
|
mdata.put_props(":orphan", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::Duplicate))
|
if (qmatch.has_flag(QueryMatch::Flags::Duplicate))
|
||||||
mdata.add_prop(":duplicate", symbol_t());
|
mdata.put_props(":duplicate", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::HasChild))
|
if (qmatch.has_flag(QueryMatch::Flags::HasChild))
|
||||||
mdata.add_prop(":has-child", symbol_t());
|
mdata.put_props(":has-child", Sexp::t());
|
||||||
if (qmatch.has_flag(QueryMatch::Flags::ThreadSubject))
|
if (qmatch.has_flag(QueryMatch::Flags::ThreadSubject))
|
||||||
mdata.add_prop(":thread-subject", symbol_t());
|
mdata.put_props(":thread-subject", Sexp::t());
|
||||||
|
|
||||||
return Sexp::make_list(std::move(mdata));
|
return mdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A message here is a Sexp::List consists of a message s-expression with
|
* A message here consists of a message s-expression with optionally a :docid
|
||||||
* optionally a :meta expression added.
|
* and/or :meta expression added.
|
||||||
*/
|
*/
|
||||||
Sexp
|
Sexp
|
||||||
Server::Private::build_message_sexp(const Message& msg,
|
Server::Private::build_message_sexp(const Message& msg,
|
||||||
Store::Id docid,
|
Store::Id docid,
|
||||||
const Option<QueryMatch&> qm) const
|
const Option<QueryMatch&> qm) const
|
||||||
{
|
{
|
||||||
auto sexp_list = msg.to_sexp_list();
|
Sexp sexp{msg.sexp()}; // copy
|
||||||
if (docid != 0)
|
if (docid != 0)
|
||||||
sexp_list.add_prop(":docid", Sexp::make_number(docid));
|
sexp.put_props(":docid", docid);
|
||||||
if (qm)
|
if (qm)
|
||||||
sexp_list.add_prop(":meta", build_metadata(*qm));
|
sexp.put_props(":meta", build_metadata(*qm));
|
||||||
|
|
||||||
return Sexp::make_list(std::move(sexp_list));
|
return sexp;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandMap
|
CommandHandler::CommandInfoMap
|
||||||
Server::Private::make_command_map()
|
Server::Private::make_command_map()
|
||||||
{
|
{
|
||||||
CommandMap cmap;
|
CommandHandler::CommandInfoMap cmap;
|
||||||
|
|
||||||
using Type = Sexp::Type;
|
using CommandInfo = CommandHandler::CommandInfo;
|
||||||
|
using ArgMap = CommandHandler::ArgMap;
|
||||||
|
using ArgInfo = CommandHandler::ArgInfo;
|
||||||
|
using Type = Sexp::Type;
|
||||||
|
using Type = Sexp::Type;
|
||||||
|
|
||||||
cmap.emplace(
|
cmap.emplace(
|
||||||
"add",
|
"add",
|
||||||
@ -352,12 +344,10 @@ make_error(Error::Code errcode, const char* frm, ...)
|
|||||||
g_vasprintf(&msg, frm, ap);
|
g_vasprintf(&msg, frm, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
Sexp::List err;
|
auto err = Sexp().put_props(":error", static_cast<int>(errcode),
|
||||||
err.add_prop(":error", Sexp::make_number(static_cast<int>(errcode)));
|
":message", msg);
|
||||||
err.add_prop(":message", Sexp::make_string(msg));
|
|
||||||
g_free(msg);
|
g_free(msg);
|
||||||
|
return err;
|
||||||
return Sexp::make_list(std::move(err));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -365,10 +355,14 @@ Server::Private::invoke(const std::string& expr) noexcept
|
|||||||
{
|
{
|
||||||
if (!keep_going_)
|
if (!keep_going_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto call{Sexp::Sexp::make_parse(expr)};
|
auto cmd{Command::make_parse(std::string{expr})};
|
||||||
Command::invoke(command_map(), call);
|
if (!cmd)
|
||||||
|
throw cmd.error();
|
||||||
|
|
||||||
|
auto res = command_handler_.invoke(*cmd);
|
||||||
|
if (!res)
|
||||||
|
throw res.error();
|
||||||
|
|
||||||
} catch (const Mu::Error& me) {
|
} catch (const Mu::Error& me) {
|
||||||
output_sexp(make_error(me.code(), "%s", me.what()));
|
output_sexp(make_error(me.code(), "%s", me.what()));
|
||||||
@ -394,32 +388,27 @@ Server::Private::invoke(const std::string& expr) noexcept
|
|||||||
* information about the newly added message (details: see code below)
|
* information about the newly added message (details: see code below)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Server::Private::add_handler(const Parameters& params)
|
Server::Private::add_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
auto path{get_string_or(params, ":path")};
|
auto path{cmd.string_arg(":path")};
|
||||||
const auto docid_res{store().add_message(path)};
|
const auto docid_res{store().add_message(*path)};
|
||||||
|
|
||||||
if (!docid_res)
|
if (!docid_res)
|
||||||
throw docid_res.error();
|
throw docid_res.error();
|
||||||
|
|
||||||
const auto docid{docid_res.value()};
|
const auto docid{docid_res.value()};
|
||||||
|
output_sexp(Sexp().put_props(":info", "add"_sym,
|
||||||
Sexp::List expr;
|
":path", *path,
|
||||||
expr.add_prop(":info", Sexp::make_symbol("add"));
|
":docid", docid));
|
||||||
expr.add_prop(":path", Sexp::make_string(path));
|
|
||||||
expr.add_prop(":docid", Sexp::make_number(docid));
|
|
||||||
|
|
||||||
output_sexp(Sexp::make_list(std::move(expr)));
|
|
||||||
|
|
||||||
auto msg_res{store().find_message(docid)};
|
auto msg_res{store().find_message(docid)};
|
||||||
if (!msg_res)
|
if (!msg_res)
|
||||||
throw Error(Error::Code::Store,
|
throw Error(Error::Code::Store,
|
||||||
"failed to get message at %s (docid=%u): %s",
|
"failed to get message at %s (docid=%u): %s",
|
||||||
path.c_str(), docid);
|
path->c_str(), docid);
|
||||||
|
|
||||||
Sexp::List update;
|
output_sexp(Sexp().put_props(":update",
|
||||||
update.add_prop(":update", build_message_sexp(msg_res.value(), docid, {}));
|
build_message_sexp(msg_res.value(), docid, {})));
|
||||||
output_sexp(Sexp::make_list(std::move(update)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 'compose' produces the un-changed *original* message sexp (ie., the message
|
/* 'compose' produces the un-changed *original* message sexp (ie., the message
|
||||||
@ -452,44 +441,42 @@ maybe_add_attachment(Message& message, const MessagePart& part, size_t index)
|
|||||||
if (!res)
|
if (!res)
|
||||||
throw res.error();
|
throw res.error();
|
||||||
|
|
||||||
Sexp::List pi;
|
Sexp pi;
|
||||||
|
|
||||||
if (auto cdescr = part.content_description(); cdescr)
|
if (auto cdescr = part.content_description(); cdescr)
|
||||||
pi.add_prop(":description", Sexp::make_string(*cdescr));
|
pi.put_props(":description", *cdescr);
|
||||||
else if (cooked_name)
|
else if (cooked_name)
|
||||||
pi.add_prop(":description", Sexp::make_string(cooked_name.value()));
|
pi.put_props(":description", cooked_name.value());
|
||||||
|
|
||||||
pi.add_prop(":file-name", Sexp::make_string(fname));
|
pi.put_props(":file-name", fname,
|
||||||
pi.add_prop(":mime-type", Sexp::make_string(
|
":mime-type",
|
||||||
part.mime_type().value_or("application/octet-stream")));
|
part.mime_type().value_or("application/octet-stream"));
|
||||||
|
|
||||||
return Some(Sexp::make_list(std::move(pi)));
|
return Some(std::move(pi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::compose_handler(const Parameters& params)
|
Server::Private::compose_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto ctype{get_symbol_or(params, ":type")};
|
const auto ctype = cmd.symbol_arg(":type").value_or("<error>");
|
||||||
|
|
||||||
Sexp::List comp_lst;
|
auto comp_lst = Sexp().put_props(":compose", Sexp::Symbol(ctype));
|
||||||
comp_lst.add_prop(":compose", Sexp::make_symbol(std::string(ctype)));
|
|
||||||
|
|
||||||
|
|
||||||
if (ctype == "reply" || ctype == "forward" ||
|
if (ctype == "reply" || ctype == "forward" ||
|
||||||
ctype == "edit" || ctype == "resend") {
|
ctype == "edit" || ctype == "resend") {
|
||||||
|
|
||||||
const unsigned docid{(unsigned)get_int_or(params, ":docid")};
|
const unsigned docid{static_cast<unsigned>(cmd.number_arg(":docid").value_or(0))};
|
||||||
auto msg{store().find_message(docid)};
|
auto msg{store().find_message(docid)};
|
||||||
if (!msg)
|
if (!msg)
|
||||||
throw Error{Error::Code::Store, "failed to get message %u", docid};
|
throw Error{Error::Code::Store, "failed to get message %u", docid};
|
||||||
|
|
||||||
comp_lst.add_prop(":original", build_message_sexp(msg.value(), docid, {}));
|
comp_lst.put_props(":original", build_message_sexp(msg.value(), docid, {}));
|
||||||
|
|
||||||
if (ctype == "forward") {
|
if (ctype == "forward") {
|
||||||
// when forwarding, attach any attachment in the orig
|
// when forwarding, attach any attachment in the orig
|
||||||
size_t index{};
|
size_t index{};
|
||||||
Sexp::List attseq;
|
Sexp attseq;
|
||||||
for (auto&& part: msg->parts()) {
|
for (auto&& part: msg->parts()) {
|
||||||
if (auto attsexp = maybe_add_attachment(
|
if (auto attsexp = maybe_add_attachment(
|
||||||
*msg, part, index); attsexp) {
|
*msg, part, index); attsexp) {
|
||||||
@ -498,10 +485,8 @@ Server::Private::compose_handler(const Parameters& params)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!attseq.empty()) {
|
if (!attseq.empty()) {
|
||||||
comp_lst.add_prop(":include",
|
comp_lst.put_props(":include", std::move(attseq),
|
||||||
Sexp::make_list(std::move(attseq)));
|
":cache-path", *msg->cache_path());
|
||||||
comp_lst.add_prop(":cache-path",
|
|
||||||
Sexp::make_string(*msg->cache_path()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,16 +494,16 @@ Server::Private::compose_handler(const Parameters& params)
|
|||||||
throw Error(Error::Code::InvalidArgument, "invalid compose type '%s'",
|
throw Error(Error::Code::InvalidArgument, "invalid compose type '%s'",
|
||||||
ctype.c_str());
|
ctype.c_str());
|
||||||
|
|
||||||
output_sexp(std::move(comp_lst));
|
output_sexp(comp_lst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::contacts_handler(const Parameters& params)
|
Server::Private::contacts_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto personal = get_bool_or(params, ":personal");
|
const auto personal = cmd.boolean_arg(":personal");
|
||||||
const auto afterstr = get_string_or(params, ":after");
|
const auto afterstr = cmd.string_arg(":after").value_or("");
|
||||||
const auto tstampstr = get_string_or(params, ":tstamp");
|
const auto tstampstr = cmd.string_arg(":tstamp").value_or("");
|
||||||
const auto maxnum = get_int_or(params, ":maxnum", 0 /*unlimited*/);
|
const auto maxnum = cmd.number_arg(":maxnum").value_or(0 /*unlimited*/);
|
||||||
|
|
||||||
const auto after{afterstr.empty() ? 0 :
|
const auto after{afterstr.empty() ? 0 :
|
||||||
parse_date_time(afterstr, true).value_or(0)};
|
parse_date_time(afterstr, true).value_or(0)};
|
||||||
@ -530,7 +515,7 @@ Server::Private::contacts_handler(const Parameters& params)
|
|||||||
static_cast<size_t>(tstamp));
|
static_cast<size_t>(tstamp));
|
||||||
|
|
||||||
auto n{0};
|
auto n{0};
|
||||||
Sexp::List contacts;
|
Sexp contacts;
|
||||||
store().contacts_cache().for_each([&](const Contact& ci) {
|
store().contacts_cache().for_each([&](const Contact& ci) {
|
||||||
|
|
||||||
/* since the last time we got some contacts */
|
/* since the last time we got some contacts */
|
||||||
@ -545,19 +530,17 @@ Server::Private::contacts_handler(const Parameters& params)
|
|||||||
|
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
contacts.add(Sexp::make_string(ci.display_name(true/*encode-if-needed*/)));
|
contacts.add(ci.display_name(true/*encode-if-needed*/));
|
||||||
return maxnum == 0 || n < maxnum;
|
return maxnum == 0 || n < maxnum;
|
||||||
});
|
});
|
||||||
|
|
||||||
Sexp::List seq;
|
Sexp seq;
|
||||||
seq.add_prop(":contacts", Sexp::make_list(std::move(contacts)));
|
seq.put_props(":contacts", contacts,
|
||||||
seq.add_prop(":tstamp",
|
":tstamp", format("%" G_GINT64_FORMAT, g_get_monotonic_time()));
|
||||||
Sexp::make_string(format("%" G_GINT64_FORMAT,
|
|
||||||
g_get_monotonic_time())));
|
|
||||||
|
|
||||||
/* dump the contacts cache as a giant sexp */
|
/* dump the contacts cache as a giant sexp */
|
||||||
g_debug("sending %d of %zu contact(s)", n, store().contacts_cache().size());
|
g_debug("sending %d of %zu contact(s)", n, store().contacts_cache().size());
|
||||||
output_sexp(std::move(seq), Server::OutputFlags::SplitList);
|
output_sexp(seq, Server::OutputFlags::SplitList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get a *list* of all messages with the given message id */
|
/* get a *list* of all messages with the given message id */
|
||||||
@ -613,10 +596,10 @@ path_from_docid(const Store& store, Store::Id docid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<Store::Id>
|
static std::vector<Store::Id>
|
||||||
determine_docids(const Store& store, const Parameters& params)
|
determine_docids(const Store& store, const Command& cmd)
|
||||||
{
|
{
|
||||||
auto docid{get_int_or(params, ":docid", 0)};
|
auto docid{cmd.number_arg(":docid").value_or(0)};
|
||||||
const auto msgid{get_string_or(params, ":msgid")};
|
const auto msgid{cmd.string_arg(":msgid").value_or("")};
|
||||||
|
|
||||||
if ((docid == 0) == msgid.empty())
|
if ((docid == 0) == msgid.empty())
|
||||||
throw Error(Error::Code::InvalidArgument,
|
throw Error(Error::Code::InvalidArgument,
|
||||||
@ -632,12 +615,12 @@ size_t
|
|||||||
Server::Private::output_results(const QueryResults& qres, size_t batch_size) const
|
Server::Private::output_results(const QueryResults& qres, size_t batch_size) const
|
||||||
{
|
{
|
||||||
size_t n{};
|
size_t n{};
|
||||||
Sexp::List headers;
|
Sexp headers;
|
||||||
|
|
||||||
const auto output_batch = [&](Sexp::List&& hdrs) {
|
const auto output_batch = [&](Sexp&& hdrs) {
|
||||||
Sexp::List batch;
|
Sexp batch;
|
||||||
batch.add_prop(":headers", Sexp::make_list(std::move(hdrs)));
|
batch.put_props(":headers", std::move(hdrs));
|
||||||
output_sexp(std::move(batch));
|
output_sexp(batch);
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto&& mi : qres) {
|
for (auto&& mi : qres) {
|
||||||
@ -649,7 +632,6 @@ Server::Private::output_results(const QueryResults& qres, size_t batch_size) con
|
|||||||
// construct sexp for a single header.
|
// construct sexp for a single header.
|
||||||
auto qm{mi.query_match()};
|
auto qm{mi.query_match()};
|
||||||
auto msgsexp{build_message_sexp(*msg, mi.doc_id(), qm)};
|
auto msgsexp{build_message_sexp(*msg, mi.doc_id(), qm)};
|
||||||
msgsexp.formatting_opts |= Sexp::FormattingOptions::SplitList;
|
|
||||||
headers.add(std::move(msgsexp));
|
headers.add(std::move(msgsexp));
|
||||||
// we output up-to-batch-size lists of messages. It's much
|
// we output up-to-batch-size lists of messages. It's much
|
||||||
// faster (on the emacs side) to handle such batches than single
|
// faster (on the emacs side) to handle such batches than single
|
||||||
@ -668,17 +650,17 @@ Server::Private::output_results(const QueryResults& qres, size_t batch_size) con
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::find_handler(const Parameters& params)
|
Server::Private::find_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto q{get_string_or(params, ":query")};
|
const auto q{cmd.string_arg(":query").value_or("")};
|
||||||
const auto threads{get_bool_or(params, ":threads", false)};
|
const auto threads{cmd.boolean_arg(":threads")};
|
||||||
// perhaps let mu4e set this as frame-lines of the appropriate frame.
|
// perhaps let mu4e set this as frame-lines of the appropriate frame.
|
||||||
const auto batch_size{get_int_or(params, ":batch-size", 110)};
|
const auto batch_size{cmd.number_arg(":batch-size").value_or(110)};
|
||||||
const auto sortfieldstr{get_symbol_or(params, ":sortfield", "")};
|
const auto sortfieldstr{cmd.symbol_arg(":sortfield").value_or("")};
|
||||||
const auto descending{get_bool_or(params, ":descending", false)};
|
const auto descending{cmd.boolean_arg(":descending")};
|
||||||
const auto maxnum{get_int_or(params, ":maxnum", -1 /*unlimited*/)};
|
const auto maxnum{cmd.number_arg(":maxnum").value_or(-1) /*unlimited*/};
|
||||||
const auto skip_dups{get_bool_or(params, ":skip-dups", false)};
|
const auto skip_dups{cmd.boolean_arg(":skip-dups")};
|
||||||
const auto include_related{get_bool_or(params, ":include-related", false)};
|
const auto include_related{cmd.boolean_arg(":include-related")};
|
||||||
|
|
||||||
auto sort_field = std::invoke([&]()->Option<Field>{
|
auto sort_field = std::invoke([&]()->Option<Field>{
|
||||||
if (sortfieldstr.size() < 2)
|
if (sortfieldstr.size() < 2)
|
||||||
@ -710,26 +692,17 @@ Server::Private::find_handler(const Parameters& params)
|
|||||||
/* before sending new results, send an 'erase' message, so the frontend
|
/* before sending new results, send an 'erase' message, so the frontend
|
||||||
* knows it should erase the headers buffer. this will ensure that the
|
* knows it should erase the headers buffer. this will ensure that the
|
||||||
* output of two finds will not be mixed. */
|
* output of two finds will not be mixed. */
|
||||||
{
|
output_sexp(Sexp().put_props(":erase", Sexp::t()));
|
||||||
Sexp::List lst;
|
|
||||||
lst.add_prop(":erase", Sexp::make_symbol("t"));
|
|
||||||
output_sexp(std::move(lst));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto foundnum{output_results(*qres, static_cast<size_t>(batch_size))};
|
const auto foundnum{output_results(*qres, static_cast<size_t>(batch_size))};
|
||||||
|
output_sexp(Sexp().put_props(":found", foundnum));
|
||||||
{
|
|
||||||
Sexp::List lst;
|
|
||||||
lst.add_prop(":found", Sexp::make_number(foundnum));
|
|
||||||
output_sexp(std::move(lst));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::help_handler(const Parameters& params)
|
Server::Private::help_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto command{get_symbol_or(params, ":command", "")};
|
const auto command{cmd.symbol_arg(":command").value_or("")};
|
||||||
const auto full{get_bool_or(params, ":full", !command.empty())};
|
const auto full{cmd.bool_arg(":full").value_or(!command.empty())};
|
||||||
|
auto&& info_map{command_handler_.info_map()};
|
||||||
|
|
||||||
if (command.empty()) {
|
if (command.empty()) {
|
||||||
std::cout << ";; Commands are s-expressions of the form\n"
|
std::cout << ";; Commands are s-expressions of the form\n"
|
||||||
@ -740,12 +713,13 @@ Server::Private::help_handler(const Parameters& params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> names;
|
std::vector<std::string> names;
|
||||||
for (auto&& name_cmd : command_map())
|
for (auto&& name_cmd: info_map)
|
||||||
names.emplace_back(name_cmd.first);
|
names.emplace_back(name_cmd.first);
|
||||||
|
|
||||||
std::sort(names.begin(), names.end());
|
std::sort(names.begin(), names.end());
|
||||||
|
|
||||||
for (auto&& name : names) {
|
for (auto&& name : names) {
|
||||||
const auto& info{command_map().find(name)->second};
|
const auto& info{info_map.find(name)->second};
|
||||||
|
|
||||||
if (!command.empty() && name != command)
|
if (!command.empty() && name != command)
|
||||||
continue;
|
continue;
|
||||||
@ -771,26 +745,27 @@ Server::Private::help_handler(const Parameters& params)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Sexp::List
|
static Sexp
|
||||||
get_stats(const Indexer::Progress& stats, const std::string& state)
|
get_stats(const Indexer::Progress& stats, const std::string& state)
|
||||||
{
|
{
|
||||||
Sexp::List lst;
|
Sexp sexp;
|
||||||
|
|
||||||
lst.add_prop(":info", Sexp::make_symbol("index"));
|
sexp.put_props(
|
||||||
lst.add_prop(":status", Sexp::make_symbol(std::string{state}));
|
":info", "index"_sym,
|
||||||
lst.add_prop(":checked", Sexp::make_number(stats.checked));
|
":status", Sexp::Symbol(state),
|
||||||
lst.add_prop(":updated", Sexp::make_number(stats.updated));
|
":checked", static_cast<int>(stats.checked),
|
||||||
lst.add_prop(":cleaned-up", Sexp::make_number(stats.removed));
|
":updated", static_cast<int>(stats.updated),
|
||||||
|
":cleaned-up", static_cast<int>(stats.removed));
|
||||||
|
|
||||||
return lst;
|
return sexp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::index_handler(const Parameters& params)
|
Server::Private::index_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
Mu::Indexer::Config conf{};
|
Mu::Indexer::Config conf{};
|
||||||
conf.cleanup = get_bool_or(params, ":cleanup");
|
conf.cleanup = cmd.boolean_arg(":cleanup");
|
||||||
conf.lazy_check = get_bool_or(params, ":lazy-check");
|
conf.lazy_check = cmd.boolean_arg(":lazy-check");
|
||||||
// ignore .noupdate with an empty store.
|
// ignore .noupdate with an empty store.
|
||||||
conf.ignore_noupdate = store().empty();
|
conf.ignore_noupdate = store().empty();
|
||||||
|
|
||||||
@ -813,20 +788,17 @@ Server::Private::index_handler(const Parameters& params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::mkdir_handler(const Parameters& params)
|
Server::Private::mkdir_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto path{get_string_or(params, ":path")};
|
const auto path{cmd.string_arg(":path").value_or("<error>")};
|
||||||
if (auto&& res = maildir_mkdir(path, 0755, FALSE); !res)
|
if (auto&& res = maildir_mkdir(path, 0755, false); !res)
|
||||||
throw res.error();
|
throw res.error();
|
||||||
|
|
||||||
Sexp::List lst;
|
output_sexp(Sexp().put_props(":info", "mkdir",
|
||||||
lst.add_prop(":info", Sexp::make_string("mkdir"));
|
":message", format("%s has been created", path.c_str())));
|
||||||
lst.add_prop(":message", Sexp::make_string(format("%s has been created", path.c_str())));
|
|
||||||
|
|
||||||
output_sexp(std::move(lst));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sexp::List
|
Sexp
|
||||||
Server::Private::perform_move(Store::Id docid,
|
Server::Private::perform_move(Store::Id docid,
|
||||||
const Message& msg,
|
const Message& msg,
|
||||||
const std::string& maildirarg,
|
const std::string& maildirarg,
|
||||||
@ -846,14 +818,14 @@ Server::Private::perform_move(Store::Id docid,
|
|||||||
if (!new_msg)
|
if (!new_msg)
|
||||||
throw new_msg.error();
|
throw new_msg.error();
|
||||||
|
|
||||||
Sexp::List seq;
|
Sexp seq;
|
||||||
seq.add_prop(":update", build_message_sexp(new_msg.value(), docid, {}));
|
seq.put_props(":update", build_message_sexp(new_msg.value(), docid, {}));
|
||||||
/* note, the :move t thing is a hint to the frontend that it
|
/* note, the :move t thing is a hint to the frontend that it
|
||||||
* could remove the particular header */
|
* could remove the particular header */
|
||||||
if (different_mdir)
|
if (different_mdir)
|
||||||
seq.add_prop(":move", Sexp::make_symbol("t"));
|
seq.put_props(":move", Sexp::t());
|
||||||
if (!no_view)
|
if (!no_view)
|
||||||
seq.add_prop(":maybe-view", Sexp::make_symbol("t"));
|
seq.put_props(":maybe-view", Sexp::t());
|
||||||
|
|
||||||
return seq;
|
return seq;
|
||||||
}
|
}
|
||||||
@ -876,7 +848,7 @@ calculate_message_flags(const Message& msg, Option<std::string> flagopt)
|
|||||||
return flags.value();
|
return flags.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
Sexp::List
|
Sexp
|
||||||
Server::Private::move_docid(Store::Id docid,
|
Server::Private::move_docid(Store::Id docid,
|
||||||
Option<std::string> flagopt,
|
Option<std::string> flagopt,
|
||||||
bool new_name,
|
bool new_name,
|
||||||
@ -891,6 +863,7 @@ Server::Private::move_docid(Store::Id docid,
|
|||||||
|
|
||||||
const auto flags = calculate_message_flags(msg.value(), flagopt);
|
const auto flags = calculate_message_flags(msg.value(), flagopt);
|
||||||
auto lst = perform_move(docid, *msg, "", flags, new_name, no_view);
|
auto lst = perform_move(docid, *msg, "", flags, new_name, no_view);
|
||||||
|
|
||||||
return lst;
|
return lst;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -904,18 +877,18 @@ Server::Private::move_docid(Store::Id docid,
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
Server::Private::move_handler(const Parameters& params)
|
Server::Private::move_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
auto maildir{get_string_or(params, ":maildir")};
|
auto maildir{cmd.string_arg(":maildir").value_or("")};
|
||||||
const auto flagopt{get_string(params, ":flags")};
|
const auto flagopt{cmd.string_arg(":flags").value_or("")};
|
||||||
const auto rename{get_bool_or(params, ":rename")};
|
const auto rename{cmd.boolean_arg(":rename")};
|
||||||
const auto no_view{get_bool_or(params, ":noupdate")};
|
const auto no_view{cmd.boolean_arg(":noupdate")};
|
||||||
const auto docids{determine_docids(store_, params)};
|
const auto docids{determine_docids(store_, cmd)};
|
||||||
|
|
||||||
if (docids.size() > 1) {
|
if (docids.size() > 1) {
|
||||||
if (!maildir.empty()) // ie. duplicate message-ids.
|
if (!maildir.empty()) // ie. duplicate message-ids.
|
||||||
throw Mu::Error{Error::Code::Store,
|
throw Mu::Error{Error::Code::Store,
|
||||||
"can't move multiple messages at the same time"};
|
"can't move multiple messages at the same time"};
|
||||||
// multi.
|
// multi.
|
||||||
for (auto&& docid : docids)
|
for (auto&& docid : docids)
|
||||||
output_sexp(move_docid(docid, flagopt,
|
output_sexp(move_docid(docid, flagopt,
|
||||||
@ -939,57 +912,50 @@ Server::Private::move_handler(const Parameters& params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::ping_handler(const Parameters& params)
|
Server::Private::ping_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto storecount{store().size()};
|
const auto storecount{store().size()};
|
||||||
if (storecount == (unsigned)-1)
|
if (storecount == (unsigned)-1)
|
||||||
throw Error{Error::Code::Store, "failed to read store"};
|
throw Error{Error::Code::Store, "failed to read store"};
|
||||||
|
|
||||||
const auto queries{get_string_vec(params, ":queries")};
|
const auto queries{cmd.string_vec_arg(":queries")
|
||||||
Sexp::List qresults;
|
.value_or(std::vector<std::string>{})};
|
||||||
|
Sexp qresults;
|
||||||
for (auto&& q : queries) {
|
for (auto&& q : queries) {
|
||||||
const auto count{store_.count_query(q)};
|
const auto count{store_.count_query(q)};
|
||||||
const auto unreadq{format("flag:unread AND (%s)", q.c_str())};
|
const auto unreadq{format("flag:unread AND (%s)", q.c_str())};
|
||||||
const auto unread{store_.count_query(unreadq)};
|
const auto unread{store_.count_query(unreadq)};
|
||||||
|
qresults.add(Sexp().put_props(":query", q,
|
||||||
Sexp::List lst;
|
":count", count,
|
||||||
lst.add_prop(":query", Sexp::make_string(q));
|
":unread", unread));
|
||||||
lst.add_prop(":count", Sexp::make_number(count));
|
|
||||||
lst.add_prop(":unread", Sexp::make_number(unread));
|
|
||||||
|
|
||||||
qresults.add(Sexp::make_list(std::move(lst)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Sexp::List addrs;
|
Sexp addrs;
|
||||||
for (auto&& addr : store().properties().personal_addresses)
|
for (auto&& addr : store().properties().personal_addresses)
|
||||||
addrs.add(Sexp::make_string(addr));
|
addrs.add(addr);
|
||||||
|
|
||||||
Sexp::List lst;
|
auto lst = Sexp().put_props(":pong", "mu");
|
||||||
lst.add_prop(":pong", Sexp::make_string("mu"));
|
auto proplst = Sexp().put_props(
|
||||||
|
":version", VERSION,
|
||||||
|
":personal-addresses", std::move(addrs),
|
||||||
|
":database-path", store().properties().database_path,
|
||||||
|
":root-maildir", store().properties().root_maildir,
|
||||||
|
":doccount", storecount,
|
||||||
|
":queries", std::move(qresults));
|
||||||
|
|
||||||
Sexp::List proplst;
|
output_sexp(lst.put_props(":props", std::move(proplst)));
|
||||||
proplst.add_prop(":version", Sexp::make_string(VERSION));
|
|
||||||
proplst.add_prop(":personal-addresses", Sexp::make_list(std::move(addrs)));
|
|
||||||
proplst.add_prop(":database-path", Sexp::make_string(store().properties().database_path));
|
|
||||||
proplst.add_prop(":root-maildir", Sexp::make_string(store().properties().root_maildir));
|
|
||||||
proplst.add_prop(":doccount", Sexp::make_number(storecount));
|
|
||||||
proplst.add_prop(":queries", Sexp::make_list(std::move(qresults)));
|
|
||||||
|
|
||||||
lst.add_prop(":props", Sexp::make_list(std::move(proplst)));
|
|
||||||
|
|
||||||
output_sexp(std::move(lst));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::quit_handler(const Parameters& params)
|
Server::Private::quit_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
keep_going_ = false;
|
keep_going_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::remove_handler(const Parameters& params)
|
Server::Private::remove_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto docid{get_int_or(params, ":docid")};
|
const auto docid{cmd.number_arg(":docid").value_or(0)};
|
||||||
const auto path{path_from_docid(store(), docid)};
|
const auto path{path_from_docid(store(), docid)};
|
||||||
|
|
||||||
if (::unlink(path.c_str()) != 0 && errno != ENOENT)
|
if (::unlink(path.c_str()) != 0 && errno != ENOENT)
|
||||||
@ -1000,99 +966,83 @@ Server::Private::remove_handler(const Parameters& params)
|
|||||||
|
|
||||||
if (!store().remove_message(path))
|
if (!store().remove_message(path))
|
||||||
g_warning("failed to remove message @ %s (%d) from store", path.c_str(), docid);
|
g_warning("failed to remove message @ %s (%d) from store", path.c_str(), docid);
|
||||||
// act as if it worked.
|
output_sexp(Sexp().put_props(":remove", docid)); // act as if it worked.
|
||||||
|
|
||||||
Sexp::List lst;
|
|
||||||
lst.add_prop(":remove", Sexp::make_number(docid));
|
|
||||||
|
|
||||||
output_sexp(std::move(lst));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::sent_handler(const Parameters& params)
|
Server::Private::sent_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto path{get_string_or(params, ":path")};
|
const auto path{cmd.string_arg(":path").value_or("")};
|
||||||
const auto docid = store().add_message(path);
|
const auto docid = store().add_message(path);
|
||||||
if (!docid)
|
if (!docid)
|
||||||
throw Error{Error::Code::Store, "failed to add path: %s",
|
throw Error{Error::Code::Store, "failed to add path: %s",
|
||||||
docid.error().what()};
|
docid.error().what()};
|
||||||
|
output_sexp(Sexp().put_props(
|
||||||
Sexp::List lst;
|
":sent", Sexp::t(),
|
||||||
lst.add_prop(":sent", Sexp::make_symbol("t"));
|
":path", path,
|
||||||
lst.add_prop(":path", Sexp::make_string(path));
|
":docid", docid.value()));
|
||||||
lst.add_prop(":docid", Sexp::make_number(docid.value()));
|
|
||||||
|
|
||||||
output_sexp(std::move(lst));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Server::Private::maybe_mark_as_read(Store::Id docid, Flags oldflags, bool rename)
|
Server::Private::view_mark_as_read(Store::Id docid, const Message& msg, bool rename)
|
||||||
{
|
{
|
||||||
const auto newflags{flags_from_delta_expr("+S-u-N", oldflags)};
|
/* move some message if the flags changes; and send either a :view (main message
|
||||||
if (!newflags || oldflags == *newflags)
|
* or :update (the rest))*/
|
||||||
return false; // nothing to do.
|
auto maybe_move = [&](Store::Id msg_docid, Flags old_flags,
|
||||||
|
bool do_rename, bool do_view)->bool {
|
||||||
|
|
||||||
const auto msg = store().move_message(docid, {}, newflags, rename);
|
const auto newflags{flags_from_delta_expr("+S-u-N", old_flags)};
|
||||||
if (!msg)
|
if (!newflags || old_flags == *newflags)
|
||||||
throw msg.error();
|
return false;
|
||||||
|
|
||||||
/* send an update */
|
auto updated_msg = store().move_message(msg_docid, {}, newflags, do_rename);
|
||||||
Sexp::List update;
|
if (!updated_msg)
|
||||||
update.add_prop(":update", build_message_sexp(*msg, docid, {}));
|
throw updated_msg.error();
|
||||||
output_sexp(Sexp::make_list(std::move(update)));
|
|
||||||
|
|
||||||
g_debug("marked message %d as read => %s", docid, msg->path().c_str());
|
output_sexp(Sexp().put_props(do_view ? ":view" : ":update",
|
||||||
|
build_message_sexp(*updated_msg, docid, {})));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
/* now get _al_ the message-ids for the given message-id,
|
||||||
}
|
* since, we want to apply the read-status to _all_. */
|
||||||
|
|
||||||
bool
|
/* first the main message */
|
||||||
Server::Private::maybe_mark_msgid_as_read(const std::string& msgid, bool rename) try
|
bool moved = maybe_move(docid, msg.flags(), rename, true/*:view*/);
|
||||||
{
|
|
||||||
const auto docids = docids_for_msgid(store_, msgid);
|
|
||||||
if (!docids.empty())
|
|
||||||
g_debug("marking %zu messages with message-id '%s' as read",
|
|
||||||
docids.size(), msgid.c_str());
|
|
||||||
|
|
||||||
for (auto&& docid: docids)
|
/* now any other message with the same message-id */
|
||||||
|
for (auto&& rel_docid: docids_for_msgid(store_, msg.message_id())) {
|
||||||
|
/* ignore main one since we already handled it. */
|
||||||
|
if (rel_docid == docid)
|
||||||
|
continue;
|
||||||
if (auto msg{store().find_message(docid)}; msg)
|
if (auto msg{store().find_message(docid)}; msg)
|
||||||
maybe_mark_as_read(docid, msg->flags(), rename);
|
maybe_move(rel_docid, msg->flags(), rename, false/*:update*/);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return moved;
|
||||||
|
|
||||||
} catch (...) { /* not fatal */
|
|
||||||
g_warning("failed to mark <%s> as read", msgid.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Server::Private::view_handler(const Parameters& params)
|
Server::Private::view_handler(const Command& cmd)
|
||||||
{
|
{
|
||||||
const auto mark_as_read{get_bool_or(params, ":mark-as-read")};
|
const auto mark_as_read{cmd.boolean_arg(":mark-as-read")};
|
||||||
/* for now, do _not_ rename, as it seems to confuse mbsync */
|
/* for now, do _not_ rename, as it seems to confuse mbsync */
|
||||||
const auto rename{false};
|
const auto rename{false};
|
||||||
//const auto rename{get_bool_or(params, ":rename")};
|
//const auto rename{get_bool_or(params, ":rename")};
|
||||||
|
|
||||||
const auto docids{determine_docids(store(), params)};
|
const auto docids{determine_docids(store(), cmd)};
|
||||||
|
|
||||||
if (docids.empty())
|
if (docids.empty())
|
||||||
throw Error{Error::Code::Store, "failed to find message for view"};
|
throw Error{Error::Code::Store, "failed to find message for view"};
|
||||||
|
|
||||||
const auto docid{docids.at(0)};
|
const auto docid{docids.at(0)};
|
||||||
auto msg = store().find_message(docid)
|
auto msg = store().find_message(docid)
|
||||||
.or_else([]{throw Error{Error::Code::Store,
|
.or_else([]{throw Error{Error::Code::Store,
|
||||||
"failed to find message for view"};}).value();
|
"failed to find message for view"};}).value();
|
||||||
|
|
||||||
if (mark_as_read) {
|
/* if the message is marked-as-read, the response is handled there;
|
||||||
// maybe mark the main message as read.
|
* otherwise, we do so here. */
|
||||||
maybe_mark_as_read(docid, msg.flags(), rename);
|
if (!mark_as_read || !view_mark_as_read(docid, msg, rename))
|
||||||
/* maybe mark _all_ messsage with same message-id as read */
|
output_sexp(Sexp().put_props(":view", build_message_sexp(msg, docid, {})));
|
||||||
maybe_mark_msgid_as_read(msg.message_id(), rename);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sexp::List seq;
|
|
||||||
seq.add_prop(":view", build_message_sexp(msg, docid, {}));
|
|
||||||
output_sexp(std::move(seq));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::Server(Store& store, Server::Output output)
|
Server::Server(Store& store, Server::Output output)
|
||||||
|
|||||||
@ -49,7 +49,7 @@ public:
|
|||||||
* @param sexp an s-expression
|
* @param sexp an s-expression
|
||||||
* @param flags flags that influence the behavior
|
* @param flags flags that influence the behavior
|
||||||
*/
|
*/
|
||||||
using Output = std::function<void(Sexp&& sexp, OutputFlags flags)>;
|
using Output = std::function<void(const Sexp& sexp, OutputFlags flags)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new server
|
* Construct a new server
|
||||||
|
|||||||
Reference in New Issue
Block a user