CpEpEngine.cpp
author Thomas
Wed, 02 May 2018 09:46:48 +0200
branchCOM-87
changeset 287 99abd7d70c1e
parent 285 9a19b5dbb271
child 289 71e95ca19464
permissions -rw-r--r--
Rename flags
vb@282
     1
// CpEpEngine.cpp : Implementation of CpEpEngine
markus@280
     2
vb@282
     3
#include "stdafx.h"
vb@282
     4
#include "CpEpEngine.h"
vb@282
     5
#include <mutex>
Thomas@285
     6
#include "GateKeeper.h"
vb@282
     7
vb@282
     8
using namespace std;
vb@282
     9
using namespace pEp::utility;
vb@282
    10
vb@282
    11
// CpEpEngine
vb@282
    12
vb@282
    13
// the init_mutex protects our initialization and destruction
vb@282
    14
// against a running keysync thread, and it ensures that the
vb@282
    15
// keysync thread actually has finished before we're destructed.
vb@282
    16
std::mutex CpEpEngine::init_mutex;
vb@282
    17
vb@282
    18
STDMETHODIMP CpEpEngine::InterfaceSupportsErrorInfo(REFIID riid)
vb@282
    19
{
vb@282
    20
    static const IID* const arr[] =
vb@282
    21
    {
vb@282
    22
        &IID_IpEpEngine,
vb@282
    23
    };
vb@282
    24
vb@282
    25
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
vb@282
    26
    {
vb@282
    27
        if (InlineIsEqualGUID(*arr[i], riid))
vb@282
    28
            return S_OK;
vb@282
    29
    }
vb@282
    30
    return S_FALSE;
vb@282
    31
}
vb@282
    32
vb@282
    33
// The second argument is optional, and currently supports PEP_STATUS.
vb@282
    34
#define FAIL(msg, ...) error(msg, __VA_ARGS__)
vb@282
    35
vb@282
    36
STDMETHODIMP CpEpEngine::VerboseLogging(VARIANT_BOOL enable)
vb@282
    37
{
vb@282
    38
    verbose_mode = enable != VARIANT_FALSE;
vb@282
    39
    return S_OK;
vb@282
    40
}
vb@282
    41
vb@282
    42
STDMETHODIMP CpEpEngine::PassiveMode(VARIANT_BOOL enable)
vb@282
    43
{
vb@282
    44
    ::config_passive_mode(get_session(), enable != VARIANT_FALSE);
vb@282
    45
    return S_OK;
vb@282
    46
}
vb@282
    47
vb@282
    48
STDMETHODIMP CpEpEngine::UnencryptedSubject(VARIANT_BOOL enable)
vb@282
    49
{
vb@282
    50
    ::config_unencrypted_subject(get_session(), enable != VARIANT_FALSE);
vb@282
    51
    return S_OK;
vb@282
    52
}
vb@282
    53
vb@282
    54
STDMETHODIMP CpEpEngine::ExportKey(BSTR fpr, BSTR * keyData)
vb@282
    55
{
vb@282
    56
    assert(fpr);
vb@282
    57
    assert(keyData);
vb@282
    58
vb@282
    59
    if (!(fpr && keyData))
vb@282
    60
        return E_INVALIDARG;
vb@282
    61
vb@282
    62
    string _fpr = utf8_string(fpr);
vb@282
    63
    char *_key_data = NULL;
vb@282
    64
    size_t _size = 0;
vb@282
    65
vb@282
    66
    ::PEP_STATUS status = ::export_key(get_session(), _fpr.c_str(), &_key_data, &_size);
vb@282
    67
    assert(status != ::PEP_OUT_OF_MEMORY);
vb@282
    68
    if (status == ::PEP_OUT_OF_MEMORY)
vb@282
    69
        return E_OUTOFMEMORY;
vb@282
    70
vb@282
    71
    if (status != ::PEP_STATUS_OK)
vb@282
    72
        return FAIL(L"export_key", status);
vb@282
    73
vb@282
    74
    _bstr_t b_key_data(utf16_string(_key_data).c_str());
vb@282
    75
    pEp_free(_key_data);
vb@282
    76
    *keyData = b_key_data.Detach();
vb@282
    77
vb@282
    78
    return S_OK;
vb@282
    79
}
vb@282
    80
vb@282
    81
STDMETHODIMP CpEpEngine::Log(BSTR title, BSTR entity, BSTR description, BSTR comment)
vb@282
    82
{
vb@282
    83
    string _title;
vb@282
    84
    string _entity;
vb@282
    85
    string _description;
vb@282
    86
    string _comment;
vb@282
    87
    HRESULT result = S_OK;
vb@282
    88
vb@282
    89
    assert(title);
vb@282
    90
    if (title)
vb@282
    91
        _title = utf8_string(title);
vb@282
    92
    else
vb@282
    93
        result = E_INVALIDARG;
vb@282
    94
vb@282
    95
    assert(entity);
vb@282
    96
    if (entity)
vb@282
    97
        _entity = utf8_string(entity);
vb@282
    98
    else
vb@282
    99
        result = E_INVALIDARG;
vb@282
   100
vb@282
   101
    if (description)
vb@282
   102
        _description = utf8_string(description);
vb@282
   103
vb@282
   104
    if (comment)
vb@282
   105
        _comment = utf8_string(comment);
vb@282
   106
vb@282
   107
    if (result != S_OK)
vb@282
   108
        return result;
vb@282
   109
vb@282
   110
    PEP_STATUS _status = ::log_event(get_session(), _title.c_str(), _entity.c_str(), _description.c_str(), _comment.c_str());
vb@282
   111
    assert(_status == PEP_STATUS_OK);
vb@282
   112
    if (_status != PEP_STATUS_OK)
vb@282
   113
        return FAIL(L"log_event", _status);
vb@282
   114
    else
vb@282
   115
        return S_OK;
vb@282
   116
}
vb@282
   117
vb@282
   118
STDMETHODIMP CpEpEngine::Trustwords(BSTR fpr, BSTR lang, LONG max_words, BSTR * words)
vb@282
   119
{
vb@282
   120
    assert(fpr);
vb@282
   121
    assert(max_words >= 0);
vb@282
   122
    assert(words);
vb@282
   123
vb@282
   124
    HRESULT result = S_OK;
vb@282
   125
vb@282
   126
    string _fpr;
vb@282
   127
    if (fpr)
vb@282
   128
        _fpr = utf8_string(fpr);
vb@282
   129
    else
vb@282
   130
        result = E_INVALIDARG;
vb@282
   131
vb@282
   132
    string _lang;
vb@282
   133
    if (lang) {
vb@282
   134
        _lang = utf8_string(lang);
vb@282
   135
        if (_lang.length()) {
vb@282
   136
            if (_lang.length() != 2)
vb@282
   137
                result = E_INVALIDARG;
vb@282
   138
        }
vb@282
   139
        else
vb@282
   140
            _lang = "en";
vb@282
   141
    }
vb@282
   142
    else
vb@282
   143
        _lang = "en";
vb@282
   144
vb@282
   145
    if (max_words < 0)
vb@282
   146
        result = E_INVALIDARG;
vb@282
   147
vb@282
   148
    if (words == NULL)
vb@282
   149
        result = E_INVALIDARG;
vb@282
   150
vb@282
   151
    if (result != S_OK)
vb@282
   152
        return result;
vb@282
   153
vb@282
   154
    char *_words = NULL;
vb@282
   155
    size_t _wsize = 0;
vb@282
   156
vb@282
   157
    PEP_STATUS status = ::trustwords(get_session(), _fpr.c_str(), _lang.c_str(), &_words, &_wsize, max_words);
vb@282
   158
    assert(status != PEP_OUT_OF_MEMORY);
vb@282
   159
    if (status == PEP_OUT_OF_MEMORY)
vb@282
   160
        return E_OUTOFMEMORY;
vb@282
   161
vb@282
   162
    if (_words == NULL) {
vb@282
   163
        *words = NULL;
vb@282
   164
        return FAIL(L"Trustwords: _words == NULL", status);
vb@282
   165
    }
vb@282
   166
    else {
vb@282
   167
        *words = utf16_bstr(_words);
vb@282
   168
        pEp_free(_words);
vb@282
   169
        return S_OK;
vb@282
   170
    }
vb@282
   171
}
vb@282
   172
vb@282
   173
STDMETHODIMP CpEpEngine::GetTrustwords(struct pEpIdentity *id1, struct pEpIdentity *id2, BSTR lang, VARIANT_BOOL full, BSTR *words)
vb@282
   174
{
vb@282
   175
    assert(id1);
vb@282
   176
    assert(id2);
vb@282
   177
    assert(words);
vb@282
   178
vb@282
   179
    if (!(id1 && id2 && words))
vb@282
   180
    {
vb@282
   181
        return E_INVALIDARG;
vb@282
   182
    }
vb@282
   183
vb@282
   184
    HRESULT result = S_OK;
vb@282
   185
vb@282
   186
    pEp_identity* _id1 = NULL;
vb@282
   187
    pEp_identity* _id2 = NULL;
vb@282
   188
    string _lang;
vb@282
   189
    *words = NULL;
vb@282
   190
vb@282
   191
    try {
vb@282
   192
        _id1 = new_identity(id1);
vb@282
   193
        _id2 = new_identity(id2);
vb@282
   194
vb@282
   195
        if (lang) {
vb@282
   196
            _lang = utf8_string(lang);
vb@282
   197
            if (_lang.length() == 0) {
vb@282
   198
                _lang = "en";
vb@282
   199
            }
vb@282
   200
            else if (_lang.length() != 2) {
vb@282
   201
                result = E_INVALIDARG;
vb@282
   202
            }
vb@282
   203
        }
vb@282
   204
        else {
vb@282
   205
            _lang = "en";
vb@282
   206
        }
vb@282
   207
    }
vb@282
   208
    catch (bad_alloc&) {
vb@282
   209
        result = E_OUTOFMEMORY;
vb@282
   210
    }
vb@282
   211
    catch (exception& ex) {
vb@282
   212
        result = FAIL(ex.what());
vb@282
   213
    }
vb@282
   214
vb@282
   215
    char* _words;
vb@282
   216
    size_t _size;
vb@282
   217
    if (result == S_OK) {
vb@282
   218
        auto status = ::get_trustwords(get_session(), _id1, _id2, _lang.c_str(), &_words, &_size, full != 0 /* convert variant bool to C bool */);
vb@282
   219
vb@282
   220
        if (status == PEP_OUT_OF_MEMORY) {
vb@282
   221
            result = E_OUTOFMEMORY;
vb@282
   222
        }
vb@282
   223
        else if (status == PEP_TRUSTWORD_NOT_FOUND) {
vb@282
   224
            result = FAIL(L"GetTrustwords: Trustword not found", status);
vb@282
   225
        }
vb@282
   226
        else if (!words) {
vb@282
   227
            result = FAIL(L"GetTrustwords: _words == NULL", status);
vb@282
   228
        }
vb@282
   229
        else {
vb@282
   230
            *words = utf16_bstr(_words);
vb@282
   231
            pEp_free(_words);
vb@282
   232
        }
vb@282
   233
    }
vb@282
   234
vb@282
   235
    free_identity(_id1);
vb@282
   236
    free_identity(_id2);
vb@282
   237
vb@282
   238
    return result;
vb@282
   239
}
vb@282
   240
