~stolowski/unity-scopes-api/fix-user-agent-in-preview-rtm

« back to all changes in this revision

Viewing changes to src/scopes/internal/OnlineAccountClientImpl.cpp

  • Committer: Pawel Stolowski
  • Date: 2014-11-04 14:33:07 UTC
  • mfrom: (241.1.20 unity-scopes-api)
  • Revision ID: pawel.stolowski@canonical.com-20141104143307-cs70gjyowjtwpk69
Merged trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2014 Canonical Ltd
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU Lesser General Public License version 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU Lesser General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com>
 
17
*/
 
18
 
 
19
#include <unity/scopes/internal/OnlineAccountClientImpl.h>
 
20
 
 
21
#include <unity/UnityExceptions.h>
 
22
 
 
23
#include <iostream>
 
24
 
 
25
namespace unity
 
26
{
 
27
 
 
28
namespace scopes
 
29
{
 
30
 
 
31
namespace internal
 
32
{
 
33
 
 
34
static void free_error(GError* e)
 
35
{
 
36
    if (e)
 
37
    {
 
38
        g_error_free(e);
 
39
    }
 
40
}
 
41
 
 
42
static void free_variant(GVariant* v)
 
43
{
 
44
    if (v)
 
45
    {
 
46
        g_variant_unref(v);
 
47
    }
 
48
}
 
49
 
 
50
static OnlineAccountClient::ServiceStatus info_to_details(AccountInfo* info, std::string const& error = "")
 
51
{
 
52
    char* client_id = nullptr;
 
53
    char* client_secret = nullptr;
 
54
    char* access_token = nullptr;
 
55
    char* token_secret = nullptr;
 
56
 
 
57
    std::lock_guard<std::mutex> info_lock(info->mutex);
 
58
 
 
59
    if (info->auth_params)
 
60
    {
 
61
        // Look up OAuth 2 parameters
 
62
        g_variant_lookup(info->auth_params.get(), "ClientId", "&s", &client_id);
 
63
        g_variant_lookup(info->auth_params.get(), "ClientSecret", "&s", &client_secret);
 
64
        // If OAuth 2 parameters are not found, fall back to OAuth 1 parameters
 
65
        if (!client_id)
 
66
        {
 
67
            g_variant_lookup(info->auth_params.get(), "ConsumerKey", "&s", &client_id);
 
68
        }
 
69
        if (!client_secret)
 
70
        {
 
71
            g_variant_lookup(info->auth_params.get(), "ConsumerSecret", "&s", &client_secret);
 
72
        }
 
73
    }
 
74
    if (info->session_data)
 
75
    {
 
76
        g_variant_lookup(info->session_data.get(), "AccessToken", "&s", &access_token);
 
77
        g_variant_lookup(info->session_data.get(), "TokenSecret", "&s", &token_secret);
 
78
    }
 
79
 
 
80
    // Gather the latest service details then trigger the client callback
 
81
    OnlineAccountClient::ServiceStatus service_status;
 
82
    service_status.account_id = info->account_id;
 
83
    service_status.service_enabled = info->service_enabled;
 
84
    service_status.service_authenticated = access_token ? info->service_enabled : false;
 
85
    service_status.client_id = client_id ? client_id : "";
 
86
    service_status.client_secret = client_secret ? client_secret : "";
 
87
    service_status.access_token = access_token ? access_token : "";
 
88
    service_status.token_secret = token_secret ? token_secret : "";
 
89
    service_status.error = error;
 
90
 
 
91
    return service_status;
 
92
}
 
93
 
 
94
static gboolean main_loop_is_running_cb(void* user_data)
 
95
{
 
96
    OnlineAccountClientImpl* account_client = reinterpret_cast<OnlineAccountClientImpl*>(user_data);
 
97
    account_client->main_loop_state_notify(true);
 
98
    return G_SOURCE_REMOVE;
 
99
}
 
100
 
 
101
static gboolean main_loop_is_stopping_cb(void* user_data)
 
102
{
 
103
    OnlineAccountClientImpl* account_client = reinterpret_cast<OnlineAccountClientImpl*>(user_data);
 
104
    account_client->main_loop_state_notify(false);
 
105
    return G_SOURCE_REMOVE;
 
106
}
 
107
 
 
108
static gboolean wake_up_event_loop_cb(void* user_data)
 
109
{
 
110
    GMainLoop* event_loop = reinterpret_cast<GMainLoop*>(user_data);
 
111
    g_main_loop_quit(event_loop);
 
112
    return G_SOURCE_REMOVE;
 
113
}
 
114
 
 
115
static void service_login_cb(GObject* source, GAsyncResult* result, void* user_data)
 
116
{
 
117
    SignonAuthSession* session = reinterpret_cast<SignonAuthSession*>(source);
 
118
    AccountInfo* info = reinterpret_cast<AccountInfo*>(user_data);
 
119
 
 
120
    std::unique_lock<std::mutex> info_lock(info->mutex);
 
121
 
 
122
    // Get session data then send a notification with the login result
 
123
    GError* error = nullptr;
 
124
    info->session_data.reset(signon_auth_session_process_finish(session, result, &error), free_variant);
 
125
    std::shared_ptr<GError> error_cleanup(error, free_error);
 
126
 
 
127
    info_lock.unlock();
 
128
    info->account_client->callback(info, error ? error->message : "");
 
129
}
 
130
 
 
131
static void service_update_cb(AgAccountService* account_service, gboolean enabled, AccountInfo* info)
 
132
{
 
133
    std::unique_lock<std::mutex> info_lock(info->mutex);
 
134
 
 
135
    // If another session is currently busy, cancel it
 
136
    if (info->session)
 
137
    {
 
138
        signon_auth_session_cancel(info->session.get());
 
139
    }
 
140
 
 
141
    // Service state has updated, clear the old session data
 
142
    info->service_enabled = enabled;
 
143
 
 
144
    if (enabled)
 
145
    {
 
146
        // Get authorization data then create a new authorization session
 
147
        std::shared_ptr<AgAuthData> auth_data(ag_account_service_get_auth_data(account_service), ag_auth_data_unref);
 
148
 
 
149
        GError* error = nullptr;
 
150
        info->session.reset(signon_auth_session_new(
 
151
            ag_auth_data_get_credentials_id(auth_data.get()), ag_auth_data_get_method(auth_data.get()), &error), g_object_unref);
 
152
        std::shared_ptr<GError> error_cleanup(error, free_error);
 
153
 
 
154
        if (error)
 
155
        {
 
156
            // Send notification that the authorization session failed
 
157
            info_lock.unlock();                                    // LCOV_EXCL_LINE
 
158
            info->account_client->callback(info, error->message);  // LCOV_EXCL_LINE
 
159
            return;
 
160
        }
 
161
 
 
162
        // Get authorization parameters then attempt to signon
 
163
        info->auth_params.reset(
 
164
            g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), nullptr)), free_variant);
 
165
 
 
166
        // Start signon process
 
167
        signon_auth_session_process_async(info->session.get(),
 
168
                                          info->auth_params.get(),
 
169
                                          ag_auth_data_get_mechanism(auth_data.get()),
 
170
                                          nullptr,
 
171
                                          service_login_cb,
 
172
                                          info);
 
173
    }
 
174
    else
 
175
    {
 
176
        // Send notification that account has been disabled
 
177
        info_lock.unlock();
 
178
        info->account_client->callback(info);
 
179
    }
 
180
}
 
