src/pEpEngine.c
branchkrista-local-7-Jan-2020
changeset 4312 1d1672a6dfb9
parent 4293 327480b07e3d
child 4315 afece9dedddf
     1.1 --- a/src/pEpEngine.c	Tue Jan 07 12:21:43 2020 +0100
     1.2 +++ b/src/pEpEngine.c	Tue Jan 07 22:35:39 2020 +0100
     1.3 @@ -503,19 +503,22 @@
     1.4      "select own_address from social_graph where own_userid = ?1 and contact_userid = ?2 ;";
     1.5  
     1.6  static const char *sql_set_revoke_contact_as_notified =
     1.7 -    "insert or replace into revocation_contact_list(fpr, contact_id) values (?1, ?2) ;";
     1.8 +    "insert or replace into revocation_contact_list(fpr, own_address, contact_id) values (?1, ?2, ?3) ;";
     1.9      
    1.10  static const char *sql_get_contacted_ids_from_revoke_fpr =
    1.11      "select * from revocation_contact_list where fpr = ?1 ;";
    1.12  
    1.13  static const char *sql_was_id_for_revoke_contacted = 
    1.14 -    "select count(*) from revocation_contact_list where fpr = ?1 and contact_id = ?2 ;";
    1.15 +    "select count(*) from revocation_contact_list where fpr = ?1 and own_address = ?2 and contact_id = ?3 ;";
    1.16 +
    1.17 +static const char *sql_has_id_contacted_address =
    1.18 +    "select count(*) from social_graph where own_address = ?1 and contact_userid = ?2 ;";
    1.19  
    1.20  // We only need user_id and address, since in the main usage, we'll call update_identity
    1.21  // on this anyway when sending out messages.
    1.22  static const char *sql_get_last_contacted =
    1.23      "select user_id, address from identity where datetime('now') < datetime(timestamp, '+14 days') ; ";
    1.24 -    
    1.25 +        
    1.26  static int user_version(void *_version, int count, char **text, char **name)
    1.27  {
    1.28      assert(_version);
    1.29 @@ -806,6 +809,112 @@
    1.30    fprintf(stderr, "(%d) %s\n", iErrCode, zMsg);
    1.31  }
    1.32  
    1.33 +static PEP_STATUS upgrade_revoc_contact_to_13(PEP_SESSION session) {
    1.34 +    // I HATE SQLITE.
    1.35 +    PEP_STATUS status = PEP_STATUS_OK;
    1.36 +    int int_result = 0;
    1.37 +
    1.38 +    // Ok, first we ADD the column so we can USE it.
    1.39 +    // We will end up propagating the "error" this first time 
    1.40 +    // (one-to-one revoke-replace relationships), but since key reset
    1.41 +    // hasn't been used in production, this is not a customer-facing 
    1.42 +    // issue.
    1.43 +    int_result = sqlite3_exec(
    1.44 +        session->db,
    1.45 +        "alter table revocation_contact_list\n"
    1.46 +        "   add column own_address text\n",
    1.47 +        NULL,
    1.48 +        NULL,
    1.49 +        NULL
    1.50 +    );
    1.51 +    assert(int_result == SQLITE_OK);
    1.52 +
    1.53 +    sqlite3_stmt* update_revoked_w_addr_stmt = NULL;
    1.54 +    const char* sql_query = "update revocation_contact_list set own_address = ?1 where fpr = ?2;";
    1.55 +    sqlite3_prepare_v2(session->db, sql_query, -1, &update_revoked_w_addr_stmt, NULL);
    1.56 +                
    1.57 +    // the best we can do here is search per address, since these
    1.58 +    // are no longer associated with an identity. For now, if we find 
    1.59 +    // something we can't add an address to, we'll delete the record.
    1.60 +    // this should not, in the current environment, ever happen, but 
    1.61 +    // since we need to make the address part of the primary key, it's 
    1.62 +    // the right thing to do. sqlite does support null fields in a primary 
    1.63 +    // key for a weird version compatibility reason, but that doesn't 
    1.64 +    // mean we should use it, and we should be *safe*, not relying 
    1.65 +    // on an implementation-specific quirk which might be sanely removed 
    1.66 +    // in a future sqlite version.
    1.67 +    stringpair_t* revoked_key_to_own_address = NULL;
    1.68 +    
    1.69 +    identity_list* id_list = NULL;
    1.70 +    status = own_identities_retrieve(session, &id_list);
    1.71 +
    1.72 +    if (!status || !id_list)
    1.73 +        return PEP_STATUS_OK; // it's empty AFAIK (FIXME)
    1.74 +    
    1.75 +    identity_list* curr_own = id_list;
    1.76 +    
    1.77 +    // Ok, go through and find any keys associated with this address  
    1.78 +    for ( ; curr_own && curr_own->ident; curr_own = curr_own->next) {
    1.79 +        if (EMPTYSTR(curr_own->ident->address)) // shouldn't happen
    1.80 +            continue;
    1.81 +        stringlist_t* keylist = NULL;
    1.82 +        status = find_keys(session, curr_own->ident->address, &keylist);
    1.83 +        stringlist_t* curr_key = keylist;
    1.84 +        for ( ; curr_key && curr_key->value; curr_key = curr_key->next) {
    1.85 +            if (EMPTYSTR(curr_key->value))
    1.86 +                continue;
    1.87 +                
    1.88 +            // We just do this lazily - if this isn't a revoked key, it 
    1.89 +            // won't do anything.
    1.90 +            sqlite3_bind_text(update_revoked_w_addr_stmt, 1, curr_own->ident->address, -1,
    1.91 +                              SQLITE_STATIC);
    1.92 +            sqlite3_bind_text(update_revoked_w_addr_stmt, 2, curr_key->value, -1,
    1.93 +                              SQLITE_STATIC);
    1.94 +                              
    1.95 +            int_result = sqlite3_step(update_revoked_w_addr_stmt);
    1.96 +            assert(int_result == SQLITE_DONE);                  
    1.97 +            sqlite3_reset(update_revoked_w_addr_stmt);                      
    1.98 +        }
    1.99 +    }  
   1.100 +    sqlite3_finalize(update_revoked_w_addr_stmt);
   1.101 +                
   1.102 +    int_result = sqlite3_exec(
   1.103 +        session->db,
   1.104 +        "delete from revocation_contact_list where own_address is NULL;\n"        
   1.105 +        "PRAGMA foreign_keys=off;\n"
   1.106 +        "BEGIN TRANSACTION;\n"
   1.107 +        "create table if not exists _revocation_contact_list_new (\n"
   1.108 +        "   fpr text not null references pgp_keypair (fpr)\n"
   1.109 +        "       on delete cascade,\n"
   1.110 +        "   own_address text,\n"
   1.111 +        "   contact_id text not null references person (id)\n"
   1.112 +        "       on delete cascade on update cascade,\n"
   1.113 +        "   timestamp integer default (datetime('now')),\n"
   1.114 +        "   PRIMARY KEY(fpr, own_address, contact_id)\n"
   1.115 +        ");\n"
   1.116 +        "INSERT INTO _revocation_contact_list_new (fpr, "
   1.117 +        "                                          own_address, "
   1.118 +        "                                          contact_id) "
   1.119 +        "   SELECT revocation_contact_list.fpr, "
   1.120 +        "          revocation_contact_list.own_address, "
   1.121 +        "          revocation_contact_list.contact_id "
   1.122 +        "   FROM revocation_contact_list "
   1.123 +        "   WHERE 1;\n"
   1.124 +        "DROP TABLE revocation_contact_list;\n"
   1.125 +        "ALTER TABLE _revocation_contact_list_new RENAME TO revocation_contact_list;\n"
   1.126 +        "COMMIT;\n"
   1.127 +        "\n"
   1.128 +        "PRAGMA foreign_keys=on;\n"
   1.129 +        ,
   1.130 +        NULL,
   1.131 +        NULL,
   1.132 +        NULL
   1.133 +    );
   1.134 +    assert(int_result == SQLITE_OK);    
   1.135 +    
   1.136 +    return status;
   1.137 +}
   1.138 +
   1.139  #ifdef USE_GPG
   1.140  PEP_STATUS pgp_import_ultimately_trusted_keypairs(PEP_SESSION session);
   1.141  #endif // USE_GPG
   1.142 @@ -934,7 +1043,7 @@
   1.143      sqlite3_busy_timeout(_session->system_db, 1000);
   1.144  
   1.145  // increment this when patching DDL
   1.146 -#define _DDL_USER_VERSION "12"
   1.147 +#define _DDL_USER_VERSION "13"
   1.148  
   1.149      if (in_first) {
   1.150  
   1.151 @@ -1052,10 +1161,11 @@
   1.152                  "create table if not exists revocation_contact_list (\n"
   1.153                  "   fpr text not null references pgp_keypair (fpr)\n"
   1.154                  "       on delete cascade,\n"
   1.155 +                "   own_address text,\n"
   1.156                  "   contact_id text not null references person (id)\n"
   1.157                  "       on delete cascade on update cascade,\n"
   1.158                  "   timestamp integer default (datetime('now')),\n"
   1.159 -                "   PRIMARY KEY(fpr, contact_id)\n"
   1.160 +                "   PRIMARY KEY(fpr, own_address, contact_id)\n"
   1.161                  ");\n"
   1.162                  ,
   1.163              NULL,
   1.164 @@ -1103,7 +1213,10 @@
   1.165          // Sometimes the user_version wasn't set correctly. 
   1.166          if (version == 1) {
   1.167              bool version_changed = true;
   1.168 -            if (table_contains_column(_session, "identity", "pEp_version_major")) {
   1.169 +            if (table_contains_column(_session, "revocation_contact_list", "own_address")) {
   1.170 +                version = 13;
   1.171 +            }
   1.172 +            else if (table_contains_column(_session, "identity", "pEp_version_major")) {
   1.173                  version = 12;
   1.174              }
   1.175              else if (db_contains_table(_session, "social_graph") > 0) {
   1.176 @@ -1555,6 +1668,12 @@
   1.177                  if (status != PEP_STATUS_OK)
   1.178                      return status;                      
   1.179              }
   1.180 +            if (version < 13) {
   1.181 +                status = upgrade_revoc_contact_to_13(_session);
   1.182 +                assert(status == PEP_STATUS_OK);
   1.183 +                if (status != PEP_STATUS_OK)
   1.184 +                    return status;
   1.185 +            }        
   1.186          }        
   1.187          else { 
   1.188              // Version from DB was 0, it means this is initial setup.
   1.189 @@ -1715,6 +1834,12 @@
   1.190      assert(int_result == SQLITE_OK);
   1.191  
   1.192      int_result = sqlite3_prepare_v2(_session->db, 
   1.193 +            sql_has_id_contacted_address,
   1.194 +            (int)strlen(sql_has_id_contacted_address), 
   1.195 +            &_session->has_id_contacted_address, NULL);
   1.196 +    assert(int_result == SQLITE_OK);
   1.197 +
   1.198 +    int_result = sqlite3_prepare_v2(_session->db, 
   1.199              sql_get_last_contacted,
   1.200              (int)strlen(sql_get_last_contacted), 
   1.201              &_session->get_last_contacted, NULL);
   1.202 @@ -2022,7 +2147,9 @@
   1.203              if (session->get_contacted_ids_from_revoke_fpr)
   1.204                  sqlite3_finalize(session->get_contacted_ids_from_revoke_fpr);  
   1.205              if (session->was_id_for_revoke_contacted)
   1.206 -                sqlite3_finalize(session->was_id_for_revoke_contacted);   
   1.207 +                sqlite3_finalize(session->was_id_for_revoke_contacted);  
   1.208 +            if (session->has_id_contacted_address)
   1.209 +                sqlite3_finalize(session->has_id_contacted_address);
   1.210              if (session->get_last_contacted)
   1.211                  sqlite3_finalize(session->get_last_contacted);                                       
   1.212              if (session->set_pgp_keypair)
   1.213 @@ -3567,6 +3694,43 @@
   1.214      return PEP_STATUS_OK;
   1.215  }
   1.216  
   1.217 +// FIXME: should be more like is there a communications relationship,
   1.218 +// since this could be either way
   1.219 +PEP_STATUS has_partner_contacted_address(PEP_SESSION session, const char* partner_id,
   1.220 +                                         const char* own_address, bool* was_contacted) {            
   1.221 +    
   1.222 +    assert(session);
   1.223 +    assert(!EMPTYSTR(partner_id));
   1.224 +    assert(!EMPTYSTR(own_address));    
   1.225 +    assert(was_contacted);
   1.226 +    
   1.227 +    if (!session || !was_contacted || EMPTYSTR(partner_id) || EMPTYSTR(own_address))
   1.228 +        return PEP_ILLEGAL_VALUE;
   1.229 +    
   1.230 +    *was_contacted = false;
   1.231 +
   1.232 +    PEP_STATUS status = PEP_STATUS_OK;
   1.233 +    
   1.234 +    sqlite3_reset(session->has_id_contacted_address);
   1.235 +    sqlite3_bind_text(session->has_id_contacted_address, 1, partner_id, -1,
   1.236 +            SQLITE_STATIC);
   1.237 +    int result = sqlite3_step(session->has_id_contacted_address);
   1.238 +    switch (result) {
   1.239 +        case SQLITE_ROW: {
   1.240 +            // yeah yeah, I know, we could be lazy here, but it looks bad.
   1.241 +            *was_contacted = (sqlite3_column_int(session->has_id_contacted_address, 0) != 0);
   1.242 +            status = PEP_STATUS_OK;
   1.243 +            break;
   1.244 +        }
   1.245 +        default:
   1.246 +            status = PEP_UNKNOWN_DB_ERROR;
   1.247 +    }
   1.248 +    sqlite3_reset(session->has_id_contacted_address);
   1.249 +            
   1.250 +    return status;
   1.251 +}
   1.252 +
   1.253 +// FIXME: problematic - can be multiple and this now matters
   1.254  PEP_STATUS get_own_ident_for_contact_id(PEP_SESSION session,
   1.255                                            const pEp_identity* contact,
   1.256                                            pEp_identity** own_ident) {