// This file is under GNU Affero General Public License 3.0 // see LICENSE.txt // System #include ///////////////////////////////////////////////// #include #include #include #include #include #include // Engine #include #include #include #include // local #include "message.hh" #include "message_api.hh" namespace pEp { namespace PythonAdapter { using namespace boost::python; Message::Blob::Blob(bloblist_t *bl, bool chained) : _bl(bl), part_of_chain(chained) { if (!_bl) { throw std::bad_alloc(); } } Message::Blob::Blob(object data, string mime_type, string filename) : _bl(new_bloblist(NULL, 0, NULL, NULL)), part_of_chain(false) { if (!_bl) { throw std::bad_alloc(); } Py_buffer src; int result = PyObject_GetBuffer(data.ptr(), &src, PyBUF_CONTIG_RO); if (result) { throw invalid_argument("need a contiguous buffer to read"); } char *mem = (char *)malloc(src.len); if (!mem) { PyBuffer_Release(&src); throw std::bad_alloc(); } memcpy(mem, src.buf, src.len); free(_bl->value); _bl->size = src.len; _bl->value = mem; PyBuffer_Release(&src); this->mime_type(mime_type); this->filename(filename); } Message::Blob::Blob(const Message::Blob &second) : _bl(second._bl), part_of_chain(true) {} Message::Blob::~Blob() { if (!part_of_chain) { free(_bl->value); free(_bl); } } string Message::Blob::_repr() { std::stringstream build; build << "Blob("; if (!_bl) { build << "b'', '', ''"; } else { build << "bytes(" << _bl->size << "), "; string mime_type; if (_bl->mime_type) mime_type = string(_bl->mime_type); string filename; if (_bl->filename) filename = string(_bl->filename); build << repr(mime_type) << ", "; build << repr(filename); } build << ")"; return build.str(); } int Message::Blob::getbuffer(PyObject *self, Py_buffer *view, int flags) { bloblist_t *bl = NULL; try { Message::Blob &blob = extract(self); bl = blob._bl; } catch (std::exception &e) { PyErr_SetString(PyExc_RuntimeError, "extract not possible"); view->obj = NULL; return -1; } if (!(bl && bl->value)) { PyErr_SetString(PyExc_RuntimeError, "no data available"); view->obj = NULL; return -1; } return PyBuffer_FillInfo(view, self, bl->value, bl->size, 0, flags); } string Message::Blob::decode(string encoding) { if (encoding == "") { string _mime_type = _bl->mime_type ? _bl->mime_type : ""; encoding = "ascii"; if (_mime_type == "application/pEp.sync") { encoding = "pep.sync"; } if (_mime_type == "application/pEp.keyreset") { encoding = "pep.distribution"; } } object codecs = import("codecs"); object _decode = codecs.attr("decode"); return call(_decode.ptr(), this, encoding); } PyBufferProcs Message::Blob::bp = { getbuffer, NULL }; Message::Message(int dir, Identity *from) : _msg(new_message((PEP_msg_direction)dir), &free_message) { if (!_msg) { throw std::bad_alloc(); } if (from) { _msg->from = ::identity_dup(*from); if (!_msg->from) { throw std::bad_alloc(); } _msg->dir = (PEP_msg_direction)dir; } } Message::Message(string mimetext) : _msg(NULL, &free_message) { message *_cpy; PEP_STATUS status = mime_decode_message(mimetext.c_str(), mimetext.size(), &_cpy, NULL); switch (status) { case PEP_STATUS_OK: if (_cpy) { _cpy->dir = PEP_dir_outgoing; } else { _cpy = new_message(PEP_dir_outgoing); } if (!_cpy) { throw std::bad_alloc(); } _msg = shared_ptr(_cpy); break; case PEP_BUFFER_TOO_SMALL: throw runtime_error("mime_decode_message: buffer too small"); case PEP_CANNOT_CREATE_TEMP_FILE: throw runtime_error("mime_decode_message: cannot create temp file"); case PEP_OUT_OF_MEMORY: throw std::bad_alloc(); default: std::stringstream build; build << "mime_decode_message: unknown error (" << (int)status << ")"; throw runtime_error(build.str()); } } Message::Message(const Message &second) : _msg(second._msg) { if (!_msg.get()) { throw std::bad_alloc(); } } Message::Message(message *msg) : _msg(::message_dup(msg), &free_message) {} Message::~Message() {} Message::operator message *() { return _msg.get(); } Message::operator const message *() const { return _msg.get(); } string Message::_str() { if (!(_msg->from && _msg->from->address && _msg->from->address[0])) { throw std::out_of_range(".from_.address missing"); } char *mimetext; string result; PEP_STATUS status = mime_encode_message(*this, false, &mimetext, false); switch (status) { case PEP_STATUS_OK: result = mimetext; free(mimetext); break; case PEP_BUFFER_TOO_SMALL: throw runtime_error("mime_encode_message: buffer too small"); case PEP_CANNOT_CREATE_TEMP_FILE: throw runtime_error("mime_encode_message: cannot create temp file"); case PEP_OUT_OF_MEMORY: throw std::bad_alloc(); default: std::stringstream build; build << "mime_encode_message: unknown error (" << (int)status << ")"; throw runtime_error(build.str()); } return result; } string Message::_repr() { std::stringstream build; build << "Message(" << repr(_str()) << ")"; return build.str(); } boost::python::tuple Message::attachments() { boost::python::list l; for (bloblist_t *bl = _msg->attachments; bl && bl->value; bl = bl->next) { l.append(Blob(bl, true)); } return boost::python::tuple(l); } void Message::attachments(boost::python::list value) { bloblist_t *bl = new_bloblist(NULL, 0, NULL, NULL); if (!bl) { throw std::bad_alloc(); } bloblist_t *_l = bl; for (int i = 0; i < len(value); i++) { Message::Blob &blob = extract(value[i]); _l = bloblist_add( _l, blob._bl->value, blob._bl->size, blob._bl->mime_type, blob._bl->filename); if (!_l) { for (_l = bl; _l && _l->value;) { free(_l->mime_type); free(_l->filename); bloblist_t *_ll = _l; _l = _l->next; free(_ll); } throw std::bad_alloc(); } } for (int i = 0; i < len(value); i++) { Message::Blob &blob = extract(value[i]); blob._bl->value = NULL; blob._bl->size = 0; free(blob._bl->mime_type); blob._bl->mime_type = NULL; free(blob._bl->filename); blob._bl->filename = NULL; } free_bloblist(_msg->attachments); _msg->attachments = bl; } void Message::remove_opt_field(string name) { ::message *msg_c = _msg.get(); PEP_STATUS status = ::message_remove_opt_field(Adapter::session(), msg_c, name.c_str()); _throw_status(status); } void Message::set_opt_field(string name, string value) { ::message *msg_c = _msg.get(); PEP_STATUS status = ::message_set_opt_field(Adapter::session(), msg_c, name.c_str(), value.c_str()); _throw_status(status); } void Message::set_recv_by(Identity &new_recv_by) { // Validate self and the new Recv-By identity. ::message *msg_c = _msg.get(); if (msg_c->dir != ::PEP_dir_incoming) { throw invalid_argument("message not incoming"); } if (new_recv_by.address().empty() || new_recv_by.user_id().empty()) { throw invalid_argument("identity incomplete"); } if (new_recv_by.user_id() != PEP_OWN_USERID) { throw invalid_argument("identity non-own"); } // Make a a copy of the given identity. ::pEp_identity *new_recv_by_c = new_recv_by; ::pEp_identity *new_recv_by_copy_c = ::identity_dup(new_recv_by_c); if (new_recv_by_copy_c == NULL) { throw std::bad_alloc(); } // If we have not failed yet this will succeed. // Destroy the old Recv-By. ::free_identity(msg_c->recv_by); msg_c->recv_by = NULL; // Use the copy as the new Recv-By. msg_c->recv_by = new_recv_by_copy_c; } Message Message::encrypt() { boost::python::list extra; return encrypt_message(*this, extra, PEP_enc_PGP_MIME, 0); } Message Message::_encrypt(boost::python::list extra, int enc_format, int flags) { if (!enc_format) { enc_format = PEP_enc_PGP_MIME; } return encrypt_message(*this, extra, enc_format, flags); } boost::python::tuple Message::decrypt(int flags) { return pEp::PythonAdapter::decrypt_message(*this, flags); } PEP_rating Message::outgoing_rating() { if (_msg->dir != PEP_dir_outgoing) { throw invalid_argument("Message.dir must be outgoing"); } if (from().address() == "") { throw invalid_argument("from.address needed"); } if (from().username() == "") { throw invalid_argument("from.username needed"); } if (len(to()) + len(cc()) == 0) { throw invalid_argument("either to or cc needed"); } PEP_STATUS status = myself(Adapter::session(), _msg->from); _throw_status(status); PEP_rating rating = PEP_rating_undefined; status = outgoing_message_rating(Adapter::session(), *this, &rating); _throw_status(status); return rating; } PEP_color Message::outgoing_color() { return _color(outgoing_rating()); } Message Message::onionize(boost::python::list relays) { return onionize(relays, boost::python::list(), (int) PEP_enc_PEP_message_v2, (int) PEP_encrypt_flag_default); } Message Message::onionize(boost::python::list relays, boost::python::list extra) { return onionize(relays, extra, (int) PEP_enc_PEP_message_v2, (int) PEP_encrypt_flag_default); } Message Message::onionize(boost::python::list relays, boost::python::list extra, int enc_format) { return onionize(relays, extra, enc_format, (int) PEP_encrypt_flag_default); } Message Message::onionize(boost::python::list relays, boost::python::list extra, int enc_format, int flags) { ::identity_list *identities_c = NULL; PEP_STATUS status = PEP_STATUS_OK; // In case of any error, be it memory allocation or type conversion, // free our temporary data before re-throwing to the caller. ::pEp_identity *identity_c = NULL; stringlist_t *extra_c = NULL; try { // Turn Python lists into C lists. extra_c = to_stringlist(extra); for (int i = len(relays) - 1; i >= 0; i--) { Identity &identity = boost::python::extract(relays [i]); ::identity_list *new_identities_c = identity_list_cons_copy(identity, identities_c); if (new_identities_c == NULL) throw std::bad_alloc(); identities_c = new_identities_c; } // Call the C function to do the actual work. ::message *in_message_c = * this; ::message *out_message_c = NULL; status = ::onionize(Adapter::session(), in_message_c, extra_c, &out_message_c, (::PEP_enc_format) enc_format, (::PEP_encrypt_flags_t) flags, identities_c); _throw_status(status); // Success. free_stringlist(extra_c); free_identity_list(identities_c); return out_message_c; } catch (const std::exception &e) { free_identity(identity_c); free_stringlist(extra_c); free_identity_list(identities_c); throw e; } } Message Message::copy() { message *dup = message_dup(*this); if (!dup) { throw std::bad_alloc(); } return Message(dup); } Message Message::deepcopy(dict &) { return copy(); } Message outgoing_message(Identity me) { if (me.address().empty() || me.user_id().empty()) { throw runtime_error("at least address and user_id of own user needed"); } ::myself(Adapter::session(), me); auto m = Message(PEP_dir_outgoing, &me); return m; } static object update(Identity ident) { if (ident.address().empty()) { throw runtime_error("at least address needed"); } update_identity(Adapter::session(), ident); return object(ident); } static boost::python::list update(boost::python::list il) { for (int i = 0; i < len(il); i++) { update(extract(il[i])); } return il; } Message incoming_message(string mime_text) { auto m = Message(mime_text); m.dir(PEP_dir_incoming); try { m.from(update(m.from())); } catch (std::out_of_range &) { } try { m.recv_by(update(m.recv_by())); } catch (std::out_of_range &) { } m.to(update(m.to())); m.cc(update(m.cc())); m.reply_to(update(m.reply_to())); return m; } } // namespace PythonAdapter } // namespace pEp