278
283
return QVariant();
287
* This helper method make sure that number without an account get registered
288
* correctly with their alternate URIs. In case there is an obvious duplication,
289
* it will try to merge both numbers.
291
void PhoneDirectoryModel::setAccount(PhoneNumber* number, Account* account ) {
292
const URI& strippedUri = number->uri();
293
const bool hasAtSign = strippedUri.hasHostname();
294
number->setAccount(account);
297
NumberWrapper* wrap = m_hDirectory[strippedUri];
299
//Let make sure none is created in the future for nothing
301
//It wont be a duplicate as none exist for this URI
302
const QString extendedUri = strippedUri+'@'+account->hostname();
303
wrap = new NumberWrapper();
304
m_hDirectory [extendedUri] = wrap;
305
m_hSortedNumbers[extendedUri] = wrap;
309
//After all this, it is possible the number is now a duplicate
310
foreach(PhoneNumber* n, wrap->numbers) {
311
if (n != number && n->account() && n->account() == number->account()) {
316
wrap->numbers << number;
322
* This version of getNumber() try to get a phone number with a contact from an URI and account
323
* It will also try to attach an account to existing numbers. This is not 100% reliable, but
324
* it is correct often enough to do it.
326
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Account* account, const QString& type)
328
return getNumber(uri,nullptr,account,type);
331
///Add new information to existing numbers and try to merge
332
PhoneNumber* PhoneDirectoryModel::fillDetails(NumberWrapper* wrap, const URI& strippedUri, Account* account, Contact* contact, const QString& type)
334
//TODO pick the best URI
335
//TODO the account hostname change corner case
336
//TODO search for account that has the same hostname as the URI
338
foreach(PhoneNumber* number, wrap->numbers) {
340
//BEGIN Check if contact can be set
342
//Check if the contact is compatible
343
const bool hasCompatibleContact = contact && (
346
(number->contact()->uid() == contact->uid())
347
&& number->contact() != contact
351
//Check if the URI match
352
const bool hasCompatibleURI = hasCompatibleContact && (number->uri().hasHostname()?(
354
strippedUri == number->uri()
355
/* Something with an hostname can be used with IP2IP */ //TODO support DHT here
356
|| (account && account->id() == Account::ProtocolName::IP2IP)
357
) : ( /* Has no hostname */
358
number->account() && number->uri()+'@'+number->account()->hostname() == strippedUri
361
//Check if the account is compatible
362
const bool hasCompatibleAccount = hasCompatibleURI && ((!account)
363
|| (!number->account())
364
/* Direct match, this is always valid */
365
|| (account == number->account())
366
/* IP2IP is a special case */ //TODO support DHT here
368
account->id() == Account::ProtocolName::IP2IP
369
&& strippedUri.hasHostname()
372
//TODO the Display name could be used to influence the choice
373
//It would need to ignore all possible translated values of unknown
374
//and only be available when another information match
376
//If everything match, set the contact
377
if (hasCompatibleAccount)
378
number->setContact(contact);
379
//END Check if the contact can be set
382
//BEGIN Check is account can be set
383
// Try to match the account
384
// Not perfect, but better than ignoring the high probabilities
385
//TODO only do it is hostname match
386
if ((!account) || account != number->account()) {
388
if (account && (!contact) && !number->account())
389
setAccount(number,account);
391
//Set a type, this has low probabilities of being invalid
392
if ((!number->hasType()) && (!type.isEmpty())) {
393
number->setCategory(NumberCategoryModel::instance()->getCategory(type));
396
//We already have enough information to confirm the choice
397
if (contact && number->contact() &&((contact->uid()) == number->contact()->uid()))
400
//END Check is account can be set
406
///Return/create a number when no information is available
281
407
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, const QString& type)
283
const QString strippedUri = PhoneNumber::stripUri(uri);
409
const URI strippedUri(uri);
284
410
NumberWrapper* wrap = m_hDirectory[strippedUri];
286
412
PhoneNumber* nb = wrap->numbers[0];
287
if ((!nb->m_hasType) && (!type.isEmpty())) {
413
if ((!nb->hasType()) && (!type.isEmpty())) {
288
414
nb->setCategory(NumberCategoryModel::instance()->getCategory(type));
317
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Account* account, const QString& type)
438
///Create a number when a more information is available duplicated ones
439
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Contact* contact, Account* account, const QString& type)
319
const QString strippedUri = PhoneNumber::stripUri(uri);
441
//Remove extra data such as "<sip:" from the main URI
442
const URI strippedUri(uri);
320
444
//See if the number is already loaded
321
NumberWrapper* wrap = m_hDirectory[strippedUri];
322
if ((!wrap) && account && uri.indexOf('@') == -1) {
445
NumberWrapper* wrap = m_hDirectory[strippedUri];
446
NumberWrapper* wrap2 = nullptr;
447
NumberWrapper* wrap3 = nullptr;
449
//Check if the URI is complete or short
450
const bool hasAtSign = strippedUri.hasHostname();
452
//Try to see if there is a better candidate with a suffix (LAN only)
453
if ( !hasAtSign && account ) {
323
454
//Append the account hostname
324
wrap = m_hDirectory[strippedUri+'@'+account->hostname()];
327
foreach(PhoneNumber* number, wrap->numbers) {
328
//Not perfect, but better than ignoring the high probabilities
329
if (!number->account())
330
number->setAccount(account);
331
if ((!number->m_hasType) && (!type.isEmpty())) {
332
number->setCategory(NumberCategoryModel::instance()->getCategory(type));
334
if ((!account) || number->account() == account)
340
PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
341
connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
342
connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
343
number->setAccount(account);
344
number->m_Index = m_lNumbers.size();
345
m_lNumbers << number;
347
wrap = new NumberWrapper();
348
m_hDirectory[strippedUri] = wrap;
349
m_hSortedNumbers[strippedUri] = wrap;
351
wrap->numbers << number;
352
emit layoutChanged();
356
PhoneNumber* PhoneDirectoryModel::getNumber(const QString& uri, Contact* contact, Account* account, const QString& type)
358
const QString strippedUri = PhoneNumber::stripUri(uri);
359
//See if the number is already loaded
360
NumberWrapper* wrap = m_hDirectory[strippedUri];
362
//TODO find something better, it is prone to collisions
363
foreach(PhoneNumber* number, wrap->numbers) {
364
if (!number->contact()) {
365
if (!number->account())
366
number->setAccount(account);
367
number->setContact(contact);
370
foreach(PhoneNumber* number, wrap->numbers) {
371
if ((!number->m_hasType) && (!type.isEmpty())) {
372
number->setCategory(NumberCategoryModel::instance()->getCategory(type));
374
if (((!contact) || number->contact() == contact) && ((!account) || number->account() == account))
380
PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
381
connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
382
connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
383
number->setAccount(account);
384
number->setContact(contact);
385
number->m_Index = m_lNumbers.size();
386
m_lNumbers << number;
388
wrap = new NumberWrapper();
389
m_hDirectory[strippedUri] = wrap;
390
m_hSortedNumbers[strippedUri] = wrap;
392
wrap->numbers << number;
393
emit layoutChanged();
455
wrap2 = m_hDirectory[strippedUri+'@'+account->hostname()];
459
PhoneNumber* confirmedCandidate = fillDetails(wrap,strippedUri,account,contact,type);
461
//URIs can be represented in multiple way, check if a more verbose version
463
PhoneNumber* confirmedCandidate2 = nullptr;
465
//Try to use a PhoneNumber with a contact when possible, work only after the
467
if (confirmedCandidate && confirmedCandidate->contact())
468
confirmedCandidate2 = fillDetails(wrap2,strippedUri,account,contact,type);
470
PhoneNumber* confirmedCandidate3 = nullptr;
472
//Else, try to see if the hostname correspond to the account and flush it
473
//This have to be done after the parent if as the above give "better"
474
//results. It cannot be merged with wrap2 as this check only work if the
475
//candidate has an account.
476
if (hasAtSign && account && strippedUri.hostname() == account->hostname()) {
477
wrap3 = m_hDirectory[strippedUri.userinfo()];
479
foreach(PhoneNumber* number, wrap3->numbers) {
480
if (number->account() == account) {
481
if (contact && ((!number->contact()) || (contact->uid() == number->contact()->uid())))
482
number->setContact(contact); //TODO Check all cases from fillDetails()
483
//TODO add alternate URI
484
confirmedCandidate3 = number;
491
//If multiple PhoneNumber are confirmed, then they are the same, merge them
492
if (confirmedCandidate3 && (confirmedCandidate || confirmedCandidate2)) {
493
confirmedCandidate3->merge(confirmedCandidate?confirmedCandidate:confirmedCandidate2);
495
else if (confirmedCandidate && confirmedCandidate2) {
496
if (confirmedCandidate->contact() && !confirmedCandidate2->contact())
497
confirmedCandidate2->merge(confirmedCandidate);
498
else if (confirmedCandidate2->contact() && !confirmedCandidate->contact())
499
confirmedCandidate->merge(confirmedCandidate2);
502
//Empirical testing resulted in this as the best return order
503
//The merge may have failed either in the "if" above or in the merging code
504
if (confirmedCandidate2)
505
return confirmedCandidate2;
506
if (confirmedCandidate)
507
return confirmedCandidate;
508
if (confirmedCandidate3)
509
return confirmedCandidate3;
511
//No better candidates were found than the original assumption, use it
513
foreach(PhoneNumber* number, wrap->numbers) {
514
if (((!account) || number->account() == account) && ((!contact) || ((*contact) == number->contact()) || (!number->contact()))) {
515
//Assume this is valid until a smarter solution is implemented to merge both
516
//For a short time, a placeholder contact and a contact can coexist, drop the placeholder
517
if (contact && (!number->contact() || (contact->uid() == number->contact()->uid())))
518
number->setContact(contact);
526
PhoneNumber* number = new PhoneNumber(strippedUri,NumberCategoryModel::instance()->getCategory(type));
527
number->setAccount(account);
528
number->setIndex( m_lNumbers.size());
530
number->setContact(contact);
531
m_lNumbers << number;
532
connect(number,SIGNAL(callAdded(Call*)),this,SLOT(slotCallAdded(Call*)));
533
connect(number,SIGNAL(changed()),this,SLOT(slotChanged()));
535
wrap = new NumberWrapper();
536
m_hDirectory [strippedUri] = wrap;
537
m_hSortedNumbers[strippedUri] = wrap;
539
//Also add its alternative URI, it should be safe to do
540
if ( !hasAtSign && account && !account->hostname().isEmpty() ) {
541
const QString extendedUri = strippedUri+'@'+account->hostname();
542
//Also check if it hasn't been created by setAccount
543
if ((!wrap2) && (!m_hDirectory[extendedUri])) {
544
wrap2 = new NumberWrapper();
545
m_hDirectory [extendedUri] = wrap2;
546
m_hSortedNumbers[extendedUri] = wrap2;
548
wrap2->numbers << number;
552
wrap->numbers << number;
553
emit layoutChanged();