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