1 #include "pEp_internal.h"
2 #include "message_api.h"
14 #define _MIN(A, B) ((B) > (A) ? (A) : (B))
17 #define _MAX(A, B) ((B) > (A) ? (B) : (A))
21 static bool string_equality(const char *s1, const char *s2)
23 if (s1 == NULL || s2 == NULL)
28 return strcmp(s1, s2) == 0;
31 static bool is_mime_type(const bloblist_t *bl, const char *mt)
35 return bl && string_equality(bl->mime_type, mt);
39 // This function presumes the file ending is a proper substring of the
40 // filename (i.e. if bl->filename is "a.pgp" and fe is ".pgp", it will
41 // return true, but if bl->filename is ".pgp" and fe is ".pgp", it will
42 // return false. This is desired behaviour.
44 static bool is_fileending(const bloblist_t *bl, const char *fe)
48 if (bl == NULL || bl->filename == NULL || fe == NULL)
51 assert(bl && bl->filename);
53 size_t fe_len = strlen(fe);
54 size_t fn_len = strlen(bl->filename);
59 assert(fn_len > fe_len);
61 return strcmp(bl->filename + (fn_len - fe_len), fe) == 0;
64 void add_opt_field(message *msg, const char *name, const char *value)
66 assert(msg && name && value);
68 if (msg && name && value) {
69 stringpair_t *pair = new_stringpair(name, value);
73 stringpair_list_t *field = stringpair_list_add(msg->opt_fields, pair);
76 free_stringpair(pair);
80 if (msg->opt_fields == NULL)
81 msg->opt_fields = field;
85 static char * combine_short_and_long(const char *shortmsg, const char *longmsg)
90 assert(strcmp(shortmsg, "pEp") != 0);
92 if (!shortmsg || strcmp(shortmsg, "pEp") == 0) {
97 char *result = strdup(longmsg);
106 const char * const subject = "Subject: ";
107 const size_t SUBJ_LEN = 9;
108 const char * const newlines = "\n\n";
109 const size_t NL_LEN = 2;
111 size_t bufsize = SUBJ_LEN + strlen(shortmsg) + NL_LEN + strlen(longmsg) + 1;
112 ptext = calloc(1, bufsize);
117 strlcpy(ptext, subject, bufsize);
118 strlcat(ptext, shortmsg, bufsize);
119 strlcat(ptext, newlines, bufsize);
120 strlcat(ptext, longmsg, bufsize);
125 static int separate_short_and_long(const char *src, char **shortmsg, char **longmsg)
127 char *_shortmsg = NULL;
128 char *_longmsg = NULL;
134 if (src == NULL || shortmsg == NULL || longmsg == NULL)
140 if (strncasecmp(src, "subject: ", 9) == 0) {
141 char *line_end = strchr(src, '\n');
143 if (line_end == NULL) {
144 _shortmsg = strdup(src + 9);
146 if (_shortmsg == NULL)
151 size_t n = line_end - src;
153 if (*(line_end - 1) == '\r')
154 _shortmsg = strndup(src + 9, n - 10);
156 _shortmsg = strndup(src + 9, n - 9);
158 if (_shortmsg == NULL)
161 while (*(src + n) && (*(src + n) == '\n' || *(src + n) == '\r'))
165 _longmsg = strdup(src + n);
167 if (_longmsg == NULL)
173 _shortmsg = strdup("");
175 if (_shortmsg == NULL)
177 _longmsg = strdup(src);
179 if (_longmsg == NULL)
183 *shortmsg = _shortmsg;
195 static PEP_STATUS copy_fields(message *dst, const message *src)
201 return PEP_ILLEGAL_VALUE;
203 free_timestamp(dst->sent);
206 dst->sent = timestamp_dup(src->sent);
207 if (dst->sent == NULL)
208 return PEP_OUT_OF_MEMORY;
211 free_timestamp(dst->recv);
214 dst->recv = timestamp_dup(src->recv);
215 if (dst->recv == NULL)
216 return PEP_OUT_OF_MEMORY;
219 free_identity(dst->from);
222 dst->from = identity_dup(src->from);
223 if (dst->from == NULL)
224 return PEP_OUT_OF_MEMORY;
227 free_identity_list(dst->to);
229 if (src->to && src->to->ident) {
230 dst->to = identity_list_dup(src->to);
232 return PEP_OUT_OF_MEMORY;
235 free_identity(dst->recv_by);
238 dst->recv_by = identity_dup(src->recv_by);
239 if (dst->recv_by == NULL)
240 return PEP_OUT_OF_MEMORY;
243 free_identity_list(dst->cc);
245 if (src->cc && src->cc->ident) {
246 dst->cc = identity_list_dup(src->cc);
248 return PEP_OUT_OF_MEMORY;
251 free_identity_list(dst->bcc);
253 if (src->bcc && src->bcc->ident) {
254 dst->bcc = identity_list_dup(src->bcc);
255 if (dst->bcc == NULL)
256 return PEP_OUT_OF_MEMORY;
259 free_identity_list(dst->reply_to);
260 dst->reply_to = NULL;
261 if (src->reply_to && src->reply_to->ident) {
262 dst->reply_to = identity_list_dup(src->reply_to);
263 if (dst->reply_to == NULL)
264 return PEP_OUT_OF_MEMORY;
267 free_stringlist(dst->in_reply_to);
268 dst->in_reply_to = NULL;
269 if (src->in_reply_to && src->in_reply_to->value) {
270 dst->in_reply_to = stringlist_dup(src->in_reply_to);
271 if (dst->in_reply_to == NULL)
272 return PEP_OUT_OF_MEMORY;
275 free_stringlist(dst->references);
276 dst->references = NULL;
277 if (src->references) {
278 dst->references = stringlist_dup(src->references);
279 if (dst->references == NULL)
280 return PEP_OUT_OF_MEMORY;
283 free_stringlist(dst->keywords);
284 dst->keywords = NULL;
285 if (src->keywords && src->keywords->value) {
286 dst->keywords = stringlist_dup(src->keywords);
287 if (dst->keywords == NULL)
288 return PEP_OUT_OF_MEMORY;
292 dst->comments = NULL;
294 dst->comments = strdup(src->comments);
295 assert(dst->comments);
296 if (dst->comments == NULL)
297 return PEP_OUT_OF_MEMORY;
300 free_stringpair_list(dst->opt_fields);
301 dst->opt_fields = NULL;
302 if (src->opt_fields) {
303 dst->opt_fields = stringpair_list_dup(src->opt_fields);
304 if (dst->opt_fields == NULL)
305 return PEP_OUT_OF_MEMORY;
308 return PEP_STATUS_OK;
311 static message * clone_to_empty_message(const message * src)
314 message * msg = NULL;
320 msg = calloc(1, sizeof(message));
327 status = copy_fields(msg, src);
328 if (status != PEP_STATUS_OK)
338 static PEP_STATUS encrypt_PGP_MIME(
345 PEP_STATUS status = PEP_STATUS_OK;
346 bool free_ptext = false;
349 char *mimetext = NULL;
351 assert(dst->longmsg == NULL);
352 dst->enc_format = PEP_enc_PGP_MIME;
354 if (src->shortmsg && strcmp(src->shortmsg, "pEp") != 0) {
355 if (session->unencrypted_subject) {
356 dst->shortmsg = strdup(src->shortmsg);
357 assert(dst->shortmsg);
358 if (dst->shortmsg == NULL)
360 ptext = src->longmsg;
363 ptext = combine_short_and_long(src->shortmsg, src->longmsg);
369 else if (src->longmsg) {
370 ptext = src->longmsg;
376 message *_src = calloc(1, sizeof(message));
380 _src->longmsg = ptext;
381 _src->longmsg_formatted = src->longmsg_formatted;
382 _src->attachments = src->attachments;
383 _src->enc_format = PEP_enc_none;
384 status = mime_encode_message(_src, true, &mimetext);
385 assert(status == PEP_STATUS_OK);
386 if (status != PEP_STATUS_OK)
395 if (mimetext == NULL)
398 status = encrypt_and_sign(session, keys, mimetext, strlen(mimetext),
404 dst->longmsg = strdup("this message was encrypted with p≡p "
405 "https://pEp-project.org");
406 assert(dst->longmsg);
407 if (dst->longmsg == NULL)
410 char *v = strdup("Version: 1");
415 bloblist_t *_a = new_bloblist(v, strlen(v), "application/pgp-encrypted", NULL);
418 dst->attachments = _a;
420 _a = bloblist_add(_a, ctext, csize, "application/octet-stream",
425 return PEP_STATUS_OK;
428 status = PEP_OUT_OF_MEMORY;
437 static PEP_STATUS encrypt_PGP_in_pieces(
444 PEP_STATUS status = PEP_STATUS_OK;
448 bool free_ptext = false;
450 assert(dst->longmsg == NULL);
451 assert(dst->attachments == NULL);
453 dst->enc_format = PEP_enc_pieces;
455 if (src->shortmsg && src->shortmsg[0] && strcmp(src->shortmsg, "pEp") != 0) {
456 if (session->unencrypted_subject) {
457 dst->shortmsg = strdup(src->shortmsg);
458 assert(dst->shortmsg);
459 if (dst->shortmsg == NULL)
461 ptext = src->longmsg;
464 ptext = combine_short_and_long(src->shortmsg, src->longmsg);
470 status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
476 dst->longmsg = ctext;
482 else if (src->longmsg && src->longmsg[0]) {
483 ptext = src->longmsg;
484 status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
487 dst->longmsg = ctext;
494 dst->longmsg = strdup("");
495 assert(dst->longmsg);
496 if (dst->longmsg == NULL)
500 if (src->longmsg_formatted && src->longmsg_formatted[0]) {
501 ptext = src->longmsg_formatted;
502 status = encrypt_and_sign(session, keys, ptext, strlen(ptext), &ctext,
506 bloblist_t *_a = bloblist_add(dst->attachments, ctext, csize,
507 "application/octet-stream", "PGPexch.htm.pgp");
510 if (dst->attachments == NULL)
511 dst->attachments = _a;
518 if (src->attachments) {
519 if (dst->attachments == NULL) {
520 dst->attachments = new_bloblist(NULL, 0, NULL, NULL);
521 if (dst->attachments == NULL)
525 bloblist_t *_s = src->attachments;
526 bloblist_t *_d = dst->attachments;
528 for (int n = 0; _s; _s = _s->next) {
529 if (_s->value == NULL && _s->size == 0) {
530 _d = bloblist_add(_d, NULL, 0, _s->mime_type, _s->filename);
535 size_t psize = _s->size;
537 status = encrypt_and_sign(session, keys, ptext, psize, &ctext,
540 char *filename = NULL;
543 size_t len = strlen(_s->filename);
544 size_t bufsize = len + 5; // length of .pgp extension + NUL
545 filename = calloc(1, bufsize);
546 if (filename == NULL)
549 strlcpy(filename, _s->filename, bufsize);
550 strlcat(filename, ".pgp", bufsize);
553 filename = calloc(1, 20);
554 if (filename == NULL)
559 snprintf(filename, 20, "Attachment%d.pgp", n);
562 _d = bloblist_add(_d, ctext, csize, "application/octet-stream",
575 return PEP_STATUS_OK;
578 status = PEP_OUT_OF_MEMORY;
586 static char * keylist_to_string(const stringlist_t *keylist)
589 size_t size = stringlist_length(keylist);
591 const stringlist_t *_kl;
592 for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
593 size += strlen(_kl->value);
596 char *result = calloc(1, size);
601 for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
602 _r = stpcpy(_r, _kl->value);
603 if (_kl->next && _kl->next->value)
604 _r = stpcpy(_r, ",");
614 static const char * rating_to_string(PEP_rating rating)
617 case PEP_rating_cannot_decrypt:
618 return "cannot_decrypt";
619 case PEP_rating_have_no_key:
620 return "have_no_key";
621 case PEP_rating_unencrypted:
622 return "unencrypted";
623 case PEP_rating_unencrypted_for_some:
624 return "unencrypted_for_some";
625 case PEP_rating_unreliable:
627 case PEP_rating_reliable:
629 case PEP_rating_trusted:
631 case PEP_rating_trusted_and_anonymized:
632 return "trusted_and_anonymized";
633 case PEP_rating_fully_anonymous:
634 return "fully_anonymous";
635 case PEP_rating_mistrust:
637 case PEP_rating_b0rken:
639 case PEP_rating_under_attack:
640 return "under_attack";
646 static void decorate_message(
649 stringlist_t *keylist
654 add_opt_field(msg, "X-pEp-Version", PEP_VERSION);
656 if (rating != PEP_rating_undefined)
657 add_opt_field(msg, "X-EncStatus", rating_to_string(rating));
660 char *_keylist = keylist_to_string(keylist);
661 add_opt_field(msg, "X-KeyList", _keylist);
666 static PEP_rating _rating(PEP_comm_type ct, PEP_rating rating)
668 if (ct == PEP_ct_unknown)
669 return PEP_rating_undefined;
671 else if (ct == PEP_ct_compromized)
672 return PEP_rating_under_attack;
674 else if (ct == PEP_ct_mistrusted)
675 return PEP_rating_mistrust;
677 if (rating == PEP_rating_unencrypted_for_some)
678 return PEP_rating_unencrypted_for_some;
680 if (ct == PEP_ct_no_encryption || ct == PEP_ct_no_encrypted_channel ||
681 ct == PEP_ct_my_key_not_included) {
682 if (rating > PEP_rating_unencrypted_for_some)
683 return PEP_rating_unencrypted_for_some;
685 return PEP_rating_unencrypted;
688 if (rating == PEP_rating_unencrypted)
689 return PEP_rating_unencrypted_for_some;
691 if (ct >= PEP_ct_confirmed_enc_anon)
692 return PEP_rating_trusted_and_anonymized;
694 else if (ct >= PEP_ct_strong_encryption)
695 return PEP_rating_trusted;
697 else if (ct >= PEP_ct_strong_but_unconfirmed && ct < PEP_ct_confirmed)
698 return PEP_rating_reliable;
701 return PEP_rating_unreliable;
704 static bool is_encrypted_attachment(const bloblist_t *blob)
710 if (blob == NULL || blob->filename == NULL)
713 ext = strrchr(blob->filename, '.');
717 if (strcmp(blob->mime_type, "application/octet-stream") == 0) {
718 if (strcmp(ext, ".pgp") == 0 || strcmp(ext, ".gpg") == 0 ||
719 strcmp(ext, ".asc") == 0)
722 else if (strcmp(blob->mime_type, "text/plain") == 0) {
723 if (strcmp(ext, ".asc") == 0)
730 static bool is_encrypted_html_attachment(const bloblist_t *blob)
733 assert(blob->filename);
734 if (blob == NULL || blob->filename == NULL)
737 if (strncmp(blob->filename, "PGPexch.htm.", 12) == 0) {
738 if (strcmp(blob->filename + 11, ".pgp") == 0 ||
739 strcmp(blob->filename + 11, ".asc") == 0)
746 static char * without_double_ending(const char *filename)
749 if (filename == NULL)
752 char *ext = strrchr(filename, '.');
756 char *result = strndup(filename, ext - filename);
761 static PEP_rating decrypt_rating(PEP_STATUS status)
764 case PEP_UNENCRYPTED:
766 case PEP_VERIFY_NO_KEY:
767 case PEP_VERIFIED_AND_TRUSTED:
768 return PEP_rating_unencrypted;
771 return PEP_rating_unreliable;
773 case PEP_DECRYPTED_AND_VERIFIED:
774 return PEP_rating_reliable;
776 case PEP_DECRYPT_NO_KEY:
777 return PEP_rating_have_no_key;
779 case PEP_DECRYPT_WRONG_FORMAT:
780 case PEP_CANNOT_DECRYPT_UNKNOWN:
781 return PEP_rating_cannot_decrypt;
784 return PEP_rating_undefined;
788 static PEP_rating key_rating(PEP_SESSION session, const char *fpr)
790 PEP_comm_type comm_type = PEP_ct_unknown;
795 if (session == NULL || fpr == NULL)
796 return PEP_rating_undefined;
798 PEP_STATUS status = get_key_rating(session, fpr, &comm_type);
799 if (status != PEP_STATUS_OK)
800 return PEP_rating_undefined;
802 return _rating(comm_type, PEP_rating_undefined);
805 static PEP_rating keylist_rating(PEP_SESSION session, stringlist_t *keylist)
807 PEP_rating rating = PEP_rating_reliable;
809 assert(keylist && keylist->value);
810 if (keylist == NULL || keylist->value == NULL)
811 return PEP_rating_undefined;
814 for (_kl = keylist; _kl && _kl->value; _kl = _kl->next) {
818 PEP_rating _rating_ = key_rating(session, _kl->value);
819 if (_rating_ <= PEP_rating_mistrust)
822 if (rating == PEP_rating_undefined)
825 if (_rating_ >= PEP_rating_reliable) {
826 status = least_trust(session, _kl->value, &ct);
827 if (status != PEP_STATUS_OK)
828 return PEP_rating_undefined;
829 if (ct == PEP_ct_unknown)
830 rating = PEP_rating_unencrypted_for_some;
832 rating = _rating(ct, rating);
834 else if (_rating_ == PEP_rating_unencrypted) {
835 if (rating > PEP_rating_unencrypted_for_some)
836 rating = PEP_rating_unencrypted_for_some;
843 static PEP_comm_type _get_comm_type(
845 PEP_comm_type max_comm_type,
849 PEP_STATUS status = update_identity(session, ident);
851 if (max_comm_type == PEP_ct_compromized)
852 return PEP_ct_compromized;
854 if (max_comm_type == PEP_ct_mistrusted)
855 return PEP_ct_mistrusted;
857 if (status == PEP_STATUS_OK) {
858 if (ident->comm_type == PEP_ct_compromized)
859 return PEP_ct_compromized;
860 else if (ident->comm_type == PEP_ct_mistrusted)
861 return PEP_ct_mistrusted;
863 return _MIN(max_comm_type, ident->comm_type);
866 return PEP_ct_unknown;
870 static void free_bl_entry(bloblist_t *bl)
880 static bool is_key(const bloblist_t *bl)
882 return (// workaround for Apple Mail bugs
883 (is_mime_type(bl, "application/x-apple-msg-attachment") &&
884 is_fileending(bl, ".asc")) ||
885 // as binary, by file name
886 ((bl->mime_type == NULL ||
887 is_mime_type(bl, "application/octet-stream")) &&
888 (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
889 is_fileending(bl, ".key") || is_fileending(bl, ".asc"))) ||
890 // explicit mime type
891 is_mime_type(bl, "application/pgp-keys") ||
892 // as text, by file name
893 (is_mime_type(bl, "text/plain") &&
894 (is_fileending(bl, ".pgp") || is_fileending(bl, ".gpg") ||
895 is_fileending(bl, ".key") || is_fileending(bl, ".asc")))
899 static void remove_attached_keys(message *msg)
902 bloblist_t *last = NULL;
903 for (bloblist_t *bl = msg->attachments; bl && bl->value; ) {
904 bloblist_t *next = bl->next;
911 msg->attachments = next;
923 bool import_attached_keys(
926 identity_list **private_idents
932 if (session == NULL || msg == NULL)
939 for (bl = msg->attachments; i < MAX_KEYS_TO_IMPORT && bl && bl->value;
942 if (bl && bl->value && bl->size && bl->size < MAX_KEY_SIZE
945 import_key(session, bl->value, bl->size, private_idents);
953 PEP_STATUS _attach_key(PEP_SESSION session, const char* fpr, message *msg)
959 PEP_STATUS status = export_key(session, fpr, &keydata, &size);
960 assert(status == PEP_STATUS_OK);
961 if (status != PEP_STATUS_OK)
965 bl = bloblist_add(msg->attachments, keydata, size, "application/pgp-keys",
968 if (msg->attachments == NULL && bl)
969 msg->attachments = bl;
971 return PEP_STATUS_OK;
974 #define ONE_WEEK (7*24*3600)
976 void attach_own_key(PEP_SESSION session, message *msg)
981 if (msg->dir == PEP_dir_incoming)
984 assert(msg->from && msg->from->fpr);
985 if (msg->from == NULL || msg->from->fpr == NULL)
988 if(_attach_key(session, msg->from->fpr, msg) != PEP_STATUS_OK)
991 char *revoked_fpr = NULL;
992 uint64_t revocation_date = 0;
994 if(get_revoked(session, msg->from->fpr,
995 &revoked_fpr, &revocation_date) == PEP_STATUS_OK &&
998 time_t now = time(NULL);
1000 if (now < (time_t)revocation_date + ONE_WEEK)
1002 _attach_key(session, revoked_fpr, msg);
1008 PEP_cryptotech determine_encryption_format(message *msg)
1012 if (is_PGP_message_text(msg->longmsg)) {
1013 msg->enc_format = PEP_enc_pieces;
1014 return PEP_crypt_OpenPGP;
1016 else if (msg->attachments && msg->attachments->next &&
1017 is_mime_type(msg->attachments, "application/pgp-encrypted") &&
1018 is_PGP_message_text(msg->attachments->next->value)
1020 msg->enc_format = PEP_enc_PGP_MIME;
1021 return PEP_crypt_OpenPGP;
1024 msg->enc_format = PEP_enc_none;
1025 return PEP_crypt_none;
1029 DYNAMIC_API PEP_STATUS encrypt_message(
1030 PEP_SESSION session,
1032 stringlist_t * extra,
1034 PEP_enc_format enc_format,
1035 PEP_encrypt_flags_t flags
1038 PEP_STATUS status = PEP_STATUS_OK;
1039 message * msg = NULL;
1040 stringlist_t * keys = NULL;
1045 assert(enc_format != PEP_enc_none);
1047 if (!(session && src && dst && enc_format != PEP_enc_none))
1048 return PEP_ILLEGAL_VALUE;
1050 if (src->dir == PEP_dir_incoming)
1051 return PEP_ILLEGAL_VALUE;
1053 determine_encryption_format(src);
1054 if (src->enc_format != PEP_enc_none)
1055 return PEP_ILLEGAL_VALUE;
1059 status = myself(session, src->from);
1060 if (status != PEP_STATUS_OK)
1063 keys = new_stringlist(src->from->fpr);
1067 stringlist_t *_k = keys;
1070 _k = stringlist_append(_k, extra);
1075 bool dest_keys_found = true;
1076 PEP_comm_type max_comm_type = PEP_ct_pEp;
1078 identity_list * _il;
1080 if ((_il = src->bcc) && _il->ident)
1082 // BCC limited support:
1083 // - App splits mails with BCC in multiple mails.
1084 // - Each email is encrypted separately
1086 if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
1088 // Only one Bcc with no other recipient allowed for now
1089 return PEP_ILLEGAL_VALUE;
1092 PEP_STATUS _status = update_identity(session, _il->ident);
1093 if (_status != PEP_STATUS_OK) {
1098 if (_il->ident->fpr && _il->ident->fpr[0]) {
1099 _k = stringlist_add(_k, _il->ident->fpr);
1102 max_comm_type = _get_comm_type(session, max_comm_type,
1106 dest_keys_found = false;
1107 status = PEP_KEY_NOT_FOUND;
1112 for (_il = src->to; _il && _il->ident; _il = _il->next) {
1113 PEP_STATUS _status = update_identity(session, _il->ident);
1114 if (_status != PEP_STATUS_OK) {
1119 if (_il->ident->fpr && _il->ident->fpr[0]) {
1120 _k = stringlist_add(_k, _il->ident->fpr);
1123 max_comm_type = _get_comm_type(session, max_comm_type,
1127 dest_keys_found = false;
1128 status = PEP_KEY_NOT_FOUND;
1132 for (_il = src->cc; _il && _il->ident; _il = _il->next) {
1133 PEP_STATUS _status = update_identity(session, _il->ident);
1134 if (_status != PEP_STATUS_OK)
1140 if (_il->ident->fpr && _il->ident->fpr[0]) {
1141 _k = stringlist_add(_k, _il->ident->fpr);
1144 max_comm_type = _get_comm_type(session, max_comm_type,
1148 dest_keys_found = false;
1149 status = PEP_KEY_NOT_FOUND;
1154 if (!dest_keys_found ||
1155 stringlist_length(keys) == 0 ||
1156 _rating(max_comm_type,
1157 PEP_rating_undefined) < PEP_rating_reliable)
1159 free_stringlist(keys);
1160 if (!session->passive_mode)
1161 attach_own_key(session, src);
1162 return PEP_UNENCRYPTED;
1165 msg = clone_to_empty_message(src);
1169 attach_own_key(session, src);
1171 switch (enc_format) {
1172 case PEP_enc_PGP_MIME:
1173 case PEP_enc_PEP: // BUG: should be implemented extra
1174 status = encrypt_PGP_MIME(session, src, keys, msg);
1177 case PEP_enc_pieces:
1178 status = encrypt_PGP_in_pieces(session, src, keys, msg);
1181 /* case PEP_enc_PEP:
1187 status = PEP_ILLEGAL_VALUE;
1191 if (status == PEP_OUT_OF_MEMORY)
1194 if (status != PEP_STATUS_OK)
1198 free_stringlist(keys);
1200 if (msg && msg->shortmsg == NULL) {
1201 msg->shortmsg = strdup("pEp");
1202 assert(msg->shortmsg);
1203 if (msg->shortmsg == NULL)
1208 decorate_message(msg, PEP_rating_undefined, NULL);
1214 status = PEP_OUT_OF_MEMORY;
1217 free_stringlist(keys);
1223 DYNAMIC_API PEP_STATUS encrypt_message_for_self(
1224 PEP_SESSION session,
1225 pEp_identity* target_id,
1228 PEP_enc_format enc_format
1231 PEP_STATUS status = PEP_STATUS_OK;
1232 message * msg = NULL;
1233 stringlist_t * keys = NULL;
1238 assert(enc_format != PEP_enc_none);
1240 if (!(session && src && dst && enc_format != PEP_enc_none))
1241 return PEP_ILLEGAL_VALUE;
1243 if (src->dir == PEP_dir_incoming)
1244 return PEP_ILLEGAL_VALUE;
1246 determine_encryption_format(src);
1247 if (src->enc_format != PEP_enc_none)
1248 return PEP_ILLEGAL_VALUE;
1250 status = myself(session, target_id);
1251 if (status != PEP_STATUS_OK)
1257 PEP_STATUS _status = update_identity(session, target_id);
1258 if (_status != PEP_STATUS_OK) {
1263 char* target_fpr = target_id->fpr;
1265 return PEP_KEY_NOT_FOUND; // FIXME: Error condition
1267 keys = new_stringlist(target_fpr);
1270 msg = clone_to_empty_message(src);
1274 switch (enc_format) {
1275 case PEP_enc_PGP_MIME:
1276 case PEP_enc_PEP: // BUG: should be implemented extra
1277 status = encrypt_PGP_MIME(session, src, keys, msg);
1280 case PEP_enc_pieces:
1281 status = encrypt_PGP_in_pieces(session, src, keys, msg);
1284 /* case PEP_enc_PEP:
1290 status = PEP_ILLEGAL_VALUE;
1294 if (status == PEP_OUT_OF_MEMORY)
1297 if (status != PEP_STATUS_OK)
1300 if (msg && msg->shortmsg == NULL) {
1301 msg->shortmsg = strdup("pEp");
1302 assert(msg->shortmsg);
1303 if (msg->shortmsg == NULL)
1311 status = PEP_OUT_OF_MEMORY;
1314 free_stringlist(keys);
1320 static bool is_a_pEpmessage(const message *msg)
1322 for (stringpair_list_t *i = msg->opt_fields; i && i->value ; i=i->next) {
1323 if (strcasecmp(i->value->key, "X-pEp-Version") == 0)
1329 // update comm_type to pEp_ct_pEp if needed
1331 static PEP_STATUS _update_identity_for_incoming_message(
1332 PEP_SESSION session,
1337 if (src->from && src->from->address) {
1338 status = update_identity(session, src->from);
1339 if (status == PEP_STATUS_OK
1340 && is_a_pEpmessage(src)
1341 && src->from->comm_type >= PEP_ct_OpenPGP_unconfirmed
1342 && src->from->comm_type != PEP_ct_pEp_unconfirmed
1343 && src->from->comm_type != PEP_ct_pEp)
1345 src->from->comm_type |= PEP_ct_pEp_unconfirmed;
1346 status = update_identity(session, src->from);
1350 return PEP_ILLEGAL_VALUE;
1353 DYNAMIC_API PEP_STATUS _decrypt_message(
1354 PEP_SESSION session,
1357 stringlist_t **keylist,
1359 PEP_decrypt_flags_t *flags,
1360 identity_list **private_il
1363 PEP_STATUS status = PEP_STATUS_OK;
1364 PEP_STATUS decrypt_status = PEP_CANNOT_DECRYPT_UNKNOWN;
1365 message *msg = NULL;
1370 stringlist_t *_keylist = NULL;
1379 if (!(session && src && dst && keylist && rating && flags))
1380 return PEP_ILLEGAL_VALUE;
1384 // Private key in unencrypted mail are ignored -> NULL
1385 bool imported_keys = import_attached_keys(session, src, NULL);
1387 // Update src->from in case we just imported a key
1388 // we would need to check signature
1389 status = _update_identity_for_incoming_message(session, src);
1390 if(status != PEP_STATUS_OK)
1393 PEP_cryptotech crypto = determine_encryption_format(src);
1397 *rating = PEP_rating_undefined;
1399 switch (src->enc_format) {
1401 *rating = PEP_rating_unencrypted;
1403 remove_attached_keys(src);
1404 status = receive_DeviceState_msg(session, src, false);
1405 if (status == PEP_MESSAGE_CONSUMED) {
1409 else if (status != PEP_STATUS_OK){
1412 return PEP_UNENCRYPTED;
1414 case PEP_enc_PGP_MIME:
1415 ctext = src->attachments->next->value;
1416 csize = src->attachments->next->size;
1419 case PEP_enc_pieces:
1420 ctext = src->longmsg;
1421 csize = strlen(ctext);
1427 status = cryptotech[crypto].decrypt_and_verify(session, ctext,
1428 csize, &ptext, &psize, &_keylist);
1429 if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
1432 decrypt_status = status;
1434 bool imported_private_key_address = false;
1437 switch (src->enc_format) {
1438 case PEP_enc_PGP_MIME:
1439 status = mime_decode_message(ptext, psize, &msg);
1440 if (status != PEP_STATUS_OK)
1444 case PEP_enc_pieces:
1445 msg = clone_to_empty_message(src);
1449 msg->longmsg = ptext;
1452 bloblist_t *_m = msg->attachments;
1453 if (_m == NULL && src->attachments && src->attachments->value) {
1454 msg->attachments = new_bloblist(NULL, 0, NULL, NULL);
1455 _m = msg->attachments;
1459 for (_s = src->attachments; _s; _s = _s->next) {
1460 if (_s->value == NULL && _s->size == 0){
1461 _m = bloblist_add(_m, NULL, 0, _s->mime_type, _s->filename);
1466 else if (is_encrypted_attachment(_s)) {
1467 stringlist_t *_keylist = NULL;
1468 char *attctext = _s->value;
1469 size_t attcsize = _s->size;
1474 status = decrypt_and_verify(session, attctext, attcsize,
1475 &ptext, &psize, &_keylist);
1476 free_stringlist(_keylist);
1479 if (is_encrypted_html_attachment(_s)) {
1480 msg->longmsg_formatted = ptext;
1484 static const char * const mime_type = "application/octet-stream";
1485 char * const filename =
1486 without_double_ending(_s->filename);
1487 if (filename == NULL)
1490 _m = bloblist_add(_m, ptext, psize, mime_type,
1498 if (msg->attachments == NULL)
1499 msg->attachments = _m;
1503 char *copy = malloc(_s->size);
1507 memcpy(copy, _s->value, _s->size);
1508 _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
1514 char *copy = malloc(_s->size);
1518 memcpy(copy, _s->value, _s->size);
1519 _m = bloblist_add(_m, copy, _s->size, _s->mime_type, _s->filename);
1528 // BUG: must implement more
1532 switch (src->enc_format) {
1533 case PEP_enc_PGP_MIME:
1534 case PEP_enc_pieces:
1535 status = copy_fields(msg, src);
1536 if (status != PEP_STATUS_OK)
1539 if (src->shortmsg == NULL || strcmp(src->shortmsg, "pEp") == 0)
1544 int r = separate_short_and_long(msg->longmsg, &shortmsg,
1549 free(msg->shortmsg);
1552 msg->shortmsg = shortmsg;
1553 msg->longmsg = longmsg;
1556 msg->shortmsg = strdup(src->shortmsg);
1557 assert(msg->shortmsg);
1558 if (msg->shortmsg == NULL)
1564 // BUG: must implement more
1568 // check for private key in decrypted message attachement while inporting
1569 identity_list *_private_il = NULL;
1570 imported_keys = import_attached_keys(session, msg, &_private_il);
1572 identity_list_length(_private_il) == 1 &&
1573 _private_il->ident->address)
1575 imported_private_key_address = true;
1578 if(private_il && imported_private_key_address){
1579 *private_il = _private_il;
1581 free_identity_list(_private_il);
1584 if(decrypt_status == PEP_DECRYPTED){
1586 // TODO optimize if import_attached_keys didn't import any key
1588 // In case message did decrypt, but no valid signature could be found
1589 // then retry decrypt+verify after importing key.
1591 // Update msg->from in case we just imported a key
1592 // we would need to check signature
1594 status = _update_identity_for_incoming_message(session, src);
1595 if(status != PEP_STATUS_OK)
1598 char *re_ptext = NULL;
1601 free_stringlist(_keylist);
1604 status = cryptotech[crypto].decrypt_and_verify(session, ctext,
1605 csize, &re_ptext, &re_psize, &_keylist);
1609 if (status > PEP_CANNOT_DECRYPT_UNKNOWN)
1612 decrypt_status = status;
1615 *rating = decrypt_rating(decrypt_status);
1617 if (*rating > PEP_rating_mistrust) {
1618 PEP_rating kl_rating = PEP_rating_undefined;
1621 kl_rating = keylist_rating(session, _keylist);
1623 if (kl_rating <= PEP_rating_mistrust) {
1624 *rating = kl_rating;
1626 else if (*rating >= PEP_rating_reliable &&
1627 kl_rating < PEP_rating_reliable) {
1628 *rating = PEP_rating_unreliable;
1630 else if (*rating >= PEP_rating_reliable &&
1631 kl_rating >= PEP_rating_reliable) {
1632 if (!(src->from && src->from->user_id && src->from->user_id[0])) {
1633 *rating = PEP_rating_unreliable;
1636 char *fpr = _keylist->value;
1637 pEp_identity *_from = new_identity(src->from->address, fpr,
1638 src->from->user_id, src->from->username);
1641 status = update_identity(session, _from);
1642 if (_from->comm_type != PEP_ct_unknown)
1643 *rating = _rating(_from->comm_type, PEP_rating_undefined);
1644 free_identity(_from);
1645 if (status != PEP_STATUS_OK)
1653 *rating = decrypt_rating(decrypt_status);
1657 // Case of own key imported from own trusted message
1658 if (// Message have been reliably decrypted
1660 *rating >= PEP_rating_trusted &&
1661 imported_private_key_address &&
1663 msg->to->ident->user_id &&
1664 strcmp(msg->to->ident->user_id, PEP_OWN_USERID) == 0
1667 *flags |= PEP_decrypt_flag_own_private_key;
1671 decorate_message(msg, *rating, _keylist);
1673 remove_attached_keys(msg);
1674 if (*rating >= PEP_rating_reliable) {
1675 status = receive_DeviceState_msg(session, msg, *rating);
1676 if (status == PEP_MESSAGE_CONSUMED) {
1680 else if (status != PEP_STATUS_OK){
1687 *keylist = _keylist;
1692 status = PEP_OUT_OF_MEMORY;
1697 free_stringlist(_keylist);
1702 DYNAMIC_API PEP_STATUS decrypt_message(
1703 PEP_SESSION session,
1706 stringlist_t **keylist,
1708 PEP_decrypt_flags_t *flags
1711 return _decrypt_message( session, src, dst, keylist, rating, flags, NULL );
1714 DYNAMIC_API PEP_STATUS own_message_private_key_details(
1715 PEP_SESSION session,
1717 pEp_identity **ident
1724 if (!(session && msg && ident))
1725 return PEP_ILLEGAL_VALUE;
1727 message *dst = NULL;
1728 stringlist_t *keylist = NULL;
1730 PEP_decrypt_flags_t flags;
1734 identity_list *private_il = NULL;
1735 PEP_STATUS status = _decrypt_message(session, msg, &dst, &keylist, &rating, &flags, &private_il);
1737 free_stringlist(keylist);
1739 if (status == PEP_STATUS_OK &&
1740 flags & PEP_decrypt_flag_own_private_key &&
1743 *ident = identity_dup(private_il->ident);
1746 free_identity_list(private_il);
1747 free_stringlist(keylist);
1754 static void _max_comm_type_from_identity_list(
1755 identity_list *identities,
1756 PEP_SESSION session,
1757 PEP_comm_type *max_comm_type,
1758 bool *comm_type_determined
1762 for (il = identities; il != NULL; il = il->next)
1766 PEP_STATUS status = update_identity(session, il->ident);
1767 if (status == PEP_STATUS_OK)
1769 *max_comm_type = _get_comm_type(session, *max_comm_type,
1771 *comm_type_determined = true;
1777 DYNAMIC_API PEP_STATUS outgoing_message_rating(
1778 PEP_SESSION session,
1783 PEP_STATUS status = PEP_STATUS_OK;
1784 PEP_comm_type max_comm_type = PEP_ct_pEp;
1785 bool comm_type_determined = false;
1790 assert(msg->dir == PEP_dir_outgoing);
1793 if (!(session && msg && rating))
1794 return PEP_ILLEGAL_VALUE;
1796 if (msg->from == NULL || msg->dir != PEP_dir_outgoing)
1797 return PEP_ILLEGAL_VALUE;
1799 *rating = PEP_rating_undefined;
1801 status = myself(session, msg->from);
1802 if (status != PEP_STATUS_OK)
1805 _max_comm_type_from_identity_list(msg->to, session,
1806 &max_comm_type, &comm_type_determined);
1808 _max_comm_type_from_identity_list(msg->cc, session,
1809 &max_comm_type, &comm_type_determined);
1811 _max_comm_type_from_identity_list(msg->bcc, session,
1812 &max_comm_type, &comm_type_determined);
1814 if (comm_type_determined == false)
1815 *rating = PEP_rating_undefined;
1817 *rating = _MAX(_rating(max_comm_type, PEP_rating_undefined),
1818 PEP_rating_unencrypted);
1820 return PEP_STATUS_OK;
1823 DYNAMIC_API PEP_STATUS identity_rating(
1824 PEP_SESSION session,
1825 pEp_identity *ident,
1829 PEP_STATUS status = PEP_STATUS_OK;
1835 if (!(session && ident && rating))
1836 return PEP_ILLEGAL_VALUE;
1839 status = myself(session, ident);
1841 status = update_identity(session, ident);
1843 if (status == PEP_STATUS_OK)
1844 *rating = _rating(ident->comm_type, PEP_rating_undefined);
1849 DYNAMIC_API PEP_STATUS get_binary_path(PEP_cryptotech tech, const char **path)
1851 PEP_STATUS status = PEP_STATUS_OK;
1855 return PEP_ILLEGAL_VALUE;
1857 if (cryptotech[tech].binary_path == NULL)
1860 status = cryptotech[tech].binary_path(path);
1866 DYNAMIC_API PEP_color color_from_rating(PEP_rating rating)
1868 if (rating == PEP_rating_b0rken)
1869 return PEP_color_no_color;
1871 if (rating < PEP_rating_undefined)
1872 return PEP_color_red;
1874 if (rating < PEP_rating_reliable)
1875 return PEP_color_no_color;
1877 if (rating < PEP_rating_trusted)
1878 return PEP_color_yellow;
1880 if (rating >= PEP_rating_trusted)
1881 return PEP_color_green;
1883 // this should never happen