account.cpp 16.3 KB
Newer Older
yanmorin's avatar
 
yanmorin committed
1
/*
Adrien Béraud's avatar
Adrien Béraud committed
2
 *  Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
3 4
 *
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
5
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
6
 *  Author : Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
yanmorin's avatar
 
yanmorin committed
7 8 9
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 3 of the License, or
yanmorin's avatar
 
yanmorin committed
11 12 13 14 15 16 17 18 19
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
21 22 23 24 25 26 27 28 29 30 31
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
yanmorin's avatar
 
yanmorin committed
32
 */
33

34 35 36
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
yanmorin's avatar
 
yanmorin committed
37
#include "account.h"
Stepan Salenikovich's avatar
Stepan Salenikovich committed
38

39
#include <algorithm>
40
#include <iterator>
Stepan Salenikovich's avatar
Stepan Salenikovich committed
41
#include <mutex>
42

Adrien Béraud's avatar
Adrien Béraud committed
43
#ifdef RING_VIDEO
44
#include "libav_utils.h"
45
#endif
yanmorin's avatar
 
yanmorin committed
46

47
#include "logger.h"
48 49
#include "manager.h"

50
#include "client/configurationmanager.h"
51
#include "account_schema.h"
52
#include "string_utils.h"
53 54 55
#include "config/yamlparser.h"

#include <yaml-cpp/yaml.h>
56

Stepan Salenikovich's avatar
Stepan Salenikovich committed
57 58 59
#include "upnp/upnp.h"
#include "ip_utils.h"

