ENGINE-559: first running version that passes the old tests - need to test the new functionality, but hey, it didn't explode. Theoretically, we now keep track of the latest pEp version received from pEp users. ENGINE-559
authorKrista 'DarthMama' Bennett <krista@pep.foundation>
Mon, 20 May 2019 07:33:05 +0200
branchENGINE-559
changeset 3730922720c27d9a
parent 3729 1c6553767712
child 3732 fb7371ca6f90
ENGINE-559: first running version that passes the old tests - need to test the new functionality, but hey, it didn't explode. Theoretically, we now keep track of the latest pEp version received from pEp users.
src/keymanagement.c
src/message_api.c
src/pEpEngine.c
src/pEpEngine.h
src/pEp_internal.h
src/stringpair.c
src/stringpair.h
     1.1 --- a/src/keymanagement.c	Sun May 19 15:13:55 2019 +0200
     1.2 +++ b/src/keymanagement.c	Mon May 20 07:33:05 2019 +0200
     1.3 @@ -1198,6 +1198,12 @@
     1.4          identity->comm_type = PEP_ct_unknown;
     1.5      }
     1.6      
     1.7 +    int major_ver = 0;
     1.8 +    int minor_ver = 0;
     1.9 +    pEp_version_major_minor(PEP_VERSION, &major_ver, &minor_ver);
    1.10 +    identity->major_ver = major_ver;
    1.11 +    identity->minor_ver = minor_ver;
    1.12 +    
    1.13      // We want to set an identity in the DB even if a key isn't found, but we have to preserve the status if
    1.14      // it's NOT ok
    1.15      if (!read_only) {
     2.1 --- a/src/message_api.c	Sun May 19 15:13:55 2019 +0200
     2.2 +++ b/src/message_api.c	Mon May 20 07:33:05 2019 +0200
     2.3 @@ -838,7 +838,7 @@
     2.4  }
     2.5  
     2.6  static message* wrap_message_as_attachment(message* envelope, 
     2.7 -    message* attachment, message_wrap_type wrap_type, bool keep_orig_subject) {
     2.8 +    message* attachment, message_wrap_type wrap_type, bool keep_orig_subject, unsigned int max_major, unsigned int max_minor) {
     2.9      
    2.10      if (!attachment)
    2.11          return NULL;
    2.12 @@ -864,9 +864,17 @@
    2.13              default:
    2.14                  inner_type_string = "INNER";
    2.15          }
    2.16 -        attachment->longmsg = encapsulate_message_wrap_info(inner_type_string, attachment->longmsg);        
    2.17 -        _envelope->longmsg = encapsulate_message_wrap_info("OUTER", _envelope->longmsg);
    2.18 -
    2.19 +        if (max_major < 2 || (max_major == 2 && max_minor == 0)) {
    2.20 +            attachment->longmsg = encapsulate_message_wrap_info(inner_type_string, attachment->longmsg);        
    2.21 +            _envelope->longmsg = encapsulate_message_wrap_info("OUTER", _envelope->longmsg);
    2.22 +        }
    2.23 +        else {
    2.24 +            _envelope->longmsg = strdup(
    2.25 +                "This message was encrypted with p≡p (https://pep.software). If you are seeing this message,\n" 
    2.26 +                "your client does not support raising message attachments. Please click on the message attachment to\n"
    2.27 +                "to view it, or better yet, consider using p≡p!\n"
    2.28 +            );
    2.29 +        }
    2.30          // 2.1, to replace the above
    2.31          add_opt_field(attachment, X_PEP_MSG_WRAP_KEY, inner_type_string); 
    2.32      }
    2.33 @@ -1711,7 +1719,10 @@
    2.34      bool has_pEp_user = false;
    2.35      
    2.36      PEP_comm_type max_comm_type = PEP_ct_pEp;
    2.37 -
    2.38 +    unsigned int max_version_major = 0;
    2.39 +    unsigned int max_version_minor = 0;
    2.40 +    pEp_version_major_minor(PEP_VERSION, &max_version_major, &max_version_minor);
    2.41 +    
    2.42      identity_list * _il = NULL;
    2.43  
    2.44      if (enc_format != PEP_enc_none && (_il = src->bcc) && _il->ident)
    2.45 @@ -1719,7 +1730,6 @@
    2.46      {
    2.47          //     - App splits mails with BCC in multiple mails.
    2.48          //     - Each email is encrypted separately
    2.49 -
    2.50          if(_il->next || (src->to && src->to->ident) || (src->cc && src->cc->ident))
    2.51          {
    2.52              // Only one Bcc with no other recipient allowed for now
    2.53 @@ -1733,6 +1743,12 @@
    2.54                  _il->ident->comm_type = PEP_ct_key_not_found;
    2.55                  _status = PEP_STATUS_OK;
    2.56              }
    2.57 +            // 0 unless set, so safe.
    2.58 +            
    2.59 +            set_min_version( _il->ident->major_ver, _il->ident->minor_ver, 
    2.60 +                             max_version_major, max_version_minor,
    2.61 +                             &max_version_major, &max_version_minor);
    2.62 +
    2.63              bool is_blacklisted = false;
    2.64              if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
    2.65                  _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
    2.66 @@ -1758,6 +1774,7 @@
    2.67          }
    2.68          else
    2.69              _status = myself(session, _il->ident);
    2.70 +        
    2.71          if (_status != PEP_STATUS_OK) {
    2.72              status = PEP_UNENCRYPTED;
    2.73              goto pEp_error;
    2.74 @@ -1785,6 +1802,11 @@
    2.75                      _il->ident->comm_type = PEP_ct_key_not_found;
    2.76                      _status = PEP_STATUS_OK;
    2.77                  }
    2.78 +                // 0 unless set, so safe.
    2.79 +                set_min_version( _il->ident->major_ver, _il->ident->minor_ver, 
    2.80 +                                 max_version_major, max_version_minor,
    2.81 +                                 &max_version_major, &max_version_minor);
    2.82 +                
    2.83                  bool is_blacklisted = false;
    2.84                  if (_il->ident->fpr && IS_PGP_CT(_il->ident->comm_type)) {
    2.85                      _status = blacklist_is_listed(session, _il->ident->fpr, &is_blacklisted);
    2.86 @@ -1906,7 +1928,7 @@
    2.87          message_wrap_type wrap_type = PEP_message_unwrapped;
    2.88          if ((enc_format != PEP_enc_inline) && (!force_v_1) && ((max_comm_type | PEP_ct_confirmed) == PEP_ct_pEp)) {
    2.89              wrap_type = ((flags & PEP_encrypt_flag_key_reset_only) ? PEP_message_key_reset : PEP_message_default);
    2.90 -            _src = wrap_message_as_attachment(NULL, src, wrap_type, false);
    2.91 +            _src = wrap_message_as_attachment(NULL, src, wrap_type, false, max_version_major, max_version_minor);
    2.92              if (!_src)
    2.93                  goto pEp_error;
    2.94          }
    2.95 @@ -2252,7 +2274,9 @@
    2.96      // if (!(flags & PEP_encrypt_flag_force_no_attached_key))
    2.97      //     _attach_key(session, target_fpr, src);
    2.98  
    2.99 -    _src = wrap_message_as_attachment(NULL, src, PEP_message_default, false);
   2.100 +    unsigned int major_ver, minor_ver;
   2.101 +    pEp_version_major_minor(PEP_VERSION, &major_ver, &minor_ver);
   2.102 +    _src = wrap_message_as_attachment(NULL, src, PEP_message_default, false, major_ver, minor_ver);
   2.103      if (!_src)
   2.104          goto pEp_error;
   2.105  
   2.106 @@ -2985,11 +3009,13 @@
   2.107  static PEP_STATUS pEp_version_upgrade_or_ignore(
   2.108          PEP_SESSION session,
   2.109          pEp_identity* ident,
   2.110 -        float version_number) {
   2.111 +        unsigned int major,
   2.112 +        unsigned int minor) {
   2.113              
   2.114      PEP_STATUS status = PEP_STATUS_OK;        
   2.115 -    if (version_number > ident->_pEp_version)
   2.116 -        status = set_pEp_version(session, ident, version_number);        
   2.117 +    int ver_compare = compare_versions(major, minor, ident->major_ver, ident->minor_ver);
   2.118 +    if (ver_compare > 0)
   2.119 +        status = set_pEp_version(session, ident, major, minor);        
   2.120      
   2.121      return status;    
   2.122  }
   2.123 @@ -2999,7 +3025,8 @@
   2.124          PEP_SESSION session, 
   2.125          pEp_identity* sender, 
   2.126          stringlist_t* keylist,
   2.127 -        float version_number) 
   2.128 +        unsigned int major,
   2.129 +        unsigned int minor) 
   2.130  {
   2.131      assert(session);
   2.132      assert(sender);
   2.133 @@ -3049,9 +3076,11 @@
   2.134          case PEP_ct_pEp:
   2.135          case PEP_ct_pEp_unconfirmed:
   2.136              // set version
   2.137 -            if (version_number == 0)
   2.138 -                version_number = 2;
   2.139 -            status = pEp_version_upgrade_or_ignore(session, sender, version_number);    
   2.140 +            if (major == 0) {
   2.141 +                major = 2;
   2.142 +                minor = 0;
   2.143 +            }
   2.144 +            status = pEp_version_upgrade_or_ignore(session, sender, major, minor);    
   2.145              break;
   2.146          default:
   2.147              status = PEP_CANNOT_SET_TRUST;
   2.148 @@ -3338,8 +3367,9 @@
   2.149      char* signer_fpr = NULL;
   2.150      bool is_pEp_msg = is_a_pEpmessage(src);
   2.151      bool myself_read_only = (src->dir == PEP_dir_incoming);
   2.152 -    float pEp_version = 0;
   2.153 -
   2.154 +    unsigned int major_ver;
   2.155 +    unsigned int minor_ver;
   2.156 +    
   2.157      // Grab input flags
   2.158      bool reencrypt = (((*flags & PEP_decrypt_flag_untrusted_server) > 0) && *keylist && !EMPTYSTR((*keylist)->value));
   2.159      
   2.160 @@ -3625,16 +3655,11 @@
   2.161                          const stringpair_list_t* pEp_protocol_version = NULL;
   2.162                          pEp_protocol_version = stringpair_list_find(inner_message->opt_fields, "X-pEp-Version");
   2.163                          
   2.164 -                        if (pEp_protocol_version && !EMPTYSTR(pEp_protocol_version->value->value)) {
   2.165 -                            // Roker is of course right. Meh :)
   2.166 -                            if (sscanf(pEp_protocol_version->value->value, "%f", &pEp_version) != 1) {
   2.167 -                                pEp_version = 0;
   2.168 -                            }
   2.169 -                        }
   2.170 +                        if (pEp_protocol_version && pEp_protocol_version->value)
   2.171 +                            pEp_version_major_minor(pEp_protocol_version->value->value, &major_ver, &minor_ver);
   2.172  
   2.173                          // Sort out pEp user status and version number based on INNER message.
   2.174                          
   2.175 -                        
   2.176                          bool is_inner = false;
   2.177                          bool is_key_reset = false;
   2.178  
   2.179 @@ -3646,20 +3671,22 @@
   2.180                          if (status != PEP_STATUS_OK)
   2.181                              goto pEp_error;                                         
   2.182                              
   2.183 -                        if (pEp_version < 2) {
   2.184 +                        if (major_ver > 2 || (major_ver == 2 && minor_ver > 0)) {
   2.185                              stringpair_list_t* searched = stringpair_list_find(inner_message->opt_fields, "X-pEp-Sender-FPR");                             
   2.186                              inner_message->_sender_fpr = ((searched && searched->value && searched->value->value) ? strdup(searched->value->value) : NULL);
   2.187                              searched = stringpair_list_find(inner_message->opt_fields, X_PEP_MSG_WRAP_KEY);
   2.188                              if (searched && searched->value && searched->value->value) {
   2.189                                  is_inner = (strcmp(searched->value->value, "INNER") == 0);
   2.190                                  if (!is_inner)
   2.191 -                                    is_key_reset = (strcmp(searched->value->value, "INNER") == 0);
   2.192 +                                    is_key_reset = (strcmp(searched->value->value, "KEY_RESET") == 0);
   2.193 +                                if (is_inner || is_key_reset)
   2.194 +                                    inner_message->opt_fields = stringpair_list_delete_by_key(inner_message->opt_fields, X_PEP_MSG_WRAP_KEY);
   2.195                              }
   2.196                          }
   2.197                          else {
   2.198 -                                is_inner = (strcmp(wrap_info, "INNER") == 0);
   2.199 -                                if (!is_inner)
   2.200 -                                    is_key_reset = (strcmp(wrap_info, "KEY_RESET") == 0);
   2.201 +                            is_inner = (strcmp(wrap_info, "INNER") == 0);
   2.202 +                            if (!is_inner)
   2.203 +                                is_key_reset = (strcmp(wrap_info, "KEY_RESET") == 0);
   2.204                          }
   2.205                              
   2.206  
   2.207 @@ -3754,7 +3781,7 @@
   2.208          // eligible signer comm_types to PEP_ct_pEp_*
   2.209          // This also sets and upgrades pEp version
   2.210          if (decrypt_status == PEP_DECRYPTED_AND_VERIFIED && is_pEp_msg && calculated_src->from)
   2.211 -            status = update_sender_to_pEp_trust(session, calculated_src->from, _keylist, pEp_version);
   2.212 +            status = update_sender_to_pEp_trust(session, calculated_src->from, _keylist, major_ver, minor_ver);
   2.213  
   2.214          /* Ok, now we have a keylist used for decryption/verification.
   2.215             now we need to update the message rating with the 
   2.216 @@ -4125,7 +4152,7 @@
   2.217          *rating = PEP_rating_undefined;
   2.218      }
   2.219      else
   2.220 -        *rating = MAX(_rating(max_comm_type), PEP_rating_unencrypted);
   2.221 +        *rating = _MAX(_rating(max_comm_type), PEP_rating_unencrypted);
   2.222  
   2.223      return PEP_STATUS_OK;
   2.224  }
     3.1 --- a/src/pEpEngine.c	Sun May 19 15:13:55 2019 +0200
     3.2 +++ b/src/pEpEngine.c	Mon May 20 07:33:05 2019 +0200
     3.3 @@ -83,7 +83,7 @@
     3.4  static const char *sql_get_identity =  
     3.5      "select fpr, username, comm_type, lang,"
     3.6      "   identity.flags | pgp_keypair.flags,"
     3.7 -    "   is_own"
     3.8 +    "   is_own, pEp_version_major, pEp_version_minor"
     3.9      "   from identity"
    3.10      "   join person on id = identity.user_id"
    3.11      "   join pgp_keypair on fpr = identity.main_key_id"
    3.12 @@ -101,7 +101,7 @@
    3.13  static const char *sql_get_identities_by_main_key_id =  
    3.14      "select address, identity.user_id, username, comm_type, lang,"
    3.15      "   identity.flags | pgp_keypair.flags,"
    3.16 -    "   is_own"
    3.17 +    "   is_own, pEp_version_major, pEp_version_minor"
    3.18      "   from identity"
    3.19      "   join person on id = identity.user_id"
    3.20      "   join pgp_keypair on fpr = identity.main_key_id"
    3.21 @@ -113,7 +113,7 @@
    3.22  
    3.23  static const char *sql_get_identity_without_trust_check =  
    3.24      "select identity.main_key_id, username, lang,"
    3.25 -    "   identity.flags, is_own"
    3.26 +    "   identity.flags, is_own, pEp_version_major, pEp_version_minor"
    3.27      "   from identity"
    3.28      "   join person on id = identity.user_id"
    3.29      "   where (case when (address = ?1) then (1)"
    3.30 @@ -127,7 +127,7 @@
    3.31  
    3.32  static const char *sql_get_identities_by_address =  
    3.33      "select user_id, identity.main_key_id, username, lang,"
    3.34 -    "   identity.flags, is_own"
    3.35 +    "   identity.flags, is_own, pEp_version_major, pEp_version_minor"
    3.36      "   from identity"
    3.37      "   join person on id = identity.user_id"
    3.38      "   where (case when (address = ?1) then (1)"
    3.39 @@ -141,7 +141,7 @@
    3.40  static const char *sql_get_identities_by_userid =  
    3.41      "select address, fpr, username, comm_type, lang,"
    3.42      "   identity.flags | pgp_keypair.flags,"
    3.43 -    "   is_own"
    3.44 +    "   is_own, pEp_version_major, pEp_version_minor"
    3.45      "   from identity"
    3.46      "   join person on id = identity.user_id"
    3.47      "   join pgp_keypair on fpr = identity.main_key_id"
    3.48 @@ -239,20 +239,25 @@
    3.49  static const char *sql_set_identity_entry = 
    3.50      "insert into identity ("
    3.51      "       address, main_key_id, "
    3.52 -    "       user_id, flags, is_own"
    3.53 +    "       user_id, flags, is_own,"
    3.54 +    "       pEp_version_major, pEp_version_minor"
    3.55      "   ) values ("
    3.56      "       ?1,"
    3.57      "       upper(replace(?2,' ','')),"
    3.58      "       ?3,"
    3.59      "       ?4,"
    3.60 -    "       ?5"
    3.61 +    "       ?5,"
    3.62 +    "       ?6,"
    3.63 +    "       ?7"
    3.64      "   );";
    3.65      
    3.66  static const char* sql_update_identity_entry =    
    3.67      "update identity "
    3.68      "   set main_key_id = upper(replace(?2,' ','')), "
    3.69      "       flags = ?4, " 
    3.70 -    "       is_own = ?5 "
    3.71 +    "       is_own = ?5, "
    3.72 +    "       pEp_version_major = ?6, "
    3.73 +    "       pEp_version_major = ?7 "    
    3.74      "   where (case when (address = ?1) then (1)"
    3.75      "               when (lower(address) = lower(?1)) then (1)"
    3.76      "               when (replace(lower(address),'.','') = replace(lower(?1),'.','')) then (1) "
    3.77 @@ -303,13 +308,25 @@
    3.78  
    3.79  static const char *sql_set_pEp_version =
    3.80      "update identity "
    3.81 -    "   set pEp_version = ?1 "
    3.82 -    "   where (case when (address = ?2) then (1)"
    3.83 -    "               when (lower(address) = lower(?2)) then (1)"
    3.84 -    "               when (replace(lower(address),'.','') = replace(lower(?2),'.','')) then (1) "
    3.85 +    "   set pEp_version_major = ?1, "
    3.86 +    "       pEp_version_minor = ?2 "
    3.87 +    "   where (case when (address = ?3) then (1)"
    3.88 +    "               when (lower(address) = lower(?3)) then (1)"
    3.89 +    "               when (replace(lower(address),'.','') = replace(lower(?3),'.','')) then (1) "
    3.90      "               else 0 "
    3.91      "          end) = 1 "
    3.92 -    "          and user_id = ?3 ;";
    3.93 +    "          and user_id = ?4 ;";
    3.94 +
    3.95 +static const char *sql_upgrade_pEp_version_by_user_id =
    3.96 +    "update identity "
    3.97 +    "   set pEp_version_major = ?1, "
    3.98 +    "       pEp_version_minor = ?2 "
    3.99 +    "       where user_id = ?3 "
   3.100 +    "           and (case when (pEp_version_major < ?1) then (1)"
   3.101 +    "                     when (pEp_version_major > ?1) then (0)"
   3.102 +    "                     when (pEp_version_minor < ?2) then (1)"
   3.103 +    "                     else 0 "
   3.104 +    "           end) = 1 ;";
   3.105  
   3.106  static const char *sql_set_trust =
   3.107      "insert into trust (user_id, pgp_keypair_fpr, comm_type) "
   3.108 @@ -403,7 +420,7 @@
   3.109  
   3.110  static const char *sql_own_identities_retrieve =  
   3.111      "select address, fpr, identity.user_id, username,"
   3.112 -    "   lang, identity.flags | pgp_keypair.flags"
   3.113 +    "   lang, identity.flags | pgp_keypair.flags, pEp_version_major, pEp_version_minor"
   3.114      "   from identity"
   3.115      "   join person on id = identity.user_id"
   3.116      "   join pgp_keypair on fpr = identity.main_key_id"
   3.117 @@ -975,7 +992,8 @@
   3.118                  "   comment text,\n"
   3.119                  "   flags integer default 0,\n"
   3.120                  "   is_own integer default 0,\n"
   3.121 -                "   pEp_version real default 0,\n"
   3.122 +                "   pEp_version_major integer default 0,\n"
   3.123 +                "   pEp_version_minor integer default 0,\n"                
   3.124                  "   timestamp integer default (datetime('now')),\n"
   3.125                  "   primary key (address, user_id)\n"
   3.126                  ");\n"
   3.127 @@ -1081,7 +1099,7 @@
   3.128          // Sometimes the user_version wasn't set correctly. 
   3.129          if (version == 1) {
   3.130              bool version_changed = true;
   3.131 -            if (table_contains_column(_session, "identity", "pEp_version")) {
   3.132 +            if (table_contains_column(_session, "identity", "pEp_version_major")) {
   3.133                  version = 12;
   3.134              }
   3.135              else if (db_contains_table(_session, "social_graph") > 0) {
   3.136 @@ -1441,13 +1459,66 @@
   3.137                  int_result = sqlite3_exec(
   3.138                      _session->db,
   3.139                      "alter table identity\n"
   3.140 -                    "   add column pEp_version real default 0\n",
   3.141 +                    "   add column pEp_version_major integer default 0;\n"
   3.142 +                    "alter table identity\n"
   3.143 +                    "   add column pEp_version_minor integer default 0;\n",                    
   3.144                      NULL,
   3.145                      NULL,
   3.146                      NULL
   3.147                  );
   3.148                  if (status != PEP_STATUS_OK)
   3.149 -                    return status;                
   3.150 +                    return status;  
   3.151 +      
   3.152 +                int_result = sqlite3_exec(
   3.153 +                    _session->db,
   3.154 +                    "update identity\n"
   3.155 +                    "   set pEp_version_major = 2\n"
   3.156 +                    "   where exists (select * from person\n"
   3.157 +                    "                     where identity.user_id = person.id\n"
   3.158 +                    "                     and identity.is_own = 0\n"
   3.159 +                    "                     and person.is_pEp_user = 1);\n",
   3.160 +                    NULL,
   3.161 +                    NULL,
   3.162 +                    NULL
   3.163 +                );
   3.164 +                if (status != PEP_STATUS_OK)
   3.165 +                    return status;  
   3.166 +                
   3.167 +                // N.B. WE DEFINE PEP_VERSION - IF WE'RE AT 9-DIGIT MAJOR OR MINOR VERSIONS, ER, BAD.
   3.168 +                char major_buf[10];
   3.169 +                char minor_buf[10];
   3.170 +                if (sscanf("%s.%s", major_buf, minor_buf) != 2)
   3.171 +                    return PEP_UNKNOWN_ERROR; // DO BETTER
   3.172 +                size_t major_len = strlen(major_buf);
   3.173 +                size_t minor_len = strlen(minor_buf);
   3.174 +                    
   3.175 +                const char* _ver_12_startstr =                     
   3.176 +                    "update identity\n"
   3.177 +                    "    set pEp_version_major = ";
   3.178 +                const char* _ver_12_midstr = ",\n"
   3.179 +                    "        pEp_version_minor = ";
   3.180 +                const char* _ver_12_endstr =     
   3.181 +                    "\n"
   3.182 +                    "    where identity.is_own = 1;\n";
   3.183 +                    
   3.184 +                size_t new_stringlen = strlen(_ver_12_startstr) + major_len +
   3.185 +                                       strlen(_ver_12_midstr) + minor_len +
   3.186 +                                       strlen(_ver_12_endstr);
   3.187 +                                       
   3.188 +                char* _ver_12_stmt = calloc(new_stringlen + 1, 1);
   3.189 +                snprintf(_ver_12_stmt, new_stringlen + 1, "%s%s%s%s%s",
   3.190 +                         _ver_12_startstr, major_buf, _ver_12_midstr, minor_buf, _ver_12_endstr);
   3.191 +                
   3.192 +                int_result = sqlite3_exec(
   3.193 +                    _session->db,
   3.194 +                    _ver_12_stmt,
   3.195 +                    NULL,
   3.196 +                    NULL,
   3.197 +                    NULL
   3.198 +                );
   3.199 +                free(_ver_12_stmt);
   3.200 +                if (status != PEP_STATUS_OK)
   3.201 +                    return status;                      
   3.202              }
   3.203          }        
   3.204          else { 
   3.205 @@ -1651,6 +1722,11 @@
   3.206              (int)strlen(sql_set_pEp_version), &_session->set_pEp_version,
   3.207              NULL);
   3.208      assert(int_result == SQLITE_OK);
   3.209 +    
   3.210 +    int_result = sqlite3_prepare_v2(_session->db, sql_upgrade_pEp_version_by_user_id,
   3.211 +            (int)strlen(sql_upgrade_pEp_version_by_user_id), &_session->upgrade_pEp_version_by_user_id,
   3.212 +            NULL);
   3.213 +    assert(int_result == SQLITE_OK);
   3.214  
   3.215      int_result = sqlite3_prepare_v2(_session->db, sql_set_trust,
   3.216              (int)strlen(sql_set_trust), &_session->set_trust, NULL);
   3.217 @@ -1892,6 +1968,8 @@
   3.218                  sqlite3_finalize(session->delete_person);                
   3.219              if (session->set_as_pEp_user)
   3.220                  sqlite3_finalize(session->set_as_pEp_user);
   3.221 +            if (session->upgrade_pEp_version_by_user_id)
   3.222 +                sqlite3_finalize(session->upgrade_pEp_version_by_user_id);
   3.223              if (session->is_pEp_user)
   3.224                  sqlite3_finalize(session->is_pEp_user);
   3.225              if (session->exists_person)
   3.226 @@ -1920,6 +1998,8 @@
   3.227                  sqlite3_finalize(session->set_identity_flags);
   3.228              if (session->unset_identity_flags)
   3.229                  sqlite3_finalize(session->unset_identity_flags);
   3.230 +            if (session->set_pEp_version)
   3.231 +                sqlite3_finalize(session->set_pEp_version);                
   3.232              if (session->exists_trust_entry)
   3.233                  sqlite3_finalize(session->exists_trust_entry);                                
   3.234              if (session->set_trust)
   3.235 @@ -2482,6 +2562,10 @@
   3.236              sqlite3_column_int(session->get_identity, 4);
   3.237          _identity->me = (unsigned int)
   3.238              sqlite3_column_int(session->get_identity, 5);
   3.239 +        _identity->major_ver =
   3.240 +            sqlite3_column_int(session->get_identity, 6);
   3.241 +        _identity->minor_ver =
   3.242 +            sqlite3_column_int(session->get_identity, 7);
   3.243      
   3.244          *identity = _identity;
   3.245          break;
   3.246 @@ -2556,6 +2640,10 @@
   3.247              sqlite3_column_int(session->get_identities_by_userid, 5);
   3.248          ident->me = (unsigned int)
   3.249              sqlite3_column_int(session->get_identities_by_userid, 6);
   3.250 +        ident->major_ver =
   3.251 +            sqlite3_column_int(session->get_identities_by_userid, 6);
   3.252 +        ident->minor_ver =
   3.253 +            sqlite3_column_int(session->get_identities_by_userid, 7);
   3.254      
   3.255          identity_list_add(*identities, ident);
   3.256          ident = NULL;
   3.257 @@ -2622,6 +2710,10 @@
   3.258              sqlite3_column_int(session->get_identities_by_main_key_id, 5);
   3.259          ident->me = (unsigned int)
   3.260              sqlite3_column_int(session->get_identities_by_main_key_id, 6);
   3.261 +        ident->major_ver =
   3.262 +            sqlite3_column_int(session->get_identities_by_main_key_id, 7);
   3.263 +        ident->minor_ver =
   3.264 +            sqlite3_column_int(session->get_identities_by_main_key_id, 8);
   3.265      
   3.266          identity_list_add(*identities, ident);
   3.267          ident = NULL;
   3.268 @@ -2692,6 +2784,10 @@
   3.269              sqlite3_column_int(session->get_identity_without_trust_check, 3);
   3.270          _identity->me = (unsigned int)
   3.271              sqlite3_column_int(session->get_identity_without_trust_check, 4);
   3.272 +        _identity->major_ver =
   3.273 +            sqlite3_column_int(session->get_identity_without_trust_check, 6);
   3.274 +        _identity->minor_ver =
   3.275 +            sqlite3_column_int(session->get_identity_without_trust_check, 7);
   3.276      
   3.277          *identity = _identity;
   3.278          break;
   3.279 @@ -2757,6 +2853,10 @@
   3.280              sqlite3_column_int(session->get_identities_by_address, 4);
   3.281          ident->me = (unsigned int)
   3.282              sqlite3_column_int(session->get_identities_by_address, 5);
   3.283 +        ident->major_ver =
   3.284 +            sqlite3_column_int(session->get_identities_by_address, 6);
   3.285 +        ident->minor_ver =
   3.286 +            sqlite3_column_int(session->get_identities_by_address, 7);
   3.287      
   3.288          if (ident_list)
   3.289              identity_list_add(ident_list, ident);
   3.290 @@ -2913,6 +3013,9 @@
   3.291              SQLITE_STATIC);
   3.292      sqlite3_bind_int(set_or_update, 4, identity->flags);
   3.293      sqlite3_bind_int(set_or_update, 5, identity->me);
   3.294 +    sqlite3_bind_int(set_or_update, 6, identity->major_ver);
   3.295 +    sqlite3_bind_int(set_or_update, 7, identity->minor_ver);
   3.296 +        
   3.297      int result = sqlite3_step(set_or_update);
   3.298      sqlite3_reset(set_or_update);
   3.299      if (result != SQLITE_DONE)
   3.300 @@ -3117,7 +3220,9 @@
   3.301      if (result != SQLITE_DONE)
   3.302          return PEP_CANNOT_SET_TRUST;
   3.303  
   3.304 -    return PEP_STATUS_OK;
   3.305 +    PEP_STATUS status = upgrade_pEp_version_by_user_id(session, user, 2, 0);
   3.306 +    
   3.307 +    return status;
   3.308  }
   3.309  
   3.310  
   3.311 @@ -3159,16 +3264,17 @@
   3.312  }
   3.313  
   3.314  // This ONLY sets the version flag. Must be called outside of a transaction.
   3.315 -PEP_STATUS set_pEp_version(PEP_SESSION session, pEp_identity* ident, float new_pEp_version) {
   3.316 +PEP_STATUS set_pEp_version(PEP_SESSION session, pEp_identity* ident, unsigned int new_ver_major, unsigned int new_ver_minor) {
   3.317      assert(session);
   3.318      assert(!EMPTYSTR(ident->user_id));
   3.319      assert(!EMPTYSTR(ident->address));
   3.320      
   3.321      sqlite3_reset(session->set_pEp_version);
   3.322 -    sqlite3_bind_double(session->set_pEp_version, 1, new_pEp_version);
   3.323 -    sqlite3_bind_text(session->set_pEp_version, 2, ident->address, -1,
   3.324 +    sqlite3_bind_double(session->set_pEp_version, 1, new_ver_major);
   3.325 +    sqlite3_bind_double(session->set_pEp_version, 2, new_ver_minor);    
   3.326 +    sqlite3_bind_text(session->set_pEp_version, 3, ident->address, -1,
   3.327              SQLITE_STATIC);
   3.328 -    sqlite3_bind_text(session->set_pEp_version, 3, ident->user_id, -1,
   3.329 +    sqlite3_bind_text(session->set_pEp_version, 4, ident->user_id, -1,
   3.330              SQLITE_STATIC);
   3.331      
   3.332      int result = sqlite3_step(session->set_pEp_version);
   3.333 @@ -3180,6 +3286,30 @@
   3.334      return PEP_STATUS_OK;
   3.335  }
   3.336  
   3.337 +// Generally ONLY called by set_as_pEp_user, and ONLY from < 2.0 to 2.0.
   3.338 +PEP_STATUS upgrade_pEp_version_by_user_id(PEP_SESSION session, 
   3.339 +        pEp_identity* ident, 
   3.340 +        unsigned int new_ver_major,
   3.341 +        unsigned int new_ver_minor
   3.342 +    ) 
   3.343 +{
   3.344 +    assert(session);
   3.345 +    assert(!EMPTYSTR(ident->user_id));
   3.346 +    
   3.347 +    sqlite3_reset(session->upgrade_pEp_version_by_user_id);
   3.348 +    sqlite3_bind_int(session->upgrade_pEp_version_by_user_id, 1, new_ver_major);
   3.349 +    sqlite3_bind_int(session->upgrade_pEp_version_by_user_id, 2, new_ver_minor);    
   3.350 +    sqlite3_bind_text(session->upgrade_pEp_version_by_user_id, 3, ident->user_id, -1,
   3.351 +            SQLITE_STATIC);
   3.352 +    
   3.353 +    int result = sqlite3_step(session->upgrade_pEp_version_by_user_id);
   3.354 +    sqlite3_reset(session->upgrade_pEp_version_by_user_id);
   3.355 +        
   3.356 +    if (result != SQLITE_DONE)
   3.357 +        return PEP_CANNOT_SET_PEP_VERSION;
   3.358 +    
   3.359 +    return PEP_STATUS_OK;    
   3.360 +}
   3.361  
   3.362  PEP_STATUS exists_person(PEP_SESSION session, pEp_identity* identity,
   3.363                           bool* exists) {            
     4.1 --- a/src/pEpEngine.h	Sun May 19 15:13:55 2019 +0200
     4.2 +++ b/src/pEpEngine.h	Mon May 20 07:33:05 2019 +0200
     4.3 @@ -632,7 +632,8 @@
     4.4      char lang[3];               // language of conversation
     4.5                                  // ISO 639-1 ALPHA-2, last byte is 0
     4.6      bool me;                    // if this is the local user herself/himself
     4.7 -    float _pEp_version;         // highest version of pEp message received, if any
     4.8 +    int major_ver;              // highest version of pEp message received, if any
     4.9 +    int minor_ver;              // highest version of pEp message received, if any
    4.10      identity_flags_t flags;     // identity_flag1 | identity_flag2 | ...
    4.11  } pEp_identity;
    4.12  
    4.13 @@ -1386,8 +1387,15 @@
    4.14  
    4.15  PEP_STATUS set_pgp_keypair(PEP_SESSION session, const char* fpr);
    4.16  
    4.17 -PEP_STATUS set_pEp_version(PEP_SESSION session, pEp_identity* ident, float new_pEp_version);
    4.18 +PEP_STATUS set_pEp_version(PEP_SESSION session, pEp_identity* ident, unsigned int new_ver_major, unsigned int new_ver_minor);
    4.19  
    4.20 +// Generally ONLY called by set_as_pEp_user, and ONLY from < 2.0 to 2.0.
    4.21 +PEP_STATUS upgrade_pEp_version_by_user_id(PEP_SESSION session, 
    4.22 +        pEp_identity* ident, 
    4.23 +        unsigned int new_ver_major,
    4.24 +        unsigned int new_ver_minor
    4.25 +    );
    4.26 +     
    4.27  // exposed for testing
    4.28  PEP_STATUS set_person(PEP_SESSION session, pEp_identity* identity,
    4.29                        bool guard_transaction);
     5.1 --- a/src/pEp_internal.h	Sun May 19 15:13:55 2019 +0200
     5.2 +++ b/src/pEp_internal.h	Mon May 20 07:33:05 2019 +0200
     5.3 @@ -181,6 +181,7 @@
     5.4      sqlite3_stmt *exists_person;    
     5.5      sqlite3_stmt *set_as_pEp_user;
     5.6      sqlite3_stmt *is_pEp_user;
     5.7 +    sqlite3_stmt *upgrade_pEp_version_by_user_id;
     5.8      sqlite3_stmt *add_into_social_graph;
     5.9      sqlite3_stmt *get_own_address_binding_from_contact;
    5.10      sqlite3_stmt *set_revoke_contact_as_notified;
    5.11 @@ -457,6 +458,68 @@
    5.12      return retval;
    5.13  }
    5.14  
    5.15 +static inline float pEp_version_numeric(const char* version_str) {
    5.16 +    float retval = 0;    
    5.17 +        
    5.18 +    if (!version_str || sscanf(version_str, "%f", &retval) != 1)
    5.19 +        return 0;
    5.20 +        
    5.21 +    return retval;    
    5.22 +}
    5.23 +
    5.24 +static inline void pEp_version_major_minor(const char* version_str, unsigned int* major, unsigned int* minor) {
    5.25 +    if (!major || !minor)
    5.26 +        return;
    5.27 +                
    5.28 +    if (!version_str || sscanf(version_str, "%u.%u", major, minor) != 2) {
    5.29 +        *major = 0;
    5.30 +        *minor = 0;
    5.31 +    }
    5.32 +        
    5.33 +    return;    
    5.34 +}
    5.35 +
    5.36 +static inline int compare_versions(unsigned int first_maj, unsigned int first_min,
    5.37 +                                   unsigned int second_maj, unsigned int second_min) {
    5.38 +    if (first_maj > second_maj)
    5.39 +        return 1;
    5.40 +    if (first_maj < second_maj)
    5.41 +        return -1;
    5.42 +    if (first_min > second_min)
    5.43 +        return 1;
    5.44 +    if (first_min < second_min)
    5.45 +        return -1;
    5.46 +    return 0;    
    5.47 +}
    5.48 +
    5.49 +static inline void set_min_version(unsigned int first_maj, unsigned int first_minor,
    5.50 +                                   unsigned int second_maj, unsigned int second_minor,
    5.51 +                                   unsigned int* result_maj, unsigned int* result_minor) {
    5.52 +    int result = compare_versions(first_maj, first_minor, second_maj, second_minor);
    5.53 +    if (result < 0) {
    5.54 +        *result_maj = first_maj;
    5.55 +        *result_minor = first_minor;
    5.56 +    }
    5.57 +    else {
    5.58 +        *result_maj = second_maj;
    5.59 +        *result_minor = second_minor;
    5.60 +    }    
    5.61 +}
    5.62 +
    5.63 +static inline void set_max_version(unsigned int first_maj, unsigned int first_minor,
    5.64 +                                   unsigned int second_maj, unsigned int second_minor,
    5.65 +                                   unsigned int* result_maj, unsigned int* result_minor) {
    5.66 +    int result = compare_versions(first_maj, first_minor, second_maj, second_minor);
    5.67 +    if (result > 0) {
    5.68 +        *result_maj = first_maj;
    5.69 +        *result_minor = first_minor;
    5.70 +    }
    5.71 +    else {
    5.72 +        *result_maj = second_maj;
    5.73 +        *result_minor = second_minor;
    5.74 +    }    
    5.75 +}
    5.76 +
    5.77  #ifndef EMPTYSTR
    5.78  #define EMPTYSTR(STR) ((STR) == NULL || (STR)[0] == '\0')
    5.79  #endif
    5.80 @@ -472,7 +535,6 @@
    5.81  #define _MAX(A, B) ((B) > (A) ? (B) : (A))
    5.82  #endif
    5.83  
    5.84 -
    5.85  // These are globals used in generating message IDs and should only be
    5.86  // computed once, as they're either really constants or OS-dependent
    5.87  
     6.1 --- a/src/stringpair.c	Sun May 19 15:13:55 2019 +0200
     6.2 +++ b/src/stringpair.c	Mon May 20 07:33:05 2019 +0200
     6.3 @@ -194,6 +194,41 @@
     6.4      }
     6.5  }
     6.6  
     6.7 +// ONLY DELETES ONE.
     6.8 +DYNAMIC_API stringpair_list_t *stringpair_list_delete_by_key(
     6.9 +        stringpair_list_t *sp_list,
    6.10 +        const char *key
    6.11 +    )
    6.12 +{
    6.13 +    assert(sp_list);
    6.14 +    assert(key);
    6.15 +
    6.16 +    if (sp_list->value == NULL) {
    6.17 +        free_stringpair_list(sp_list);
    6.18 +        return NULL;
    6.19 +    }
    6.20 +
    6.21 +    if (key == NULL)
    6.22 +        return sp_list;
    6.23 +
    6.24 +    stringpair_list_t *_sl;
    6.25 +    stringpair_list_t *last = NULL;
    6.26 +    for (_sl = sp_list; _sl && _sl->value && _sl->value->key; _sl = _sl->next) {
    6.27 +        if (strcmp(_sl->value->key, key) == 0) {
    6.28 +            if (last == NULL)
    6.29 +                sp_list = sp_list->next;
    6.30 +            else
    6.31 +                last->next = _sl->next;
    6.32 +            _sl->next = NULL;
    6.33 +            free_stringpair_list(_sl);
    6.34 +            break;
    6.35 +        }
    6.36 +        last = _sl;
    6.37 +    }
    6.38 +    return sp_list;
    6.39 +}
    6.40 +
    6.41 +
    6.42  DYNAMIC_API stringpair_list_t *stringpair_list_find(
    6.43          stringpair_list_t *stringpair_list,
    6.44          const char *key
     7.1 --- a/src/stringpair.h	Sun May 19 15:13:55 2019 +0200
     7.2 +++ b/src/stringpair.h	Mon May 20 07:33:05 2019 +0200
     7.3 @@ -159,8 +159,13 @@
     7.4          const char *key
     7.5      );
     7.6  
     7.7 +// ONLY DELETES ONE.
     7.8 +DYNAMIC_API stringpair_list_t *stringpair_list_delete_by_key(
     7.9 +        stringpair_list_t *sp_list,
    7.10 +        const char *key
    7.11 +    );
    7.12 +
    7.13  
    7.14  #ifdef __cplusplus
    7.15  }
    7.16  #endif
    7.17 -