181
 
 
182
static void account_enabled_cb(AgManager* manager, AgAccountId account_id, OnlineAccountClientImpl* account_client)
 
183
{
 
184
    if (account_client->has_account(account_id))
 
185
    {
 
186
        // We are already watching this account
 
187
        return;
 
188
    }
 
189
    std::shared_ptr<AgAccount> account(ag_manager_get_account(manager, account_id), g_object_unref);
 
190
    if (!account)
 
191
    {
 
192
        // The account was not found
 
193
        return;  // LCOV_EXCL_LINE
 
194
    }
 
195
    // Find the service we're concerned with
 
196
    std::shared_ptr<GList> services(ag_account_list_services(account.get()), ag_service_list_free);
 
197
    GList* it;
 
198
    for (it = services.get(); it; it = it->next)
 
199
    {
 
200
        AgService* service = reinterpret_cast<AgService*>(it->data);
 
201
        if (account_client->service_name() == ag_service_get_name(service))
 
202
        {
 
203
            std::shared_ptr<AgAccountService> account_service(ag_account_service_new(account.get(), service), g_object_unref);
 
204
            std::shared_ptr<AccountInfo> info(new AccountInfo);
 
205
            info->service_enabled = false;
 
206
            info->account_client = account_client;
 
207
            info->account_service.reset(reinterpret_cast<AgAccountService*>(g_object_ref(account_service.get())), g_object_unref);
 
208
 
 
209
            AgAccount* account = ag_account_service_get_account(account_service.get());
 
210
            g_object_get(account, "id", &info->account_id, nullptr);
 
211
 
 
212
            // Watch for changes to this service
 
213
            info->service_update_signal_id_ = g_signal_connect(account_service.get(), "enabled", G_CALLBACK(service_update_cb), info.get());
 
214
 
 
215
            // Set initial state
 
216
            service_update_cb(account_service.get(), ag_account_service_get_enabled(account_service.get()), info.get());
 
217
            account_client->add_account(account_id, info);
 
218
            break;
 
219
        }
 
220
    }
 
221
}
 