Guillaume Roguez's avatar
Guillaume Roguez committed
60 61
namespace ring {

62 63 64 65 66 67 68 69
const char * const Account::AUDIO_CODECS_KEY            = "audioCodecs";  // 0/9/110/111/112/
const char * const Account::VIDEO_CODECS_KEY            = "videoCodecs";
const char * const Account::VIDEO_CODEC_ENABLED         = "enabled";
const char * const Account::VIDEO_CODEC_NAME            = "name";
const char * const Account::VIDEO_CODEC_PARAMETERS      = "parameters";
const char * const Account::VIDEO_CODEC_BITRATE         = "bitrate";
const char * const Account::RINGTONE_PATH_KEY           = "ringtonePath";
const char * const Account::RINGTONE_ENABLED_KEY        = "ringtoneEnabled";
70
const char * const Account::VIDEO_ENABLED_KEY           = "videoEnabled";
71 72 73 74 75
const char * const Account::DISPLAY_NAME_KEY            = "displayName";
const char * const Account::ALIAS_KEY                   = "alias";
const char * const Account::TYPE_KEY                    = "type";
const char * const Account::ID_KEY                      = "id";
const char * const Account::USERNAME_KEY                = "username";
76
const char * const Account::AUTHENTICATION_USERNAME_KEY = "authenticationUsername";
77 78 79 80 81
const char * const Account::PASSWORD_KEY                = "password";
const char * const Account::HOSTNAME_KEY                = "hostname";
const char * const Account::ACCOUNT_ENABLE_KEY          = "enable";
const char * const Account::ACCOUNT_AUTOANSWER_KEY      = "autoAnswer";
const char * const Account::MAILBOX_KEY                 = "mailbox";
82
const char * const Account::DEFAULT_USER_AGENT          = PACKAGE_NAME "/" PACKAGE_VERSION;
83
const char * const Account::USER_AGENT_KEY              = "useragent";
84
const char * const Account::HAS_CUSTOM_USER_AGENT_KEY   = "hasCustomUserAgent";
85
const char * const Account::PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled";
86
const char * const Account::UPNP_ENABLED_KEY            = "upnpEnabled";
87

88 89 90 91 92
using std::map;
using std::string;
using std::vector;


93 94
Account::Account(const string &accountID)
    : accountID_(accountID)
95 96 97
    , username_()
    , hostname_()
    , alias_()
98
    , enabled_(true)
99
    , autoAnswerEnabled_(false)
100
    , registrationState_(RegistrationState::UNREGISTERED)
101
    , audioCodecList_()
102
    , videoCodecList_()
103
    , audioCodecStr_()
104
    , ringtonePath_("")
105 106
    , ringtoneEnabled_(true)
    , displayName_("")
107
    , userAgent_(DEFAULT_USER_AGENT)
108
    , hasCustomUserAgent_(false)
109
    , mailBox_()
110
    , upnpEnabled_(false)
Julien Bonjean's avatar
Julien Bonjean committed
111
{
112 113 114 115
    std::random_device rdev;
    std::seed_seq seed {rdev(), rdev()};
    rand_.seed(seed);

116
    // Initialize the codec order, used when creating a new account
117
    loadDefaultCodecs();
118
    #ifdef __ANDROID__
Adrien Béraud's avatar
Adrien Béraud committed
119
        ringtonePath_ = "/data/data/cx.ring/files/ringtones/konga.ul";
120
    #else
Adrien Béraud's avatar
Adrien Béraud committed
121
        ringtonePath_ = "/usr/share/ring/ringtones/konga.ul";
122
    #endif
yanmorin's avatar
 
yanmorin committed
123 124
}

Julien Bonjean's avatar
Julien Bonjean committed
125
Account::~Account()
126
{}
127

Guillaume Roguez's avatar
Guillaume Roguez committed
128 129 130 131 132 133 134 135 136 137 138 139
void
Account::attachCall(const string& id)
{
    callIDSet_.insert(id);
}

void
Account::detachCall(const string& id)
{
    callIDSet_.erase(id);
}

140 141 142
void
Account::freeAccount()
{
Guillaume Roguez's avatar
Guillaume Roguez committed
143 144
    for (const auto& id : callIDSet_)
        Manager::instance().hangupCall(id);
145
    doUnregister();
146 147
}

148
void Account::setRegistrationState(RegistrationState state)
Julien Bonjean's avatar
Julien Bonjean committed
149
{
Tristan Matthews's avatar
Tristan Matthews committed
150 151
    if (state != registrationState_) {
        registrationState_ = state;
Emmanuel Milou's avatar
Emmanuel Milou committed
152
        // Notify the client
153
        ConfigurationManager *c(Manager::instance().getConfigurationManager());
154
        c->registrationStateChanged(accountID_, static_cast<int32_t>(registrationState_));
155
        c->volatileAccountDetailsChanged(accountID_, getVolatileAccountDetails());
Emmanuel Milou's avatar
Emmanuel Milou committed
156
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
157
}
Emmanuel Milou's avatar
Emmanuel Milou committed
158

159
void Account::loadDefaultCodecs()
Julien Bonjean's avatar
Julien Bonjean committed
160
{
161 162 163 164
    // TODO
    // CodecMap codecMap = Manager::instance ().getCodecDescriptorMap ().getCodecsMap();

    // Initialize codec
165
    vector<string> result;
166 167 168 169
    result.push_back("0");
    result.push_back("3");
    result.push_back("8");
    result.push_back("9");
170
    result.push_back("104");
171 172 173
    result.push_back("110");
    result.push_back("111");
    result.push_back("112");
174

175
    setActiveAudioCodecs(result);
Adrien Béraud's avatar
Adrien Béraud committed
176
#ifdef RING_VIDEO
177
    // we don't need to validate via setVideoCodecs, since these are defaults
Alexandre Lision's avatar
Alexandre Lision committed
178
    videoCodecList_ = libav_utils::getDefaultVideoCodecs();
179
#endif
180
}
181

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

void Account::serialize(YAML::Emitter &out)
{
    out << YAML::Key << ID_KEY << YAML::Value << accountID_;
    out << YAML::Key << ALIAS_KEY << YAML::Value << alias_;
    out << YAML::Key << ACCOUNT_ENABLE_KEY << YAML::Value << enabled_;
    out << YAML::Key << TYPE_KEY << YAML::Value << getAccountType();
    out << YAML::Key << AUDIO_CODECS_KEY << YAML::Value << audioCodecStr_;
    out << YAML::Key << MAILBOX_KEY << YAML::Value << mailBox_;
    out << YAML::Key << ACCOUNT_AUTOANSWER_KEY << YAML::Value << autoAnswerEnabled_;
    out << YAML::Key << RINGTONE_ENABLED_KEY << YAML::Value << ringtoneEnabled_;
    out << YAML::Key << RINGTONE_PATH_KEY << YAML::Value << ringtonePath_;
    out << YAML::Key << HAS_CUSTOM_USER_AGENT_KEY << YAML::Value << hasCustomUserAgent_;
    out << YAML::Key << USER_AGENT_KEY << YAML::Value << userAgent_;
    out << YAML::Key << USERNAME_KEY << YAML::Value << username_;
    out << YAML::Key << DISPLAY_NAME_KEY << YAML::Value << displayName_;
    out << YAML::Key << HOSTNAME_KEY << YAML::Value << hostname_;
199
    out << YAML::Key << UPNP_ENABLED_KEY << YAML::Value << upnpEnabled_;
200 201 202 203
}

void Account::unserialize(const YAML::Node &node)
{
Guillaume Roguez's avatar
Guillaume Roguez committed
204 205
    using yaml_utils::parseValue;

206 207 208 209 210 211 212 213 214 215
    parseValue(node, ALIAS_KEY, alias_);
    parseValue(node, ACCOUNT_ENABLE_KEY, enabled_);
    parseValue(node, USERNAME_KEY, username_);
    parseValue(node, ACCOUNT_AUTOANSWER_KEY, autoAnswerEnabled_);
    //parseValue(node, PASSWORD_KEY, password_);

    parseValue(node, MAILBOX_KEY, mailBox_);
    parseValue(node, AUDIO_CODECS_KEY, audioCodecStr_);

    // Update codec list which one is used for SDP offer
Guillaume Roguez's avatar
Guillaume Roguez committed
216
    setActiveAudioCodecs(split_string(audioCodecStr_, '/'));
217 218 219 220 221 222 223
    parseValue(node, DISPLAY_NAME_KEY, displayName_);
    parseValue(node, HOSTNAME_KEY, hostname_);

    parseValue(node, HAS_CUSTOM_USER_AGENT_KEY, hasCustomUserAgent_);
    parseValue(node, USER_AGENT_KEY, userAgent_);
    parseValue(node, RINGTONE_PATH_KEY, ringtonePath_);
    parseValue(node, RINGTONE_ENABLED_KEY, ringtoneEnabled_);
224 225

    parseValue(node, UPNP_ENABLED_KEY, upnpEnabled_);
226 227 228 229 230
}

void Account::setAccountDetails(const std::map<std::string, std::string> &details)
{
    // Account setting common to SIP and IAX
Guillaume Roguez's avatar
Guillaume Roguez committed
231 232 233 234 235 236 237 238 239 240
    parseString(details, Conf::CONFIG_ACCOUNT_ALIAS, alias_);
    parseBool(details, Conf::CONFIG_ACCOUNT_ENABLE, enabled_);
    parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username_);
    parseString(details, Conf::CONFIG_ACCOUNT_HOSTNAME, hostname_);
    parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_);
    parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_);
    parseBool(details, Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_);
    parseBool(details, Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_);
    parseString(details, Conf::CONFIG_RINGTONE_PATH, ringtonePath_);
    parseBool(details, Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_);
