contact-shared.cpp 10.1 KB
Newer Older
1 2
/*
 * %kadu copyright begin%
Piotr Galiszewski's avatar
Piotr Galiszewski committed
3
 * Copyright 2009, 2010, 2011 Piotr Galiszewski (piotr.galiszewski@kadu.im)
4
 * Copyright 2009 Bartłomiej Zimoń (uzi18@o2.pl)
5
 * Copyright 2010, 2011, 2012, 2013, 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
6
 * Copyright 2009, 2010, 2011, 2012, 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * %kadu copyright end%
 *
 * 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 the Free Software Foundation; either version 2 of
 * the License, or (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, see <http://www.gnu.org/licenses/>.
 */
22

23
#include "accounts/account-manager.h"
Rafał Malinowski's avatar
Rafał Malinowski committed
24
#include "accounts/account.h"
25
#include "buddies/buddy-manager.h"
26
#include "configuration/configuration.h"
27
#include "configuration/deprecated-configuration-api.h"
28
#include "contacts/contact-manager.h"
29
#include "core/injected-factory.h"
30
#include "misc/change-notifier.h"
31
#include "protocols/protocol-factory.h"
Rafał Malinowski's avatar
Rafał Malinowski committed
32
#include "protocols/protocol.h"
33
#include "protocols/protocols-manager.h"
34
#include "roster/roster-entry-state.h"
35
#include "roster/roster-entry.h"
36 37 38

#include "contact-shared.h"

39 40 41
ContactShared::ContactShared(const QUuid &uuid)
        : Shared(uuid), Priority(-1), MaximumImageSize(0), UnreadMessagesCount(0), Blocking(false),
          IgnoreNextStatusChange(false)
42 43 44 45 46
{
}

ContactShared::~ContactShared()
{
47
    ref.ref();
48

49
    disconnect(m_protocolsManager, 0, this, 0);
50

51
    protocolFactoryUnregistered(m_protocolsManager->byName(ContactAccount->protocolName()));
52

53 54
    delete OwnerBuddy;
    delete ContactAccount;
55 56
}

57 58
void ContactShared::setAccountManager(AccountManager *accountManager)
{
59
    m_accountManager = accountManager;
60 61 62 63
}

void ContactShared::setBuddyManager(BuddyManager *buddyManager)
{
64
    m_buddyManager = buddyManager;
65 66 67 68
}

void ContactShared::setConfiguration(Configuration *configuration)
{
69
    m_configuration = configuration;
70 71 72 73
}

void ContactShared::setContactManager(ContactManager *contactManager)
{
74
    m_contactManager = contactManager;
75 76 77 78
}

void ContactShared::setProtocolsManager(ProtocolsManager *protocolsManager)
{
79
    m_protocolsManager = protocolsManager;
80 81 82 83
}

void ContactShared::init()
{
84 85
    Entry = new RosterEntry(this);
    connect(&Entry->hasLocalChangesNotifier(), SIGNAL(changed()), this, SIGNAL(updatedLocally()));
86

87 88
    ContactAccount = new Account();
    OwnerBuddy = new Buddy();
89

90 91 92 93 94 95
    connect(
        m_protocolsManager, SIGNAL(protocolFactoryRegistered(ProtocolFactory *)), this,
        SLOT(protocolFactoryRegistered(ProtocolFactory *)));
    connect(
        m_protocolsManager, SIGNAL(protocolFactoryUnregistered(ProtocolFactory *)), this,
        SLOT(protocolFactoryUnregistered(ProtocolFactory *)));
96

97
    connect(&changeNotifier(), SIGNAL(changed()), this, SLOT(changeNotifierChanged()));
98 99
}

100
StorableObject *ContactShared::storageParent()
Rafał Malinowski's avatar
Rafał Malinowski committed
101
{
102
    return m_contactManager;
Rafał Malinowski's avatar
Rafał Malinowski committed
103 104 105 106
}

QString ContactShared::storageNodeName()
{
107
    return QStringLiteral("Contact");
Rafał Malinowski's avatar
Rafał Malinowski committed
108 109
}