vb@282
   241
STDMETHODIMP CpEpEngine::GetMessageTrustwords(
vb@282
   242
    /* [in] */ struct TextMessage *msg,
vb@282
   243
    /* [in] */ struct pEpIdentity *receivedBy,
vb@282
   244
    /* [in] */ SAFEARRAY *keylist,
vb@282
   245
    /* [defaultvalue][in] */ BSTR lang,
vb@282
   246
    /* [defaultvalue][in] */ VARIANT_BOOL full,
vb@282
   247
    /* [retval][out] */ BSTR *words) {
vb@282
   248
    assert(msg);
vb@282
   249
    assert(receivedBy);
vb@282
   250
    assert(words);
vb@282
   251
vb@282
   252
    if (!(msg && receivedBy && words))
vb@282
   253
    {
vb@282
   254
        return E_INVALIDARG;
vb@282
   255
    }
vb@282
   256
vb@282
   257
    HRESULT result = S_OK;
vb@282
   258
vb@282
   259
    pEp_identity * _received_by = NULL;
vb@282
   260
    ::message * _msg = NULL;
vb@282
   261
    ::stringlist_t *_keylist = NULL;
vb@282
   262
    string _lang;
vb@282
   263
    *words = NULL;
vb@282
   264
vb@282
   265
    try {
vb@282
   266
        _received_by = new_identity(receivedBy);
vb@282
   267
        _msg = text_message_to_C(msg);
vb@282
   268
vb@282
   269
        if (keylist) {
vb@282
   270
            _keylist = new_stringlist(keylist);
vb@282
   271
        }
vb@282
   272
vb@282
   273
        if (lang) {
vb@282
   274
            _lang = utf8_string(lang);
vb@282
   275
            if (_lang.length() == 0) {
vb@282
   276
                _lang = "en";
vb@282
   277
            }
vb@282
   278
            else if (_lang.length() != 2) {
vb@282
   279
                result = E_INVALIDARG;
vb@282
   280
            }
vb@282
   281
        }
vb@282
   282
        else {
vb@282
   283
            _lang = "en";
vb@282
   284
        }
vb@282
   285
    }
vb@282
   286
    catch (bad_alloc&) {
vb@282
   287
        result = E_OUTOFMEMORY;
vb@282
   288
    }
vb@282
   289
    catch (exception& ex) {
vb@282
   290
        result = FAIL(ex.what());
vb@282
   291
    }
vb@282
   292
vb@282
   293
    char* _words = NULL;
vb@282
   294
    if (result == S_OK) {
vb@282
   295
        auto status = ::get_message_trustwords(
vb@282
   296
            get_session(),
vb@282
   297
            _msg,
vb@282
   298
            _keylist,
vb@282
   299
            _received_by,
vb@282
   300
            _lang.c_str(),
vb@282
   301
            &_words,
vb@282
   302
            full != 0 /* convert variant bool to C bool */);
vb@282
   303
vb@282
   304
        if (status == PEP_OUT_OF_MEMORY) {
vb@282
   305
            result = E_OUTOFMEMORY;
vb@282
   306
        }
vb@282
   307
        else if (status == PEP_TRUSTWORD_NOT_FOUND) {
vb@282
   308
            result = FAIL(L"GetTrustwords: Trustword not found", status);
vb@282
   309
        }
vb@282
   310
        else if (!words) {
vb@282
   311
            result = FAIL(L"GetTrustwords: _words == NULL", status);
vb@282
   312
        }
vb@282
   313
        else {
vb@282
   314
            *words = utf16_bstr(_words);
vb@282
   315
        }
vb@282
   316
    }
vb@282
   317
vb@282
   318
    ::pEp_free(_words);
vb@282
   319
    ::free_message(_msg);
vb@282
   320
    ::free_stringlist(_keylist);
vb@282
   321
    ::free_identity(_received_by);
vb@282
   322
vb@282
   323
    return result;
vb@282
   324
}
vb@282
   325
vb@282
   326
STDMETHODIMP CpEpEngine::GetCrashdumpLog(LONG maxlines, BSTR * log)
vb@282
   327
{
vb@282
   328
    // COM-18: Currently, long == int on windows, so the check
vb@282
   329
    // for INT_MAX is not strictly necessary. However, the code
vb@282
   330
    // might get copy-pasted to other adapters in the future,
vb@282
   331
    // so safety first...
vb@282
   332
    assert(maxlines >= 0 && maxlines <= INT_MAX);
vb@282
   333
    assert(log);
vb@282
   334
vb@282
   335
    if (!(maxlines >= 0 && maxlines <= INT_MAX && log))
vb@282
   336
        return E_INVALIDARG;
vb@282
   337
vb@282
   338
    char *_log;
vb@282
   339
    PEP_STATUS status = ::get_crashdump_log(get_session(), (int)maxlines, &_log);
vb@282
   340
    assert(status == PEP_STATUS_OK);
vb@282
   341
    if (status == PEP_OUT_OF_MEMORY)
vb@282
   342
        return E_OUTOFMEMORY;
vb@282
   343
    if (status != PEP_STATUS_OK)
vb@282
   344
        return FAIL(L"GetCrashdumpLog", status);
vb@282
   345
    if (_log == NULL)
vb@282
   346
        return FAIL(L"GetCrashdumpLog: _log == NULL");
vb@282
   347
vb@282
   348
    *log = utf16_bstr(_log);
vb@282
   349
    pEp_free(_log);
vb@282
   350
    return S_OK;
vb@282
   351
}
vb@282
   352
vb@282
   353
STDMETHODIMP CpEpEngine::GetEngineVersion(BSTR * engine_version)
vb@282
   354
{
vb@282
   355
    assert(engine_version);
vb@282
   356
vb@282
   357
    if (!engine_version)
vb@282
   358
        return E_INVALIDARG;
vb@282
   359
vb@282
   360
    const char *_engine_version = ::get_engine_version();
vb@282
   361
vb@282
   362
    if (_engine_version == NULL)
vb@282
   363
        return FAIL(L"GetEngineVersion: _engine_version == NULL");
vb@282
   364
vb@282
   365
    *engine_version = utf16_bstr(_engine_version);
vb@282
   366
vb@282
   367
    return S_OK;
vb@282
   368
}
vb@282
   369
vb@282
   370
STDMETHODIMP CpEpEngine::GetLanguageList(BSTR * languages)
vb@282
   371
{
vb@282
   372
    assert(languages);
vb@282
   373
vb@282
   374
    if (!languages)
vb@282
   375
        return E_INVALIDARG;
vb@282
   376
vb@282
   377
    char *_languages;
vb@282
   378
    PEP_STATUS status = ::get_languagelist(get_session(), &_languages);
vb@282
   379
    assert(status == PEP_STATUS_OK);
vb@282
   380
    if (status == PEP_OUT_OF_MEMORY)
vb@282
   381
        return E_OUTOFMEMORY;
vb@282
   382
    if (status != PEP_STATUS_OK)
vb@282
   383
        return FAIL(L"GetLanguageList", status);
vb@282
   384
    if (_languages == NULL)
vb@282
   385
        return FAIL(L"GetLanguageList: _languages == NULL");
vb@282
   386
vb@282
   387
    *languages = utf16_bstr(_languages);
vb@282
   388
    pEp_free(_languages);
vb@282
   389
    return S_OK;
vb@282
   390
}
vb@282
   391
vb@282
   392
STDMETHODIMP CpEpEngine::SetIdentityFlags(struct pEpIdentity *identity, pEpIdentityFlags flags)
vb@282
   393
{
vb@282
   394
    assert(identity);
vb@282
   395
    if (!identity)
vb@282
   396
        return E_INVALIDARG;
vb@282
   397
vb@282
   398
    ::pEp_identity *_ident = nullptr;
vb@282
   399
vb@282
   400
    try {
vb@282
   401
        _ident = new_identity(identity);
vb@282
   402
        assert(_ident);
vb@282
   403
        if (_ident == NULL)
vb@282
   404
            return E_OUTOFMEMORY;
vb@282
   405
    }
vb@282
   406
    catch (bad_alloc&) {
vb@282
   407
        return E_OUTOFMEMORY;
vb@282
   408
    }
vb@282
   409
    catch (exception& ex) {
vb@282
   410
        return FAIL(ex.what());;
vb@282
   411
    }
vb@282
   412
vb@282
   413
    PEP_STATUS status = ::set_identity_flags(get_session(), _ident, (identity_flags_t)flags);
vb@282
   414
    ::free_identity(_ident);
vb@282
   415
    if (status != PEP_STATUS_OK)
vb@282
   416
        return FAIL(_T("SetIdentityFlags"), status);
vb@282
   417
vb@282
   418
    return S_OK;
vb@282
   419
}
vb@282
   420
vb@282
   421
STDMETHODIMP CpEpEngine::UnsetIdentityFlags(struct pEpIdentity *identity, pEpIdentityFlags flags)
vb@282
   422
{
vb@282
   423
    assert(identity);
vb@282
   424
    if (!identity)
vb@282
   425
        return E_INVALIDARG;
vb@282
   426
vb@282
   427
    ::pEp_identity *_ident = nullptr;
vb@282
   428
vb@282
   429
    try {
vb@282
   430
        _ident = new_identity(identity);
vb@282
   431
        assert(_ident);
vb@282
   432
        if (_ident == NULL)
vb@282
   433
            return E_OUTOFMEMORY;
vb@282
   434
    }
vb@282
   435
    catch (bad_alloc&) {
vb@282
   436
        return E_OUTOFMEMORY;
vb@282
   437
    }
vb@282
   438
    catch (exception& ex) {
vb@282
   439
        return FAIL(ex.what());;
vb@282
   440
    }
vb@282
   441
vb@282
   442
    PEP_STATUS status = ::unset_identity_flags(get_session(), _ident, (identity_flags_t)flags);
vb@282
   443
    ::free_identity(_ident);
vb@282
   444
    if (status != PEP_STATUS_OK)
vb@282
   445
        return FAIL(_T("UnsetIdentityFlags"), status);
vb@282
   446
vb@282
   447
    return S_OK;
vb@282
   448
}
vb@282
   449
vb@282
   450
STDMETHODIMP CpEpEngine::StartKeyserverLookup()
vb@282
   451
{
vb@282
   452
    if (identity_queue.load())
vb@282
   453
        return S_OK;
vb@282
   454
vb@282
   455
    identity_queue.store(new identity_queue_t());
vb@282
   456
    keymanagement_thread = new thread(::do_keymanagement, retrieve_next_identity, (void *)identity_queue.load());
vb@282
   457
vb@282
   458
    return S_OK;
vb@282
   459
}
vb@282
   460
vb@282
   461
STDMETHODIMP CpEpEngine::StopKeyserverLookup()
vb@282
   462
{
vb@282
   463
    if (identity_queue.load() == NULL)
vb@282
   464
        return S_OK;
vb@282
   465
vb@282
   466
    identity_queue_t *_iq = identity_queue.load();
vb@282
   467
    identity_queue.store(NULL);
vb@282
   468
vb@282
   469
    pEp_identity_cpp shutdown;
vb@282
   470
    _iq->push_front(shutdown);
vb@282
   471
vb@282
   472
    keymanagement_thread->join();
vb@282
   473
    delete keymanagement_thread;
vb@282
   474
    keymanagement_thread = NULL;
vb@282
   475
vb@282
   476
    delete _iq;
vb@282
   477
vb@282
   478
    return S_OK;
vb@282
   479
}
vb@282
   480