241
    if (hasCustomUserAgent_)
Guillaume Roguez's avatar
Guillaume Roguez committed
242
        parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_);
243 244
    else
        userAgent_ = DEFAULT_USER_AGENT;
245
    parseBool(details, Conf::CONFIG_UPNP_ENABLED, upnpEnabled_);
246 247 248 249 250 251
}

std::map<std::string, std::string> Account::getAccountDetails() const
{
    std::map<std::string, std::string> a;

Guillaume Roguez's avatar
Guillaume Roguez committed
252 253 254 255 256 257
    a[Conf::CONFIG_ACCOUNT_ALIAS] = alias_;
    a[Conf::CONFIG_ACCOUNT_ENABLE] = enabled_ ? "true" : "false";
    a[Conf::CONFIG_ACCOUNT_TYPE] = getAccountType();
    a[Conf::CONFIG_ACCOUNT_HOSTNAME] = hostname_;
    a[Conf::CONFIG_ACCOUNT_USERNAME] = username_;
    a[Conf::CONFIG_ACCOUNT_MAILBOX] = mailBox_;
258 259 260

    RegistrationState state(registrationState_);

261 262
    // This method should only stores user-settable fields
    // For legacy reasons, the STATUS will be kept for some time
Guillaume Roguez's avatar
Guillaume Roguez committed
263 264 265 266 267 268
    a[Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS] = mapStateNumberToString(state);
    a[Conf::CONFIG_ACCOUNT_USERAGENT] = hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT;
    a[Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT] = hasCustomUserAgent_ ? TRUE_STR : FALSE_STR;
    a[Conf::CONFIG_ACCOUNT_AUTOANSWER] = autoAnswerEnabled_ ? TRUE_STR : FALSE_STR;
    a[Conf::CONFIG_RINGTONE_ENABLED] = ringtoneEnabled_ ? TRUE_STR : FALSE_STR;
    a[Conf::CONFIG_RINGTONE_PATH] = ringtonePath_;
269
    a[Conf::CONFIG_UPNP_ENABLED] = upnpEnabled_ ? TRUE_STR : FALSE_STR;
270 271 272 273

    return a;
}