222
 
 
223
static void account_deleted_cb(AgManager*, AgAccountId account_id, OnlineAccountClientImpl* account_client)
 
224
{
 
225
    // A disabled event should have been sent prior to this, so no
 
226
    // need to send any notification.
 
227
    account_client->remove_account(account_id);
 
228
}
 
229
 
 
230
OnlineAccountClientImpl::OnlineAccountClientImpl(std::string const& service_name,
 
231
                                                 std::string const& service_type,
 
232
                                                 std::string const& provider_name,
 
233
                                                 OnlineAccountClient::MainLoopSelect main_loop_select)
 
234
    : service_name_(service_name)
 
235
    , service_type_(service_type)
 
236
    , provider_name_(provider_name)
 
237
    , main_loop_select_(main_loop_select)
 
238
    , main_loop_is_running_(main_loop_select != OnlineAccountClient::CreateInternalMainLoop)
 
239
{
 
240
    // If we are responsible for the main loop
 
241
    if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
 
242
    {
 
243
        // Wait here until either the main loop begins running, or the thread exits
 
244
        std::unique_lock<std::mutex> lock(mutex_);
 
245
        main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);
 
246
        cond_.wait_for(lock, std::chrono::seconds(5), [this] { return main_loop_is_running_; });
 
247
 
 
248
        if (!main_loop_is_running_)
 
249
        {
 
250
            // LCOV_EXCL_START
 
251
            if (main_loop_)
 
252
            {
 
253
                // Quit the main loop, causing the thread to exit
 
254
                g_main_loop_quit(main_loop_.get());
 
255
            }
 
256
            if (main_loop_thread_.joinable())
 
257
            {
 
258
                lock.unlock();
 
259
                main_loop_thread_.join();
 
260
                lock.lock();
 
261
            }
 
262
            if (thread_exception_)
 
263
            {
 
264
                std::rethrow_exception(thread_exception_);
 
265
            }
 
266
            else
 
267
            {
 
268
                throw unity::ResourceException("OnlineAccountClientImpl(): main_loop_thread failed to start.");
 
269
            }
 
270
            // LCOV_EXCL_STOP
 
271
        }
 
272
    }
 
273
    else
 
274
    {
 
275
        construct();
 
276
    }
 
277
}
 
278
 
 
279
OnlineAccountClientImpl::~OnlineAccountClientImpl()
 
280
{
 
281
    {
 
282
        std::lock_guard<std::mutex> lock(mutex_);
 
283
        if (thread_exception_)
 
284
        {
 
285
            // LCOV_EXCL_START
 
286
            try
 
287
            {
 
288
                std::rethrow_exception(thread_exception_);
 
289
            }
 
290
            catch (std::exception const& e)
 
291
            {
 
292
                std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an exception: " << e.what() << std::endl;
 
293
            }
 
294
            catch (...)
 
295
            {
 
296
                std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an unknown exception" << std::endl;
 
297
            }
 
298
            // LCOV_EXCL_STOP
 
299
        }
 
300
    }
 
301
 
 
302
    // If we are responsible for the main loop
 
303
    if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
 
304
    {
 
305
        // Invoke tear_down() from within the main loop
 
306
        std::unique_lock<std::mutex> lock(mutex_);
 
307
        g_main_context_invoke(main_loop_context_.get(), main_loop_is_stopping_cb, this);
 
308
        cond_.wait(lock, [this] { return !main_loop_is_running_; });
 
309
 
 
310
        if (main_loop_)
 
311
        {
 
312
            // Quit the main loop, causing the thread to exit
 
313
            g_main_loop_quit(main_loop_.get());
 
314
        }
 
315
        if (main_loop_thread_.joinable())
 
316
        {
 
317
            lock.unlock();
 
318
            main_loop_thread_.join();
 
319
            lock.lock();
 
320
        }
 
321
    }
 
322
    else
 
323
    {
 
324
        tear_down();
 
325
    }
 
326
 
 
327
    std::unique_lock<std::mutex> lock(callback_mutex_);
 
328
    if (callback_thread_.joinable())
 
329
    {
 
330
        lock.unlock();
 
331
        callback_thread_.join();
 
332
        lock.lock();
 
333
    }
 
334
}
 