vb@282
   481
STDMETHODIMP CpEpEngine::Myself(struct pEpIdentity *ident, struct pEpIdentity *result)
vb@282
   482
{
vb@282
   483
    assert(ident);
vb@282
   484
    assert(result);
vb@282
   485
vb@282
   486
    if (!(ident && result))
vb@282
   487
        return E_INVALIDARG;
vb@282
   488
vb@282
   489
    ::pEp_identity *_ident = 0;
vb@282
   490
vb@282
   491
    try {
vb@282
   492
        _ident = new_identity(ident);
vb@282
   493
        assert(_ident);
vb@282
   494
        if (_ident == NULL)
vb@282
   495
            return E_OUTOFMEMORY;
vb@282
   496
    }
vb@282
   497
    catch (bad_alloc&) {
vb@282
   498
        return E_OUTOFMEMORY;
vb@282
   499
    }
vb@282
   500
    catch (exception& ex) {
vb@282
   501
        return FAIL(ex.what());;
vb@282
   502
    }
vb@282
   503
vb@282
   504
vb@282
   505
    // DEBUG CODE - REMOVE BEFORE RELEASE!
vb@282
   506
    // SyncHandshakeResult handshakeResult;
vb@282
   507
    //
vb@282
   508
    // HRESULT res = Fire_NotifyHandshake(ident, result, signal, &handshakeResult);
vb@282
   509
    // 
vb@282
   510
    // HRESULT res2 = Fire_TestEvent(15, _bstr_t( "hallo"));
vb@282
   511
vb@282
   512
    PEP_STATUS status = ::myself(get_session(), _ident);
vb@282
   513
vb@282
   514
    if (status == PEP_STATUS_OK) {
vb@282
   515
        assert(_ident->fpr);
vb@282
   516
        copy_identity(result, _ident);
vb@282
   517
        ::free_identity(_ident);
vb@282
   518
        return S_OK;
vb@282
   519
    }
vb@282
   520
    else {
vb@282
   521
        ::free_identity(_ident);
vb@282
   522
        if (status == PEP_OUT_OF_MEMORY)
vb@282
   523
            return E_OUTOFMEMORY;
vb@282
   524
        else
vb@282
   525
            return FAIL(L"myself", status);
vb@282
   526
    }
vb@282
   527
}
vb@282
   528
vb@282
   529
STDMETHODIMP CpEpEngine::UpdateIdentity(struct pEpIdentity *ident, struct pEpIdentity *result)
vb@282
   530
{
vb@282
   531
    assert(ident);
vb@282
   532
    assert(result);
vb@282
   533
vb@282
   534
    if (!(ident && result))
vb@282
   535
        return E_INVALIDARG;
vb@282
   536
vb@282
   537
    ::pEp_identity *_ident;
vb@282
   538
    try {
vb@282
   539
        _ident = new_identity(ident);
vb@282
   540
    }
vb@282
   541
    catch (bad_alloc&) {
vb@282
   542
        return E_OUTOFMEMORY;
vb@282
   543
    }
vb@282
   544
    catch (exception& ex) {
vb@282
   545
        return FAIL(ex.what());
vb@282
   546
    }
vb@282
   547
vb@282
   548
    assert(_ident);
vb@282
   549
    if (_ident == NULL)
vb@282
   550
        return E_OUTOFMEMORY;
vb@282
   551
vb@282
   552
    PEP_STATUS status = ::update_identity(get_session(), _ident);
vb@282
   553
vb@282
   554
    if (status == PEP_STATUS_OK) {
vb@282
   555
        copy_identity(result, _ident);
vb@282
   556
        ::free_identity(_ident);
vb@282
   557
        return S_OK;
vb@282
   558
    }
vb@282
   559
    else if (status == PEP_GET_KEY_FAILED || status == PEP_KEY_NOT_FOUND) {
vb@282
   560
        if (_ident->fpr) {
vb@282
   561
            pEp_free(_ident->fpr);
vb@282
   562
            _ident->fpr = NULL;
vb@282
   563
        }
vb@282
   564
        copy_identity(result, _ident);
vb@282
   565
        result->Fpr = NULL;
vb@282
   566
        ::free_identity(_ident);
vb@282
   567
        return S_OK;
vb@282
   568
    }
vb@282
   569
    else {
vb@282
   570
        ::free_identity(_ident);
vb@282
   571
        if (status == PEP_OUT_OF_MEMORY)
vb@282
   572
            return E_OUTOFMEMORY;
vb@282
   573
        else
vb@282
   574
            return FAIL(L"UpdateIdentity", status);
vb@282
   575
    }
vb@282
   576
}
vb@282
   577
vb@282
   578
STDMETHODIMP CpEpEngine::KeyMistrusted(struct pEpIdentity *ident)
vb@282
   579
{
vb@282
   580
    ::pEp_identity *_ident;
vb@282
   581
vb@282
   582
    assert(ident);
vb@282
   583
    if (!ident)
vb@282
   584
        return E_INVALIDARG;
vb@282
   585
vb@282
   586
    try {
vb@282
   587
        _ident = new_identity(ident);
vb@282
   588
    }
vb@282
   589
    catch (bad_alloc&) {
vb@282
   590
        return E_OUTOFMEMORY;
vb@282
   591
    }
vb@282
   592
    catch (exception& ex) {
vb@282
   593
        return FAIL(ex.what());;
vb@282
   594
    }
vb@282
   595
vb@282
   596
    PEP_STATUS status = ::key_mistrusted(get_session(), _ident);
vb@282
   597
    free_identity(_ident);
vb@282
   598
vb@282
   599
    if (status == PEP_OUT_OF_MEMORY)
vb@282
   600
        return E_OUTOFMEMORY;
vb@282
   601
vb@282
   602
    if (status == PEP_KEY_NOT_FOUND)
vb@282
   603
        return FAIL(L"key not found");
vb@282
   604
vb@282
   605
    if (status != ::PEP_STATUS_OK)
vb@282
   606
        return FAIL(L"cannot revoke compromized key", status);
vb@282
   607
vb@282
   608
    return S_OK;
vb@282
   609
}
vb@282
   610
vb@282
   611
STDMETHODIMP CpEpEngine::UndoLastMistrust()
vb@282
   612
{
vb@282
   613
    PEP_STATUS status = ::undo_last_mistrust(get_session());
vb@282
   614
vb@282
   615
    if (status == PEP_CANNOT_FIND_IDENTITY)
vb@282
   616
        return FAIL(L"Cannot find identity!", status);
vb@282
   617
vb@282
   618
    if (status != ::PEP_STATUS_OK)
vb@282
   619
        return FAIL(L"cannot revoke compromized key", status);
vb@282
   620
vb@282
   621
    return S_OK;
vb@282
   622
}
vb@282
   623
vb@282
   624
STDMETHODIMP CpEpEngine::IsPepUser(/* [in] */ struct pEpIdentity *ident, /* [retval][out] */ VARIANT_BOOL *ispEp) 
vb@282
   625
{
vb@282
   626
    ::pEp_identity *_ident;
vb@282
   627
vb@282
   628
    assert(ident);
vb@282
   629
    if (!ident)
vb@282
   630
        return E_INVALIDARG;
vb@282
   631
vb@282
   632
    try {
vb@282
   633
        _ident = new_identity(ident);
vb@282
   634
    }
vb@282
   635
    catch (bad_alloc&) {
vb@282
   636
        return E_OUTOFMEMORY;
vb@282
   637
    }
vb@282
   638
    catch (exception& ex) {
vb@282
   639
        return FAIL(ex.what());;
vb@282
   640
    }
vb@282
   641
vb@282
   642
    bool is_pep = FALSE;
vb@282
   643
    PEP_STATUS status = ::is_pep_user(get_session(), _ident, &is_pep);
vb@282
   644
vb@282
   645
    *ispEp = is_pep;
vb@282
   646
vb@282
   647
    if (status == PEP_CANNOT_FIND_PERSON)
vb@282
   648
        return FAIL(L"Cannot find identity!", status);
vb@282
   649
vb@282
   650
    if (status == PEP_ILLEGAL_VALUE)
vb@282
   651
        return E_INVALIDARG;
vb@282
   652
vb@282
   653
    if (status != ::PEP_STATUS_OK)
vb@282
   654
        return FAIL(L"Engine is_pep_user returned error", status);
vb@282
   655
vb@282
   656
    return S_OK;
vb@282
   657
}
vb@282
   658
vb@282
   659
STDMETHODIMP CpEpEngine::KeyResetTrust(struct pEpIdentity *ident)
vb@282
   660
{
vb@282
   661
    ::pEp_identity *_ident;
vb@282
   662
vb@282
   663
    assert(ident);
vb@282
   664
vb@282
   665
    if (!ident)
vb@282
   666
        return E_INVALIDARG;
vb@282
   667
vb@282
   668
    try {
vb@282
   669
        _ident = new_identity(ident);
vb@282
   670
    }
vb@282
   671
    catch (bad_alloc&) {
vb@282
   672
        return E_OUTOFMEMORY;
vb@282
   673
    }
vb@282
   674
    catch (exception& ex) {
vb@282
   675
        return FAIL(ex.what());;
vb@282
   676
    }
vb@282
   677
vb@282
   678
    PEP_STATUS status = ::key_reset_trust(get_session(), _ident);
vb@282
   679
    free_identity(_ident);
vb@282
   680
vb@282
   681
    if (status == PEP_OUT_OF_MEMORY)
vb@282
   682
        return E_OUTOFMEMORY;
vb@282
   683
vb@282
   684
    if (status == PEP_KEY_NOT_FOUND)
vb@282
   685
        return FAIL(L"key not found");
vb@282
   686
vb@282
   687
    if (status != ::PEP_STATUS_OK)
vb@282
   688
        return FAIL(L"cannot reset trust", status);
vb@282
   689
vb@282
   690
    return S_OK;
vb@282
   691
}
vb@282
   692
vb@282
   693
int CpEpEngine::examine_identity(pEp_identity *ident, void *management)
vb@282
   694
{
vb@282
   695
    assert(ident);
vb@282
   696
    assert(management);
vb@282
   697
    if (!(ident && management))
vb@282
   698
        return -1;
vb@282
   699
vb@282
   700
    CpEpEngine *me = (CpEpEngine *)management;
vb@282
   701
vb@282
   702
    if (me->identity_queue.load() == NULL)
vb@282
   703
        return 0;
vb@282
   704
vb@282
   705
    try {
vb@282
   706
        me->identity_queue.load()->push_back(ident);
vb@282
   707
    }
vb@282
   708
    catch (exception&) {
vb@282
   709
        return -1;
vb@282
   710
    }
vb@282
   711
vb@282
   712
    return 0;
vb@282
   713
}
vb@282
   714
vb@282
   715