274 275 276 277
std::map<std::string, std::string> Account::getVolatileAccountDetails() const
{
    std::map<std::string, std::string> a;

Guillaume Roguez's avatar
Guillaume Roguez committed
278
    a[Conf::CONFIG_ACCOUNT_REGISTRATION_STATUS] = mapStateNumberToString(registrationState_);
279 280 281
    return a;
}

Adrien Béraud's avatar
Adrien Béraud committed
282
#ifdef RING_VIDEO
283 284 285 286 287 288 289 290
static bool
isPositiveInteger(const string &s)
{
    string::const_iterator it = s.begin();
    while (it != s.end() and std::isdigit(*it))
        ++it;
    return not s.empty() and it == s.end();
}
291

292 293 294 295 296
static bool
isBoolean(const string &s)
{
    return s == "true" or s == "false";
}
297

298 299 300 301 302 303 304 305 306 307 308 309 310
template <typename Predicate>
static bool
isFieldValid(const map<string, string> &codec, const char *field, Predicate p)
{
    map<string, string>::const_iterator key(codec.find(field));
    return key != codec.end() and p(key->second);
}

static bool
isCodecValid(const map<string, string> &codec, const vector<map<string, string> > &defaults)
{
    const map<string, string>::const_iterator name(codec.find(Account::VIDEO_CODEC_NAME));
    if (name == codec.end()) {
Adrien Béraud's avatar
Adrien Béraud committed
311
        RING_ERR("Field \"name\" missing in codec specification");
312
        return false;
313 314
    }

315 316 317 318 319 320
    // check that it's in the list of valid codecs and that it has all the required fields
    for (const auto &i : defaults) {
        const auto defaultName = i.find(Account::VIDEO_CODEC_NAME);
        if (defaultName->second == name->second) {
            return isFieldValid(codec, Account::VIDEO_CODEC_BITRATE, isPositiveInteger)
                and isFieldValid(codec, Account::VIDEO_CODEC_ENABLED, isBoolean);
321
        }
322
    }
Adrien Béraud's avatar
Adrien Béraud committed
323
    RING_ERR("Codec %s not supported", name->second.c_str());
324 325
    return false;
}
326

327 328 329
static bool
isCodecListValid(const vector<map<string, string> > &list)
{
Alexandre Lision's avatar
Alexandre Lision committed
330
    const auto defaults(libav_utils::getDefaultVideoCodecs());
331
    if (list.size() != defaults.size()) {
Adrien Béraud's avatar
Adrien Béraud committed
332
        RING_ERR("New codec list has a different length than the list of supported codecs");
333 334 335
        return false;
    }

336 337 338
    // make sure that all codecs are present
    for (const auto &i : list) {
        if (not isCodecValid(i, defaults))
339 340
            return false;
    }
341
    return true;
342 343 344
}
#endif

345
void Account::setVideoCodecs(const vector<map<string, string> > &list)
346
{
Adrien Béraud's avatar
Adrien Béraud committed
347
#ifdef RING_VIDEO
348 349
    if (isCodecListValid(list))
        videoCodecList_ = list;
350 351
#else
    (void) list;
352
#endif
353
}
354

355 356 357
// Convert a list of payloads in a special format, readable by the server.
// Required format: payloads separated by slashes.
// @return std::string The serializable string
358 359
static std::string
join_string(const std::vector<std::string> &v)
360 361 362 363 364 365
{
    std::ostringstream os;
    std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(os, "/"));
    return os.str();
}

366
void Account::setActiveAudioCodecs(const vector<string> &list)
Julien Bonjean's avatar
Julien Bonjean committed
367
{
368
    // first clear the previously stored codecs
369
    audioCodecList_.clear();
370

Emmanuel Milou's avatar
Emmanuel Milou committed
371
    // list contains the ordered payload of active codecs picked by the user for this account
372
    // we used the codec vector to save the order.
373 374
    for (const auto &item : list) {
        int payload = std::atoi(item.c_str());
Tristan Matthews's avatar
Tristan Matthews committed
375
        audioCodecList_.push_back(payload);
Emmanuel Milou's avatar
Emmanuel Milou committed
376 377
    }

378
    // update the codec string according to new codec selection
379
    audioCodecStr_ = join_string(list);
Emmanuel Milou's avatar
Emmanuel Milou committed
380
}
381

