Browse Source

LIB-13: "Define Coding Standards And Project Structure" - Reformat (almost) the whole codebase using clang-format.

pull/8/head
heck 4 years ago
parent
commit
9d810df05e
  1. 55
      Adapter.cc
  2. 28
      Adapter.hh
  3. 25
      Adapter.hxx
  4. 0
      README.md
  5. 4
      Semaphore.hh
  6. 3
      call_with_lock.cc
  7. 10
      call_with_lock.hh
  8. 24
      callback_dispatcher.cc
  9. 12
      callback_dispatcher.hh
  10. 6
      constant_time_algo.cc
  11. 3
      constant_time_algo.hh
  12. 21
      locked_queue.hh
  13. 131
      message_cache.cc
  14. 41
      message_cache.hh
  15. 15
      pEpLog.cc
  16. 14
      pEpLog.hh
  17. 35
      passphrase_cache.cc
  18. 6
      passphrase_cache.hh
  19. 9
      passphrase_cache.hxx
  20. 8
      slurp.cc
  21. 3
      slurp.hh
  22. 6
      status_to_string.cc
  23. 3
      status_to_string.hh

55
Adapter.cc

@ -14,32 +14,36 @@ using namespace std;
thread_local pEp::Adapter::Session pEp::Adapter::session; thread_local pEp::Adapter::Session pEp::Adapter::session;
namespace pEp { namespace pEp {
void throw_status(PEP_STATUS status) void throw_status(::PEP_STATUS status)
{ {
if (status == PEP_STATUS_OK) if (status == ::PEP_STATUS_OK) {
return; return;
if (status >= 0x400 && status <= 0x4ff) }
if (status >= 0x400 && status <= 0x4ff) {
return; return;
if (status == PEP_STATEMACHINE_CANNOT_SEND) }
if (status == ::PEP_STATEMACHINE_CANNOT_SEND) {
return; return;
if (status == PEP_OUT_OF_MEMORY) }
if (status == ::PEP_OUT_OF_MEMORY) {
throw bad_alloc(); throw bad_alloc();
if (status == PEP_ILLEGAL_VALUE) }
if (status == ::PEP_ILLEGAL_VALUE) {
throw invalid_argument("illegal value"); throw invalid_argument("illegal value");
}
string _status = status_to_string(status); string _status = status_to_string(status);
throw RuntimeError(_status, status); throw RuntimeError(_status, status);
} }
RuntimeError::RuntimeError(const std::string& _text, PEP_STATUS _status) RuntimeError::RuntimeError(const std::string &_text, ::PEP_STATUS _status)
: std::runtime_error(_text.c_str()), text(_text), status(_status) : std::runtime_error(_text.c_str()), text(_text), status(_status)
{ {
} }
namespace Adapter { namespace Adapter {
messageToSend_t _messageToSend = nullptr; ::messageToSend_t _messageToSend = nullptr;
notifyHandshake_t _notifyHandshake = nullptr; ::notifyHandshake_t _notifyHandshake = nullptr;
std::thread _sync_thread; std::thread _sync_thread;
::utility::locked_queue<SYNC_EVENT, ::free_Sync_event> sync_evt_q; ::utility::locked_queue<SYNC_EVENT, ::free_Sync_event> sync_evt_q;
@ -50,36 +54,34 @@ namespace pEp {
return _sync_thread.get_id(); return _sync_thread.get_id();
} }
int _inject_sync_event(SYNC_EVENT ev, void *management) int _inject_sync_event(::SYNC_EVENT ev, void *management)
{ {
try { try {
if (ev == nullptr) { if (ev == nullptr) {
sync_evt_q.clear(); sync_evt_q.clear();
sync_evt_q.push_back(ev); sync_evt_q.push_back(ev);
} } else {
else {
sync_evt_q.push_front(ev); sync_evt_q.push_front(ev);
} }
} } catch (exception &) {
catch (exception&) {
return 1; return 1;
} }
return 0; return 0;
} }
PEP_STATUS _ensure_passphrase(PEP_SESSION session, const char *fpr) PEP_STATUS _ensure_passphrase(::PEP_SESSION session, const char *fpr)
{ {
return passphrase_cache.ensure_passphrase(session, fpr); return passphrase_cache.ensure_passphrase(session, fpr);
} }
// threshold: max waiting time in seconds // threshold: max waiting time in seconds
SYNC_EVENT _retrieve_next_sync_event(void *management, unsigned threshold) ::SYNC_EVENT _retrieve_next_sync_event(void *management, unsigned threshold)
{ {
SYNC_EVENT syncEvent = nullptr; ::SYNC_EVENT syncEvent = nullptr;
const bool success = sync_evt_q.try_pop_front(syncEvent, std::chrono::seconds(threshold)); const bool success = sync_evt_q.try_pop_front(syncEvent, std::chrono::seconds(threshold));
if (!success) { if (!success) {
return new_sync_timeout_event(); return ::new_sync_timeout_event();
} }
return syncEvent; return syncEvent;
@ -90,11 +92,11 @@ namespace pEp {
return _sync_thread.get_id() == this_thread::get_id(); return _sync_thread.get_id() == this_thread::get_id();
} }
PEP_SESSION Session::operator()(session_action action) ::PEP_SESSION Session::operator()(session_action action)
{ {
std::lock_guard<mutex> lock(m); std::lock_guard<mutex> lock(m);
PEP_STATUS status = PEP_STATUS_OK; ::PEP_STATUS status = ::PEP_STATUS_OK;
switch (action) { switch (action) {
case release: case release:
@ -105,7 +107,7 @@ namespace pEp {
case init: case init:
if (!_session.get()) { if (!_session.get()) {
PEP_SESSION session_; ::PEP_SESSION session_;
status = ::init(&session_, _messageToSend, _inject_sync_event, _ensure_passphrase); status = ::init(&session_, _messageToSend, _inject_sync_event, _ensure_passphrase);
throw_status(status); throw_status(status);
_session = SessionPtr{session_, ::release}; _session = SessionPtr{session_, ::release};
@ -113,7 +115,7 @@ namespace pEp {
break; break;
default: default:
status = PEP_ILLEGAL_VALUE; status = ::PEP_ILLEGAL_VALUE;
} }
throw_status(status); throw_status(status);
@ -140,8 +142,7 @@ namespace pEp {
SYNC_EVENT ev; SYNC_EVENT ev;
try { try {
ev = sync_evt_q.back(); ev = sync_evt_q.back();
} } catch (std::underflow_error &) {
catch (std::underflow_error&) {
return false; return false;
} }
if (ev) { if (ev) {
@ -150,5 +151,5 @@ namespace pEp {
return true; return true;
} }
} }
} } // namespace Adapter
} } // namespace pEp

28
Adapter.hh

@ -5,10 +5,10 @@
#define LIBPEPADAPTER_ADAPTER_HH #define LIBPEPADAPTER_ADAPTER_HH
#include <functional> #include <functional>
#include <memory>
#include <stdexcept>
#include <string> #include <string>
#include <thread> #include <thread>
#include <stdexcept>
#include <memory>
#include <pEp/sync_api.h> #include <pEp/sync_api.h>
@ -17,17 +17,17 @@ namespace pEp {
// throws std::bad_alloc if status==PEP_OUT_OF_MEMORY, // throws std::bad_alloc if status==PEP_OUT_OF_MEMORY,
// throws std::invalid_argument if status==PEP_ILLEGAL_VALUE, // throws std::invalid_argument if status==PEP_ILLEGAL_VALUE,
// throws RuntimeError when 'status' represents another exceptional value. // throws RuntimeError when 'status' represents another exceptional value.
void throw_status(PEP_STATUS status); void throw_status(::PEP_STATUS status);
struct RuntimeError : std::runtime_error { struct RuntimeError : std::runtime_error {
RuntimeError(const std::string& _text, PEP_STATUS _status); RuntimeError(const std::string &_text, ::PEP_STATUS _status);
std::string text; std::string text;
PEP_STATUS status; ::PEP_STATUS status;
}; };
namespace Adapter { namespace Adapter {
int _inject_sync_event(SYNC_EVENT ev, void *management); int _inject_sync_event(::SYNC_EVENT ev, void *management);
PEP_STATUS _ensure_passphrase(PEP_SESSION session, const char *fpr); ::PEP_STATUS _ensure_passphrase(::PEP_SESSION session, const char *fpr);
template<class T = void> template<class T = void>
void startup( void startup(
@ -35,8 +35,7 @@ namespace pEp {
notifyHandshake_t notifyHandshake, notifyHandshake_t notifyHandshake,
T *obj = nullptr, T *obj = nullptr,
std::function<void(T *)> _startup = nullptr, std::function<void(T *)> _startup = nullptr,
std::function< void (T *) > _shutdown = nullptr std::function<void(T *)> _shutdown = nullptr);
);
// returns 'true' when called from the "sync" thread, 'false' otherwise. // returns 'true' when called from the "sync" thread, 'false' otherwise.
bool on_sync_thread(); bool on_sync_thread();
@ -44,7 +43,8 @@ namespace pEp {
// returns the thread id of the sync thread // returns the thread id of the sync thread
std::thread::id sync_thread_id(); std::thread::id sync_thread_id();
enum session_action { enum session_action
{
init, init,
release release
}; };
@ -59,14 +59,14 @@ namespace pEp {
extern thread_local Session session; extern thread_local Session session;
// injects a NULL event into sync_event_queue to denote sync thread to shutdown, // injects a NULL event into sync_event_queue to denote sync thread to
// and joins & removes the sync thread // shutdown, and joins & removes the sync thread
void shutdown(); void shutdown();
bool is_sync_running(); bool is_sync_running();
bool in_shutdown(); bool in_shutdown();
} } // namespace Adapter
} } // namespace pEp
#include "Adapter.hxx" #include "Adapter.hxx"

25
Adapter.hxx

@ -14,14 +14,14 @@ namespace pEp {
namespace Adapter { namespace Adapter {
using std::function; using std::function;
extern messageToSend_t _messageToSend; extern ::messageToSend_t _messageToSend;
extern notifyHandshake_t _notifyHandshake; extern ::notifyHandshake_t _notifyHandshake;
extern std::thread _sync_thread; extern std::thread _sync_thread;
extern ::utility::locked_queue< SYNC_EVENT, ::free_Sync_event > sync_evt_q; extern ::utility::locked_queue<::SYNC_EVENT, ::free_Sync_event> sync_evt_q;
extern std::mutex m; extern std::mutex m;
SYNC_EVENT _retrieve_next_sync_event(void *management, unsigned threshold); ::SYNC_EVENT _retrieve_next_sync_event(void *management, unsigned threshold);
static std::exception_ptr _ex; static std::exception_ptr _ex;
static std::atomic_bool register_done{false}; static std::atomic_bool register_done{false};
@ -43,14 +43,17 @@ namespace pEp {
{ {
// TODO: Do we need to use a passphraseWrap here??? // TODO: Do we need to use a passphraseWrap here???
pEpLog("register_sync_callbacks()"); pEpLog("register_sync_callbacks()");
PEP_STATUS status = register_sync_callbacks(session(), nullptr, ::PEP_STATUS status = ::register_sync_callbacks(
_notifyHandshake, _retrieve_next_sync_event); session(),
nullptr,
_notifyHandshake,
_retrieve_next_sync_event);
pEpLog("register_sync_callbacks() return:" << status); pEpLog("register_sync_callbacks() return:" << status);
try { try {
throw_status(status); throw_status(status);
register_done.store(true); register_done.store(true);
} } catch (...) {
catch (...) {
_ex = std::current_exception(); _ex = std::current_exception();
register_done.store(true); register_done.store(true);
return; return;
@ -58,7 +61,7 @@ namespace pEp {
} }
pEpLog("sync protocol loop started"); pEpLog("sync protocol loop started");
do_sync_protocol(session(), (void *)obj); ::do_sync_protocol(session(), (void *)obj);
pEpLog("sync protocol loop ended"); pEpLog("sync protocol loop ended");
unregister_sync_callbacks(session()); unregister_sync_callbacks(session());
@ -104,7 +107,7 @@ namespace pEp {
} }
} }
} }
} } // namespace Adapter
} } // namespace pEp
#endif // LIBPEPADAPTER_ADAPTER_HXX #endif // LIBPEPADAPTER_ADAPTER_HXX

0
readme.md → README.md

4
Semaphore.hh

@ -46,8 +46,6 @@ namespace pEp {
cv.notify_all(); cv.notify_all();
} }
}; };
} } // namespace pEp
#endif // LIBPEPADAPTER_SEMAPHORE_HH #endif // LIBPEPADAPTER_SEMAPHORE_HH

3
call_with_lock.cc

@ -3,7 +3,6 @@
#include "call_with_lock.hh" #include "call_with_lock.hh"
namespace pEp namespace pEp {
{
std::mutex call_with_lock_mutex; std::mutex call_with_lock_mutex;
} }

10
call_with_lock.hh

@ -6,13 +6,13 @@
#include <mutex> #include <mutex>
namespace pEp namespace pEp {
{
extern std::mutex call_with_lock_mutex; extern std::mutex call_with_lock_mutex;
// TODO: use && and std::forward<> to avoid copying of the arguments. // TODO: use && and std::forward<> to avoid copying of the arguments.
// It is not relevant, yet, because at the moment we use this function template only // It is not relevant, yet, because at the moment we use this function
// for init() and release() which have cheap-to-copy pointer parameters only // template only for init() and release() which have cheap-to-copy pointer
// parameters only
template<class R, class... Args> template<class R, class... Args>
R call_with_lock(R (*fn)(Args...), Args... args) R call_with_lock(R (*fn)(Args...), Args... args)
{ {
@ -21,6 +21,6 @@ namespace pEp
} }
} } // namespace pEp
#endif // LIBPEPADAPTER_CALL_WITH_LOCK_HH #endif // LIBPEPADAPTER_CALL_WITH_LOCK_HH

24
callback_dispatcher.cc

@ -14,8 +14,10 @@ namespace pEp {
return callback_dispatcher._messageToSend(msg); return callback_dispatcher._messageToSend(msg);
} }
PEP_STATUS CallbackDispatcher::notifyHandshake(::pEp_identity *me, PEP_STATUS CallbackDispatcher::notifyHandshake(
::pEp_identity *partner, ::sync_handshake_signal signal) ::pEp_identity *me,
::pEp_identity *partner,
::sync_handshake_signal signal)
{ {
return callback_dispatcher._notifyHandshake(me, partner, signal); return callback_dispatcher._notifyHandshake(me, partner, signal);
} }
@ -24,8 +26,7 @@ namespace pEp {
::messageToSend_t messageToSend, ::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake, ::notifyHandshake_t notifyHandshake,
proc on_startup, proc on_startup,
proc shutdown proc shutdown)
)
{ {
assert(messageToSend); assert(messageToSend);
if (!messageToSend) { if (!messageToSend) {
@ -77,8 +78,10 @@ namespace pEp {
pEpLog("called"); pEpLog("called");
callback_dispatcher.semaphore.go(); callback_dispatcher.semaphore.go();
pEp::Adapter::startup<CallbackDispatcher>(CallbackDispatcher::messageToSend, pEp::Adapter::startup<CallbackDispatcher>(
CallbackDispatcher::notifyHandshake, &callback_dispatcher, CallbackDispatcher::messageToSend,
CallbackDispatcher::notifyHandshake,
&callback_dispatcher,
&CallbackDispatcher::on_startup, &CallbackDispatcher::on_startup,
&CallbackDispatcher::on_shutdown); &CallbackDispatcher::on_shutdown);
@ -143,8 +146,10 @@ namespace pEp {
return PEP_STATUS_OK; return PEP_STATUS_OK;
} }
PEP_STATUS CallbackDispatcher::_notifyHandshake(::pEp_identity *me, PEP_STATUS CallbackDispatcher::_notifyHandshake(
::pEp_identity *partner, ::sync_handshake_signal signal) ::pEp_identity *me,
::pEp_identity *partner,
::sync_handshake_signal signal)
{ {
for (auto target : targets) { for (auto target : targets) {
if (target.notifyHandshake) { if (target.notifyHandshake) {
@ -169,5 +174,4 @@ namespace pEp {
return PEP_STATUS_OK; return PEP_STATUS_OK;
} }
}; }; // namespace pEp

12
callback_dispatcher.hh

@ -34,8 +34,7 @@ namespace pEp {
::messageToSend_t messageToSend, ::messageToSend_t messageToSend,
::notifyHandshake_t notifyHandshake, ::notifyHandshake_t notifyHandshake,
proc on_startup = nullptr, proc on_startup = nullptr,
proc on_shutdown = nullptr proc on_shutdown = nullptr);
);
void remove(::messageToSend_t messageToSend); void remove(::messageToSend_t messageToSend);
static void start_sync(); static void start_sync();
@ -45,8 +44,8 @@ namespace pEp {
static PEP_STATUS notifyHandshake( static PEP_STATUS notifyHandshake(
::pEp_identity *me, ::pEp_identity *me,
::pEp_identity *partner, ::pEp_identity *partner,
::sync_handshake_signal signal ::sync_handshake_signal signal);
);
protected: protected:
void on_startup(); void on_startup();
void on_shutdown(); void on_shutdown();
@ -55,13 +54,12 @@ namespace pEp {
PEP_STATUS _notifyHandshake( PEP_STATUS _notifyHandshake(
::pEp_identity *me, ::pEp_identity *me,
::pEp_identity *partner, ::pEp_identity *partner,
::sync_handshake_signal signal ::sync_handshake_signal signal);
);
friend const char *PassphraseCache::add(const std::string &passphrase); friend const char *PassphraseCache::add(const std::string &passphrase);
}; };
extern CallbackDispatcher callback_dispatcher; extern CallbackDispatcher callback_dispatcher;
} } // namespace pEp
#endif // LIBPEPADAPTER_CALLBACK_DISPATCHER_HH #endif // LIBPEPADAPTER_CALLBACK_DISPATCHER_HH

6
constant_time_algo.cc

@ -3,16 +3,14 @@
#include "constant_time_algo.hh" #include "constant_time_algo.hh"
namespace pEp namespace pEp {
{
bool constant_time_equal(const std::string &a, const std::string &b) bool constant_time_equal(const std::string &a, const std::string &b)
{ {
if (a.size() != b.size()) if (a.size() != b.size())
return false; return false;
unsigned d = 0; unsigned d = 0;
for(std::size_t idx = 0; idx<a.size(); ++idx) for (std::size_t idx = 0; idx < a.size(); ++idx) {
{
d |= (static_cast<unsigned>(a[idx]) ^ static_cast<unsigned>(b[idx])); d |= (static_cast<unsigned>(a[idx]) ^ static_cast<unsigned>(b[idx]));
} }

3
constant_time_algo.hh

@ -6,8 +6,7 @@
#include <string> #include <string>
namespace pEp namespace pEp {
{
// Returns false if a.size() != b.size(). // Returns false if a.size() != b.size().
// Compares always _all_ characters of 'a' and 'b' so runtime does not // Compares always _all_ characters of 'a' and 'b' so runtime does not
// depends on the character position where the strings differ. // depends on the character position where the strings differ.

21
locked_queue.hh

@ -8,11 +8,9 @@
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
namespace utility namespace utility {
{
template<class T, void (*Deleter)(T) = nullptr> template<class T, void (*Deleter)(T) = nullptr>
class locked_queue class locked_queue {
{
typedef std::recursive_mutex Mutex; typedef std::recursive_mutex Mutex;
typedef std::unique_lock<Mutex> Lock; typedef std::unique_lock<Mutex> Lock;
@ -30,10 +28,8 @@ namespace utility
void clear() void clear()
{ {
Lock L(_mtx); Lock L(_mtx);
if(Deleter != nullptr) if (Deleter != nullptr) {
{ for (auto q : _q) {
for(auto q : _q)
{
Deleter(q); Deleter(q);
} }
} }
@ -86,8 +82,7 @@ namespace utility
{ {
Lock L(_mtx); Lock L(_mtx);
++_waiting; ++_waiting;
if(! _cv.wait_until(L, end_time, [this]{ return !_q.empty(); } ) ) if (!_cv.wait_until(L, end_time, [this] { return !_q.empty(); })) {
{
--_waiting; --_waiting;
return false; return false;
} }
@ -104,8 +99,7 @@ namespace utility
{ {
Lock L(_mtx); Lock L(_mtx);
++_waiting; ++_waiting;
if(! _cv.wait_until(L, end_time, [this]{ return !_q.empty(); } ) ) if (!_cv.wait_until(L, end_time, [this] { return !_q.empty(); })) {
{
--_waiting; --_waiting;
return false; return false;
} }
@ -122,8 +116,7 @@ namespace utility
{ {
Lock L(_mtx); Lock L(_mtx);
++_waiting; ++_waiting;
if(! _cv.wait_for(L, duration, [this]{ return !_q.empty(); } ) ) if (!_cv.wait_for(L, duration, [this] { return !_q.empty(); })) {
{
--_waiting; --_waiting;
return false; return false;
} }

131
message_cache.cc

@ -25,11 +25,9 @@ namespace pEp {
message **dst, message **dst,
stringlist_t **keylist, stringlist_t **keylist,
PEP_rating *rating, PEP_rating *rating,
PEP_decrypt_flags_t *flags PEP_decrypt_flags_t *flags)
)
{ {
return message_cache.decrypt_message(session, src, dst, keylist, return message_cache.decrypt_message(session, src, dst, keylist, rating, flags);
rating, flags);
} }
PEP_STATUS MessageCache::cache_mime_encode_message( PEP_STATUS MessageCache::cache_mime_encode_message(
@ -37,23 +35,19 @@ namespace pEp {
const message *msg, const message *msg,
bool omit_fields, bool omit_fields,
char **mimetext, char **mimetext,
bool has_pEp_msg_attachment bool has_pEp_msg_attachment)
)
{ {
which _one = (which)one; which _one = (which)one;
return message_cache.mime_encode_message(_one, msg, omit_fields, return message_cache.mime_encode_message(_one, msg, omit_fields, mimetext, has_pEp_msg_attachment);
mimetext, has_pEp_msg_attachment);
} }
PEP_STATUS MessageCache::cache_mime_decode_message( PEP_STATUS MessageCache::cache_mime_decode_message(
const char *mimetext, const char *mimetext,
size_t size, size_t size,
message **msg, message **msg,
bool* has_possible_pEp_msg bool *has_possible_pEp_msg)
)
{ {
return message_cache.mime_decode_message(mimetext, size, msg, return message_cache.mime_decode_message(mimetext, size, msg, has_possible_pEp_msg);
has_possible_pEp_msg);
} }
PEP_STATUS MessageCache::cache_encrypt_message( PEP_STATUS MessageCache::cache_encrypt_message(
@ -62,11 +56,9 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags)
)
{ {
return message_cache.encrypt_message(session, src, extra, dst, return message_cache.encrypt_message(session, src, extra, dst, enc_format, flags);
enc_format, flags);
} }
PEP_STATUS MessageCache::cache_encrypt_message_for_self( PEP_STATUS MessageCache::cache_encrypt_message_for_self(
@ -76,11 +68,10 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags)
)
{ {
return message_cache.encrypt_message_for_self(session, target_id, src, return message_cache
extra, dst, enc_format, flags); .encrypt_message_for_self(session, target_id, src, extra, dst, enc_format, flags);
} }
PEP_STATUS MessageCache::cache_release(std::string id) PEP_STATUS MessageCache::cache_release(std::string id)
@ -96,8 +87,8 @@ namespace pEp {
::free_message(_cache.at(id).src); ::free_message(_cache.at(id).src);
::free_message(_cache.at(id).dst); ::free_message(_cache.at(id).dst);
_cache.erase(id); _cache.erase(id);
} catch (...) {
} }
catch (...) { }
} }
static char *dup(const char *src) static char *dup(const char *src)
@ -173,12 +164,12 @@ namespace pEp {
return dst; return dst;
} }
static ::message_ref_list *dup(const ::message_ref_list *src) { static ::message_ref_list *dup(const ::message_ref_list *src)
{
if (!src) if (!src)
return nullptr; return nullptr;
::message_ref_list *dst = (::message_ref_list *) ::calloc(1, ::message_ref_list *dst = (::message_ref_list *)::calloc(1, sizeof(::message_ref_list));
sizeof(::message_ref_list));
assert(dst); assert(dst);
if (!dst) if (!dst)
throw std::bad_alloc(); throw std::bad_alloc();
@ -187,8 +178,7 @@ namespace pEp {
for (const message_ref_list *s = src; s; s = s->next) { for (const message_ref_list *s = src; s; s = s->next) {
d->msg_ref = s->msg_ref; d->msg_ref = s->msg_ref;
if (s->next) { if (s->next) {
d->next = (::message_ref_list *) ::calloc(1, d->next = (::message_ref_list *)::calloc(1, sizeof(::message_ref_list));
sizeof(::message_ref_list));
assert(d); assert(d);
if (!d) if (!d)
throw std::bad_alloc(); throw std::bad_alloc();
@ -251,9 +241,9 @@ namespace pEp {
if (_id == "") { if (_id == "") {
dst->opt_fields = dup(src->opt_fields); dst->opt_fields = dup(src->opt_fields);
} } else {
else { dst->opt_fields = ::new_stringpair_list(
dst->opt_fields = ::new_stringpair_list(::new_stringpair("X-pEp-Adapter-Cache-ID", _id.c_str())); ::new_stringpair("X-pEp-Adapter-Cache-ID", _id.c_str()));
if (!dst->opt_fields) if (!dst->opt_fields)
throw std::bad_alloc(); throw std::bad_alloc();
dst->opt_fields->next = dup(src->opt_fields); dst->opt_fields->next = dup(src->opt_fields);
@ -274,8 +264,7 @@ namespace pEp {
// order of attachments // order of attachments
if (std::string(bl->mime_type) == "application/octet-stream" && if (std::string(bl->mime_type) == "application/octet-stream" &&
std::string(bl->next->mime_type) == std::string(bl->next->mime_type) == "application/pgp-encrypted") {
"application/pgp-encrypted") {
bloblist_t *one = bl->next; bloblist_t *one = bl->next;
bloblist_t *two = bl; bloblist_t *two = bl;
bl = one; bl = one;
@ -306,8 +295,7 @@ namespace pEp {
message **dst, message **dst,
stringlist_t **keylist, stringlist_t **keylist,
PEP_rating *rating, PEP_rating *rating,
PEP_decrypt_flags_t *flags PEP_decrypt_flags_t *flags)
)
{ {
if (!src || cacheID(src) == "") if (!src || cacheID(src) == "")
return PEP_ILLEGAL_VALUE; return PEP_ILLEGAL_VALUE;
@ -324,8 +312,7 @@ namespace pEp {
correctAttachmentsOrder(src->attachments); correctAttachmentsOrder(src->attachments);
::message *_dst = nullptr; ::message *_dst = nullptr;
PEP_STATUS status = ::decrypt_message(session, src, &_dst, keylist, PEP_STATUS status = ::decrypt_message(session, src, &_dst, keylist, rating, flags);
rating, flags);
*dst = empty_message_copy(_dst, _id); *dst = empty_message_copy(_dst, _id);
{ {
@ -342,8 +329,7 @@ namespace pEp {
const message *msg, const message *msg,
bool omit_fields, bool omit_fields,
char **mimetext, char **mimetext,
bool has_pEp_msg_attachment bool has_pEp_msg_attachment)
)
{ {
if (!msg || cacheID(msg) == "") if (!msg || cacheID(msg) == "")
return PEP_ILLEGAL_VALUE; return PEP_ILLEGAL_VALUE;
@ -357,16 +343,14 @@ namespace pEp {
std::lock_guard<std::mutex> l(_mtx); std::lock_guard<std::mutex> l(_mtx);
::message *_src = _cache.at(cacheID(msg)).src; ::message *_src = _cache.at(cacheID(msg)).src;
swapContent(_msg, _src); swapContent(_msg, _src);
} } else /* msg_dst */ {
else /* msg_dst */ {
std::lock_guard<std::mutex> l(_mtx); std::lock_guard<std::mutex> l(_mtx);
::message *_dst = _cache.at(cacheID(msg)).dst; ::message *_dst = _cache.at(cacheID(msg)).dst;
swapContent(_msg, _dst); swapContent(_msg, _dst);
} }
removeCacheID(_msg); removeCacheID(_msg);
PEP_STATUS status = ::mime_encode_message(_msg, omit_fields, mimetext, PEP_STATUS status = ::mime_encode_message(_msg, omit_fields, mimetext, has_pEp_msg_attachment);
has_pEp_msg_attachment);
::free_message(_msg); ::free_message(_msg);
cache_release(cacheID(msg)); cache_release(cacheID(msg));
@ -383,18 +367,15 @@ namespace pEp {
// if opt_fields is an empty list generate a new list // if opt_fields is an empty list generate a new list
if (!msg->opt_fields || !msg->opt_fields->value) { if (!msg->opt_fields || !msg->opt_fields->value) {
free_stringpair_list(msg->opt_fields); free_stringpair_list(msg->opt_fields);
msg->opt_fields = msg->opt_fields = ::new_stringpair_list(
::new_stringpair_list(::new_stringpair("X-pEp-Adapter-Cache-ID", ::new_stringpair("X-pEp-Adapter-Cache-ID", cid.c_str()));
cid.c_str()));
if (!msg->opt_fields) if (!msg->opt_fields)
throw std::bad_alloc(); throw std::bad_alloc();
} } else {
else {
// add the cache ID as first field to an existing list // add the cache ID as first field to an existing list
auto spl = msg->opt_fields; auto spl = msg->opt_fields;
msg->opt_fields = msg->opt_fields = ::new_stringpair_list(
::new_stringpair_list(::new_stringpair("X-pEp-Adapter-Cache-ID", ::new_stringpair("X-pEp-Adapter-Cache-ID", cid.c_str()));
cid.c_str()));
if (!msg->opt_fields) { if (!msg->opt_fields) {
msg->opt_fields = spl; msg->opt_fields = spl;
throw std::bad_alloc(); throw std::bad_alloc();
@ -407,8 +388,7 @@ namespace pEp {
{ {
for (auto spl = msg->opt_fields; spl && spl->value; spl = spl->next) { for (auto spl = msg->opt_fields; spl && spl->value; spl = spl->next) {
assert(spl->value->key); assert(spl->value->key);
if (spl->value->key && std::string(spl->value->key) == if (spl->value->key && std::string(spl->value->key) == "X-pEp-Adapter-Cache-ID") {
"X-pEp-Adapter-Cache-ID") {
assert(spl->value->value); assert(spl->value->value);
if (spl->value->value) if (spl->value->value)
return spl->value->value; return spl->value->value;
@ -422,23 +402,18 @@ namespace pEp {
void MessageCache::removeCacheID(::message *msg) void MessageCache::removeCacheID(::message *msg)
{ {
// if the first element in the list is the cache ID then skip // if the first element in the list is the cache ID then skip
if (msg->opt_fields && msg->opt_fields->value && if (msg->opt_fields && msg->opt_fields->value && msg->opt_fields->value->key &&
msg->opt_fields->value->key && std::string(msg->opt_fields->value->key) == "X-pEp-Adapter-Cache-ID") {
std::string(msg->opt_fields->value->key) ==
"X-pEp-Adapter-Cache-ID") {
auto n = msg->opt_fields->next; auto n = msg->opt_fields->next;
msg->opt_fields->next = nullptr; msg->opt_fields->next = nullptr;
::free_stringpair_list(msg->opt_fields); ::free_stringpair_list(msg->opt_fields);
msg->opt_fields = n; msg->opt_fields = n;
} } else {
else {
// go through the list and remove // go through the list and remove
::stringpair_list_t *prev = nullptr; ::stringpair_list_t *prev = nullptr;
for (auto spl = msg->opt_fields; spl && spl->value; spl = for (auto spl = msg->opt_fields; spl && spl->value; spl = spl->next) {
spl->next) {
assert(spl->value->key); assert(spl->value->key);
if (spl->value->key && if (spl->value->key && std::string(spl->value->key) == "X-pEp-Adapter-Cache-ID") {
std::string(spl->value->key) == "X-pEp-Adapter-Cache-ID") {
auto next = spl->next; auto next = spl->next;
spl->next = nullptr; spl->next = nullptr;
::free_stringpair_list(spl); ::free_stringpair_list(spl);
@ -454,21 +429,19 @@ namespace pEp {
const char *mimetext, const char *mimetext,
size_t size, size_t size,
message **msg, message **msg,
bool* has_possible_pEp_msg bool *has_possible_pEp_msg)
)
{ {
::message *_msg = nullptr; ::message *_msg = nullptr;
PEP_STATUS status = ::mime_decode_message(mimetext, size, &_msg, PEP_STATUS status = ::mime_decode_message(mimetext, size, &_msg, has_possible_pEp_msg);
has_possible_pEp_msg);
if (status) if (status)
return status; return status;
generateCacheID(_msg); *msg = empty_message_copy(_msg); generateCacheID(_msg);
*msg = empty_message_copy(_msg);
{ {
std::lock_guard<std::mutex> l(_mtx); std::lock_guard<std::mutex> l(_mtx);
message_cache._cache.emplace(std::make_pair(cacheID(_msg), message_cache._cache.emplace(std::make_pair(cacheID(_msg), cache_entry(_msg, nullptr)));
cache_entry(_msg, nullptr)));
} }
return status; return status;
@ -480,8 +453,7 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags)
)
{ {
::message *_msg; ::message *_msg;
std::string _id = cacheID(src); std::string _id = cacheID(src);
@ -492,8 +464,7 @@ namespace pEp {
} }
::message *_dst = nullptr; ::message *_dst = nullptr;
PEP_STATUS status = ::encrypt_message(session, src, extra, &_dst, PEP_STATUS status = ::encrypt_message(session, src, extra, &_dst, enc_format, flags);
enc_format, flags);
*dst = empty_message_copy(_dst, _id); *dst = empty_message_copy(_dst, _id);
{ {
@ -513,8 +484,7 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags)
)
{ {
::message *_msg; ::message *_msg;
std::string _id = cacheID(src); std::string _id = cacheID(src);
@ -525,8 +495,14 @@ namespace pEp {
} }
::message *_dst = nullptr; ::message *_dst = nullptr;
PEP_STATUS status = ::encrypt_message_for_self(session, target_id, src, PEP_STATUS status = ::encrypt_message_for_self(
extra, &_dst, enc_format, flags); session,
target_id,
src,
extra,
&_dst,
enc_format,
flags);
*dst = empty_message_copy(_dst, _id); *dst = empty_message_copy(_dst, _id);
{ {
@ -538,5 +514,4 @@ namespace pEp {
return status; return status;
} }
}; }; // namespace pEp

41
message_cache.hh

@ -13,8 +13,7 @@
namespace pEp { namespace pEp {
class MessageCache { class MessageCache {
struct cache_entry { struct cache_entry {
cache_entry(::message *s, ::message *d) cache_entry(::message *s, ::message *d) : src(s), dst(d) {}
: src(s), dst(d) { }
::message *src; ::message *src;
::message *dst; ::message *dst;
@ -30,22 +29,24 @@ namespace pEp {
public: public:
MessageCache(); MessageCache();
enum which { msg_src = 0, msg_dst = 1 }; enum which
{
msg_src = 0,
msg_dst = 1
};
static PEP_STATUS cache_mime_decode_message( static PEP_STATUS cache_mime_decode_message(
const char *mimetext, const char *mimetext,
size_t size, size_t size,
message **msg, message **msg,
bool* has_possible_pEp_msg bool *has_possible_pEp_msg);
);
static PEP_STATUS cache_mime_encode_message( static PEP_STATUS cache_mime_encode_message(
int one, int one,
const message *msg, const message *msg,
bool omit_fields, bool omit_fields,
char **mimetext, char **mimetext,
bool has_pEp_msg_attachment bool has_pEp_msg_attachment);
);
static PEP_STATUS cache_decrypt_message( static PEP_STATUS cache_decrypt_message(
PEP_SESSION session, PEP_SESSION session,
@ -53,8 +54,7 @@ namespace pEp {
message **dst, message **dst,
stringlist_t **keylist, stringlist_t **keylist,
PEP_rating *rating, PEP_rating *rating,
PEP_decrypt_flags_t *flags PEP_decrypt_flags_t *flags);
);
static PEP_STATUS cache_encrypt_message( static PEP_STATUS cache_encrypt_message(
PEP_SESSION session, PEP_SESSION session,
@ -62,8 +62,7 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags);
);
static PEP_STATUS cache_encrypt_message_for_self( static PEP_STATUS cache_encrypt_message_for_self(
PEP_SESSION session, PEP_SESSION session,
@ -72,8 +71,7 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags);
);
static PEP_STATUS cache_release(std::string id); static PEP_STATUS cache_release(std::string id);
@ -86,16 +84,14 @@ namespace pEp {
const char *mimetext, const char *mimetext,
size_t size, size_t size,
message **msg, message **msg,
bool* has_possible_pEp_msg bool *has_possible_pEp_msg);
);
PEP_STATUS mime_encode_message( PEP_STATUS mime_encode_message(
which one, which one,
const message *src, const message *src,
bool omit_fields, bool omit_fields,
char **mimetext, char **mimetext,
bool has_pEp_msg_attachment bool has_pEp_msg_attachment);
);
PEP_STATUS decrypt_message( PEP_STATUS decrypt_message(
PEP_SESSION session, PEP_SESSION session,
@ -103,8 +99,7 @@ namespace pEp {
message **dst, message **dst,
stringlist_t **keylist, stringlist_t **keylist,
PEP_rating *rating, PEP_rating *rating,
PEP_decrypt_flags_t *flags PEP_decrypt_flags_t *flags);
);
PEP_STATUS encrypt_message( PEP_STATUS encrypt_message(
PEP_SESSION session, PEP_SESSION session,
@ -112,8 +107,7 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags);
);
PEP_STATUS encrypt_message_for_self( PEP_STATUS encrypt_message_for_self(
PEP_SESSION session, PEP_SESSION session,
@ -122,14 +116,13 @@ namespace pEp {
stringlist_t *extra, stringlist_t *extra,
message **dst, message **dst,
PEP_enc_format enc_format, PEP_enc_format enc_format,
PEP_encrypt_flags_t flags PEP_encrypt_flags_t flags);
);
void generateCacheID(::message *msg); void generateCacheID(::message *msg);
static std::string cacheID(const ::message *msg); static std::string cacheID(const ::message *msg);
}; };
extern MessageCache message_cache; extern MessageCache message_cache;
}; }; // namespace pEp
#endif // LIBPEPADAPTER_MESSAGE_CACHE_HH #endif // LIBPEPADAPTER_MESSAGE_CACHE_HH

15
pEpLog.cc

@ -16,15 +16,18 @@ std::mutex mtx;
std::atomic_bool is_enabled{false}; std::atomic_bool is_enabled{false};
void set_enabled(bool enabled) { void set_enabled(bool enabled)
{
is_enabled.store(enabled); is_enabled.store(enabled);
} }
bool get_enabled() { bool get_enabled()
{
return is_enabled.load(); return is_enabled.load();
} }
void log(std::string msg) { void log(std::string msg)
{
if (is_enabled.load()) { if (is_enabled.load()) {
std::lock_guard<std::mutex> l(mtx); std::lock_guard<std::mutex> l(mtx);
#ifdef ANDROID #ifdef ANDROID
@ -35,6 +38,6 @@ void log(std::string msg) {
} }
} }
} // pEpLog } // namespace pEpLog
} // Adapter } // namespace Adapter
} // pEp } // namespace pEp

14
pEpLog.hh

@ -29,7 +29,9 @@
// use set_enabled_<backend>(bool) to turn logging on/off per backend // use set_enabled_<backend>(bool) to turn logging on/off per backend
#ifdef NDEBUG #ifdef NDEBUG
#define pEpLog(msg) do{}while(0) #define pEpLog(msg) \
do { \
} while (0)
#else #else
#ifdef ANDROID #ifdef ANDROID
#include <android/log.h> #include <android/log.h>
@ -38,7 +40,8 @@
#define pEpLog(msg) \ #define pEpLog(msg) \
do { \ do { \
std::stringstream msg_ss; \ std::stringstream msg_ss; \
msg_ss << std::this_thread::get_id() << " - " << __FILE__ << "::" << __FUNCTION__ << " - " << msg; \ msg_ss << std::this_thread::get_id() << " - " << __FILE__ << "::" << __FUNCTION__ \
<< " - " << msg; \
pEp::Adapter::pEpLog::log(msg_ss.str()); \ pEp::Adapter::pEpLog::log(msg_ss.str()); \
} while (0) } while (0)
#endif // NDEBUG #endif // NDEBUG
@ -53,10 +56,9 @@ void set_enabled(bool is_enabled);
bool get_enabled(); bool get_enabled();
} // pEpLog } // namespace pEpLog
} // Adapter } // namespace Adapter
} // pEp } // namespace pEp
#endif // LIBPEPADAPTER_PEPLOG_HH #endif // LIBPEPADAPTER_PEPLOG_HH

35
passphrase_cache.cc

@ -9,19 +9,19 @@
pEp::PassphraseCache pEp::passphrase_cache; pEp::PassphraseCache pEp::passphrase_cache;
namespace pEp { namespace pEp {
PassphraseCache::cache_entry::cache_entry(const std::string& p, time_point t) : PassphraseCache::cache_entry::cache_entry(const std::string& p, time_point t)
passphrase{p, 0, PassphraseCache::cache_entry::max_len}, tp{t} : passphrase{p, 0, PassphraseCache::cache_entry::max_len}, tp{t}
{ } {
}
PassphraseCache::PassphraseCache(size_t max_size, duration timeout) :
_max_size{max_size}, _timeout{timeout}, _which(_cache.end()), PassphraseCache::PassphraseCache(size_t max_size, duration timeout)
first_time(true) : _max_size{max_size}, _timeout{timeout}, _which(_cache.end()), first_time(true)
{ } {
}
PassphraseCache::PassphraseCache(const PassphraseCache& second) :
_cache{second._cache}, _max_size{second._max_size}, PassphraseCache::PassphraseCache(const PassphraseCache& second)
_timeout{second._timeout}, _stored{second._stored}, : _cache{second._cache}, _max_size{second._max_size}, _timeout{second._timeout},
_which(_cache.end()), first_time(true) _stored{second._stored}, _which(_cache.end()), first_time(true)
{ {
cleanup(); cleanup();
} }
@ -146,12 +146,10 @@ namespace pEp {
try { try {
::config_passphrase(session ? session : Adapter::session(), latest_passphrase(_copy)); ::config_passphrase(session ? session : Adapter::session(), latest_passphrase(_copy));
return PEP_STATUS_OK; return PEP_STATUS_OK;
} } catch (pEp::PassphraseCache::Empty&) {
catch (pEp::PassphraseCache::Empty&) {
new_copy = true; new_copy = true;
return PEP_PASSPHRASE_REQUIRED; return PEP_PASSPHRASE_REQUIRED;
} } catch (pEp::PassphraseCache::Exhausted&) {
catch (pEp::PassphraseCache::Exhausted&) {
new_copy = true; new_copy = true;
return PEP_WRONG_PASSPHRASE; return PEP_WRONG_PASSPHRASE;
} }
@ -172,5 +170,4 @@ namespace pEp {
return status; return status;
} }
}; }; // namespace pEp

6
passphrase_cache.hh

@ -71,8 +71,8 @@ namespace pEp {
// status = ::encrypt_message(session, src, extra, dst, enc_format, flags) // status = ::encrypt_message(session, src, extra, dst, enc_format, flags)
// using for_each_passphrase() // using for_each_passphrase()
template<typename... A> PEP_STATUS api(PEP_STATUS f(PEP_SESSION, A...), template<typename... A>
PEP_SESSION session, A... a); PEP_STATUS api(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a);
static const char* latest_passphrase(PassphraseCache& _cache); static const char* latest_passphrase(PassphraseCache& _cache);
using passphrase_callee = std::function<bool(std::string)>; using passphrase_callee = std::function<bool(std::string)>;
@ -85,7 +85,7 @@ namespace pEp {
}; };
extern PassphraseCache passphrase_cache; extern PassphraseCache passphrase_cache;
}; }; // namespace pEp
#include "passphrase_cache.hxx" #include "passphrase_cache.hxx"

9
passphrase_cache.hxx

@ -7,8 +7,8 @@
#include "passphrase_cache.hh" #include "passphrase_cache.hh"
namespace pEp { namespace pEp {
template<typename... A> PEP_STATUS PassphraseCache::api( template<typename... A>
PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a) PEP_STATUS PassphraseCache::api(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a)
{ {
PEP_STATUS status; PEP_STATUS status;
@ -18,12 +18,11 @@ namespace pEp {
return true; return true;
status = f(session, a...); status = f(session, a...);
return status != PEP_PASSPHRASE_REQUIRED && return status != PEP_PASSPHRASE_REQUIRED && status != PEP_WRONG_PASSPHRASE;
status != PEP_WRONG_PASSPHRASE;
}); });
return status; return status;
} }
}; }; // namespace pEp
#endif // LIBPEPADAPTER_PASSPHRASE_CACHE_HXX #endif // LIBPEPADAPTER_PASSPHRASE_CACHE_HXX

8
slurp.cc

@ -6,14 +6,11 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
namespace pEp namespace pEp {
{
std::string slurp(const std::string& filename) std::string slurp(const std::string& filename)
{ {
std::ifstream input(filename.c_str(), std::ios_base::binary); std::ifstream input(filename.c_str(), std::ios_base::binary);
if(!input) if (!input) {
{
throw std::runtime_error("Cannot read file \"" + filename + "\"! "); throw std::runtime_error("Cannot read file \"" + filename + "\"! ");
} }
@ -21,5 +18,4 @@ std::string slurp(const std::string& filename)
sstr << input.rdbuf(); sstr << input.rdbuf();
return sstr.str(); return sstr.str();
} }
} // end of namespace pEp } // end of namespace pEp

3
slurp.hh

@ -6,8 +6,7 @@
#include <string> #include <string>
namespace pEp namespace pEp {
{
// reads a whole file and returns it as std::string // reads a whole file and returns it as std::string
// throws std::runtime_error() if the file cannot be read. Empty file is not an error. // throws std::runtime_error() if the file cannot be read. Empty file is not an error.
std::string slurp(const std::string& filename); std::string slurp(const std::string& filename);

6
status_to_string.cc

@ -5,16 +5,14 @@
#include <sstream> #include <sstream>
namespace pEp namespace pEp {
{
// in pEpEngine.h positive values are hex, negative are decimal. :-o // in pEpEngine.h positive values are hex, negative are decimal. :-o
// TODO: the code should be generated! // TODO: the code should be generated!
std::string status_to_string(PEP_STATUS status) std::string status_to_string(PEP_STATUS status)
{ {
std::stringstream ss; std::stringstream ss;
if(status>0) if (status > 0) {
{
ss << "0x" << std::hex << status; ss << "0x" << std::hex << status;
} else { } else {
ss << status; ss << status;

3
status_to_string.hh

@ -8,8 +8,7 @@
#include <pEp/status_to_string.h> #include <pEp/status_to_string.h>
#include <string> #include <string>
namespace pEp namespace pEp {
{
// creates a textual string (returned by pep_status_to_string() ) and the numerical status value. // creates a textual string (returned by pep_status_to_string() ) and the numerical status value.
// in pEpEngine.h positive values are in hex value, negatives in decimal. So we follow this. // in pEpEngine.h positive values are in hex value, negatives in decimal. So we follow this.
std::string status_to_string(PEP_STATUS status); std::string status_to_string(PEP_STATUS status);

Loading…
Cancel
Save