::pEp_identity * CpEpEngine::retrieve_next_identity(void *management)
vb@282
   716
{
vb@282
   717
    assert(management);
vb@282
   718
    if (!management)
vb@282
   719
        return NULL;
vb@282
   720
vb@282
   721
    identity_queue_t *iq = (identity_queue_t *)management;
vb@282
   722
vb@282
   723
    do /* poll queue */ {
vb@282
   724
        if (iq->size())
vb@282
   725
            break;
vb@282
   726
        ::Sleep(100);
vb@282
   727
    } while (true);
vb@282
   728
vb@282
   729
    ::pEp_identity *_ident;
vb@282
   730
    pEp_identity_cpp& ident = iq->front();
vb@282
   731
vb@282
   732
    if (ident.address.size() == 0)
vb@282
   733
        return NULL;
vb@282
   734
vb@282
   735
    _ident = ident.to_pEp_identity();
vb@282
   736
    iq->pop_front();
vb@282
   737
vb@282
   738
    return _ident;
vb@282
   739
}
vb@282
   740
vb@282
   741
PEP_STATUS CpEpEngine::messageToSend(void * obj, message *msg)
vb@282
   742
{
vb@282
   743
    assert(msg);
vb@282
   744
    assert(obj);
vb@282
   745
    if (!(msg && obj))
vb@282
   746
        return PEP_ILLEGAL_VALUE;
vb@282
   747
vb@282
   748
    TextMessage _msg;
vb@282
   749
    memset(&_msg, 0, sizeof(TextMessage));
vb@282
   750
vb@282
   751
    text_message_from_C(&_msg, msg);
vb@282
   752
    CpEpEngine *me = (CpEpEngine *)obj;
vb@282
   753
    HRESULT r = me->Fire_MessageToSend(&_msg);
vb@282
   754
    assert(r == S_OK);
vb@282
   755
    clear_text_message(&_msg);
vb@282
   756
    if (r == E_OUTOFMEMORY)
vb@282
   757
        return PEP_OUT_OF_MEMORY;
vb@282
   758
    if (r != S_OK)
vb@282
   759
        return PEP_UNKNOWN_ERROR;
vb@282
   760
vb@282
   761
    return PEP_STATUS_OK;
vb@282
   762
}
vb@282
   763
vb@282
   764
STDMETHODIMP CpEpEngine::BlacklistAdd(BSTR fpr)
vb@282
   765
{
vb@282
   766
    assert(fpr);
vb@282
   767
    if (!fpr)
vb@282
   768
        return E_INVALIDARG;
vb@282
   769
vb@282
   770
    string _fpr = utf8_string(fpr);
vb@282
   771
    PEP_STATUS status = ::blacklist_add(get_session(), _fpr.c_str());
vb@282
   772
    assert(status == PEP_STATUS_OK);
vb@282
   773
    if (status != PEP_STATUS_OK)
vb@282
   774
        return FAIL(L"blacklist_add failed in pEp engine", status);
vb@282
   775
vb@282
   776
    return S_OK;
vb@282
   777
}
vb@282
   778
vb@282
   779
STDMETHODIMP CpEpEngine::BlacklistDelete(BSTR fpr)
vb@282
   780
{
vb@282
   781
    assert(fpr);
vb@282
   782
    if (!fpr)
vb@282
   783
        return E_INVALIDARG;
vb@282
   784
vb@282
   785
    string _fpr = utf8_string(fpr);
vb@282
   786
    PEP_STATUS status = ::blacklist_delete(get_session(), _fpr.c_str());
vb@282
   787
    assert(status == PEP_STATUS_OK);
vb@282
   788
    if (status != PEP_STATUS_OK)
vb@282
   789
        return FAIL(L"blacklist_delete failed in pEp engine", status);
vb@282
   790
vb@282
   791
    return S_OK;
vb@282
   792
}
vb@282
   793
vb@282
   794
STDMETHODIMP CpEpEngine::BlacklistIsListed(BSTR fpr, VARIANT_BOOL *listed)
vb@282
   795
{
vb@282
   796
    assert(fpr);
vb@282
   797
    assert(listed);
vb@282
   798
vb@282
   799
    if (!(fpr && listed))
vb@282
   800
        return E_INVALIDARG;
vb@282
   801
vb@282
   802
    string _fpr = utf8_string(fpr);
vb@282
   803
    bool result;
vb@282
   804
    PEP_STATUS status = ::blacklist_is_listed(get_session(), _fpr.c_str(), &result);
vb@282
   805
    assert(status == PEP_STATUS_OK);
vb@282
   806
    if (status != PEP_STATUS_OK)
vb@282
   807
        return FAIL(L"blacklist_is_listed failed in pEp engine", status);
vb@282
   808
vb@282
   809
    *listed = result ? VARIANT_TRUE : VARIANT_FALSE;
vb@282
   810
    return S_OK;
vb@282
   811
}
vb@282
   812
vb@282
   813
STDMETHODIMP CpEpEngine::BlacklistRetrieve(SAFEARRAY **blacklist)
vb@282
   814
{
vb@282
   815
    assert(blacklist);
vb@282
   816
vb@282
   817
    if (!blacklist)
vb@282
   818
        return E_INVALIDARG;
vb@282
   819
vb@282
   820
    ::stringlist_t *_blacklist = NULL;
vb@282
   821
    PEP_STATUS status = ::blacklist_retrieve(get_session(), &_blacklist);
vb@282
   822
    assert(status == PEP_STATUS_OK);
vb@282
   823
    if (status != PEP_STATUS_OK)
vb@282
   824
        return FAIL(L"blacklist_retrieve failed in pEp engine", status);
vb@282
   825
    assert(_blacklist);
vb@282
   826
vb@282
   827
    *blacklist = string_array(_blacklist);
vb@282
   828
    ::free_stringlist(_blacklist);
vb@282
   829
    return S_OK;
vb@282
   830
}
vb@282
   831
vb@282
   832
HRESULT CpEpEngine::error(_bstr_t msg)
vb@282
   833
{
vb@282
   834
    _bstr_t helpFile = L"";
vb@282
   835
    _bstr_t source = L"pEp COM Adapter";
vb@282
   836
vb@282
   837
    ICreateErrorInfo *cei;
vb@282
   838
    if (SUCCEEDED(CreateErrorInfo(&cei))) {
vb@282
   839
        cei->SetDescription(msg);
vb@282
   840
        cei->SetGUID(__uuidof(IpEpEngine));
vb@282
   841
        cei->SetHelpContext(0);
vb@282
   842
        cei->SetHelpFile(helpFile);
vb@282
   843
        cei->SetSource(source);
vb@282
   844
vb@282
   845
        IErrorInfo *errinfo;
vb@282
   846
        if (SUCCEEDED(cei->QueryInterface(IID_IErrorInfo, (LPVOID FAR*) &errinfo))) {
vb@282
   847
            SetErrorInfo(0, errinfo);
vb@282
   848
            errinfo->Release();
vb@282
   849
        }
vb@282
   850
        cei->Release();
vb@282
   851
    }
vb@282
   852
    return E_FAIL;
vb@282
   853
}
vb@282
   854
vb@282
   855
HRESULT CpEpEngine::error(_bstr_t msg, PEP_STATUS status)
vb@282
   856
{
vb@282
   857
    std::stringstream stream;
vb@282
   858
    stream << msg;
vb@282
   859
    stream << ": ";
vb@282
   860
    stream << std::hex << status;
vb@282
   861
vb@282
   862
    error(stream.str().c_str());
vb@282
   863
vb@282
   864
    if (status == ::PEP_OUT_OF_MEMORY)
vb@282
   865
        return E_OUTOFMEMORY;
vb@282
   866
vb@282
   867
    return MAKE_HRESULT(1, FACILITY_ITF, (0xFFFF & status));
vb@282
   868
}
vb@282
   869
vb@282
   870
STDMETHODIMP CpEpEngine::EncryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY * extra, pEpEncryptFlags flags, pEpEncFormat encFormat)
vb@282
   871
{
vb@282
   872
    assert(src);
vb@282
   873
    assert(dst);
vb@282
   874
vb@282
   875
    if (!(src && dst))
vb@282
   876
        return E_INVALIDARG;
vb@282
   877
vb@282
   878
    ::message *_src = text_message_to_C(src);
vb@282
   879
vb@282
   880
    _PEP_enc_format _encFormat = (_PEP_enc_format)encFormat;
vb@282
   881
vb@282
   882
    // COM-19: Initialize msg_dst to NULL, or we end up calling
vb@282
   883
    // free_message() below with a pointer to random garbage in
vb@282
   884
    // case of an error in encrypt_message().
vb@282
   885
    ::message *msg_dst = NULL;
vb@282
   886
    ::stringlist_t *_extra = new_stringlist(extra); // can cope with NULL
vb@282
   887
vb@282
   888
    // _PEP_enc_format used to be intentionally hardcoded to PEP_enc_PEP:
vb@282
   889
    // Since COM-74, this has been changed to an explicit parameter, to allow the engine to attach
vb@282
   890
    // the keys and headers to outgoing, unencrypted messages.
vb@282
   891
    PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
vb@282
   892
    PEP_STATUS status = ::encrypt_message(get_session(), _src, _extra, &msg_dst, _encFormat, engineFlags);
vb@282
   893
    ::free_stringlist(_extra);
vb@282
   894
vb@282
   895
    if (status == PEP_STATUS_OK)
vb@282
   896
        text_message_from_C(dst, msg_dst);
vb@282
   897
    else
vb@282
   898
        text_message_from_C(dst, _src);
vb@282
   899
vb@282
   900
    ::free_message(msg_dst);
vb@282
   901
    ::free_message(_src);
vb@282
   902
vb@282
   903
    if (status == PEP_OUT_OF_MEMORY)
vb@282
   904
        return E_OUTOFMEMORY;
vb@282
   905
vb@282
   906
    // COM-41: Enhanced PEP status handling
vb@282
   907
    if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
vb@282
   908
        return FAIL("Failure to encrypt message", status);
vb@282
   909
vb@282
   910
    // Statii like PEP_UNENCRYPTED due to no private key
vb@282
   911
    // should not be a catastrophic failure here. Using S_FALSE
vb@282
   912
    // still allows clients to differentiate with S_OK,
vb@282
   913
    // although this does not work out of the box with
vb@282
   914
    // the standard .NET mapping of COM.
vb@282
   915
    if (status != PEP_STATUS_OK)
vb@282
   916
        return S_FALSE;
vb@282
   917
vb@282
   918
    return S_OK;
vb@282
   919
}
vb@282
   920
Thomas@285
   921
