You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

407 lines
19 KiB

4 years ago
6 years ago
6 years ago
4 years ago
6 years ago
4 years ago
6 years ago
4 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
6 years ago
4 years ago
  1. // This file is under GNU Affero General Public License 3.0
  2. // see LICENSE.txt
  3. #include "pEpmodule.hh"
  4. #include <boost/locale.hpp>
  5. #include <string>
  6. #include <sstream>
  7. #include <iomanip>
  8. #include "basic_api.hh"
  9. #include "message_api.hh"
  10. #include "user_interface.hh"
  11. #include "adapter.hh"
  12. #include <mutex>
  13. #include <pEp/message_api.h>
  14. #include <pEp/sync_api.h>
  15. namespace pEp {
  16. namespace PythonAdapter {
  17. using namespace std;
  18. Adapter adapter;
  19. static const char *version_string = "p≡p Python adapter version 0.2";
  20. static string about()
  21. {
  22. string version = string(version_string) + "\np≡p version "
  23. + PEP_VERSION + "\n";
  24. return version;
  25. }
  26. void _throw_status(PEP_STATUS status)
  27. {
  28. if (status == PEP_STATUS_OK)
  29. return;
  30. if (status >= 0x400 && status <= 0x4ff)
  31. return;
  32. if (status == PEP_OUT_OF_MEMORY)
  33. throw bad_alloc();
  34. if (status == PEP_ILLEGAL_VALUE)
  35. throw invalid_argument("illegal value");
  36. stringstream build;
  37. build << setfill('0') << "p≡p 0x" << setw(4) << hex << status;
  38. throw runtime_error(build.str());
  39. }
  40. }
  41. }
  42. BOOST_PYTHON_MODULE(pEp)
  43. {
  44. using namespace boost::python;
  45. using namespace boost::locale;
  46. using namespace pEp::PythonAdapter;
  47. docstring_options doc_options(true, false, false);
  48. generator gen;
  49. std::locale::global(gen(""));
  50. scope().attr("about") = about();
  51. auto identity_class = class_<pEp::PythonAdapter::Identity>("Identity",
  52. "Identity(address, username, user_id='', fpr='', comm_type=0, lang='en')\n"
  53. "\n"
  54. "represents a p≡p identity\n"
  55. "\n"
  56. "an identity is a network address, under which a user is represented in\n"
  57. "the network\n"
  58. "\n"
  59. " address network address, either an SMTP address or a URI\n"
  60. " username real name or nickname for user\n"
  61. " user_id ID this user is handled by the application\n"
  62. " fpr full fingerprint of the key being used as key ID,\n"
  63. " hex encoded\n"
  64. " comm_type first rating level of this communication channel\n"
  65. " lang ISO 639-1 language code for language being preferred\n"
  66. " on this communication channel\n"
  67. )
  68. .def(boost::python::init<string>())
  69. .def(boost::python::init<string, string>())
  70. .def(boost::python::init<string, string, string>())
  71. .def(boost::python::init<string, string, string, string>())
  72. .def(boost::python::init<string, string, string, string, int>())
  73. .def(boost::python::init<string, string, string, string, int, string>())
  74. .def("__repr__", &pEp::PythonAdapter::Identity::_repr)
  75. .def("__str__", &pEp::PythonAdapter::Identity::_str,
  76. "string representation of this identity\n"
  77. "following the pattern 'username < address >'\n"
  78. )
  79. .add_property("address", (string(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::address,
  80. (void(pEp::PythonAdapter::Identity::*)(string)) &pEp::PythonAdapter::Identity::address,
  81. "email address or URI")
  82. .add_property("fpr", (string(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::fpr,
  83. (void(pEp::PythonAdapter::Identity::*)(string)) &pEp::PythonAdapter::Identity::fpr,
  84. "key ID (full fingerprint, hex encoded)")
  85. .add_property("user_id", (string(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::user_id,
  86. (void(pEp::PythonAdapter::Identity::*)(string)) &pEp::PythonAdapter::Identity::user_id,
  87. "ID of person associated or 'pEp_own_userId' if own identity")
  88. .add_property("username", (string(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::username,
  89. (void(pEp::PythonAdapter::Identity::*)(string)) &pEp::PythonAdapter::Identity::username,
  90. "name in full of person associated")
  91. .add_property("comm_type", (int(pEp::PythonAdapter::Identity::*)())
  92. (PEP_comm_type(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::comm_type,
  93. (void(pEp::PythonAdapter::Identity::*)(int))
  94. (void(pEp::PythonAdapter::Identity::*)(PEP_comm_type)) &pEp::PythonAdapter::Identity::comm_type,
  95. "communication type, first rating level (p≡p internal)")
  96. .add_property("lang", (string(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::lang,
  97. (void(pEp::PythonAdapter::Identity::*)(string)) &pEp::PythonAdapter::Identity::lang,
  98. "ISO 639-1 language code")
  99. .add_property("flags", (identity_flags_t(pEp::PythonAdapter::Identity::*)()) &pEp::PythonAdapter::Identity::flags,
  100. (void(pEp::PythonAdapter::Identity::*)(identity_flags_t)) &pEp::PythonAdapter::Identity::flags,
  101. "flags (p≡p internal)")
  102. .add_property("rating", &pEp::PythonAdapter::Identity::rating, "rating of Identity")
  103. .add_property("color", &pEp::PythonAdapter::Identity::color, "color of Identity")
  104. .def("__deepcopy__", &pEp::PythonAdapter::Identity::deepcopy)
  105. .def("update", &pEp::PythonAdapter::Identity::update, "update Identity")
  106. .def("__copy__", &pEp::PythonAdapter::Identity::copy);
  107. identity_class.attr("PEP_OWN_USERID") = "pEp_own_userId";
  108. auto blob_class = class_<Message::Blob>("Blob",
  109. "Blob(data, mime_type='', filename='')\n"
  110. "\n"
  111. "Binary large object\n"
  112. "\n"
  113. " data bytes-like object\n"
  114. " mime_type MIME type for the data\n"
  115. " filename filename to store the data\n" ,
  116. boost::python::init< object, char const*, char const* >(args("data", "mime_type", "filename")))
  117. .def(boost::python::init<object, string>())
  118. .def(boost::python::init<object>())
  119. .def("__repr__", &Message::Blob::_repr)
  120. .def("__len__", &Message::Blob::size, "size of Blob data in bytes")
  121. .def("decode", (string(Message::Blob::*)()) &Message::Blob::decode)
  122. .def("decode", (string(Message::Blob::*)(string)) &Message::Blob::decode,
  123. "text = blob.decode(encoding='')\n"
  124. "\n"
  125. "decode Blob data into string depending on MIME type if encoding=''\n"
  126. "\n"
  127. " mime_type='application/pEp.sync' decode as 'pEp.sync'\n"
  128. " other mime_type decode as 'ascii' by default\n"
  129. )
  130. .add_property("mime_type", (string(Message::Blob::*)()) &Message::Blob::mime_type,
  131. (void(Message::Blob::*)(string)) &Message::Blob::mime_type,
  132. "MIME type of object in Blob")
  133. .add_property("filename", (string(Message::Blob::*)()) &Message::Blob::filename,
  134. (void(Message::Blob::*)(string)) &Message::Blob::filename,
  135. "filename of object in Blob");
  136. ((PyTypeObject *)(void *)blob_class.ptr())->tp_as_buffer = &Message::Blob::bp;
  137. auto message_class = class_<Message>("Message",
  138. "Message(dir=1, from=None)\n"
  139. "\n"
  140. "new p≡p message\n"
  141. "\n"
  142. " dir 1 for outgoing, 2 for incoming\n"
  143. " from Identity() of sender\n"
  144. "\n"
  145. "Message(mime_text)\n"
  146. "\n"
  147. "new incoming p≡p message\n"
  148. "\n"
  149. " mime_text text in Multipurpose Internet Mail Extensions format\n"
  150. )
  151. .def(boost::python::init<int>())
  152. .def(boost::python::init<int, pEp::PythonAdapter::Identity *>())
  153. .def(boost::python::init<string>())
  154. .def("__str__", &Message::_str,
  155. "the string representation of a Message is it's MIME text"
  156. )
  157. .def("__repr__", &Message::_repr)
  158. .add_property("dir", (int(Message::*)())
  159. (PEP_msg_direction(Message::*)()) &Message::dir,
  160. (void(Message::*)(int))
  161. (void(Message::*)(PEP_msg_direction)) &Message::dir,
  162. "0: incoming, 1: outgoing message")
  163. .add_property("id", (string(Message::*)()) &Message::id,
  164. (void(Message::*)(string)) &Message::id,
  165. "message ID")
  166. .add_property("shortmsg", (string(Message::*)()) &Message::shortmsg,
  167. (void(Message::*)(string)) &Message::shortmsg,
  168. "subject or short message")
  169. .add_property("longmsg", (string(Message::*)()) &Message::longmsg,
  170. (void(Message::*)(string)) &Message::longmsg,
  171. "body or long version of message")
  172. .add_property("longmsg_formatted", (string(Message::*)()) &Message::longmsg_formatted,
  173. (void(Message::*)(string)) &Message::longmsg_formatted,
  174. "HTML body or fromatted long version of message")
  175. .add_property("attachments", (boost::python::tuple(Message::*)()) &Message::attachments,
  176. (void(Message::*)(boost::python::list)) &Message::attachments,
  177. "tuple of Blobs with attachments; setting moves Blobs to attachment tuple")
  178. .add_property("sent", (time_t(Message::*)()) &Message::sent,
  179. (void(Message::*)(time_t)) &Message::sent,
  180. "time when message was sent in UTC seconds since epoch")
  181. .add_property("recv", (time_t(Message::*)()) &Message::recv,
  182. (void(Message::*)(time_t)) &Message::recv,
  183. "time when message was received in UTC seconds since epoch")
  184. .add_property("from_", (pEp::PythonAdapter::Identity(Message::*)()) &Message::from,
  185. (void(Message::*)(object)) &Message::from,
  186. "identity where message is from")
  187. .add_property("to", (boost::python::list(Message::*)()) &Message::to,
  188. (void(Message::*)(boost::python::list)) &Message::to,
  189. "list of identities message is going to")
  190. .add_property("recv_by", (pEp::PythonAdapter::Identity(Message::*)()) &Message::recv_by,
  191. (void(Message::*)(object)) &Message::recv_by,
  192. "identity where message was received by")
  193. .add_property("cc", (boost::python::list(Message::*)()) &Message::cc,
  194. (void(Message::*)(boost::python::list)) &Message::cc,
  195. "list of identities message is going cc")
  196. .add_property("bcc", (boost::python::list(Message::*)()) &Message::bcc,
  197. (void(Message::*)(boost::python::list)) &Message::bcc,
  198. "list of identities message is going bcc")
  199. .add_property("reply_to", (boost::python::list(Message::*)()) &Message::reply_to,
  200. (void(Message::*)(boost::python::list)) &Message::reply_to,
  201. "list of identities where message will be replied to")
  202. .add_property("in_reply_to", (boost::python::list(Message::*)()) &Message::in_reply_to,
  203. (void(Message::*)(boost::python::list)) &Message::in_reply_to,
  204. "in_reply_to list")
  205. .add_property("references", (boost::python::list(Message::*)()) &Message::references,
  206. (void(Message::*)(boost::python::list)) &Message::references,
  207. "message IDs of messages this one is referring to")
  208. .add_property("keywords", (boost::python::list(Message::*)()) &Message::keywords,
  209. (void(Message::*)(boost::python::list)) &Message::keywords,
  210. "keywords this message should be stored under")
  211. .add_property("comments", (string(Message::*)()) &Message::comments,
  212. (void(Message::*)(string)) &Message::comments,
  213. "comments added to message")
  214. .add_property("opt_fields", (dict(Message::*)()) &Message::opt_fields,
  215. (void(Message::*)(dict)) &Message::opt_fields,
  216. "opt_fields of message")
  217. .add_property("enc_format", (int(Message::*)())
  218. (PEP_enc_format(Message::*)()) &Message::enc_format,
  219. (void(Message::*)(int))
  220. (void(Message::*)(PEP_enc_format)) &Message::enc_format,
  221. "0: unencrypted, 1: inline PGP, 2: S/MIME, 3: PGP/MIME, 4: p≡p format")
  222. .def("encrypt", (Message(Message::*)())&Message::encrypt)
  223. .def("encrypt", (Message(Message::*)(boost::python::list))&Message::_encrypt)
  224. .def("encrypt", (Message(Message::*)(boost::python::list,int))&Message::_encrypt)
  225. .def("encrypt", (Message(Message::*)(boost::python::list,int,int))&Message::_encrypt,
  226. "msg2 = msg1.encrypt(extra_keys=[], enc_format='pEp', flags=0)\n"
  227. "\n"
  228. "encrypts a p≡p message and returns the encrypted message\n"
  229. "\n"
  230. " extra_keys list of strings with fingerprints for extra keys to use\n"
  231. " for encryption\n"
  232. " enc_format 0 for none, 1 for partitioned, 2 for S/MIME,\n"
  233. " 3 for PGP/MIME, 4 for pEp\n"
  234. " flags 1 is force encryption\n"
  235. )
  236. .def("decrypt", &Message::decrypt,
  237. "msg2, keys, rating, consumed, flags = msg1.decrypt()\n"
  238. "\n"
  239. "decrypts a p≡p message and returns a tuple with data\n"
  240. "\n"
  241. " msg the decrypted p≡p message\n"
  242. " keys a list of keys being used\n"
  243. " rating the rating of the message as integer\n"
  244. " consumed boolean denoting message is consumed by sync\n"
  245. " flags flags set while decryption (reserved)\n"
  246. )
  247. .add_property("outgoing_rating", &Message::outgoing_rating, "rating outgoing message will have")
  248. .add_property("outgoing_color", &Message::outgoing_color, "color outgoing message will have")
  249. .def("__deepcopy__", &Message::deepcopy)
  250. .def("__copy__", &Message::copy);
  251. // basic API
  252. def("update_identity", &pEp::PythonAdapter::update_identity,
  253. "update_identity(ident)\n"
  254. "\n"
  255. "update identity information\n"
  256. "call this to complete identity information when you at least have an address\n"
  257. );
  258. def("myself", &pEp::PythonAdapter::myself,
  259. "myself(ident)\n"
  260. "\n"
  261. "ensures that the own identity is being complete\n"
  262. "supply ident.address and ident.username\n"
  263. );
  264. def("trust_personal_key", &pEp::PythonAdapter::trust_personal_key,
  265. "trust_personal_key(ident)\n"
  266. "\n"
  267. "mark a key as trusted with a person\n"
  268. );
  269. enum_<identity_flags>("identity_flags")
  270. .value("PEP_idf_not_for_sync", PEP_idf_not_for_sync)
  271. .value("PEP_idf_list", PEP_idf_list)
  272. .value("PEP_idf_devicegroup", PEP_idf_devicegroup);
  273. def("set_identity_flags", &pEp::PythonAdapter::set_identity_flags,
  274. "set_identity_flags(ident, flags)\n"
  275. "\n"
  276. "set identity flags\n"
  277. );
  278. def("unset_identity_flags", &pEp::PythonAdapter::unset_identity_flags,
  279. "unset_identity_flags(ident, flags)\n"
  280. "\n"
  281. "unset identity flags\n"
  282. );
  283. // message API
  284. enum_<PEP_rating>("PEP_rating")
  285. .value("PEP_rating_undefined", PEP_rating_undefined)
  286. .value("PEP_rating_cannot_decrypt", PEP_rating_cannot_decrypt)
  287. .value("PEP_rating_have_no_key", PEP_rating_have_no_key)
  288. .value("PEP_rating_unencrypted", PEP_rating_unencrypted)
  289. .value("PEP_rating_unencrypted_for_some", PEP_rating_unencrypted_for_some)
  290. .value("PEP_rating_unreliable", PEP_rating_unreliable)
  291. .value("PEP_rating_reliable", PEP_rating_reliable)
  292. .value("PEP_rating_trusted", PEP_rating_trusted)
  293. .value("PEP_rating_trusted_and_anonymized", PEP_rating_trusted_and_anonymized)
  294. .value("PEP_rating_fully_anonymous", PEP_rating_fully_anonymous)
  295. .value("PEP_rating_mistrust", PEP_rating_mistrust)
  296. .value("PEP_rating_b0rken", PEP_rating_b0rken)
  297. .value("PEP_rating_under_attack", PEP_rating_under_attack);
  298. def("incoming_message", &incoming_message,
  299. "msg = incoming_message(mime_text)\n"
  300. "\n"
  301. "create an incoming message from a MIME text"
  302. );
  303. def("outgoing_message", &outgoing_message,
  304. "msg = outgoing_message(ident)\n"
  305. "\n"
  306. "create an outgoing message using an own identity"
  307. );
  308. def("color", &_color,
  309. "c = color(rating)\n"
  310. "\n"
  311. "calculate color value out of rating"
  312. );
  313. def("trustwords", &_trustwords,
  314. "text = trustwords(ident_own, ident_partner)\n"
  315. "\n"
  316. "calculate trustwords for two Identities");
  317. def("config_keep_sync_msg", &_config_keep_sync_msg,
  318. "config_keep_sync_msg(enabled)\n"
  319. "\n"
  320. "configure if sync messages are being kept or automatically removed (default)");
  321. // Sync API
  322. enum_<sync_handshake_signal>("sync_handshake_signal")
  323. .value("SYNC_NOTIFY_UNDEFINED" , SYNC_NOTIFY_UNDEFINED)
  324. .value("SYNC_NOTIFY_INIT_ADD_OUR_DEVICE" , SYNC_NOTIFY_INIT_ADD_OUR_DEVICE)
  325. .value("SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE" , SYNC_NOTIFY_INIT_ADD_OTHER_DEVICE)
  326. .value("SYNC_NOTIFY_INIT_FORM_GROUP" , SYNC_NOTIFY_INIT_FORM_GROUP)
  327. .value("SYNC_NOTIFY_INIT_MOVE_OUR_DEVICE" , SYNC_NOTIFY_INIT_MOVE_OUR_DEVICE)
  328. .value("SYNC_NOTIFY_TIMEOUT" , SYNC_NOTIFY_TIMEOUT)
  329. .value("SYNC_NOTIFY_ACCEPTED_DEVICE_ADDED" , SYNC_NOTIFY_ACCEPTED_DEVICE_ADDED)
  330. .value("SYNC_NOTIFY_ACCEPTED_GROUP_CREATED", SYNC_NOTIFY_ACCEPTED_GROUP_CREATED)
  331. .value("SYNC_NOTIFY_ACCEPTED_DEVICE_MOVED" , SYNC_NOTIFY_ACCEPTED_DEVICE_MOVED)
  332. .value("SYNC_NOTIFY_OVERTAKEN" , SYNC_NOTIFY_OVERTAKEN);
  333. auto user_interface_class = class_<UserInterface, UserInterface_callback, boost::noncopyable>(
  334. "UserInterface",
  335. "class MyUserInterface(UserInterface):\n"
  336. " def notifyHandshake(self, me, partner):\n"
  337. " ...\n"
  338. "\n"
  339. "p≡p User Interface class\n"
  340. "To be used as a mixin\n"
  341. )
  342. .def("notifyHandshake", &UserInterface::notifyHandshake,
  343. "notifyHandshake(self, me, partner)\n"
  344. "\n"
  345. " me own identity\n"
  346. " partner identity of communication partner\n"
  347. "\n"
  348. "overwrite this method with an implementation of a handshake dialog")
  349. .def("deliverHandshakeResult", &UserInterface::deliverHandshakeResult,
  350. "deliverHandshakeResult(self, partber, result)\n"
  351. "\n"
  352. " partner identity of communication partner\n"
  353. " result -1: cancel, 0: accepted, 1: rejected\n"
  354. "\n"
  355. "call to deliver the handshake result of the handshake dialog")
  356. ;
  357. auto adapter_class = class_<pEp::PythonAdapter::Adapter, pEp::PythonAdapter::Adapter_callback, boost::noncopyable>(
  358. "Adapter",
  359. "class MyAdapter(Adapter):\n"
  360. " def messageToSend(self, Message msg):\n"
  361. " ...\n"
  362. "\n"
  363. "p≡p Adapter class\n"
  364. "To be used as a mixin\n"
  365. )
  366. .def("messageToSend", &pEp::PythonAdapter::Adapter::messageToSend,
  367. "messageToSend(self, msg):\n"
  368. "\n"
  369. " msg message, which has to be send out\n"
  370. "\n"
  371. "overwrite this method with an implementation, which is sending the message\n"
  372. );
  373. // codecs
  374. call< object >(((object)(import("codecs").attr("register"))).ptr(), make_function(sync_search));
  375. }