335
 
 
336
void OnlineAccountClientImpl::set_service_update_callback(OnlineAccountClient::ServiceUpdateCallback callback)
 
337
{
 
338
    std::unique_lock<std::mutex> lock(mutex_);
 
339
    if (thread_exception_)
 
340
    {
 
341
        std::rethrow_exception(thread_exception_);  // LCOV_EXCL_LINE
 
342
    }
 
343
 
 
344
    std::lock_guard<std::mutex> callback_lock(callback_mutex_);
 
345
    callback_ = callback;
 
346
 
 
347
    if (callback_ != nullptr)
 
348
    {
 
349
        std::vector<OnlineAccountClient::ServiceStatus> known_statuses;
 
350
        for (auto const& info : accounts_)
 
351
        {
 
352
            // We only want to invoke the callback for non-busy sessions, as signon sessions that are currently
 
353
            // busy will end with a callback anyway. info->session is cleared once a signon process has fully
 
354
            // completed, therefore, if the session is null, we know that it is not currently busy.
 
355
            std::unique_lock<std::mutex> info_lock(info.second->mutex);
 
356
            if (info.second->session == nullptr)
 
357
            {
 
358
                info_lock.unlock();
 
359
                known_statuses.push_back(info_to_details(info.second.get()));
 
360
            }
 
361
        }
 
362
 
 
363
        // Invoke callback for all known service statuses
 
364
        lock.unlock();
 
365
        for (auto const& status : known_statuses)
 
366
        {
 
367
            callback_(status);
 
368
        }
 
369
    }
 
370
}
 
371
 
 
372
void OnlineAccountClientImpl::refresh_service_statuses()
 
373
{
 
374
    std::unique_lock<std::mutex> lock(mutex_);
 
375
    if (thread_exception_)
 
376
    {
 
377
        std::rethrow_exception(thread_exception_);  // LCOV_EXCL_LINE
 
378
    }
 
379
 
 
380
    std::shared_ptr<GList> enabled_accounts(ag_manager_list(manager_.get()), ag_manager_list_free);
 
381
    GList* it;
 
382
    for (it = enabled_accounts.get(); it; it = it->next)
 
383
    {
 
384
        AgAccountId account_id = GPOINTER_TO_UINT(it->data);
 
385
        std::shared_ptr<AgAccount> account(ag_manager_get_account(manager_.get(), account_id), g_object_unref);
 
386
        std::string provider_name = ag_account_get_provider_name(account.get());
 
387
        if (provider_name == provider_name_)
 
388
        {
 
389
            lock.unlock();
 
390
            account_enabled_cb(manager_.get(), account_id, this);
 
391
            lock.lock();
 
392
        }
 
393
    }
 
394
}
 
395
 
 
396
std::vector<OnlineAccountClient::ServiceStatus> OnlineAccountClientImpl::get_service_statuses()
 
397
{
 
398
    std::unique_lock<std::mutex> lock(mutex_);
 
399
    if (thread_exception_)
 
400
    {
 
401
        std::rethrow_exception(thread_exception_);  // LCOV_EXCL_LINE
 
402
    }
 
403
 
 
404
    // Return all service statuses
 
405
    std::vector<OnlineAccountClient::ServiceStatus> service_statuses;
 
406
    for (auto const& info : accounts_)
 
407
    {
 
408
        flush_pending_session(info.second.get(), lock);
 
409
        service_statuses.push_back(info_to_details(info.second.get()));
 
410
    }
 
411
    return service_statuses;
 
412
}
 
413
 
 
414
void OnlineAccountClientImpl::register_account_login_item(Result& result,
 
415
                                                          CannedQuery const& query,
 
416
                                                          OnlineAccountClient::PostLoginAction login_passed_action,
 
417
                                                          OnlineAccountClient::PostLoginAction login_failed_action)
 