STDMETHODIMP CpEpEngine::EncryptMessageAndAddPrivKey(TextMessage * src, TextMessage * dst, BSTR to_fpr, pEpEncryptFlags flags, pEpEncFormat encFormat)
Thomas@285
   922
{
Thomas@285
   923
    assert(src);
Thomas@285
   924
    assert(dst);
Thomas@285
   925
    assert(to_fpr);
Thomas@285
   926
Thomas@285
   927
    if (!(src && dst))
Thomas@285
   928
        return E_INVALIDARG;
Thomas@285
   929
Thomas@285
   930
    ::message *_src = text_message_to_C(src);
Thomas@285
   931
Thomas@285
   932
    _PEP_enc_format _encFormat = (_PEP_enc_format)encFormat;
Thomas@285
   933
Thomas@285
   934
    // COM-19: Initialize msg_dst to NULL, or we end up calling
Thomas@285
   935
    // free_message() below with a pointer to random garbage in
Thomas@285
   936
    // case of an error in encrypt_message().
Thomas@285
   937
    ::message *msg_dst = NULL;
Thomas@285
   938
Thomas@285
   939
    string _to_fpr = utf8_string(to_fpr);
Thomas@285
   940
                                                    // _PEP_enc_format used to be intentionally hardcoded to PEP_enc_PEP:
Thomas@285
   941
                                                    // Since COM-74, this has been changed to an explicit parameter, to allow the engine to attach
Thomas@285
   942
                                                    // the keys and headers to outgoing, unencrypted messages.
Thomas@285
   943
    PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
Thomas@285
   944
    PEP_STATUS status = ::encrypt_message_and_add_priv_key(get_session(), _src, &msg_dst, _to_fpr.c_str(), _encFormat, engineFlags);
Thomas@285
   945
Thomas@285
   946
    if (status == PEP_STATUS_OK)
Thomas@285
   947
        text_message_from_C(dst, msg_dst);
Thomas@285
   948
    else
Thomas@285
   949
        text_message_from_C(dst, _src);
Thomas@285
   950
Thomas@285
   951
    ::free_message(msg_dst);
Thomas@285
   952
    ::free_message(_src);
Thomas@285
   953
Thomas@285
   954
    if (status == PEP_OUT_OF_MEMORY)
Thomas@285
   955
        return E_OUTOFMEMORY;
Thomas@285
   956
Thomas@285
   957
    // COM-41: Enhanced PEP status handling
Thomas@285
   958
    if ((status != PEP_STATUS_OK) && (status < PEP_UNENCRYPTED || status >= PEP_TRUSTWORD_NOT_FOUND))
Thomas@285
   959
        return FAIL("Failure to encrypt message", status);
Thomas@285
   960
Thomas@285
   961
    // Statii like PEP_UNENCRYPTED due to no private key
Thomas@285
   962
    // should not be a catastrophic failure here. Using S_FALSE
Thomas@285
   963
    // still allows clients to differentiate with S_OK,
Thomas@285
   964
    // although this does not work out of the box with
Thomas@285
   965
    // the standard .NET mapping of COM.
Thomas@285
   966
    if (status != PEP_STATUS_OK)
Thomas@285
   967
        return S_FALSE;
Thomas@285
   968
Thomas@285
   969
    return S_OK;
Thomas@285
   970
}
Thomas@285
   971
vb@282
   972
STDMETHODIMP CpEpEngine::EncryptMessageForSelf(pEpIdentity * targetId, TextMessage * src,
vb@282
   973
    /* [in] */ SAFEARRAY *extra, TextMessage * dst, pEpEncryptFlags flags)
vb@282
   974
{
vb@282
   975
    assert(targetId);
vb@282
   976
    assert(src);
vb@282
   977
    assert(dst);
vb@282
   978
vb@282
   979
    if (!(targetId && src && dst))
vb@282
   980
        return E_INVALIDARG;
vb@282
   981
vb@282
   982
    PEP_encrypt_flags_t engineFlags = (PEP_encrypt_flags_t)flags;
vb@282
   983
vb@282
   984
    ::pEp_identity *_target_id = new_identity(targetId);
vb@282
   985
vb@282
   986
    ::message *_src = text_message_to_C(src);
vb@282
   987
vb@282
   988
    ::stringlist_t* _extra = NULL;
vb@282
   989
    HRESULT result = S_OK;
vb@282
   990
    ::message *msg_dst = NULL;
vb@282
   991
    PEP_STATUS status = PEP_STATUS_OK;
vb@282
   992
vb@282
   993
    try {
vb@282
   994
        if (extra) {
vb@282
   995
            _extra = new_stringlist(extra);
vb@282
   996
        }
vb@282
   997
vb@282
   998
        // COM-19: Initialize msg_dst to NULL, or we end up calling
vb@282
   999
        // free_message() below with a pointer to random garbage in
vb@282
  1000
        // case of an error in encrypt_message_for_self().
vb@282
  1001
        status = ::encrypt_message_for_self(get_session(), _target_id, _src, _extra, &msg_dst, PEP_enc_PEP, engineFlags);
vb@282
  1002
vb@282
  1003
        if (status == PEP_STATUS_OK)
vb@282
  1004
            text_message_from_C(dst, msg_dst);
vb@282
  1005
        else
vb@282
  1006
            text_message_from_C(dst, _src);
vb@282
  1007
    } catch (bad_alloc&) {
vb@282
  1008
        result = E_OUTOFMEMORY;
vb@282
  1009
    }
vb@282
  1010
    catch (exception& ex) {
vb@282
  1011
        result = FAIL(ex.what());
vb@282
  1012
    }
vb@282
  1013
vb@282
  1014
    ::free_message(msg_dst);
vb@282
  1015
    ::free_message(_src);
vb@282
  1016
    ::free_identity(_target_id);
vb@282
  1017
    ::free_stringlist(_extra);
vb@282
  1018
vb@282
  1019
    if (status == PEP_OUT_OF_MEMORY)
vb@282
  1020
        return E_OUTOFMEMORY;
vb@282
  1021
vb@282
  1022
    // Different to encrypt_message, this should never fail (we ought to always
vb@282
  1023
    // have a private key for ourself).#
vb@282
  1024
    if (status != PEP_STATUS_OK)
vb@282
  1025
        return FAIL("Failure to encrypt message", status);
vb@282
  1026
vb@282
  1027
    return result;
vb@282
  1028
}
vb@282
  1029
vb@282
  1030
STDMETHODIMP CpEpEngine::DecryptMessage(TextMessage * src, TextMessage * dst, SAFEARRAY ** keylist, pEpDecryptFlags *flags, pEpRating *rating)
vb@282
  1031
{
vb@282
  1032
    assert(src);
vb@282
  1033
    assert(dst);
vb@282
  1034
    assert(keylist);
vb@282
  1035
    assert(flags);
vb@282
  1036
    assert(rating);
vb@282
  1037
vb@282
  1038
    if (!(src && dst && keylist && flags && rating))
vb@282
  1039
        return E_INVALIDARG;
vb@282
  1040
vb@282
  1041
    *keylist = NULL;
vb@282
  1042
    *rating = pEpRatingUndefined;
vb@282
  1043
vb@282
  1044
    ::message *_src = text_message_to_C(src);
vb@282
  1045
    ::message *msg_dst = NULL;
vb@282
  1046
    ::stringlist_t *_keylist = NULL;
vb@282
  1047
    ::PEP_rating _rating;
vb@282
  1048
vb@282
  1049
    PEP_decrypt_flags_t engineflags = (PEP_decrypt_flags_t) *flags;
vb@282
  1050
    PEP_STATUS status = ::decrypt_message(get_session(), _src, &msg_dst, &_keylist, &_rating, &engineflags);
vb@282
  1051
vb@282
  1052
    *flags = (pEpDecryptFlags)engineflags;
vb@282
  1053
vb@282
  1054
    if (engineflags && PEP_decrypt_flag_src_modified)
vb@282
  1055
        text_message_from_C(src, _src);
vb@282
  1056
vb@282
  1057
    if (msg_dst)
vb@282
  1058
        text_message_from_C(dst, msg_dst);
vb@282
  1059
vb@282
  1060
    ::free_message(_src);
vb@282
  1061
    ::free_message(msg_dst);
vb@282
  1062
vb@282
  1063
    if (_keylist) {
vb@282
  1064
        *keylist = string_array(_keylist);
vb@282
  1065
        free_stringlist(_keylist);
vb@282
  1066
    }
vb@282
  1067
vb@282
  1068
    *rating = (pEpRating)_rating;
vb@282
  1069
vb@282
  1070
    return S_OK;
vb@282
  1071
}
vb@282
  1072
vb@282
  1073
STDMETHODIMP CpEpEngine::ReEvaluateMessageRating(TextMessage * msg, SAFEARRAY * x_KeyList, pEpRating x_EncStatus, pEpRating *rating)
vb@282
  1074
{
vb@282
  1075
    assert(msg);
vb@282
  1076
    assert(x_EncStatus != PEP_rating_undefined);
vb@282
  1077
    assert(rating);
vb@282
  1078
vb@282
  1079
    if (!(msg && x_EncStatus != PEP_rating_undefined && rating))
vb@282
  1080
        return E_INVALIDARG;
vb@282
  1081
vb@282
  1082
    *rating = pEpRatingUndefined;
vb@282
  1083
vb@282
  1084
    ::message *_msg = text_message_to_C(msg);
vb@282
  1085
    ::stringlist_t *_keylist = new_stringlist(x_KeyList);
vb@282
  1086
    ::PEP_rating _rating = PEP_rating_undefined;
vb@282
  1087
vb@282
  1088
    PEP_STATUS status = ::re_evaluate_message_rating(get_session(), _msg, _keylist, (PEP_rating)x_EncStatus, &_rating);
vb@282
  1089
vb@282
  1090
    ::free_stringlist(_keylist);
vb@282
  1091
    ::free_message(_msg);
vb@282
  1092
vb@282
  1093
    *rating = (pEpRating)_rating;
vb@282
  1094
vb@282
  1095
    return S_OK;
vb@282
  1096
}
vb@282
  1097
vb@282
  1098
STDMETHODIMP CpEpEngine::OutgoingMessageRating(TextMessage *msg, pEpRating * pVal)
vb@282
  1099
{
vb@282
  1100
    assert(msg);
vb@282
  1101
    assert(pVal);
vb@282
  1102
vb@282
  1103
    if (!(msg  && pVal))
vb@282
  1104
        return E_INVALIDARG;
vb@282
  1105
vb@282
  1106
    ::message *_msg = text_message_to_C(msg);
vb@282
  1107
vb@282
  1108
    PEP_rating _rating;
vb@282
  1109
    PEP_STATUS status = ::outgoing_message_rating(get_session(), _msg, &_rating);
vb@282
  1110
    if (status != PEP_STATUS_OK)
vb@282
  1111
        return FAIL(L"cannot get message rating", status);
vb@282
  1112
vb@282
  1113
    *pVal = (pEpRating)_rating;
vb@282
  1114
    return S_OK;
vb@282
  1115
}
vb@282
  1116
vb@282
  1117
STDMETHODIMP CpEpEngine::IdentityRating(struct pEpIdentity *ident, pEpRating * pVal)
vb@282
  1118
{
vb@282
  1119
    ::pEp_identity *_ident;
vb@282
  1120
vb@282
  1121
    assert(ident);
vb@282
  1122
    assert(pVal);
vb@282
  1123
vb@282
  1124
    if (!(ident  && pVal))
vb@282
  1125
        return E_INVALIDARG;
vb@282
  1126
vb@282
  1127
    try {
vb@282
  1128
        _ident = new_identity(ident);
vb@282
  1129
    }
vb@282
  1130
    catch (bad_alloc&) {
vb@282
  1131
        return E_OUTOFMEMORY;
vb@282
  1132
    }
vb@282
  1133
    catch (exception& ex) {
vb@282
  1134
        return FAIL(ex.what());;
vb@282
  1135
    }
vb@282
  1136
vb@282
  1137
    PEP_rating _rating;
vb@282
  1138
    PEP_STATUS status = ::identity_rating(get_session(), _ident, &_rating);
vb@282
  1139
    free_identity(_ident);
vb@282
  1140
vb@282
  1141
    if (status != PEP_STATUS_OK)
vb@282
  1142
        return FAIL(L"cannot get message color", status);
vb@282
  1143
vb@282
  1144
    *pVal = (pEpRating)_rating;
vb@282
  1145
    return S_OK;
vb@282
  1146
}
vb@282
  1147