382
string Account::mapStateNumberToString(RegistrationState state)
383
{
384 385 386 387 388 389 390 391 392 393 394 395 396 397
#define CASE_STATE(X) case RegistrationState::X: \
                           return #X

    switch (state) {
        CASE_STATE(UNREGISTERED);
        CASE_STATE(TRYING);
        CASE_STATE(REGISTERED);
        CASE_STATE(ERROR_GENERIC);
        CASE_STATE(ERROR_AUTH);
        CASE_STATE(ERROR_NETWORK);
        CASE_STATE(ERROR_HOST);
        CASE_STATE(ERROR_SERVICE_UNAVAILABLE);
        CASE_STATE(ERROR_EXIST_STUN);
        CASE_STATE(ERROR_NOT_ACCEPTABLE);
398 399 400
        default:
            return "ERROR_GENERIC";
    }
401

402
#undef CASE_STATE
403
}
404 405 406 407 408 409 410

vector<map<string, string> >
Account::getAllVideoCodecs() const
{
    return videoCodecList_;
}

411 412 413 414 415
static bool
is_inactive(const map<string, string> &codec)
{
    map<string, string>::const_iterator iter = codec.find(Account::VIDEO_CODEC_ENABLED);
    return iter == codec.end() or iter->second != "true";
416 417
}

418 419 420 421 422 423 424 425
vector<int>
Account::getDefaultAudioCodecs()
{
    vector<int> result;
    result.push_back(0);
    result.push_back(3);
    result.push_back(8);
    result.push_back(9);
426
    result.push_back(104);
427 428 429 430 431 432 433
    result.push_back(110);
    result.push_back(111);
    result.push_back(112);

    return result;
}

434 435 436
vector<map<string, string> >
Account::getActiveVideoCodecs() const
{
437 438 439
    if (not videoEnabled_)
        return vector<map<string, string>>();

440 441 442 443 444
    // FIXME: validate video codec details first
    vector<map<string, string> > result(videoCodecList_);
    result.erase(std::remove_if(result.begin(), result.end(), is_inactive), result.end());
    return result;
}
445 446 447 448

#define find_iter()                             \
        const auto iter = details.find(key);    \
        if (iter == details.end()) {            \
Adrien Béraud's avatar
Adrien Béraud committed
449
            RING_ERR("Couldn't find key \"%s\"", key); \
450 451 452 453 454 455 456 457 458 459 460 461 462 463
            return;                             \
        }

void
Account::parseString(const std::map<std::string, std::string> &details, const char *key, std::string &s)
{
    find_iter();
    s = iter->second;
}

void
Account::parseBool(const std::map<std::string, std::string> &details, const char *key, bool &b)
{
    find_iter();
Tristan Matthews's avatar
Tristan Matthews committed
464
    b = iter->second == TRUE_STR;
465 466 467
}

#undef find_iter
Guillaume Roguez's avatar
Guillaume Roguez committed
468

469 470 471 472 473 474 475 476 477
/**
 * Checks whether the upnp settings is enabled in the account.
 * If so, tries to get a controller with a valid IGD.
 * If not, destroys the controller.
 *
 * Returns whether or not there is a controller with a valid IGD to use.
 */
bool
Account::checkUPnP()
Stepan Salenikovich's avatar
Stepan Salenikovich committed
478 479 480
{
    std::unique_lock<std::mutex> lk(upnp_mtx);

481 482 483 484 485 486 487 488 489 490
    if (upnpEnabled_ != (bool)upnp_) {
        if (upnpEnabled_){
            upnp_.reset(new upnp::Controller());
            if (upnp_->hasValidIGD())
                upnpIp_ = upnp_->getExternalIP();
        } else {
            upnp_.reset();
            upnpIp_ = IpAddr{};
        }
    }
Stepan Salenikovich's avatar
Stepan Salenikovich committed
491

492
    return upnp_ and upnp_->hasValidIGD();
Stepan Salenikovich's avatar
Stepan Salenikovich committed
493 494
}

495 496 497 498
/**
 * Returns whether or not the upnp controller has a valid IGD
 * with which to make port mappings
 */
Stepan Salenikovich's avatar
Stepan Salenikovich committed
499 500 501 502
bool
Account::getUseUPnP() const
{
    std::unique_lock<std::mutex> lk(upnp_mtx);
503
    return upnp_ and upnp_->hasValidIGD();
Stepan Salenikovich's avatar
Stepan Salenikovich committed
504 505
}

Guillaume Roguez's avatar
Guillaume Roguez committed
506
} // namespace ring