# # Machekku Entity Time Support # # Version: 0.1.2 # Date: 2007-02-09 # # iris/xmpp-im/types.cpp | 2 # iris/xmpp-im/xmpp_tasks.cpp | 2 # src/entitytimetask.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++ # src/entitytimetask.h | 49 +++++++++++++++++ # src/infodlg.cpp | 35 +++++++++++- # src/infodlg.h | 3 - # src/psiaccount.cpp | 4 + # src/src.pri | 4 + # src/systeminfo.cpp | 16 ++++- # src/timeserver.cpp | 83 +++++++++++++++++++++++++++++ # src/timeserver.h | 35 ++++++++++++ # src/userlist.cpp | 43 +++++++++++++++ # src/userlist.h | 7 ++ # 13 files changed, 396 insertions(+), 10 deletions(-) # diff -rN -u old-psit-1/iris/xmpp-im/types.cpp new-psit-1/iris/xmpp-im/types.cpp --- old-psit-1/iris/xmpp-im/types.cpp 2007-02-09 02:50:51.234375000 +0100 +++ new-psit-1/iris/xmpp-im/types.cpp 2007-02-09 02:50:57.828125000 +0100 @@ -1697,7 +1697,7 @@ QDomElement t = root.elementsByTagNameNS("jabber:x:delay", "x").item(0).toElement(); if(!t.isNull()) { d->timeStamp = stamp2TS(t.attribute("stamp")); - d->timeStamp = d->timeStamp.addSecs(timeZoneOffset * 3600); + d->timeStamp = d->timeStamp.addSecs(timeZoneOffset * 60); d->spooled = true; } else { diff -rN -u old-psit-1/iris/xmpp-im/xmpp_tasks.cpp new-psit-1/iris/xmpp-im/xmpp_tasks.cpp --- old-psit-1/iris/xmpp-im/xmpp_tasks.cpp 2007-02-09 02:50:51.187500000 +0100 +++ new-psit-1/iris/xmpp-im/xmpp_tasks.cpp 2007-02-09 02:50:57.859375000 +0100 @@ -700,7 +700,7 @@ if(i.hasAttribute("stamp")) { QDateTime dt; if(stamp2TS(i.attribute("stamp"), &dt)) - dt = dt.addSecs(client()->timeZoneOffset() * 3600); + dt = dt.addSecs(client()->timeZoneOffset() * 60); p.setTimeStamp(dt); } } diff -rN -u old-psit-1/src/entitytimetask.cpp new-psit-1/src/entitytimetask.cpp --- old-psit-1/src/entitytimetask.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new-psit-1/src/entitytimetask.cpp 2007-02-09 02:51:00.671875000 +0100 @@ -0,0 +1,123 @@ +/* + * entitytimetask.cpp - Entity time fetching task + * Copyright (C) 2007 Maciej Niedzielski + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "entitytimetask.h" +#include "xmpp_xmlcommon.h" + +using namespace XMPP; + +/** + * \class EntityTimeTask + * \brief Gets entity time + * + * This task can be used to get time zone information of an entity. + */ + + +// convert [+|-]hh:mm to minutes +static Maybe stringToOffset(const QString &off) +{ + QTime t = QTime::fromString(off.mid(1), "hh:mm"); + + if (t.isValid() && off[0] == '+' || off[0] == '-') { + int m = t.hour() * 60 + t.minute(); + if (off[0] == '-') + m = -m; + return m; + } + else { + return Maybe(); + } +} + +/** + * \brief Create new task. + */ +EntityTimeTask::EntityTimeTask(Task* parent) : Task(parent) +{ +} + +/** + * \brief Queried entity's JID. + */ +const Jid & EntityTimeTask::jid() const +{ + return jid_; +} + +/** + * \brief Prepares the task to get information from JID. + */ +void EntityTimeTask::get(const Jid &jid) +{ + jid_ = jid; + iq_ = createIQ(doc(), "get", jid_.full(), id()); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://www.xmpp.org/extensions/xep-0202.html#ns"); + iq_.appendChild(query); +} + +void EntityTimeTask::onGo() +{ + send(iq_); +} + +bool EntityTimeTask::take(const QDomElement &x) +{ + if (!iqVerify(x, jid_, id())) + return false; + + if (x.attribute("type") == "result") { + bool found = false; + QDomElement q = queryTag(x); + QDomElement tag; + tag = findSubTag(q, "utc", &found); + if (found) + utc_ = tagContent(tag); + tag = findSubTag(q, "tzo", &found); + if (found) { + tzoString_ = tagContent(tag); + tzo_ = stringToOffset(tzoString_); + } + setSuccess(); + } + else { + setError(x); + } + + return true; +} + +/** + * \brief Timezone offset in [+|-]hh:mm format (or empty string if no data). + */ +const QString& EntityTimeTask::timezoneOffsetString() const +{ + return tzoString_; +} + +/** + * \brief Timezone offset in minutes (if available). + */ +Maybe EntityTimeTask::timezoneOffset() const +{ + return tzo_; +} diff -rN -u old-psit-1/src/entitytimetask.h new-psit-1/src/entitytimetask.h --- old-psit-1/src/entitytimetask.h 1970-01-01 01:00:00.000000000 +0100 +++ new-psit-1/src/entitytimetask.h 2007-02-09 02:51:00.687500000 +0100 @@ -0,0 +1,49 @@ +/* + * entitytimetask.h - Entity time fetching task + * Copyright (C) 2007 Maciej Niedzielski + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ENTITYTIMETASK_H +#define ENTITYTIMETASK_H + +#include +#include "xmpp_task.h" +#include "xmpp_jid.h" +#include "maybe.h" + +class EntityTimeTask : public XMPP::Task +{ +public: + EntityTimeTask(Task*); + + void onGo(); + bool take(const QDomElement &); + void get(const XMPP::Jid &jid); + const XMPP::Jid & jid() const; + + const QString& timezoneOffsetString() const; + Maybe timezoneOffset() const; + +private: + QDomElement iq_; + XMPP::Jid jid_; + QString utc_, tzoString_; + Maybe tzo_; +}; + +#endif diff -rN -u old-psit-1/src/infodlg.cpp new-psit-1/src/infodlg.cpp --- old-psit-1/src/infodlg.cpp 2007-02-09 02:50:51.062500000 +0100 +++ new-psit-1/src/infodlg.cpp 2007-02-09 02:51:01.000000000 +0100 @@ -40,6 +40,7 @@ #include "iconset.h" #include "common.h" #include "lastactivitytask.h" +#include "entitytimetask.h" #include "vcardfactory.h" #include "iconwidget.h" #include "contactview.h" @@ -117,7 +118,7 @@ updateStatus(); foreach(UserListItem* u, d->pa->findRelevant(j)) { foreach(UserResource r, u->userResourceList()) { - requestClientVersion(d->jid.withResource(r.name())); + requestResourceInfo(d->jid.withResource(r.name())); } if (u->userResourceList().isEmpty() && u->lastAvailable().isNull()) { requestLastActivity(); @@ -578,13 +579,24 @@ } } -void InfoDlg::requestClientVersion(const Jid& j) +/** + * \brief Requests per-resource information. + * + * Gets information about client version and time. + */ +void InfoDlg::requestResourceInfo(const Jid& j) { d->infoRequested += j.full(); + JT_ClientVersion *jcv = new JT_ClientVersion(d->pa->client()->rootTask()); connect(jcv, SIGNAL(finished()), SLOT(clientVersionFinished())); jcv->get(j); jcv->go(true); + + EntityTimeTask *jet = new EntityTimeTask(d->pa->client()->rootTask()); + connect(jet, SIGNAL(finished()), SLOT(entityTimeFinished())); + jet->get(j); + jet->go(true); } void InfoDlg::clientVersionFinished() @@ -604,6 +616,23 @@ } } +void InfoDlg::entityTimeFinished() +{ + EntityTimeTask *j = (EntityTimeTask *)sender(); + if(j->success()) { + foreach(UserListItem* u, d->pa->findRelevant(j->jid())) { + UserResourceList::Iterator rit = u->userResourceList().find(j->jid().resource()); + bool found = (rit == u->userResourceList().end()) ? false: true; + if(!found) + continue; + + (*rit).setTimezone(j->timezoneOffset()); + d->pa->contactProfile()->updateEntry(*u); + updateStatus(); + } + } +} + void InfoDlg::requestLastActivity() { LastActivityTask *jla = new LastActivityTask(d->jid.bare(),d->pa->client()->rootTask()); @@ -629,7 +658,7 @@ { if (d->jid.compare(j,false)) { if (!d->infoRequested.contains(j.withResource(r.name()).full())) - requestClientVersion(j.withResource(r.name())); + requestResourceInfo(j.withResource(r.name())); } } diff -rN -u old-psit-1/src/infodlg.h new-psit-1/src/infodlg.h --- old-psit-1/src/infodlg.h 2007-02-09 02:50:51.062500000 +0100 +++ new-psit-1/src/infodlg.h 2007-02-09 02:51:01.015625000 +0100 @@ -58,6 +58,7 @@ void contactUnavailable(const Jid &, const Resource &); void contactUpdated(const Jid &); void clientVersionFinished(); + void entityTimeFinished(); void requestLastActivityFinished(); void jt_finished(); void doSubmit(); @@ -76,7 +77,7 @@ bool edited(); void setEdited(bool); void setPreviewPhoto(const QString& str); - void requestClientVersion(const XMPP::Jid& j); + void requestResourceInfo(const XMPP::Jid& j); void requestLastActivity(); }; diff -rN -u old-psit-1/src/psiaccount.cpp new-psit-1/src/psiaccount.cpp --- old-psit-1/src/psiaccount.cpp 2007-02-09 02:50:51.062500000 +0100 +++ new-psit-1/src/psiaccount.cpp 2007-02-09 02:51:06.062500000 +0100 @@ -113,6 +113,7 @@ #include "certutil.h" #include "proxy.h" #include "psicontactlist.h" +#include "timeserver.h" #ifdef PSI_PLUGINS #include "pluginmanager.h" @@ -563,6 +564,9 @@ d->httpAuthManager = new HttpAuthManager(d->client->rootTask()); connect(d->httpAuthManager, SIGNAL(confirmationRequest(const PsiHttpAuthRequest &)), SLOT(incomingHttpAuthRequest(const PsiHttpAuthRequest &))); + // Time server + new TimeServer(d->client->rootTask()); + // Initialize Adhoc Commands server d->ahcManager = new AHCServerManager(this); d->rcSetStatusServer = 0; diff -rN -u old-psit-1/src/src.pri new-psit-1/src/src.pri --- old-psit-1/src/src.pri 2007-02-09 02:50:51.046875000 +0100 +++ new-psit-1/src/src.pri 2007-02-09 02:51:07.000000000 +0100 @@ -165,6 +165,8 @@ $$PWD/xdata_widget.h \ $$PWD/statuspreset.h \ $$PWD/lastactivitytask.h \ + $$PWD/entitytimetask.h \ + $$PWD/timeserver.h \ $$PWD/mucmanager.h \ $$PWD/mucjoindlg.h \ $$PWD/mucconfigdlg.h \ @@ -276,6 +278,8 @@ $$PWD/psiactionlist.cpp \ $$PWD/xdata_widget.cpp \ $$PWD/lastactivitytask.cpp \ + $$PWD/entitytimetask.cpp \ + $$PWD/timeserver.cpp \ $$PWD/statuspreset.cpp \ $$PWD/mucmanager.cpp \ $$PWD/mucjoindlg.cpp \ diff -rN -u old-psit-1/src/systeminfo.cpp new-psit-1/src/systeminfo.cpp --- old-psit-1/src/systeminfo.cpp 2007-02-09 02:50:51.031250000 +0100 +++ new-psit-1/src/systeminfo.cpp 2007-02-09 02:51:07.078125000 +0100 @@ -37,7 +37,7 @@ if(s.at(0) == '+') s.remove(0,1); s.truncate(s.length()-2); - timezone_offset_ = s.toInt(); + timezone_offset_ = s.toInt() * 60; // FIX-ME: should really read the offset in minutes } strcpy(fmt, "%Z"); strftime(str, 256, fmt, localtime(&x)); @@ -144,14 +144,12 @@ #if defined(Q_WS_WIN) TIME_ZONE_INFORMATION i; - //GetTimeZoneInformation(&i); - //timezone_offset_ = (-i.Bias) / 60; memset(&i, 0, sizeof(i)); bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT); int bias = i.Bias; if(inDST) bias += i.DaylightBias; - timezone_offset_ = (-bias) / 60; + timezone_offset_ = -bias; timezone_str_ = ""; for(int n = 0; n < 32; ++n) { int w = inDST ? i.DaylightName[n] : i.StandardName[n]; @@ -192,3 +190,13 @@ } SystemInfo* SystemInfo::instance_ = NULL; + +/** + * \fn int SystemInfo::timezoneOffset() + * \brief Local timezone offset in minutes. + */ + +/** + * \fn const QString& SystemInfo::timezoneString() const + * \brief Local timezone name. + */ diff -rN -u old-psit-1/src/timeserver.cpp new-psit-1/src/timeserver.cpp --- old-psit-1/src/timeserver.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new-psit-1/src/timeserver.cpp 2007-02-09 02:51:07.375000000 +0100 @@ -0,0 +1,83 @@ +/* + * timeserver.cpp - Entity time server + * Copyright (C) 2001, 2002, 2007 Justin Karneges, Maciej Niedzielski + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "timeserver.h" +#include "systeminfo.h" +#include "xmpp_xmlcommon.h" +#include + +using namespace XMPP; + + +/** + * \class TimeServer + * \brief Server current time + * + * This serving task answers XEP-0202 and XEP-0090 queries + */ + +TimeServer::TimeServer(Task *parent) +:Task(parent) +{ +} + +TimeServer::~TimeServer() +{ +} + +bool TimeServer::take(const QDomElement &e) +{ + if (e.tagName() != "iq" || e.attribute("type") != "get") + return false; + + QString ns = queryNS(e); + if (ns == "http://www.xmpp.org/extensions/xep-0202.html#ns") { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", ns); + iq.appendChild(query); + + QDateTime local = QDateTime::currentDateTime(); + int off = SystemInfo::instance()->timezoneOffset(); + QTime t = QTime(0, 0).addSecs(abs(off)*60); + QString tzo = (off < 0 ? "-" : "+") + t.toString("HH:mm"); + query.appendChild(textTag(doc(), "tzo", tzo)); + query.appendChild(textTag(doc(), "utc", local.toUTC().toString(Qt::ISODate) + "Z")); + + send(iq); + return true; + } + else if (ns == "jabber:iq:time") { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "jabber:iq:time"); + iq.appendChild(query); + + QDateTime local = QDateTime::currentDateTime(); + QString str = SystemInfo::instance()->timezoneString(); + query.appendChild(textTag(doc(), "utc", TS2stamp(local.toUTC()))); + query.appendChild(textTag(doc(), "tz", str)); + query.appendChild(textTag(doc(), "display", QString("%1 %2").arg(local.toString()).arg(str))); + + send(iq); + return true; + } + return false; +} diff -rN -u old-psit-1/src/timeserver.h new-psit-1/src/timeserver.h --- old-psit-1/src/timeserver.h 1970-01-01 01:00:00.000000000 +0100 +++ new-psit-1/src/timeserver.h 2007-02-09 02:51:07.437500000 +0100 @@ -0,0 +1,35 @@ +/* + * timeserver.h - Entity time server + * Copyright (C) 2007 Maciej Niedzielski + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef TIMESERVER_H +#define TIMESERVER_H + +#include "xmpp_task.h" + +class TimeServer : public XMPP::Task +{ + Q_OBJECT +public: + TimeServer(Task *); + ~TimeServer(); + bool take(const QDomElement &); +}; + +#endif diff -rN -u old-psit-1/src/userlist.cpp new-psit-1/src/userlist.cpp --- old-psit-1/src/userlist.cpp 2007-02-09 02:50:51.015625000 +0100 +++ new-psit-1/src/userlist.cpp 2007-02-09 02:51:12.562500000 +0100 @@ -103,6 +103,43 @@ } } +/** + * \brief Timezone offset in minutes (if available). + */ +Maybe UserResource::timezoneOffset() const +{ + return v_tzo; +} + +/** + * \brief Timezone offset as string (or empty string if no data). + * + * String is formatted as "UTC[+|-]h[:mm]". + */ +const QString& UserResource::timezoneOffsetString() const +{ + return v_tzoString; +} + +/** + * \brief Set timezone offset (in minutes). + */ +void UserResource::setTimezone(Maybe off) +{ + v_tzo = off; + + if (off.hasValue()) { + QTime t = QTime(0, 0).addSecs(abs(off.value())*60); + QString u = QString("UTC") + (off.value() < 0 ? "-" : "+"); + u += QString::number(t.hour()); + if (t.minute()) + u += QString(":%1").arg(t.minute()); + v_tzoString = u; + } + else + v_tzoString = ""; +} + const QString & UserResource::publicKeyID() const { return v_keyID; @@ -563,6 +600,12 @@ if (!r.geoLocation().isNull()) str += QString("
") + QObject::tr("Geolocation") + ": " + QString::number(r.geoLocation().lat().value()) + "/" + QString::number(r.geoLocation().lon().value()) + ""; + // Entity Time + if (r.timezoneOffset().hasValue()) { + QDateTime dt = QDateTime::currentDateTime().toUTC().addSecs(r.timezoneOffset().value()*60); + str += QString("
") + QObject::tr("Time") + QString(": %1 (%2)").arg(dt.toString(Qt::TextDate)).arg(r.timezoneOffsetString()) + ""; + } + // client if(!r.versionString().isEmpty() && PsiOptions::instance()->getOption("options.ui.contactlist.tooltip.client-version").toBool()) { QString ver = r.versionString(); diff -rN -u old-psit-1/src/userlist.h new-psit-1/src/userlist.h --- old-psit-1/src/userlist.h 2007-02-09 02:50:51.000000000 +0100 +++ new-psit-1/src/userlist.h 2007-02-09 02:51:12.578125000 +0100 @@ -30,6 +30,7 @@ #include "mood.h" #include "geolocation.h" #include "physicallocation.h" +#include "maybe.h" class AvatarFactory; @@ -48,6 +49,10 @@ const QString& clientOS() const; void setClient(const QString& name, const QString& version, const QString& os); + Maybe timezoneOffset() const; + const QString& timezoneOffsetString() const; + void setTimezone(Maybe tzo); + const QString & publicKeyID() const; int pgpVerifyStatus() const; QDateTime sigTimestamp() const; @@ -64,6 +69,8 @@ private: QString v_ver, v_clientName, v_clientVersion, v_clientOS, v_keyID; + Maybe v_tzo; + QString v_tzoString; QString v_tune; GeoLocation v_geoLocation; PhysicalLocation v_physicalLocation;