vb@282
  1148
STDMETHODIMP CpEpEngine::ColorFromRating(pEpRating rating, pEpColor * pVal)
vb@282
  1149
{
vb@282
  1150
    assert(pVal);
vb@282
  1151
vb@282
  1152
    if (!pVal)
vb@282
  1153
        return E_INVALIDARG;
vb@282
  1154
vb@282
  1155
    PEP_rating engineRating = (PEP_rating)rating;
vb@282
  1156
    PEP_color _color = ::color_from_rating(engineRating);
vb@282
  1157
vb@282
  1158
    *pVal = (pEpColor)_color;
vb@282
  1159
vb@282
  1160
    return S_OK;
vb@282
  1161
}
vb@282
  1162
vb@282
  1163
STDMETHODIMP CpEpEngine::OwnIdentitiesRetrieve(LPSAFEARRAY* ownIdentities)
vb@282
  1164
{
vb@282
  1165
    assert(ownIdentities);
vb@282
  1166
    if (!ownIdentities)
vb@282
  1167
        return E_INVALIDARG;
vb@282
  1168
vb@282
  1169
    *ownIdentities = nullptr;
vb@282
  1170
vb@282
  1171
    ::identity_list *il = nullptr;
vb@282
  1172
    PEP_STATUS status = ::own_identities_retrieve(get_session(), &il);
vb@282
  1173
    if (status == PEP_OUT_OF_MEMORY) {
vb@282
  1174
        return E_OUTOFMEMORY;
vb@282
  1175
    }
vb@282
  1176
    else if (status != PEP_STATUS_OK)
vb@282
  1177
    {
vb@282
  1178
        return FAIL(_T("OwnIdentitiesRetrieve"), status);
vb@282
  1179
    }
vb@282
  1180
vb@282
  1181
    SAFEARRAY * _own_identities = nullptr;
vb@282
  1182
    try {
vb@282
  1183
        _own_identities = array_from_C<pEpIdentity, identity_list>(il);
vb@282
  1184
    }
vb@282
  1185
    catch (exception& ex)
vb@282
  1186
    {
vb@282
  1187
        ::free_identity_list(il);
vb@282
  1188
        try {
vb@282
  1189
            dynamic_cast<bad_alloc&>(ex);
vb@282
  1190
        }
vb@282
  1191
        catch (bad_cast&)
vb@282
  1192
        {
vb@282
  1193
            return FAIL(ex.what());
vb@282
  1194
        }
vb@282
  1195
        return E_OUTOFMEMORY;
vb@282
  1196
    }
vb@282
  1197
    free_identity_list(il);
vb@282
  1198
vb@282
  1199
    *ownIdentities = _own_identities;
vb@282
  1200
    return S_OK;
vb@282
  1201
}
vb@282
  1202
vb@282
  1203
STDMETHODIMP CpEpEngine::TrustPersonalKey(struct pEpIdentity *ident, struct pEpIdentity *result)
vb@282
  1204
{
vb@282
  1205
    ::pEp_identity *_ident;
vb@282
  1206
vb@282
  1207
    assert(ident);
vb@282
  1208
    assert(result);
vb@282
  1209
vb@282
  1210
    if (!ident || !result)
vb@282
  1211
        return E_INVALIDARG;
vb@282
  1212
vb@282
  1213
    try {
vb@282
  1214
        _ident = new_identity(ident);
vb@282
  1215
    }
vb@282
  1216
    catch (bad_alloc&) {
vb@282
  1217
        return E_OUTOFMEMORY;
vb@282
  1218
    }
vb@282
  1219
    catch (exception& ex) {
vb@282
  1220
        return FAIL(ex.what());;
vb@282
  1221
    }
vb@282
  1222
vb@282
  1223
    if (verbose_mode) {
vb@282
  1224
        stringstream ss;
vb@282
  1225
        ss << "TrustPersonalKey called with ";
vb@282
  1226
        ss << utf8_string(ident->Address);
vb@282
  1227
        ss << L": ";
vb@282
  1228
        ss << ident->CommType;
vb@282
  1229
        verbose(ss.str());
vb@282
  1230
    }
vb@282
  1231
vb@282
  1232
    PEP_STATUS status = ::trust_personal_key(get_session(), _ident);
vb@282
  1233
vb@282
  1234
    if (verbose_mode) {
vb@282
  1235
        stringstream ss;
vb@282
  1236
        ss << "result ";
vb@282
  1237
        ss << status;
vb@282
  1238
        ss << " for ";
vb@282
  1239
        ss << _ident->address;
vb@282
  1240
        ss << L": ";
vb@282
  1241
        ss << _ident->comm_type;
vb@282
  1242
        verbose(ss.str());
vb@282
  1243
    }
vb@282
  1244
vb@282
  1245
    if (status == PEP_STATUS_OK)
vb@282
  1246
        copy_identity(result, _ident);
vb@282
  1247
vb@282
  1248
    free_identity(_ident);
vb@282
  1249
    if (status == PEP_OUT_OF_MEMORY)
vb@282
  1250
        return E_OUTOFMEMORY;
vb@282
  1251
    else if (status != PEP_STATUS_OK)
vb@282
  1252
        return FAIL(L"failure while executing TrustPersonalKey()", status);
vb@282
  1253
vb@282
  1254
    return S_OK;
vb@282
  1255
}
vb@282
  1256
vb@282
  1257
// keysync api
vb@282
  1258
vb@282
  1259
void CpEpEngine::start_keysync()
vb@282
  1260
{
vb@282
  1261
    // acquire the lock
vb@282
  1262
    std::unique_lock<std::recursive_mutex> lock(keysync_mutex);
vb@282
  1263
vb@282
  1264
    // Assert if we're not already running.
vb@282
  1265
    assert(!this->keysync_thread);
vb@282
  1266
vb@282
  1267
    // Ensure we are not aborting the new thread due to a
vb@282
  1268
    // left over flag.
vb@282
  1269
    keysync_abort_requested = false;
vb@282
  1270
vb@282
  1271
    // Init our keysync session
vb@282
  1272
    { // begin lock scope
vb@282
  1273
        std::lock_guard<std::mutex> lock(init_mutex);
vb@282
  1274
        PEP_STATUS status = ::init(&keysync_session);
vb@282
  1275
        ::register_sync_callbacks(keysync_session, (void*)this, messageToSend, notifyHandshake, inject_sync_msg, retrieve_next_sync_msg);
vb@282
  1276
        assert(status == PEP_STATUS_OK);
vb@282
  1277
    } // end lock scope
vb@282
  1278
vb@282
  1279
    attach_sync_session(get_session(), keysync_session);
vb@282
  1280
vb@282
  1281
    // We need to marshal the callbacks to the keysync thread
vb@282
  1282
    LPSTREAM marshaled_callbacks;
vb@282
  1283
vb@282
  1284
    auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, client_callbacks, &marshaled_callbacks);
vb@282
  1285
    assert(result == S_OK);
vb@282
  1286
vb@282
  1287
    // Star the keysync thread
vb@282
  1288
    keysync_thread = new thread(do_keysync_in_thread, this, marshaled_callbacks);
vb@282
  1289
}
vb@282
  1290
vb@282
  1291
void CpEpEngine::do_keysync_in_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks)
vb@282
  1292
{
vb@282
  1293
    assert(self);
vb@282
  1294
    assert(marshaled_callbacks);
vb@282
  1295
vb@282
  1296
    // We need to initialize COM here for successfull delivery of the callbacks.
vb@282
  1297
    // As we don't create any COM instances in our thread, the COMINIT value is
vb@282
  1298
    // currently irrelevant, so we go with the safest value.
vb@282
  1299
    auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
vb@282
  1300
    assert(res == S_OK);
vb@282
  1301
vb@282
  1302
    LPVOID vp;
vb@282
  1303
vb@282
  1304
    res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
vb@282
  1305
    assert(SUCCEEDED(res));
vb@282
  1306
vb@282
  1307
    self->client_last_signalled_polling_state = false;
vb@282
  1308
    self->client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
vb@282
  1309
vb@282
  1310
    ::do_sync_protocol(self->keysync_session, self);
vb@282
  1311
vb@282
  1312
    self->client_callbacks_on_sync_thread->Release();
vb@282
  1313
vb@282
  1314
    self->client_callbacks_on_sync_thread = NULL;
vb@282
  1315
vb@282
  1316
    CoUninitialize();
vb@282
  1317
}
vb@282
  1318
vb@282
  1319
void CpEpEngine::stop_keysync()
vb@282
  1320
{
vb@282
  1321
    // acquire the lock
vb@282
  1322
    std::unique_lock<std::recursive_mutex> lock(keysync_mutex);
vb@282
  1323
vb@282
  1324
    // Do nothing if keysync is not running.
vb@282
  1325
    if (!keysync_thread)
vb@282
  1326
        return;
vb@282
  1327
vb@282
  1328
    assert(!keysync_abort_requested);
vb@282
  1329
    // signal that we're gonna abort
vb@282
  1330
    keysync_abort_requested = true;
vb@282
  1331
vb@282
  1332
    // Notify the keysync thread
vb@282
  1333
    keysync_condition.notify_all();
vb@282
  1334
vb@282
  1335
    // Wait for the other thread to finish and clean up
vb@282
  1336
    while (keysync_abort_requested)
vb@282
  1337
        keysync_condition.wait(lock);
vb@282
  1338
vb@282
  1339
    // collect the child thread for the thread to end
vb@282
  1340
    keysync_thread->join();
vb@282
  1341
vb@282
  1342
    // clean up
vb@282
  1343
    delete keysync_thread;
vb@282
  1344
    keysync_thread = NULL;
vb@282
  1345
vb@282
  1346
    ::detach_sync_session(get_session());
vb@282
  1347
    ::unregister_sync_callbacks(keysync_session);
vb@282
  1348
vb@282
  1349
    std::lock_guard<std::mutex> releaselock(init_mutex);
vb@282
  1350
    release(keysync_session);
vb@282
  1351
    keysync_session = NULL;
vb@282
  1352
}
vb@282
  1353
vb@282
  1354
int CpEpEngine::inject_sync_msg(void * msg, void * management)
vb@282
  1355
{
vb@282
  1356
    assert(msg);
vb@282
  1357
    assert(management);
vb@282
  1358
    // check argument
vb@282
  1359
    if (!msg)
vb@282
  1360
        return E_INVALIDARG;
vb@282
  1361
    if (!management)
vb@282
  1362
        return ERROR_INVALID_HANDLE;
vb@282
  1363
vb@282
  1364
    CpEpEngine* me = (CpEpEngine*)management;
vb@282
  1365
vb@282
  1366
    // acquire the lock
vb@282
  1367
    std::unique_lock<std::recursive_mutex> lock(me->keysync_mutex);
vb@282
  1368
vb@282
  1369
    // check whether we're in a valid state running:
vb@282
  1370
    if (!me->keysync_thread)
vb@282
  1371
        return E_ASYNC_OPERATION_NOT_STARTED;
vb@282
  1372
vb@282
  1373
    // queue the message
vb@282
  1374
    me->keysync_queue.push(msg);
vb@282
  1375
vb@282
  1376
    // notify the receivers
vb@282
  1377
    me->keysync_condition.notify_all();
vb@282
  1378
vb@282
  1379
    return S_OK;
vb@282
  1380
}
vb@282
  1381