418
{
 
419
    // If no URI is set on this result, default to a canned query to refresh the scope results
 
420
    if (result.uri().empty())
 
421
    {
 
422
        const unity::scopes::CannedQuery refresh_query(query.scope_id(), query.query_string(), query.query_string().empty() ? query.department_id() : "");
 
423
        result.set_uri(refresh_query.to_uri());
 
424
    }
 
425
 
 
426
    VariantMap account_details_map;
 
427
    account_details_map["service_name"] = service_name_;
 
428
    account_details_map["service_type"] = service_type_;
 
429
    account_details_map["provider_name"] = provider_name_;
 
430
    account_details_map["login_passed_action"] = static_cast<int>(login_passed_action);
 
431
    account_details_map["login_failed_action"] = static_cast<int>(login_failed_action);
 
432
 
 
433
    result["online_account_details"] = Variant(account_details_map);
 
434
}
 
435
 
 
436
void OnlineAccountClientImpl::register_account_login_item(PreviewWidget& widget,
 
437
                                                          OnlineAccountClient::PostLoginAction login_passed_action,
 
438
                                                          OnlineAccountClient::PostLoginAction login_failed_action)
 
439
{
 
440
    VariantMap account_details_map;
 
441
    account_details_map["service_name"] = service_name_;
 
442
    account_details_map["service_type"] = service_type_;
 
443
    account_details_map["provider_name"] = provider_name_;
 
444
    account_details_map["login_passed_action"] = static_cast<int>(login_passed_action);
 
445
    account_details_map["login_failed_action"] = static_cast<int>(login_failed_action);
 
446
 
 
447
    widget.add_attribute_value("online_account_details", Variant(account_details_map));
 
448
}
 
449
 
 
450
void OnlineAccountClientImpl::construct()
 
451
{
 
452
    manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);
 
453
 
 
454
    // Watch for changes to accounts
 
455
    account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);
 
456
    account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);
 
457
 
 
458
    // Now check initial state
 
459
    refresh_service_statuses();
 
460
}
 
461
 
 
462
void OnlineAccountClientImpl::tear_down()
 
463
{
 
464
    // Disconnect signal handlers
 
465
    g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);
 
466
    g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);
 
467
 
 
468
    // Remove all accounts
 
469
    {
 
470
        std::unique_lock<std::mutex> lock(mutex_);
 
471
        for (auto const& info : accounts_)
 
472
        {
 
473
            // Before we nuke the map, ensure that any pending sessions are done
 
474
            {
 
475
                std::lock_guard<std::mutex> info_lock(info.second->mutex);
 
476
                g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);
 
477
            }
 
478
            flush_pending_session(info.second.get(), lock);
 
479
        }
 
480
        accounts_.clear();
 
481
    }
 
482
}
 
483
 
 
484
void OnlineAccountClientImpl::flush_pending_session(AccountInfo* info, std::unique_lock<std::mutex>& lock)
 
485
{
 
486
    std::unique_lock<std::mutex> info_lock(info->mutex);
 
487
 
 
488
    // Wait until all currently running login sessions are done
 
489
    // (ensures that accounts_ is up to date)
 
490
    std::shared_ptr<GMainLoop> event_loop;
 
491
    event_loop.reset(g_main_loop_new(main_loop_context_.get(), true), g_main_loop_unref);
 
492
    while(info->session)
 
493
    {
 
494
        // We need to wait inside an event loop to allow for the main application loop to
 
495
        // process its pending events
 
496
        std::shared_ptr<GSource> source;
 
497
        source.reset(g_timeout_source_new(10), g_source_unref);
 
498
        g_source_set_callback(source.get(), wake_up_event_loop_cb, event_loop.get(), NULL);
 
499
        g_source_attach(source.get(), main_loop_context_.get());
 
500
 
 
501
        info_lock.unlock();
 
502
        lock.unlock();
 
503
        g_main_loop_run(event_loop.get());
 
504
        lock.lock();
 
505
        info_lock.lock();
 
506
    }
 
507
}
 
508
 
 
509
void OnlineAccountClientImpl::main_loop_state_notify(bool is_running)
 
510
{
 
511
    bool was_running = false;
 
512
    {
 
513
        std::lock_guard<std::mutex> lock(mutex_);
 
514
        was_running = main_loop_is_running_;
 
515
    }
 
516
    if (!was_running && is_running)
 
517
    {
 
518
        construct();
 
519
    }
 
520
    else if (was_running && !is_running)
 
521
    {
 
522
        tear_down();
 
523
    }
 
524
 
 
525
    std::lock_guard<std::mutex> lock(mutex_);
 
526
    main_loop_is_running_ = is_running;
 
527
    cond_.notify_all();
 
528
}
 
529
 
 
530
std::shared_ptr<AgManager> OnlineAccountClientImpl::manager()
 