110 111
void ContactShared::load()
{
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    if (!isValidStorage())
        return;

    Shared::load();

    Id = loadValue<QString>("Id");
    Priority = loadValue<int>("Priority", -1);

    // TODO: remove after 01.05.2015
    // It's an explicit hack for update path from 0.10.1-0.11.x to 0.12+. 0.10/0.11 didn't
    // have Detached property. But they did have an explicit hack for totally ignoring
    // what Facebook says about groups, thus allowing users to place their Facebook contacts
    // in groups in Kadu. And without below hack all this group information is overriden
    // by useless a Facebook-provided group until we try to upload something to roster
    // for the first time, we fail and only then we set Detached to true, when group
    // information is already lost.
    bool detached =
        hasValue("Detached") ? loadValue<bool>("Detached", false) : Id.endsWith(QStringLiteral("@chat.facebook.com"));
    bool dirty = loadValue<bool>("Dirty", true);
    if (detached)
        Entry->setDetached();
    else if (dirty)
        Entry->setHasLocalChanges();
    else
        Entry->setSynchronized();

    *ContactAccount = m_accountManager->byUuid(loadValue<QString>("Account"));
    doSetOwnerBuddy(m_buddyManager->byUuid(loadValue<QString>("Buddy")));

    protocolFactoryRegistered(m_protocolsManager->byName(ContactAccount->protocolName()));
    addToBuddy();
143 144
}

145 146
void ContactShared::aboutToBeRemoved()
{
147 148 149
    // clean up references
    removeFromBuddy();
    doSetOwnerBuddy(Buddy::null);
150

151
    changeNotifier().notify();
152 153
}

154 155
void ContactShared::store()
{
156 157
    if (!isValidStorage())
        return;
158

159
    ensureLoaded();
160

161
    Shared::store();
162

163 164
    storeValue("Id", Id);
    storeValue("Priority", Priority);
165

166 167 168
    storeValue("Dirty", RosterEntryState::Synchronized != Entry->state());
    // Detached property needs to be always stored, see the load() method.
    storeValue("Detached", RosterEntryState::Detached == Entry->state());
169

170 171
    storeValue("Account", ContactAccount->uuid().toString());
    storeValue("Buddy", !isAnonymous() ? OwnerBuddy->uuid().toString() : QString());
172
    removeValue("Avatar");
173
    removeValue("Contact");
174 175
}

176 177
bool ContactShared::shouldStore()
{
178
    ensureLoaded();
179

180 181
    if (!UuidStorableObject::shouldStore())
        return false;
182

183 184
    if (Id.isEmpty() || ContactAccount->uuid().isNull())
        return false;
185

186 187 188
    // we dont need data for non-roster contacts only from 4 version of sql schema
    if (m_configuration->deprecatedApi()->readNumEntry("History", "Schema", 0) < 4)
        return true;
189

190
    return !isAnonymous() || rosterEntry()->requiresSynchronization() || customProperties()->shouldStore();
191
}
192

193
void ContactShared::addToBuddy()
194
{
195 196 197
    // dont add to buddy if details are not available
    if (*OwnerBuddy)
        OwnerBuddy->addContact(this);
198 199
}

200
void ContactShared::removeFromBuddy()
201
{
202 203
    if (*OwnerBuddy)
        OwnerBuddy->removeContact(this);
204 205
}

206
void ContactShared::setOwnerBuddy(const Buddy &buddy)
207
{
208
    ensureLoaded();
209

210 211
    if (*OwnerBuddy == buddy)
        return;
212

213 214 215 216 217
    /* NOTE: This guard is needed to avoid deleting this object when removing
     * Contact from Buddy which may hold last reference to it and thus wants to
     * delete it. But we don't want this to happen.
     */
    Contact guard(this);
218

219 220 221
    removeFromBuddy();
    doSetOwnerBuddy(buddy);
    addToBuddy();
222

223 224
    Entry->setHasLocalChanges();
    changeNotifier().notify();
225

226
    emit buddyUpdated();
227 228
}