vb@282
  1382
void * CpEpEngine::retrieve_next_sync_msg(void * management, time_t *timeout)
vb@282
  1383
{
vb@282
  1384
    // sanity check
vb@282
  1385
    assert(management);
vb@282
  1386
    if (!(management))
vb@282
  1387
        return NULL;
vb@282
  1388
vb@282
  1389
    CpEpEngine* me = (CpEpEngine*)management;
vb@282
  1390
vb@282
  1391
    if ((timeout && *timeout)
vb@282
  1392
        && me->client_callbacks_on_sync_thread
vb@282
  1393
        && me->client_last_signalled_polling_state == false)
vb@282
  1394
    {
vb@282
  1395
        me->client_callbacks_on_sync_thread->NeedFastPolling(VARIANT_TRUE);
vb@282
  1396
        me->client_last_signalled_polling_state = true;
vb@282
  1397
    }
vb@282
  1398
    else if (!(timeout && *timeout)
vb@282
  1399
        && me->client_callbacks_on_sync_thread
vb@282
  1400
        && me->client_last_signalled_polling_state == true)
vb@282
  1401
    {
vb@282
  1402
        me->client_callbacks_on_sync_thread->NeedFastPolling(VARIANT_FALSE);
vb@282
  1403
        me->client_last_signalled_polling_state = false;
vb@282
  1404
    }
vb@282
  1405
vb@282
  1406
    // acquire the lock
vb@282
  1407
    std::unique_lock<std::recursive_mutex> lock(me->keysync_mutex);
vb@282
  1408
vb@282
  1409
    if (me->notify_handshake_finished)
vb@282
  1410
        me->notify_handshake_deliver_result();
vb@282
  1411
vb@282
  1412
    if (timeout && *timeout) {
vb@282
  1413
        std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now()
vb@282
  1414
            + std::chrono::seconds(*timeout);
vb@282
  1415
vb@282
  1416
        while (me->keysync_queue.empty() && !me->keysync_abort_requested)
vb@282
  1417
        {
vb@282
  1418
            auto status = me->keysync_condition.wait_until(lock, end_time);
vb@282
  1419
vb@282
  1420
            if (me->notify_handshake_finished)
vb@282
  1421
                me->notify_handshake_deliver_result();
vb@282
  1422
vb@282
  1423
            if (status == std::cv_status::timeout)
vb@282
  1424
            {
vb@282
  1425
                *timeout = 1; // Signal timeout
vb@282
  1426
                return NULL;
vb@282
  1427
            }
vb@282
  1428
            else
vb@282
  1429
            {
vb@282
  1430
                std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
vb@282
  1431
vb@282
  1432
                if (now < end_time)
vb@282
  1433
                {
vb@282
  1434
                    *timeout = std::chrono::duration_cast<std::chrono::seconds>(end_time - now).count();
vb@282
  1435
                }
vb@282
  1436
                else
vb@282
  1437
                {
vb@282
  1438
                    *timeout = 0;
vb@282
  1439
                }
vb@282
  1440
            }
vb@282
  1441
        }
vb@282
  1442
    }
vb@282
  1443
    else
vb@282
  1444
    {
vb@282
  1445
        while (me->keysync_queue.empty() && !me->keysync_abort_requested)
vb@282
  1446
        {
vb@282
  1447
            me->keysync_condition.wait(lock);
vb@282
  1448
vb@282
  1449
            if (me->notify_handshake_finished)
vb@282
  1450
                me->notify_handshake_deliver_result();
vb@282
  1451
        }
vb@282
  1452
    }
vb@282
  1453
vb@282
  1454
    if (me->keysync_abort_requested) {
vb@282
  1455
        // we acknowledge that we're quitting...
vb@282
  1456
        me->keysync_abort_requested = false;
vb@282
  1457
vb@282
  1458
        // We signal the main thread that we got his signal
vb@282
  1459
        // so it can gain the mutex again and call join() on us.
vb@282
  1460
        me->keysync_condition.notify_all();
vb@282
  1461
vb@282
  1462
        // and tell the pep engine we're done.
vb@282
  1463
        if (timeout)
vb@282
  1464
            *timeout = 0; // signal for termination.
vb@282
  1465
        return NULL;
vb@282
  1466
    }
vb@282
  1467
vb@282
  1468
    assert(!me->keysync_queue.empty());
vb@282
  1469
vb@282
  1470
    // Pop the message and return it.
vb@282
  1471
    void* msg = me->keysync_queue.front();
vb@282
  1472
    assert(msg);
vb@282
  1473
vb@282
  1474
    me->keysync_queue.pop();
vb@282
  1475
vb@282
  1476
    return msg;
vb@282
  1477
}
vb@282
  1478
Thomas@285
  1479
// Force an update check now
Thomas@285
  1480
STDMETHODIMP CpEpEngine::UpdateNow()
Thomas@285
  1481
{
Thomas@285
  1482
    try
Thomas@285
  1483
    {
Thomas@285
  1484
        ::pEp::GateKeeper::update_now();
Thomas@285
  1485
    }
Thomas@285
  1486
    catch (bad_alloc&) {
Thomas@285
  1487
        return E_OUTOFMEMORY;
Thomas@285
  1488
    }
Thomas@285
  1489
    catch (exception& ex) {
Thomas@285
  1490
        return FAIL(ex.what());;
Thomas@285
  1491
    }
Thomas@285
  1492
}
vb@282
  1493
vb@282
  1494
// Event callbacks
vb@282
  1495
vb@282
  1496
STDMETHODIMP CpEpEngine::RegisterCallbacks(IpEpEngineCallbacks* new_callbacks)
vb@282
  1497
{
vb@282
  1498
    // check for valid parameter
vb@282
  1499
    if (!new_callbacks)
vb@282
  1500
        return E_INVALIDARG;
vb@282
  1501
vb@282
  1502
    // don't allow double registration.
vb@282
  1503
    if (this->client_callbacks)
vb@282
  1504
        return E_ILLEGAL_STATE_CHANGE;
vb@282
  1505
vb@282
  1506
    this->client_callbacks = new_callbacks;
vb@282
  1507
    new_callbacks->AddRef();
vb@282
  1508
vb@282
  1509
    start_keysync();
vb@282
  1510
vb@282
  1511
    return S_OK;
vb@282
  1512
}
vb@282
  1513
vb@282
  1514
STDMETHODIMP CpEpEngine::UnregisterCallbacks()
vb@282
  1515
{
vb@282
  1516
    // don't allow double deregistration.
vb@282
  1517
    // S_FALSE still is no error (as double deregistration is not fatal).
vb@282
  1518
    if (!this->client_callbacks)
vb@282
  1519
        return S_FALSE;
vb@282
  1520
vb@282
  1521
    stop_keysync();
vb@282
  1522
vb@282
  1523
    this->client_callbacks->Release();
vb@282
  1524
vb@282
  1525
    this->client_callbacks = NULL;
vb@282
  1526
vb@282
  1527
    return S_OK;
vb@282
  1528
}
vb@282
  1529
vb@282
  1530
STDMETHODIMP CpEpEngine::OpenPGPListKeyinfo(BSTR search_pattern, LPSAFEARRAY* keyinfo_list) {
vb@282
  1531
    assert(keyinfo_list);
vb@282
  1532
vb@282
  1533
    if (keyinfo_list == NULL)
vb@282
  1534
        return E_INVALIDARG;
vb@282
  1535
vb@282
  1536
    string _pattern = "";
vb@282
  1537
    if (search_pattern)
vb@282
  1538
        _pattern = utf8_string(search_pattern);
vb@282
  1539
    ::stringpair_list_t* _keyinfo_list = NULL;
vb@282
  1540
vb@282
  1541
    PEP_STATUS status = ::OpenPGP_list_keyinfo(get_session(), _pattern.c_str(), &_keyinfo_list);
vb@282
  1542
    assert(status != PEP_OUT_OF_MEMORY);
vb@282
  1543
    if (status == PEP_OUT_OF_MEMORY)
vb@282
  1544
        return E_OUTOFMEMORY;
vb@282
  1545
vb@282
  1546
    if (status != ::PEP_STATUS_OK)
vb@282
  1547
        return FAIL(L"OpenPGP_list_keyinfo", status);
vb@282
  1548
vb@282
  1549
    if (_keyinfo_list && _keyinfo_list->value) {
vb@282
  1550
        ::opt_field_array_from_C(_keyinfo_list, keyinfo_list);
vb@282
  1551
    }
vb@282
  1552
    else {
vb@282
  1553
        ::free_stringpair_list(_keyinfo_list);
vb@282
  1554
        return FAIL(L"OpenPGP_list_keyinfo: no keys found");
vb@282
  1555
    }
vb@282
  1556
vb@282
  1557
    ::free_stringpair_list(_keyinfo_list);
vb@282
  1558
    return S_OK;
vb@282
  1559
vb@282
  1560
}
vb@282
  1561
vb@282
  1562
STDMETHODIMP CpEpEngine::SetOwnKey(pEpIdentity * ident, BSTR fpr, struct pEpIdentity *result)
vb@282
  1563
{
vb@282
  1564
	assert(ident);
vb@282
  1565
	assert(result);
vb@282
  1566
	assert(fpr);
vb@282
  1567
vb@282
  1568
	if (!(ident && result))
vb@282
  1569
		return E_INVALIDARG;
vb@282
  1570
vb@282
  1571
	::pEp_identity *_ident;
vb@282
  1572
	try {
vb@282
  1573
		_ident = new_identity(ident);
vb@282
  1574
	}
vb@282
  1575
	catch (bad_alloc&) {
vb@282
  1576
		return E_OUTOFMEMORY;
vb@282
  1577
	}
vb@282
  1578
	catch (exception& ex) {
vb@282
  1579
		return FAIL(ex.what());
vb@282
  1580
	}
vb@282
  1581
vb@282
  1582
	assert(_ident);
vb@282
  1583
	if (_ident == NULL)
vb@282
  1584
		return E_OUTOFMEMORY;
vb@282
  1585
vb@282
  1586
	string _fpr = utf8_string(fpr);
vb@282
  1587
	PEP_STATUS status = ::set_own_key(get_session(), _ident, _fpr.c_str());
vb@282
  1588
vb@282
  1589
	if (status == PEP_STATUS_OK) {
vb@282
  1590
		copy_identity(result, _ident);
vb@282
  1591
		::free_identity(_ident);
vb@282
  1592
		return S_OK;
vb@282
  1593
	}
vb@282
  1594
	else {
vb@282
  1595
		::free_identity(_ident);
vb@282
  1596
		if (status == PEP_OUT_OF_MEMORY)
vb@282
  1597
			return E_OUTOFMEMORY;
vb@282
  1598
		else
vb@282
  1599
			return FAIL(L"SetOwnKey", status);
vb@282
  1600
	}
vb@282
  1601
vb@282
  1602
	return S_OK;
vb@282
  1603
}
vb@282
  1604
vb@282
  1605