531
{
 
532
    return manager_;
 
533
}
 
534
 
 
535
std::string OnlineAccountClientImpl::service_name()
 
536
{
 
537
    return service_name_;
 
538
}
 
539
 
 
540
std::shared_ptr<GMainContext> OnlineAccountClientImpl::main_loop_context()
 
541
{
 
542
    return main_loop_context_;
 
543
}
 
544
 
 
545
void OnlineAccountClientImpl::callback(AccountInfo* info, std::string const& error)
 
546
{
 
547
    std::unique_lock<std::mutex> lock(callback_mutex_);
 
548
    if (callback_thread_.joinable())
 
549
    {
 
550
        lock.unlock();
 
551
        callback_thread_.join();
 
552
        lock.lock();
 
553
    }
 
554
 
 
555
    OnlineAccountClient::ServiceStatus status = info_to_details(info, error);
 
556
    callback_thread_ = std::thread([this, status]
 
557
    {
 
558
        std::lock_guard<std::mutex> lock(callback_mutex_);
 
559
        if (callback_)
 
560
        {
 
561
            callback_(status);
 
562
        }
 
563
    });
 
564
 
 
565
    // Clear session info
 
566
    std::lock_guard<std::mutex> info_lock(info->mutex);
 
567
    info->session = nullptr;
 
568
}
 
569
 
 
570
bool OnlineAccountClientImpl::has_account(AgAccountId const& account_id)
 
571
{
 
572
    std::lock_guard<std::mutex> lock(mutex_);
 
573
    return accounts_.find(account_id) != accounts_.end();
 
574
}
 
575
 
 
576
void OnlineAccountClientImpl::add_account(AgAccountId const& account_id, std::shared_ptr<AccountInfo> account_info)
 
577
{
 
578
    std::lock_guard<std::mutex> lock(mutex_);
 
579
    accounts_.insert(std::make_pair(account_id, account_info));
 
580
}
 
581
 
 
582
void OnlineAccountClientImpl::remove_account(AgAccountId const& account_id)
 
583
{
 
584
    std::unique_lock<std::mutex> lock(mutex_);
 
585
 
 
586
    // Get account info
 
587
    auto info_it = accounts_.find(account_id);
 
588
    if (info_it == accounts_.end())
 
589
    {
 
590
        return;
 
591
    }
 
592
    auto info = info_it->second;
 
593
 
 
594
    // Before we nuke the pointer, ensure that any pending sessions are done
 
595
    {
 
596
        std::lock_guard<std::mutex> info_lock(info->mutex);
 
597
        g_signal_handler_disconnect(info->account_service.get(), info->service_update_signal_id_);
 
598
    }
 
599
    flush_pending_session(info.get(), lock);
 
600
 
 
601
    // Remove account info from accounts_ map
 
602
    accounts_.erase(account_id);
 
603
}
 
604
 
 
605
void OnlineAccountClientImpl::main_loop_thread()
 
606
{
 
607
    // If something goes wrong causing the thread to abort, the destruction of this pointer update the
 
608
    // main_loop_is_running_ state accordingly.
 
609
    std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(false); });
 
610
 
 
611
    try
 
612
    {
 
613
        main_loop_context_.reset(g_main_context_new(), g_main_context_unref);
 
614
        g_main_context_push_thread_default(main_loop_context_.get());
 
615
 
 
616
        // Stick a method call into the main loop to notify the constructor when the main loop begins running
 
617
        g_main_context_invoke(main_loop_context_.get(), main_loop_is_running_cb, this);
 
618
 
 
619
        // Run the main loop
 
620
        main_loop_.reset(g_main_loop_new(main_loop_context_.get(), true), g_main_loop_unref);
 
621
        g_main_loop_run(main_loop_.get());
 
622
    }
 
623
    // LCOV_EXCL_START
 
624
    catch (std::exception const& e)
 
625
    {
 
626
        std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: " << e.what() << std::endl;
 
627
        std::lock_guard<std::mutex> lock(mutex_);
 
628
        thread_exception_ = std::current_exception();
 
629
    }
 
630
    catch (...)
 
631
    {
 
632
        std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: unknown exception" << std::endl;
 
633
        std::lock_guard<std::mutex> lock(mutex_);
 
634
        thread_exception_ = std::current_exception();
 
635
    }
 
636
    // LCOV_EXCL_STOP
 
637
}
 
638
 
 
639
}  // namespace internal
 
640
 
 
641
}  // namespace scopes
 
642
 
 
643
}  // namespace unity