229
void ContactShared::setContactAccount(const Account &account)
230
{
231
    ensureLoaded();
232

233 234
    if (*ContactAccount == account)
        return;
235

236 237
    if (*ContactAccount && ContactAccount->protocolHandler() && ContactAccount->protocolHandler()->protocolFactory())
        protocolFactoryUnregistered(ContactAccount->protocolHandler()->protocolFactory());
238

239
    *ContactAccount = account;
240

241 242
    if (*ContactAccount && ContactAccount->protocolHandler() && ContactAccount->protocolHandler()->protocolFactory())
        protocolFactoryRegistered(ContactAccount->protocolHandler()->protocolFactory());
243

244
    changeNotifier().notify();
245 246
}

247
void ContactShared::protocolFactoryRegistered(ProtocolFactory *protocolFactory)
248
{
249
    ensureLoaded();
250

251 252
    if (!protocolFactory || !*ContactAccount || ContactAccount->protocolName() != protocolFactory->name())
        return;
253

254
    changeNotifier().notify();
255 256
}

257
void ContactShared::protocolFactoryUnregistered(ProtocolFactory *protocolFactory)
258
{
259
    ensureLoaded();
260

261 262
    if (!protocolFactory || ContactAccount->protocolName() != protocolFactory->name())
        return;
263

264 265 266 267 268 269
    /* NOTE: This guard is needed to avoid deleting this object when detaching
     * Contact from Buddy which may hold last reference to it and thus wants to
     * delete it. But we don't want this to happen.
     */
    Contact guard(this);
    changeNotifier().notify();
270 271
}

272 273
void ContactShared::setId(const QString &id)
{
274
    ensureLoaded();
275

276 277
    if (Id == id)
        return;
278

279 280
    QString oldId = Id;
    Id = id;
281

282
    changeNotifier().notify();
283
}
284

285
RosterEntry *ContactShared::rosterEntry()
286
{
287
    ensureLoaded();
288

289
    return Entry;
290
}
291

292 293 294
/*
 * @todo: move this comment somewhere
 *
295 296 297 298 299 300 301 302 303 304 305 306
 * Sets state if this contact to \p dirty. All contacts are dirty by default.
 *
 * Dirty contacts with anonymous owner buddies are considered dirty removed and will
 * never be added to roster as long as this state lasts and will in effect be removed
 * from remote roster. Dirty contacts with not anonymous owner buddies are considered
 * dirty added and will always be added to roster, even if remote roster marked
 * them as removed.
 *
 * When adding contacts with anononymous owner buddies to the manager, always make sure
 * to mark them not dirty, otherwise they will be considered dirty removed and will
 * not be added to roster if remote roster says so, which is probably not what one expects.
 */
307

308 309
void ContactShared::changeNotifierChanged()
{
310
    emit updated();
311 312
}

313 314
void ContactShared::doSetOwnerBuddy(const Buddy &buddy)
{
315 316
    if (*OwnerBuddy)
        disconnect(*OwnerBuddy, 0, this, 0);
317

318
    *OwnerBuddy = buddy;
319

320 321
    if (*OwnerBuddy)
        connect(*OwnerBuddy, SIGNAL(updated()), this, SIGNAL(buddyUpdated()));
322 323
}

324 325
void ContactShared::setPriority(int priority)
{
326 327 328 329 330 331 332
    ensureLoaded();
    if (Priority != priority)
    {
        Priority = priority;
        changeNotifier().notify();
        emit priorityUpdated();
    }
333 334
}

335 336
bool ContactShared::isAnonymous()
{
337
    ensureLoaded();
338

339 340
    if (!OwnerBuddy)
        return true;
341

342 343
    if (!(*OwnerBuddy))
        return true;
344

345
    return OwnerBuddy->isAnonymous();
346 347
}

348 349
QString ContactShared::display(bool useBuddyData)
{
350
    ensureLoaded();
351

352 353
    if (!useBuddyData || !OwnerBuddy || !(*OwnerBuddy) || OwnerBuddy->display().isEmpty())
        return Id;
354

355
    return OwnerBuddy->display();
356 357
}

358
KaduShared_PropertyPtrReadDef(ContactShared, Account, contactAccount, ContactAccount)
359
    KaduShared_PropertyPtrReadDef(ContactShared, Buddy, ownerBuddy, OwnerBuddy)
Bartosz Brachaczek's avatar
Bartosz Brachaczek committed
360 361

#include "moc_contact-shared.cpp"