PageRenderTime 21ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/qutim-0.3.1/protocols/oscar/src/icqcontact.cpp

#
C++ | 436 lines | 348 code | 47 blank | 41 comment | 74 complexity | 1633ed6edd77ebc78cbd48797f8b6e45 MD5 | raw file
Possible License(s): CC-BY-SA-4.0, GPL-3.0, LGPL-2.1, GPL-2.0
  1. /****************************************************************************
  2. **
  3. ** qutIM - instant messenger
  4. **
  5. ** Copyright Š 2011 Ruslan Nigmatullin <euroelessar@yandex.ru>
  6. **
  7. *****************************************************************************
  8. **
  9. ** $QUTIM_BEGIN_LICENSE$
  10. ** This program is free software: you can redistribute it and/or modify
  11. ** it under the terms of the GNU General Public License as published by
  12. ** the Free Software Foundation, either version 3 of the License, or
  13. ** (at your option) any later version.
  14. **
  15. ** This program is distributed in the hope that it will be useful,
  16. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  18. ** See the GNU General Public License for more details.
  19. **
  20. ** You should have received a copy of the GNU General Public License
  21. ** along with this program. If not, see http://www.gnu.org/licenses/.
  22. ** $QUTIM_END_LICENSE$
  23. **
  24. ****************************************************************************/
  25. #include "icqcontact_p.h"
  26. #include "messages.h"
  27. #include "buddycaps.h"
  28. #include "icqaccount_p.h"
  29. #include "metainfo/infometarequest.h"
  30. #include "qutim/chatsession.h"
  31. #include "qutim/notification.h"
  32. #include "qutim/tooltip.h"
  33. #include "qutim/extensionicon.h"
  34. #include <QApplication>
  35. #include <QVariant>
  36. #include <qutim/authorizationdialog.h>
  37. namespace qutim_sdk_0_3 {
  38. namespace oscar {
  39. ChatStateUpdater::ChatStateUpdater()
  40. {
  41. m_timer.setInterval(500);
  42. connect(&m_timer, SIGNAL(timeout()), SLOT(sendState()));
  43. }
  44. void ChatStateUpdater::updateState(IcqContact *contact, ChatState state)
  45. {
  46. m_states.insert(contact, state);
  47. if (!m_timer.isActive())
  48. m_timer.start();
  49. }
  50. void ChatStateUpdater::sendState()
  51. {
  52. QMutableHashIterator<QWeakPointer<IcqContact>, ChatState> it(m_states);
  53. while (it.hasNext()) {
  54. IcqContact *contact = it.next().key().data();
  55. if (!contact) {
  56. it.remove(); // Contact is destroyed
  57. continue;
  58. }
  59. Status::Type status = contact->account()->status().type();
  60. if (status == Status::Offline || status == Status::Connecting) {
  61. it.remove(); // We can't send any packets in offline or connecting state.
  62. continue;
  63. }
  64. if (contact->account()->connection()->testRate(MessageFamily, MessageMtn)) {
  65. sendState(contact, it.value());
  66. it.remove();
  67. }
  68. }
  69. if (m_states.isEmpty())
  70. m_timer.stop();
  71. }
  72. void ChatStateUpdater::sendState(IcqContact *contact, ChatState state)
  73. {
  74. MTN type = MtnUnknown;
  75. if (state == ChatStatePaused)
  76. type = MtnTyped;
  77. else if (state == ChatStateComposing)
  78. type = MtnBegun;
  79. else if (state == ChatStateGone)
  80. type = MtnGone;
  81. else if (state == ChatStateInActive || state == ChatStateActive)
  82. type = MtnFinished;
  83. if (type == MtnUnknown)
  84. return;
  85. SNAC sn(MessageFamily, MessageMtn);
  86. sn.append(Cookie(true));
  87. sn.append<quint16>(1); // channel?
  88. sn.append<quint8>(contact->id());
  89. sn.append<quint16>(type);
  90. contact->account()->connection()->send(sn);
  91. }
  92. Q_GLOBAL_STATIC(ChatStateUpdater, chatStateUpdater)
  93. void IcqContactPrivate::clearCapabilities()
  94. {
  95. flags = 0;
  96. capabilities.clear();
  97. }
  98. void IcqContactPrivate::requestNick()
  99. {
  100. Q_Q(IcqContact);
  101. if (name.isEmpty()) {
  102. ShortInfoMetaRequest *req = new ShortInfoMetaRequest(q);
  103. QObject::connect(req, SIGNAL(done(bool)), q, SLOT(infoReceived(bool)));
  104. req->send();
  105. }
  106. }
  107. void IcqContactPrivate::setCapabilities(const Capabilities &caps)
  108. {
  109. clearCapabilities();
  110. foreach(const Capability &capability, caps)
  111. {
  112. if (capability.match(ICQ_CAPABILITY_HTMLMSGS))
  113. flags |= html_support;
  114. else if (capability.match(ICQ_CAPABILITY_UTF8))
  115. flags |= utf8_support;
  116. else if (capability.match(ICQ_CAPABILITY_SRVxRELAY))
  117. flags |= srvrelay_support;
  118. }
  119. capabilities = caps;
  120. emit q_ptr->capabilitiesChanged(caps);
  121. }
  122. FeedbagItem IcqContactPrivate::getNotInListGroup()
  123. {
  124. FeedbagItem group = account->feedbag()->groupItem(QLatin1String("Default Group"), Feedbag::GenerateId);
  125. if (!group.isInList()) {
  126. group.setField(SsiGroupDefault);
  127. group.add();
  128. }
  129. return group;
  130. }
  131. IcqContact::IcqContact(const QString &uin, IcqAccount *account) :
  132. Contact(account), d_ptr(new IcqContactPrivate)
  133. {
  134. Q_D(IcqContact);
  135. d->q_ptr = this;
  136. d->account = account;
  137. d->uin = uin;
  138. d->isInList = false;
  139. d->status = Status::Offline;
  140. d->clearCapabilities();
  141. d->state = ChatStateGone;
  142. }
  143. IcqContact::~IcqContact()
  144. {
  145. }
  146. QStringList IcqContact::tags() const
  147. {
  148. Q_D(const IcqContact);
  149. return d->tags;
  150. }
  151. QString IcqContact::id() const
  152. {
  153. Q_D(const IcqContact);
  154. return d->uin;
  155. }
  156. QString IcqContact::name() const
  157. {
  158. Q_D(const IcqContact);
  159. return d->name;
  160. }
  161. Status IcqContact::status() const
  162. {
  163. Q_D(const IcqContact);
  164. return d->status;
  165. }
  166. QString IcqContact::avatar() const
  167. {
  168. return d_func()->avatar;
  169. }
  170. QString IcqContact::proto() const
  171. {
  172. return d_func()->proto;
  173. }
  174. bool IcqContact::isInList() const
  175. {
  176. return d_func()->isInList;
  177. }
  178. bool IcqContact::sendMessage(const Message &message)
  179. {
  180. if (account()->status() == Status::Offline)
  181. return false;
  182. return account()->d_func()->messageSender->appendMessage(this, message);
  183. }
  184. void IcqContact::setName(const QString &name)
  185. {
  186. Q_D(IcqContact);
  187. FeedbagItem item = d->account->feedbag()->buddyForChange(d->uin);
  188. if (!item.isInList())
  189. return;
  190. if (!name.isEmpty())
  191. item.setField(SsiBuddyNick, name);
  192. else
  193. item.removeField(SsiBuddyNick);
  194. item.update();
  195. // if (d->item.isNull())
  196. // return;
  197. // d->name = name;
  198. // d->requestNick(); // requestNick will do nothing if the name is not empty
  199. // if (!name.isEmpty())
  200. // d->item.setField(SsiBuddyNick, name);
  201. // else
  202. // d->item.removeField(SsiBuddyNick);
  203. // if (d->item.isInList())
  204. // d->item.update();
  205. //// foreach (FeedbagItem item, d->items) {
  206. //// if (!name.isEmpty())
  207. //// item.setField(SsiBuddyNick, name);
  208. //// else
  209. //// item.removeField(SsiBuddyNick);
  210. //// item.update();
  211. // }
  212. }
  213. void IcqContact::setTags(const QStringList &tags)
  214. {
  215. Q_D(IcqContact);
  216. if (d->tags == tags)
  217. return;
  218. Feedbag *feedbag = d->account->feedbag();
  219. FeedbagItem item = feedbag->buddyForChange(d->uin);
  220. if (!item.isInList())
  221. return;
  222. FeedbagItem groupItem;
  223. foreach (const QString &tag, tags) {
  224. groupItem = feedbag->groupItem(tag);
  225. if (!groupItem.isNull())
  226. break;
  227. }
  228. if (groupItem.isNull()) {
  229. QString group = tags.value(0);
  230. FeedbagItem groupItem;
  231. if (group.isEmpty())
  232. groupItem = d->getNotInListGroup();
  233. else
  234. groupItem = feedbag->groupItem(group, Feedbag::GenerateId);
  235. if (!groupItem.isInList())
  236. groupItem.add();
  237. }
  238. if (item.groupId() != groupItem.groupId()) {
  239. FeedbagItem oldItem = item;
  240. oldItem.remove();
  241. item.setInList(false);
  242. item.setId(feedbag->uniqueItemId(SsiBuddy));
  243. item.setGroup(groupItem.groupId());
  244. }
  245. TLV tagsData(SsiBuddyTags);
  246. foreach (const QString &tag, tags)
  247. tagsData.append<quint16>(tag);
  248. item.setField(tagsData);
  249. item.updateOrAdd();
  250. }
  251. void IcqContact::setInList(bool inList)
  252. {
  253. Q_D(IcqContact);
  254. if (inList == isInList())
  255. return;
  256. FeedbagItem item = d->account->feedbag()->buddyForChange(d->uin);
  257. if (inList) {
  258. if (item.isInList())
  259. return;
  260. FeedbagItem group = d->getNotInListGroup();
  261. item.setGroup(group.groupId());
  262. item.add();
  263. } else {
  264. if (!item.isInList())
  265. return;
  266. item.remove();
  267. }
  268. }
  269. IcqAccount *IcqContact::account()
  270. {
  271. return d_func()->account;
  272. }
  273. const Capabilities &IcqContact::capabilities() const
  274. {
  275. return d_func()->capabilities;
  276. }
  277. const DirectConnectionInfo &IcqContact::dcInfo() const
  278. {
  279. return d_func()->dc_info;
  280. }
  281. void IcqContact::setAvatar(const QString &avatar)
  282. {
  283. Q_D(IcqContact);
  284. d->avatar = avatar;
  285. emit avatarChanged(avatar);
  286. }
  287. void IcqContact::setStatus(const Status &status, bool notification)
  288. {
  289. Q_D(IcqContact);
  290. Status previous = d->status;
  291. d->status = status;
  292. if (status == Status::Offline) {
  293. d->clearCapabilities();
  294. emit capabilitiesChanged(Capabilities());
  295. d->onlineSince = QDateTime();
  296. d->awaySince = QDateTime();
  297. d->regTime = QDateTime();
  298. }
  299. if (notification &&
  300. (status.subtype() != previous.subtype() ||
  301. status.text() != previous.text()))
  302. {
  303. NotificationRequest request(this, status, previous);
  304. request.send();
  305. }
  306. emit statusChanged(status, previous);
  307. }
  308. ChatState IcqContact::chatState() const
  309. {
  310. return d_func()->state;
  311. }
  312. void IcqContact::updateFromItem()
  313. {
  314. Q_D(IcqContact);
  315. FeedbagItem item = d->account->feedbag()->item(SsiBuddy, d->uin);
  316. }
  317. bool IcqContact::event(QEvent *ev)
  318. {
  319. Q_D(IcqContact);
  320. if (ev->type() == ChatStateEvent::eventType()) {
  321. ChatStateEvent *chatEvent = static_cast<ChatStateEvent *>(ev);
  322. chatStateUpdater()->updateState(this, chatEvent->chatState());
  323. return true;
  324. } else if (ev->type() == ToolTipEvent::eventType()) {
  325. ToolTipEvent *event = static_cast<ToolTipEvent*>(ev);
  326. QString statusText = status().text();
  327. if (!statusText.isEmpty())
  328. event->addField(QString(), statusText, 80);
  329. QDateTime time;
  330. if (!d->onlineSince.isNull()) {
  331. time = QDateTime::currentDateTime();
  332. time = time.addSecs(-static_cast<int>(d->onlineSince.toTime_t()));
  333. time = time.toUTC();
  334. event->addField(QT_TRANSLATE_NOOP("ContactList", "Online time"),
  335. QString("%1d %2h %3m %4s")
  336. .arg(time.date().day() - 1)
  337. .arg(time.time().hour())
  338. .arg(time.time().minute())
  339. .arg(time.time().second()),
  340. 30);
  341. event->addField(QT_TRANSLATE_NOOP("ContactList", "Signed on"),
  342. d->onlineSince.toLocalTime().toString(Qt::DefaultLocaleShortDate),
  343. 30);
  344. }
  345. if (!d->awaySince.isNull()) {
  346. event->addField(QT_TRANSLATE_NOOP("ContactList", "Away since"),
  347. d->awaySince.toLocalTime().toString(Qt::DefaultLocaleShortDate),
  348. 30);
  349. }
  350. if (!d->regTime.isNull()) {
  351. event->addField(QT_TRANSLATE_NOOP("ContactList", "Reg. date"),
  352. d->regTime.toLocalTime().toString(Qt::DefaultLocaleShortDate),
  353. 30);
  354. }
  355. } else if(ev->type() == Authorization::Reply::eventType()) {
  356. Authorization::Reply *reply = static_cast<Authorization::Reply*>(ev);
  357. debug() << "handle auth reply" << (reply->replyType() == Authorization::Reply::Accept);
  358. bool answer = (reply->replyType() == Authorization::Reply::Accept);
  359. SNAC snac(ListsFamily, ListsCliAuthResponse);
  360. snac.append<quint8>(id()); // uin.
  361. snac.append<quint8>(answer ? 0x01 : 0x00); // auth flag.
  362. snac.append<quint16>(QString()); // reason
  363. snac.append<quint16>(0);
  364. account()->connection()->send(snac);
  365. return true;
  366. } else if(ev->type() == Authorization::Request::eventType()) {
  367. debug() << "Handle auth request";
  368. Authorization::Request *request = static_cast<Authorization::Request*>(ev);
  369. SNAC snac(ListsFamily, ListsRequestAuth);
  370. snac.append<quint8>(d->uin);
  371. snac.append<quint16>(request->body());
  372. snac.append<quint16>(0);
  373. account()->connection()->send(snac);
  374. return true;
  375. }
  376. return Contact::event(ev);
  377. }
  378. void IcqContact::infoReceived(bool ok)
  379. {
  380. ShortInfoMetaRequest *req = qobject_cast<ShortInfoMetaRequest*>(sender());
  381. Q_ASSERT(req);
  382. if (ok) {
  383. QString name = req->value(Nick, QString());
  384. if (!name.isEmpty())
  385. setName(name);
  386. }
  387. req->deleteLater();
  388. }
  389. } } // namespace qutim_sdk_0_3::oscar