HRESULT CpEpEngine::Fire_MessageToSend(TextMessage * msg)
vb@282
  1606
{
vb@282
  1607
    assert(msg);
vb@282
  1608
    assert(this->client_callbacks_on_sync_thread);
vb@282
  1609
vb@282
  1610
    if (!msg)
vb@282
  1611
        return E_INVALIDARG;
vb@282
  1612
vb@282
  1613
    if (!this->client_callbacks_on_sync_thread)
vb@282
  1614
        return E_ILLEGAL_METHOD_CALL;
vb@282
  1615
vb@282
  1616
    auto result = this->client_callbacks_on_sync_thread->MessageToSend(msg);
vb@282
  1617
vb@282
  1618
    return result;
vb@282
  1619
}
vb@282
  1620
vb@282
  1621
// This method is called from the keysync thread, and dispatches
vb@282
  1622
// the handshake asynchroneously to a background thread,
vb@282
  1623
// so the engine can continue working.
vb@282
  1624
PEP_STATUS CpEpEngine::notifyHandshake(void * obj, pEp_identity *self, pEp_identity *partner, sync_handshake_signal signal)
vb@282
  1625
{
vb@282
  1626
    assert(self && partner);
vb@282
  1627
    if (!(self && partner))
vb@282
  1628
        return PEP_ILLEGAL_VALUE;
vb@282
  1629
vb@282
  1630
    CpEpEngine *me = (CpEpEngine *)obj;
vb@282
  1631
vb@282
  1632
    if (me->notify_handshake_active) {
vb@282
  1633
        // We don't support concurrent handshakes currently, 
vb@282
  1634
        // with the exception of an abort of the handshake, 
vb@282
  1635
        // which we deliver synchroneously (as it's non-blocking).
vb@282
  1636
        if (signal == SYNC_NOTIFY_TIMEOUT) {
vb@282
  1637
            pEpIdentity timeout_self;
vb@282
  1638
            pEpIdentity timeout_partner;
vb@282
  1639
            SyncHandshakeSignal timeout_signal = (SyncHandshakeSignal)signal;
vb@282
  1640
            copy_identity(&timeout_self, self);
vb@282
  1641
            copy_identity(&timeout_partner, partner);
vb@282
  1642
            SyncHandshakeResult result;
vb@282
  1643
            auto res = me->client_callbacks_on_sync_thread->NotifyHandshake(&timeout_self, &timeout_partner, timeout_signal, &result);
vb@282
  1644
vb@282
  1645
            clear_identity_s(timeout_self);
vb@282
  1646
            clear_identity_s(timeout_partner);
vb@282
  1647
vb@282
  1648
            if (FAILED(res)) {
vb@282
  1649
                IErrorInfo* errorInfo = NULL;
vb@282
  1650
                if (FAILED(GetErrorInfo(0, &errorInfo)))
vb@282
  1651
                    errorInfo = NULL;
vb@282
  1652
vb@282
  1653
                // The _com_error takes ownership of the errorInfo
vb@282
  1654
                // and will Release() it. It can also cope with
vb@282
  1655
                // NULL errorInfos.
vb@282
  1656
                _com_error error(res, errorInfo);
vb@282
  1657
vb@282
  1658
                string _description = utf8_string(
vb@282
  1659
                    error.ErrorMessage());
vb@282
  1660
vb@282
  1661
                string _comment = utf8_string(error.Description());
vb@282
  1662
vb@282
  1663
                auto source = error.Source();
vb@282
  1664
                if (source.length() > 0) {
vb@282
  1665
                    _comment += "\r\nSource: ";
vb@282
  1666
                    _comment += utf8_string(source);
vb@282
  1667
                }
vb@282
  1668
vb@282
  1669
                ::log_event(me->keysync_session,
vb@282
  1670
                    "Error on NotifyHandshakeTimeout",
vb@282
  1671
                    "pEp COM Adapter",
vb@282
  1672
                    _description.c_str(),
vb@282
  1673
                    _comment.c_str());
vb@282
  1674
vb@282
  1675
                return PEP_UNKNOWN_ERROR;
vb@282
  1676
            }
vb@282
  1677
vb@282
  1678
            if (res != S_OK)
vb@282
  1679
vb@282
  1680
                return PEP_STATUS_OK;
vb@282
  1681
        }
vb@282
  1682
vb@282
  1683
        ::log_event(me->keysync_session, "Reentrant notify_handshake call!", "pEp COM Adapter", NULL, NULL);
vb@282
  1684
        return PEP_UNKNOWN_ERROR;
vb@282
  1685
    }
vb@282
  1686
vb@282
  1687
    assert(!(me->notify_handshake_active
vb@282
  1688
        || me->notify_handshake_finished
vb@282
  1689
        || me->notify_handshake_thread));
vb@282
  1690
vb@282
  1691
    me->notify_handshake_active = true;
vb@282
  1692
vb@282
  1693
    copy_identity(&me->notify_handshake_self, self);
vb@282
  1694
    copy_identity(&me->notify_handshake_partner, partner);
vb@282
  1695
    me->notify_handshake_signal = (SyncHandshakeSignal)signal;
vb@282
  1696
vb@282
  1697
    // We need to marshal the callbacks to the keysync thread
vb@282
  1698
    LPSTREAM marshaled_callbacks;
vb@282
  1699
vb@282
  1700
    auto result = CoMarshalInterThreadInterfaceInStream(IID_IpEpEngineCallbacks, me->client_callbacks_on_sync_thread, &marshaled_callbacks);
vb@282
  1701
    assert(result == S_OK);
vb@282
  1702
vb@282
  1703
    me->notify_handshake_thread = new thread(notify_handshake_background_thread, me, marshaled_callbacks);
vb@282
  1704
vb@282
  1705
    return PEP_STATUS_OK;
vb@282
  1706
}
vb@282
  1707
vb@282
  1708
// This method also runs in the keysync thread, called by
vb@282
  1709
// retrieve_next_sync_msg() to deliver back the results
vb@282
  1710
// of the sync into the engine.
vb@282
  1711
void CpEpEngine::notify_handshake_deliver_result()
vb@282
  1712
{
vb@282
  1713
    assert(notify_handshake_active
vb@282
  1714
        && notify_handshake_finished);
vb@282
  1715
    if (!(notify_handshake_active
vb@282
  1716
        && notify_handshake_finished))
vb@282
  1717
        return;
vb@282
  1718
vb@282
  1719
    notify_handshake_thread->join();
vb@282
  1720
    notify_handshake_thread = NULL;
vb@282
  1721
vb@282
  1722
    Identity partner = new_identity(&notify_handshake_partner);
vb@282
  1723
vb@282
  1724
    if (FAILED(notify_handshake_error))
vb@282
  1725
    {
vb@282
  1726
        IErrorInfo *errorInfo = NULL;
vb@282
  1727
vb@282
  1728
        if (notify_handshake_error_info) {
vb@282
  1729
            LPVOID lp = NULL;
vb@282
  1730
            auto res = CoGetInterfaceAndReleaseStream(notify_handshake_error_info, IID_IErrorInfo, &lp);
vb@282
  1731
vb@282
  1732
            if (SUCCEEDED(res) && lp)
vb@282
  1733
                errorInfo = static_cast<IErrorInfo*>(lp);
vb@282
  1734
        }
vb@282
  1735
vb@282
  1736
        // The _com_error takes ownership of the errorInfo
vb@282
  1737
        // and will Release() it. It can also cope with
vb@282
  1738
        // NULL errorInfos.
vb@282
  1739
        _com_error error(notify_handshake_error, errorInfo);
vb@282
  1740
vb@282
  1741
        string _description = utf8_string(
vb@282
  1742
            error.ErrorMessage());
vb@282
  1743
vb@282
  1744
        string _comment = utf8_string(error.Description());
vb@282
  1745
vb@282
  1746
        auto source = error.Source();
vb@282
  1747
        if (source.length() > 0) {
vb@282
  1748
            _comment += "\r\nSource: ";
vb@282
  1749
            _comment += utf8_string(source);
vb@282
  1750
        }
vb@282
  1751
vb@282
  1752
        ::log_event(keysync_session,
vb@282
  1753
            "Notify Handshake Failed!",
vb@282
  1754
            "pEp COM Adapter",
vb@282
  1755
            _description.c_str(),
vb@282
  1756
            _comment.c_str());
vb@282
  1757
vb@282
  1758
        ::deliverHandshakeResult(keysync_session, partner, SYNC_HANDSHAKE_CANCEL);
vb@282
  1759
    }
vb@282
  1760
    else {
vb@282
  1761
        ::deliverHandshakeResult(
vb@282
  1762
            keysync_session,
vb@282
  1763
            partner,
vb@282
  1764
            (sync_handshake_result)notify_handshake_result);
vb@282
  1765
    }
vb@282
  1766
    notify_handshake_error_info = NULL;
vb@282
  1767
vb@282
  1768
    clear_identity_s(notify_handshake_self);
vb@282
  1769
    clear_identity_s(notify_handshake_partner);
vb@282
  1770
    notify_handshake_active = false;
vb@282
  1771
    notify_handshake_finished = false;
vb@282
  1772
}
vb@282
  1773
vb@282
  1774
// Method on the background thread, calling into Outlook to
vb@282
  1775
// trigger the Handshake notification, and then scheduling
vb@282
  1776
// the result back to the main thread.
vb@282
  1777
void CpEpEngine::notify_handshake_background_thread(CpEpEngine* self, LPSTREAM marshaled_callbacks)
vb@282
  1778
{
vb@282
  1779
    assert(self);
vb@282
  1780
vb@282
  1781
    // We need to initialize COM here for successfull delivery of the callbacks.
vb@282
  1782
    // As we don't create any COM instances in our thread, the COMINIT value is
vb@282
  1783
    // currently irrelevant, so we go with the safest value.
vb@282
  1784
    auto res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
vb@282
  1785
    assert(res == S_OK);
vb@282
  1786
vb@282
  1787
    LPVOID vp;
vb@282
  1788
vb@282
  1789
    res = CoGetInterfaceAndReleaseStream(marshaled_callbacks, IID_IpEpEngineCallbacks, &vp);
vb@282
  1790
    assert(SUCCEEDED(res));
vb@282
  1791
vb@282
  1792
    auto client_callbacks_on_sync_thread = static_cast<IpEpEngineCallbacks*>(vp);
vb@282
  1793
vb@282
  1794
    self->notify_handshake_error = client_callbacks_on_sync_thread->NotifyHandshake(
vb@282
  1795
        &self->notify_handshake_self,
vb@282
  1796
        &self->notify_handshake_partner,
vb@282
  1797
        self->notify_handshake_signal,
vb@282
  1798
        &self->notify_handshake_result);
vb@282
  1799
vb@282
  1800
    if (FAILED(self->notify_handshake_error)) {
vb@282
  1801
        IErrorInfo* errorInfo = NULL;
vb@282
  1802
vb@282
  1803
        res = GetErrorInfo(0, &errorInfo);
vb@282
  1804
vb@282
  1805
        if (res = S_OK && errorInfo != NULL) {
vb@282
  1806
            res = CoMarshalInterThreadInterfaceInStream(
vb@282
  1807
                IID_IErrorInfo,
vb@282
  1808
                errorInfo,
vb@282
  1809
                &self->notify_handshake_error_info);
vb@282
  1810
vb@282
  1811
            errorInfo->Release();
vb@282
  1812
        }
vb@282
  1813
    }
vb@282
  1814
vb@282
  1815
    // notify the keysync thread.
vb@282
  1816
    self->notify_handshake_finished = true;
vb@282
  1817
    self->keysync_condition.notify_all();
vb@282
  1818
}