diff -urN psi-gentoo-actual-orig/iris/include/im.h psi-gentoo-actual/iris/include/im.h --- iris/include/im.h 2005-11-29 02:44:36.000000000 +0100 +++ psi-gentoo-actual/iris/include/im.h 2005-11-29 02:56:45.000000000 +0100 @@ -45,8 +45,38 @@ Private *d; }; + typedef enum { Unknown, To, Cc, Bcc, ReplyTo, ReplyRoom, NoReply, OriginalFrom, OriginalTo } AddressType; + + class Address + { + public: + Address(const AddressType &type=Unknown, const Jid &jid=""); + Address(const Address &); + Address & operator=(const Address &); + ~Address(); + + Jid jid() const; + QString uri() const; + QString node() const; + QString desc() const; + bool delivered() const; + AddressType type() const; + + void setJid(const Jid &); + void setUri(const QString &); + void setNode(const QString &); + void setDesc(const QString &); + void setDelivered(const bool &); + void setType(const AddressType &); + + private: + class Private; + Private *d; + }; + class RosterItem; typedef QValueList UrlList; + typedef QValueList
AddressList; typedef QValueList RosterItemList; typedef QMap StringMap; @@ -108,6 +138,13 @@ QString xencrypted() const; void setXEncrypted(const QString &s); + // JEP-0033 + AddressList addressList() const; + void addressAdd(const Address &a); + void addressesClear(); + AddressList addressFind(AddressType t) const; + void setAddressList(const AddressList &list); + // Obsolete invitation QString invite() const; void setInvite(const QString &s); @@ -316,6 +353,7 @@ bool canDisco() const; bool isGateway() const; bool haveVCard() const; + bool canAdHoc() const; enum FeatureID { FID_Invalid = -1, @@ -326,6 +364,7 @@ FID_Disco, FID_Gateway, FID_VCard, + FID_AdHoc, // private Psi actions FID_Add diff -urN psi-gentoo-actual-orig/iris/xmpp-im/types.cpp psi-gentoo-actual/iris/xmpp-im/types.cpp --- iris/xmpp-im/types.cpp 2005-11-29 02:44:36.000000000 +0100 +++ psi-gentoo-actual/iris/xmpp-im/types.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -172,6 +172,159 @@ } //---------------------------------------------------------------------------- +// Address +//---------------------------------------------------------------------------- +class Address::Private +{ +public: + Jid jid; + QString uri; + QString node; + QString desc; + bool delivered; + AddressType type; +}; + +//! \brief Construct Address object with a given Type and Jid. +//! +//! This function will construct a Address object. +//! \param AddressType - type (default: Unknown) +//! \param Jid - specify address (default: empty string) +//! \sa setType() setJid() +Address::Address(const AddressType &type, const Jid &jid) +{ + d = new Private; + d->type = type; + d->jid = jid; + d->delivered = false; +} + +//! \brief Construct Address object. +//! +//! Overloaded constructor which will constructs a exact copy of the Address object that was passed to the constructor. +//! \param Address - Address Object +Address::Address(const Address &from) +{ + d = new Private; + *this = from; +} + +//! \brief operator overloader needed for d pointer (Internal). +Address & Address::operator=(const Address &from) +{ + *d = *from.d; + return *this; +} + +//! \brief destroy Address object. +Address::~Address() +{ + delete d; +} + +//! \brief Get Jid information. +//! +//! Returns jid information. +Jid Address::jid() const +{ + return d->jid; +} + +//! \brief Get Uri information. +//! +//! Returns desction of the Address. +QString Address::uri() const +{ + return d->uri; +} + +//! \brief Get Node information. +//! +//! Returns node of the Address. +QString Address::node() const +{ + return d->node; +} + +//! \brief Get Description information. +//! +//! Returns desction of the Address. +QString Address::desc() const +{ + return d->desc; +} + +//! \brief Get Delivered information. +//! +//! Returns delivered of the Address. +bool Address::delivered() const +{ + return d->delivered; +} + +//! \brief Get Type information. +//! +//! Returns type of the Address. +AddressType Address::type() const +{ + return d->type; +} + +//! \brief Set Address information. +//! +//! Set jid information. +//! \param jid - jid +void Address::setJid(const Jid &jid) +{ + d->jid = jid; +} + +//! \brief Set Address information. +//! +//! Set uri information. +//! \param uri - url string (eg: http://psi.affinix.com/) +void Address::setUri(const QString &uri) +{ + d->uri = uri; +} + +//! \brief Set Node information. +//! +//! Set node information. +//! \param node - node string +void Address::setNode(const QString &node) +{ + d->node = node; +} + +//! \brief Set Description information. +//! +//! Set description of the url. +//! \param desc - description of url +void Address::setDesc(const QString &desc) +{ + d->desc = desc; +} + +//! \brief Set delivered information. +//! +//! Set delivered information. +//! \param delivered - delivered flag +void Address::setDelivered(const bool &delivered) +{ + d->delivered = delivered; +} + +//! \brief Set Type information. +//! +//! Set type information. +//! \param type - type +void Address::setType(const AddressType &type) +{ + d->type = type; +} + +//---------------------------------------------------------------------------- // Message //---------------------------------------------------------------------------- class Message::Private @@ -187,6 +340,7 @@ // extensions QDateTime timeStamp; UrlList urlList; + AddressList addressList; QValueList eventList; QString eventId; RosterItemList rosterItemList; @@ -392,6 +546,45 @@ d->urlList = list; } +//! \brief Return list of addresses attached to message. +AddressList Message::addressList() const +{ + return d->addressList; +} + +//! \brief Add Address to the address list. +//! +//! \param address - address to append +void Message::addressAdd(const Address &a) +{ + d->addressList += a; +} + +//! \brief clear out the address list. +void Message::addressesClear() +{ + d->addressList.clear(); +} + +AddressList Message::addressFind(AddressType t) const +{ + AddressList matches; + for(QValueList
::ConstIterator ait = d->addressList.begin(); ait != d->addressList.end(); ++ait) { + if((*ait).type() == t) { + matches.append(*ait); + } + } + return matches; +} + +//! \brief Set addresses to send +//! +//! \param list - list of addresses to send +void Message::setAddressList(const AddressList &list) +{ + d->addressList = list; +} + QString Message::eventId() const { return d->eventId; @@ -608,6 +801,56 @@ if(!d->xencrypted.isEmpty()) s.appendChild(s.createTextElement("jabber:x:encrypted", "x", d->xencrypted)); + // addresses + if (!d->addressList.isEmpty()) { + QDomElement a = s.createElement("http://jabber.org/protocol/address", "addresses"); + + for(QValueList
::ConstIterator ait = d->addressList.begin(); ait != d->addressList.end(); ++ait) { + QDomElement e = s.createElement("http://jabber.org/protocol/address", "address"); + if(!(*ait).jid().isEmpty()) + e.setAttribute("jid", (*ait).jid().full()); + if(!(*ait).uri().isEmpty()) + e.setAttribute("uri", (*ait).uri()); + if(!(*ait).node().isEmpty()) + e.setAttribute("node", (*ait).node()); + if(!(*ait).desc().isEmpty()) + e.setAttribute("desc", (*ait).desc()); + if((*ait).delivered()) + e.setAttribute("delivered", "true"); + switch ((*ait).type()) { + case To: + e.setAttribute("type", "to"); + break; + case Cc: + e.setAttribute("type", "cc"); + break; + case Bcc: + e.setAttribute("type", "bcc"); + break; + case ReplyTo: + e.setAttribute("type", "replyto"); + break; + case ReplyRoom: + e.setAttribute("type", "replyroom"); + break; + case NoReply: + e.setAttribute("type", "noreply"); + break; + case OriginalFrom: + e.setAttribute("type", "ofrom"); + break; + case OriginalTo: + e.setAttribute("type", "oto"); + break; + case Unknown: + // Add nothing + break; + } + a.appendChild(e); + } + s.appendChild(a); + } + // obsolete invite // if(!d->invite.isEmpty()) { // QDomElement e = s.createElement("jabber:x:conference", "x"); @@ -733,6 +976,41 @@ else d->xencrypted = QString(); + // addresses + d->addressList.clear(); + nl = root.elementsByTagNameNS("http://jabber.org/protocol/address", "addresses"); + if (nl.count()) { + QDomElement t = nl.item(0).toElement(); + nl = t.elementsByTagName("address"); + for(n = 0; n < nl.count(); ++n) { + QDomElement t = nl.item(n).toElement(); + Address a; + a.setJid(t.attribute("jid")); + a.setUri(t.attribute("uri")); + a.setNode(t.attribute("node")); + a.setDesc(t.attribute("desc")); + a.setDelivered(t.attribute("delivered")); + QString type = t.attribute("type"); + if (type == "to") + a.setType(To); + else if (type == "cc") + a.setType(Cc); + else if (type == "bcc") + a.setType(Bcc); + else if (type == "replyto") + a.setType(ReplyTo); + else if (type == "replyroom") + a.setType(ReplyRoom); + else if (type == "noreply") + a.setType(NoReply); + else if (type == "ofrom") + a.setType(OriginalFrom); + else if (type == "oto") + a.setType(OriginalTo); + d->addressList += a; + } + } + // invite t = root.elementsByTagNameNS("jabber:x:conference", "x").item(0).toElement(); if(!t.isNull()) { @@ -1582,6 +1860,15 @@ return test(ns); } +#define FID_ADHOC "http://jabber.org/protocol/commands" +bool Features::canAdHoc() const +{ + QStringList ns; + ns << FID_ADHOC; + + return test(ns); +} + // custom Psi acitons #define FID_ADD "psi:add" @@ -1600,6 +1887,7 @@ id2s[FID_Gateway] = tr("Gateway"); id2s[FID_Disco] = tr("Service Discovery"); id2s[FID_VCard] = tr("VCard"); + id2s[FID_AdHoc] = tr("Execute command"); // custom Psi actions id2s[FID_Add] = tr("Add to roster"); @@ -1615,6 +1903,7 @@ id2f[FID_Gateway] = FID_GATEWAY; id2f[FID_Disco] = FID_DISCO; id2f[FID_VCard] = FID_VCARD; + id2f[FID_AdHoc] = FID_ADHOC; // custom Psi actions id2f[FID_Add] = FID_ADD; @@ -1643,6 +1932,8 @@ return FID_Disco; else if ( haveVCard() ) return FID_VCard; + else if ( canAdHoc() ) + return FID_AdHoc; else if ( test(FID_ADD) ) return FID_Add; diff -urN psi-gentoo-actual-orig/src/adhoc.cpp psi-gentoo-actual/src/adhoc.cpp --- src/adhoc.cpp 1970-01-01 01:00:00.000000000 +0100 +++ psi-gentoo-actual/src/adhoc.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -0,0 +1,1041 @@ +/* + * adhoc.cpp - Client & Server implementation of JEP-50 (Ad-Hoc Commands) + * Copyright (C) 2005 Remko Troncon + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#include "adhoc.h" +#include "psiaccount.h" + +#include "xmpp_xmlcommon.h" +#include "xmpp_tasks.h" +#include "xmpp_xdata.h" +#include "xdata_widget.h" + +using namespace XMPP; + +#define AHC_NS "http://jabber.org/protocol/commands" +#define XMPPSTANZA_NS "urn:ietf:params:xml:ns:xmpp-stanzas" + +// -------------------------------------------------------------------------- +// AHCommand: The class representing an Ad-Hoc command request or reply. +// -------------------------------------------------------------------------- + +AHCommand::AHCommand(const QString& node, const QString& sessionId, Action action) : node_(node), hasData_(false), status_(NoStatus), defaultAction_(NoAction), action_(action), sessionId_(sessionId) +{ +} + +AHCommand::AHCommand(const QString& node, XData data, const QString& sessionId, Action action) : node_(node), hasData_(true), data_(data), status_(NoStatus), defaultAction_(NoAction), action_(action), sessionId_(sessionId) +{ +} + +AHCommand::AHCommand(const QDomElement& q) : hasData_(false), defaultAction_(NoAction) +{ + // Parse attributes + QString status = q.attribute("status"); + setStatus(string2status(status)); + node_ = q.attribute("node"); + action_ = string2action(q.attribute("action")); + sessionId_ = q.attribute("sessionid"); + + // Parse the body + for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) { + QDomElement e = n.toElement(); + if (e.isNull()) + continue; + + QString tag = e.tagName(); + + // A form + if (tag == "x" && e.attribute("xmlns") =="jabber:x:data") { + data_.fromXml(e); + hasData_ = true; + } + + // Actions + else if (tag == "actions") { + QString execute = e.attribute("execute"); + if (!execute.isEmpty()) + setDefaultAction(string2action(execute)); + + for (QDomNode m = e.firstChild(); !m.isNull(); m = m.nextSibling()) { + Action a = string2action(m.toElement().tagName()); + if (a == Prev || a == Next || a == Complete) + actions_ += a; + } + } + } +} + +QDomElement AHCommand::toXml(QDomDocument* doc, bool submit) const +{ + QDomElement command = doc->createElement("command"); + command.setAttribute("xmlns", AHC_NS); + if (status_ != NoStatus) + command.setAttribute("status",status2string(status())); + if (hasData()) + command.appendChild(data().toXml(doc, submit)); + if (action_ != Execute) + command.setAttribute("action",action2string(action_)); + command.setAttribute("node", node_); + if (!sessionId_.isEmpty()) + command.setAttribute("sessionid", sessionId_); + + return command; +} + + +AHCommand AHCommand::formReply(const AHCommand& c, const XData& data) +{ + AHCommand r(c.node(), data, c.sessionId()); + r.setStatus(AHCommand::Executing); + return r; +} + +AHCommand AHCommand::formReply(const AHCommand& c, const XData& data, const QString& sessionId) +{ + AHCommand r(c.node(), data, sessionId); + r.setStatus(AHCommand::Executing); + return r; +} + +AHCommand AHCommand::canceledReply(const AHCommand& c) +{ + AHCommand r(c.node(), c.sessionId()); + r.setStatus(Canceled); + return r; +} + +AHCommand AHCommand::completedReply(const AHCommand& c) +{ + AHCommand r(c.node(), c.sessionId()); + r.setStatus(Completed); + return r; +} + +AHCommand AHCommand::completedReply(const AHCommand& c, const XData& d) +{ + AHCommand r(c.node(), d, c.sessionId()); + r.setStatus(Completed); + return r; +} + +//AHCommand AHCommand::errorReply(const AHCommand& c, const AHCError& error) +//{ +// AHCommand r(c.node(), c.sessionId()); +// r.setError(error); +// return r; +//} + +void AHCommand::setStatus(Status s) +{ + status_ = s; +} + +void AHCommand::setError(const AHCError& e) +{ + error_ = e; +} + +void AHCommand::setDefaultAction(Action a) +{ + defaultAction_ = a; +} + +QString AHCommand::status2string(Status status) +{ + QString s; + switch (status) { + case Executing : s = "executing"; break; + case Completed : s = "completed"; break; + case Canceled : s = "canceled"; break; + case NoStatus : s = ""; break; + } + return s; +} + +QString AHCommand::action2string(Action action) +{ + QString s; + switch (action) { + case Prev : s = "prev"; break; + case Next : s = "next"; break; + case Cancel : s = "cancel"; break; + case Complete : s = "complete"; break; + default: break; + } + return s; +} + +AHCommand::Action AHCommand::string2action(const QString& s) +{ + if (s == "prev") + return Prev; + else if (s == "next") + return Next; + else if (s == "complete") + return Complete; + else if (s == "cancel") + return Cancel; + else + return Execute; +} + +AHCommand::Status AHCommand::string2status(const QString& s) +{ + if (s == "canceled") + return Canceled; + else if (s == "completed") + return Completed; + else if (s == "executing") + return Executing; + else + return NoStatus; +} + + +// -------------------------------------------------------------------------- +// AHCError: The class representing an Ad-Hoc command error +// -------------------------------------------------------------------------- + +AHCError::AHCError(ErrorType t) : type_(t) +{ +} + +AHCError::AHCError(const QDomElement& e) : type_(None) +{ + QString errorGeneral = "", errorSpecific = ""; + + for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { + QDomElement i = n.toElement(); + if(i.isNull()) + continue; + + QString tag = i.tagName(); + + if ((tag == "bad-request" || tag == "not-allowed" || tag == "forbidden" || tag == "forbidden" || tag == "item-not-found" || tag == "feature-not-implemented") && e.attribute("xmlns") == XMPPSTANZA_NS) { + errorGeneral = tag; + } + else if ((tag == "malformed-action" || tag == "bad-action" || tag == "bad-locale" || tag == "bad-payload" || tag == "bad-sessionid" || tag == "session-expired") && e.attribute("xmlns") == AHC_NS) { + errorSpecific = tag; + } + } + + type_ = strings2error(errorGeneral, errorSpecific); +} + +QDomElement AHCError::toXml(QDomDocument* doc) const +{ + QDomElement err = doc->createElement("error"); + + // Error handling + if (type_ != None) { + QString desc, specificCondition = ""; + switch (type_) { + case MalformedAction: + desc = "bad-request"; + specificCondition = "malformed-action"; + break; + case BadAction: + desc = "bad-request"; + specificCondition = "bad-action"; + break; + case BadLocale: + desc = "bad-request"; + specificCondition = "bad-locale"; + break; + case BadPayload: + desc = "bad-request"; + specificCondition = "bad-payload"; + break; + case BadSessionID: + desc = "bad-request"; + specificCondition = "bad-sessionid"; + break; + case SessionExpired: + desc = "not-allowed"; + specificCondition = "session-expired"; + break; + case Forbidden: + desc = "forbidden"; + break; + case ItemNotFound: + desc = "item-not-found"; + break; + case FeatureNotImplemented: + desc = "feature-not-implemented"; + break; + case None: + break; + } + + // General error condition + QDomElement generalElement = doc->createElement(desc); + generalElement.setAttribute("xmlns", XMPPSTANZA_NS); + err.appendChild(generalElement); + + // Specific error condition + if (!specificCondition.isEmpty()) { + QDomElement generalElement = doc->createElement(specificCondition); + generalElement.setAttribute("xmlns", AHC_NS); + err.appendChild(generalElement); + } + } + + return err; +} + +AHCError::ErrorType AHCError::strings2error(const QString& g, const QString& s) +{ + if (s == "malformed-action") + return MalformedAction; + if (s == "bad-action") + return BadAction; + if (s == "bad-locale") + return BadLocale; + if (s == "bad-payload") + return BadPayload; + if (s == "bad-sessionid") + return BadSessionID; + if (s == "session-expired") + return SessionExpired; + if (g == "forbidden") + return Forbidden; + if (g == "item-not-found") + return ItemNotFound; + if (g == "feature-not-implemented") + return FeatureNotImplemented; + return None; +} + +QString AHCError::error2description(const AHCError& e) +{ + QString desc; + switch (e.type()) { + case MalformedAction: + desc = QString("The responding JID does not understand the specified action"); + break; + case BadAction: + desc = QString("The responding JID cannot accept the specified action"); + break; + case BadLocale: + desc = QString("The responding JID cannot accept the specified language/locale"); + break; + case BadPayload: + desc = QString("The responding JID cannot accept the specified payload (eg the data form did not provide one or more required fields)"); + break; + case BadSessionID: + desc = QString("The responding JID cannot accept the specified sessionid"); + break; + case SessionExpired: + desc = QString("The requesting JID specified a sessionid that is no longer active (either because it was completed, canceled, or timed out)"); + break; + case Forbidden: + desc = QString("The requesting JID is not allowed to execute the command"); + break; + case ItemNotFound: + desc = QString("The responding JID cannot find the requested command node"); + break; + case FeatureNotImplemented: + desc = QString("The responding JID does not support Ad-hoc commands"); + break; + case None: + break; + } + return desc; +} + +// -------------------------------------------------------------------------- +// AHCommandServer: The server-side implementation of an Ad-hoc command. +// -------------------------------------------------------------------------- + +AHCommandServer::AHCommandServer(AHCServerManager* manager) : manager_(manager) +{ + manager_->addServer(this); +} + + +// -------------------------------------------------------------------------- +// JT_AHCServer: Task to handle ad-hoc command requests +// -------------------------------------------------------------------------- + +class JT_AHCServer : public Task +{ + Q_OBJECT + +public: + JT_AHCServer(Task*, AHCServerManager*); + bool take(const QDomElement& e); + void sendReply(const AHCommand&, const Jid& to, const QString& id); + +protected: + bool commandListQuery(const QDomElement& e); + bool commandExecuteQuery(const QDomElement& e); + void sendCommandList(const QString& to, const QString& from, const QString& id); + +private: + AHCServerManager* manager_; +}; + + +JT_AHCServer::JT_AHCServer(Task* t, AHCServerManager* manager) : Task(t), manager_(manager) +{ +} + +bool JT_AHCServer::take(const QDomElement& e) +{ + // Check if it's a query + if (e.tagName() != "iq") + return false; + + return (commandListQuery(e) || commandExecuteQuery(e)); +} + +bool JT_AHCServer::commandListQuery(const QDomElement& e) +{ + if (e.attribute("type") == "get") { + bool found; + QDomElement q = findSubTag(e, "query", &found); + if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#items" && q.attribute("node") == AHC_NS) { + sendCommandList(e.attribute("from"),e.attribute("to"),e.attribute("id")); + return true; + } + else if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#info" && q.attribute("node") == AHC_NS) { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info"); + iq.appendChild(query); + + send(iq); + return true; + } + else if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#items" && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + query.setAttribute("node",q.attribute("node")); + iq.appendChild(query); + + send(iq); + return true; + } + else if (found && q.attribute("xmlns") == "http://jabber.org/protocol/disco#info" && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) { + QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id")); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info"); + query.setAttribute("node",q.attribute("node")); + iq.appendChild(query); + QDomElement identity; + QDomElement feature; + + identity = doc()->createElement("identity"); + identity.setAttribute("category", "automation"); + identity.setAttribute("type", "command-node"); + query.appendChild(identity); + + feature = doc()->createElement("feature"); + feature.setAttribute("var", AHC_NS); + query.appendChild(feature); + + feature = doc()->createElement("feature"); + feature.setAttribute("var", "jabber:x:data"); + query.appendChild(feature); + + send(iq); + return true; + } + + } + + return false; +} + +bool JT_AHCServer::commandExecuteQuery(const QDomElement& e) +{ + if (e.attribute("type") == "set") { + bool found; + QDomElement q = findSubTag(e, "command", &found); + if (found && q.attribute("xmlns") == AHC_NS) { + if (manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) { + AHCommand command(q); + manager_->execute(command, Jid(e.attribute("from")), e.attribute("id")); + return true; + } + return false; + } + else + return false; + } + return false; +} + +void JT_AHCServer::sendCommandList(const QString& to, const QString& from, const QString& id) +{ + // Create query element + QDomElement iq = createIQ(doc(), "result", to, id); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + query.setAttribute("node", AHC_NS); + iq.appendChild(query); + + // Add all commands + AHCServerManager::ServerList l = manager_->commands(Jid(to)); + for (AHCommandServer* c = l.first(); c; c = l.next()) { + QDomElement command = doc()->createElement("item"); + command.setAttribute("jid",from); + command.setAttribute("name",c->name()); + command.setAttribute("node",c->node()); + query.appendChild(command); + } + + // Send the message + send(iq); +} + +void JT_AHCServer::sendReply(const AHCommand& c, const Jid& to, const QString& id) +{ + // if (c.error().type() != AHCError::None) { + QDomElement iq = createIQ(doc(), "result", to.full(), id); + QDomElement command = c.toXml(doc(), false); + iq.appendChild(command); + send(iq); + // } + // else { + // } +} + +// -------------------------------------------------------------------------- + +AHCServerManager::AHCServerManager(PsiAccount *pa) : pa_(pa) +{ + server_task_ = new JT_AHCServer(pa_->client()->rootTask(), this); +} + +void AHCServerManager::addServer(AHCommandServer* server) +{ + servers_.append(server); +} + +AHCServerManager::ServerList AHCServerManager::commands(const Jid& j) const +{ + ServerList list; + for (ServerList::ConstIterator it = servers_.begin(); it != servers_.end(); ++it) { + if ((*it)->isAllowed(j)) + list.append(*it); + } + return list; +} + + +void AHCServerManager::execute(const AHCommand& command, const Jid& requester, QString id) +{ + AHCommandServer* c = findServer(command.node()); + + // Check if the command is provided + if (!c) { + //server_task_->sendReply(AHCommand::errorReply(command,AHCError(AHCError::ItemNotFound)), requester, id); + return; + } + + // Check if the requester is allowed to execute the command + if (c->isAllowed(requester)) { + if (command.action() == AHCommand::Cancel) { + c->cancel(command); + server_task_->sendReply(AHCommand::canceledReply(command), requester, id); + } + else + // Execute the command & send back the response + server_task_->sendReply(c->execute(command, requester), requester, id); + } + else { + //server_task_->sendReply(AHCommand::errorReply(command,AHCError(AHCError::Forbidden)), requester, id); + return; + } +} + + +AHCommandServer* AHCServerManager::findServer(const QString& node) const +{ + for (ServerList::ConstIterator it = servers_.begin(); it != servers_.end(); ++it) { + if ((*it)->node() == node) + return (*it); + } + return 0; +} + +bool AHCServerManager::hasServer(const QString& node, const Jid& requester) const +{ + AHCommandServer* c = findServer(node); + + // Check if the command is provided + if (!c) { + return false; + } + + if (c->isAllowed(requester)) { + return true; + } + else { + return false; + } +} + + +// -------------------------------------------------------------------------- + +bool operator<(const AHCommandItem &ci1, const AHCommandItem &ci2) +{ + return ci1.name < ci2.name; +} + +bool operator<=(const AHCommandItem &ci1, const AHCommandItem &ci2) +{ + return ci1.name <= ci2.name; +} + +bool operator==(const AHCommandItem &ci1, const AHCommandItem &ci2) +{ + return ci1.name == ci2.name; +} + +bool operator>(const AHCommandItem &ci1, const AHCommandItem &ci2) +{ + return ci1.name > ci2.name; +} + +bool operator>=(const AHCommandItem &ci1, const AHCommandItem &ci2) +{ + return ci1.name >= ci2.name; +} + +// -------------------------------------------------------------------------- +// JT_AHCGetList: A Task to retreive the available commands of a client +// -------------------------------------------------------------------------- + +class JT_AHCGetList : public Task +{ +public: + JT_AHCGetList(Task* t, const Jid& j); + + void onGo(); + bool take(const QDomElement &x); + + const QValueList& commands() const { return commands_; } + +private: + Jid receiver_; + QValueList commands_; +}; + + +JT_AHCGetList::JT_AHCGetList(Task* t, const Jid& j) : Task(t), receiver_(j) +{ +} + +void JT_AHCGetList::onGo() +{ + QDomElement e = createIQ(doc(), "get", receiver_.full(), id()); + QDomElement q = doc()->createElement("query"); + q.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); + q.setAttribute("node", AHC_NS); + e.appendChild(q); + send(e); +} + +bool JT_AHCGetList::take(const QDomElement& e) +{ + if(!iqVerify(e, receiver_, id())) { + return false; + } + + if (e.attribute("type") == "result") { + // Extract commands + commands_.clear(); + bool found; + QDomElement commands = findSubTag(e, "query", &found); + if(found) { + for(QDomNode n = commands.firstChild(); !n.isNull(); n = n.nextSibling()) { + QDomElement i = n.toElement(); + if(i.isNull()) + continue; + + if(i.tagName() == "item") { + AHCommandItem ci; + ci.node = i.attribute("node"); + ci.name = i.attribute("name"); + ci.jid = i.attribute("jid"); + commands_ += ci; + } + } + qHeapSort(commands_); + } + setSuccess(); + return true; + } + else { + setError(e); + return false; + } +} + + +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +// JT_AHCExecute: A task to execute a command +// -------------------------------------------------------------------------- + +class JT_AHCExecute : public Task +{ +public: + JT_AHCExecute(Task* t, PsiAccount* pa, Jid j, const AHCommand&); + + void onGo(); + bool take(const QDomElement &x); + +private: + Jid receiver_; + PsiAccount* account_; + AHCommand command_; +}; + + +JT_AHCExecute::JT_AHCExecute(Task* t, PsiAccount* pa, Jid j, const AHCommand& command) : Task(t), receiver_(j), account_(pa), command_(command) +{ +} + +void JT_AHCExecute::onGo() +{ + QDomElement e = createIQ(doc(), "set", receiver_.full(), id()); + e.appendChild(command_.toXml(doc(),true)); + send(e); +} + +bool JT_AHCExecute::take(const QDomElement& e) +{ + if(!iqVerify(e, receiver_, id())) { + return false; + } + + // Result of a command + if (e.attribute("type") == "result") { + bool found; + QDomElement i = findSubTag(e, "command", &found); + if (found) { + AHCommand c(i); + if (c.status() == AHCommand::Executing) { + AHCFormDlg *w = new AHCFormDlg(c,receiver_,account_); + w->show(); + } + else if (c.status() == AHCommand::Completed && i.childNodes().count() > 0) { + AHCFormDlg *w = new AHCFormDlg(c,receiver_,account_, true); + w->show(); + } + setSuccess(); + return true; + } + } + // Error + /*else if (e.attribute("type") == "set") { + AHCError err(e); + if (err.type() != None) { + QMessageBox::critical(0, tr("Error"), AHCommand::error2description(err.type()), QMessageBox::Ok, QMessageBox::NoButton); + } + return true; + }*/ + setError(e); + return false; +} + +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +// AHCommandDlg +// -------------------------------------------------------------------------- + +AHCommandDlg::AHCommandDlg(PsiAccount* pa, const Jid& receiver) : pa_(pa), receiver_(receiver) +{ + QVBoxLayout *vb = new QVBoxLayout(this, 11, 6); + + // Command list + Buttons + QLabel* lb_commands = new QLabel(tr("Command:"),this); + vb->addWidget(lb_commands); + QHBoxLayout *hb1 = new QHBoxLayout(vb); + cb_commands = new QComboBox(this); + cb_commands->setSizePolicy ( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + hb1->addWidget(cb_commands); + /*pb_info = new QPushButton(tr("Info"), this); + hb1->addWidget(pb_info);*/ + pb_execute = new QPushButton(tr("Execute"), this); + hb1->addWidget(pb_execute); + connect(pb_execute, SIGNAL(clicked()), SLOT(executeCommand())); + + vb->addStretch(1); + + // Bottom row + QHBoxLayout *hb2 = new QHBoxLayout(vb); + + busy = new BusyWidget(this); + hb2->addWidget(busy); + hb2->addStretch(1); + + // Refresh button + pb_refresh = new QPushButton(tr("Refresh"), this); + hb2->addWidget(pb_refresh); + connect(pb_refresh, SIGNAL(clicked()), SLOT(refreshCommands())); + + // Close button + pb_close = new QPushButton(tr("Close"), this); + hb2->addWidget(pb_close); + connect(pb_close, SIGNAL(clicked()), SLOT(close())); + pb_close->setDefault(true); + pb_close->setFocus(); + + setCaption(QString("Execute Command (%1)").arg(receiver.full())); + + // Initialize other things + task_list_ = 0; + + // Load commands + refreshCommands(); +} + + +void AHCommandDlg::refreshCommands() +{ + cb_commands->clear(); + pb_execute->setEnabled(false); + //pb_info->setEnabled(false); + busy->start(); + if (task_list_) + disconnect(task_list_,SIGNAL(finished()),this,SLOT(listReceived())); + + task_list_= new JT_AHCGetList(pa_->client()->rootTask(),receiver_); + connect(task_list_,SIGNAL(finished()),SLOT(listReceived())); + task_list_->go(true); +} + +void AHCommandDlg::listReceived() +{ + QValueList l = task_list_->commands(); + QValueList::ConstIterator it; + for (it = l.begin(); it != l.end(); ++it) { + cb_commands->insertItem((*it).name); + commands_.append(*it); + } + pb_execute->setEnabled(cb_commands->count()>0); + busy->stop(); + + // FIXME delete task_list_ ?; + task_list_ = 0; +} + +void AHCommandDlg::executeCommand() +{ + if (cb_commands->count() > 0) { + busy->start(); + Jid to(commands_[cb_commands->currentItem()].jid); + QString node = commands_[cb_commands->currentItem()].node; + JT_AHCExecute* t = new JT_AHCExecute(pa_->client()->rootTask(),pa_,to,AHCommand(node)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); + } +} + +void AHCommandDlg::commandExecuted() +{ + busy->stop(); + close(); +} + +void AHCommandDlg::executeCommand(PsiAccount* pa, const Jid& j, const QString &node) +{ + JT_AHCExecute* t = new JT_AHCExecute(pa->client()->rootTask(),pa,j,AHCommand(node)); + t->go(true); +} + +// -------------------------------------------------------------------------- + +AHCFormDlg::AHCFormDlg(const AHCommand& r, const Jid& receiver, PsiAccount* account, bool final) : receiver_(receiver), account_(account) +{ + // Save node + node_ = r.node(); + sessionId_ = r.sessionId(); + + QVBoxLayout *vb = new QVBoxLayout(this, 11, 6); + + // Instructions + if (!r.data().instructions().isEmpty()) { + QLabel* lb_instructions = new QLabel(r.data().instructions(),this); + vb->addWidget(lb_instructions); + } + + // XData form + xdata_ = new XDataWidget(this); + xdata_->setFields(r.data().fields()); + vb->addWidget(xdata_); + + vb->addStretch(1); + + // Buttons + QHBoxLayout *hb = new QHBoxLayout(vb); + pb_complete = pb_cancel = pb_prev = pb_next = 0; + if (!final) { + + busy = new BusyWidget(this); + hb->addWidget(busy); + hb->addStretch(1); + + if (r.actions().empty()) { + // Single stage dialog + pb_complete = new QPushButton(tr("Finish"),this); + connect(pb_complete,SIGNAL(clicked()),SLOT(doExecute())); + hb->addWidget(pb_complete); + } + else { + // Multi-stage dialog + + // Previous + pb_prev = new QPushButton(tr("Previous"),this); + if (r.actions().contains(AHCommand::Prev)) { + if (r.defaultAction() == AHCommand::Prev) { + connect(pb_prev,SIGNAL(clicked()),SLOT(doExecute())); + pb_prev->setDefault(true); + pb_prev->setFocus(); + } + else + connect(pb_prev,SIGNAL(clicked()),SLOT(doPrev())); + pb_prev->setEnabled(true); + } + else + pb_prev->setEnabled(false); + hb->addWidget(pb_prev); + + // Next + pb_next = new QPushButton(tr("Next"),this); + if (r.actions().contains(AHCommand::Next)) { + if (r.defaultAction() == AHCommand::Next) { + connect(pb_next,SIGNAL(clicked()),SLOT(doExecute())); + pb_next->setDefault(true); + pb_next->setFocus(); + } + else + connect(pb_next,SIGNAL(clicked()),SLOT(doNext())); + pb_next->setEnabled(true); + } + else { + pb_next->setEnabled(false); + } + hb->addWidget(pb_next); + + // Complete + pb_complete = new QPushButton(tr("Finish"),this); + if (r.actions().contains(AHCommand::Complete)) { + if (r.defaultAction() == AHCommand::Complete) { + connect(pb_complete,SIGNAL(clicked()),SLOT(doExecute())); + pb_complete->setDefault(true); + pb_complete->setFocus(); + } + else + connect(pb_complete,SIGNAL(clicked()),SLOT(doComplete())); + pb_complete->setEnabled(true); + } + else { + pb_complete->setEnabled(false); + } + hb->addWidget(pb_complete); + } + pb_cancel = new QPushButton(tr("Cancel"), this); + connect(pb_cancel, SIGNAL(clicked()),SLOT(doCancel())); + hb->addWidget(pb_cancel); + } + else { + pb_complete = new QPushButton(tr("Ok"),this); + connect(pb_complete,SIGNAL(clicked()),SLOT(close())); + hb->addWidget(pb_complete); + } + + if (!r.data().title().isEmpty()) { + setCaption(QString("%1 (%2)").arg(r.data().title()).arg(receiver.full())); + } + else { + setCaption(QString("%1").arg(receiver.full())); + } +} + +void AHCFormDlg::doPrev() +{ + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Prev)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); +} + +void AHCFormDlg::doNext() +{ + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Next)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); +} + +void AHCFormDlg::doExecute() +{ + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); +} + +void AHCFormDlg::doComplete() +{ + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Complete)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); +} + +void AHCFormDlg::doCancel() +{ + busy->start(); + JT_AHCExecute* t = new JT_AHCExecute(account_->client()->rootTask(),account_,receiver_,AHCommand(node_,sessionId_,AHCommand::Cancel)); + connect(t,SIGNAL(finished()),SLOT(commandExecuted())); + t->go(true); +} + +void AHCFormDlg::commandExecuted() +{ + busy->stop(); + close(); +} + +XData AHCFormDlg::data() const +{ + XData x; + x.setFields(xdata_->fields()); + x.setType(XData::Data_Submit); + return x; +} + +// -------------------------------------------------------------------------- + +#include "adhoc.moc" diff -urN psi-gentoo-actual-orig/src/adhoc.h psi-gentoo-actual/src/adhoc.h --- src/adhoc.h 1970-01-01 01:00:00.000000000 +0100 +++ psi-gentoo-actual/src/adhoc.h 2005-11-29 02:53:17.000000000 +0100 @@ -0,0 +1,242 @@ +/* + * adhoc.h - Client & Server implementation of JEP-50 (Ad-Hoc Commands) + * Copyright (C) 2005 Remko Troncon + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADHOC_H +#define ADHOC_H + +#include +#include +#include +#include + +#include "xmpp.h" +#include "xmpp_xdata.h" +#include "busywidget.h" + +using namespace XMPP; + +class PsiAccount; +class QObject; + + +// -------------------------------------------------------------------------- +// General Ad-Hoc Commands classes +// -------------------------------------------------------------------------- + +class AHCError +{ +public: + enum ErrorType { + None, MalformedAction, BadAction, BadLocale, + BadPayload, BadSessionID, SessionExpired, Forbidden, ItemNotFound, + FeatureNotImplemented + }; + + AHCError(ErrorType = None); + AHCError(const QDomElement& e); + + ErrorType type() const { return type_; } + static QString error2description(const AHCError&); + QDomElement toXml(QDomDocument* doc) const; + +protected: + static ErrorType strings2error(const QString&, const QString&); + +private: + ErrorType type_; +}; + + +class AHCommand +{ +public: + // Types + enum Action { NoAction, Execute, Prev, Next, Complete, Cancel }; + enum Status { NoStatus, Completed, Executing, Canceled }; + typedef QValueList ActionList; + + // Constructors + AHCommand(const QString& node, const QString& sessionId = "", Action action = Execute); + AHCommand(const QString& node, XData data, const QString& sessionId = "", Action action = Execute); + AHCommand(const QDomElement &e); + + // Inspectors + const QString& node() const { return node_; } + bool hasData() const { return hasData_; } + const XData& data() const { return data_; } + const ActionList& actions() const { return actions_; } + Action defaultAction() const { return defaultAction_; } + Status status() const { return status_; } + Action action() const { return action_; } + const QString& sessionId() const { return sessionId_; } + const AHCError& error() const { return error_; } + + // XML conversion + QDomElement toXml(QDomDocument* doc, bool submit) const; + + // Helper constructors + static AHCommand formReply(const AHCommand&, const XData&); + static AHCommand formReply(const AHCommand&, const XData&, const QString& sessionId); + static AHCommand canceledReply(const AHCommand&); + static AHCommand completedReply(const AHCommand&); + static AHCommand completedReply(const AHCommand&, const XData&); + //static AHCommand errorReply(const AHCommand&, const AHCError&); + +protected: + void setStatus(Status s); + void setError(const AHCError& e); + void setDefaultAction(Action a); + + static QString action2string(Action); + static QString status2string(Status); + static Action string2action(const QString&); + static Status string2status(const QString&); + +private: + QString node_; + bool hasData_; + XData data_; + Status status_; + Action defaultAction_; + ActionList actions_; + Action action_; + QString sessionId_; + AHCError error_; +}; + + +// -------------------------------------------------------------------------- +// Ad-Hoc Commands Server classes +// -------------------------------------------------------------------------- + +class AHCServerManager; +class JT_AHCServer; +class XDataWidget; + +class AHCommandServer +{ +public: + AHCommandServer(AHCServerManager*); + + virtual QString name() const = 0; + virtual QString node() const = 0; + virtual bool isAllowed(const Jid&) const { return true; } + virtual AHCommand execute(const AHCommand&, const Jid& requester) = 0; + virtual void cancel(const AHCommand&) { } + +protected: + AHCServerManager* manager() const { return manager_; } + +private: + AHCServerManager* manager_; +}; + +class AHCServerManager +{ +public: + AHCServerManager(PsiAccount* pa); + void addServer(AHCommandServer*); + + typedef QPtrList ServerList; + ServerList commands(const Jid&) const; + void execute(const AHCommand& command, const Jid& requester, QString id); + PsiAccount* account() const { return pa_; } + bool hasServer(const QString& node, const Jid& requester) const; + +protected: + AHCommandServer* findServer(const QString& node) const; + +private: + PsiAccount* pa_; + JT_AHCServer* server_task_; + ServerList servers_; +}; + +// -------------------------------------------------------------------------- + +// -------------------------------------------------------------------------- +// Ad-Hoc Commands Client Classes +// -------------------------------------------------------------------------- + +class QComboBox; +class QPushButton; +class JT_AHCGetList; + +typedef struct { QString jid, node, name; } AHCommandItem; + +bool operator<(const AHCommandItem &ci1, const AHCommandItem &ci2); +bool operator<=(const AHCommandItem &ci1, const AHCommandItem &ci2); +bool operator==(const AHCommandItem &ci1, const AHCommandItem &ci2); +bool operator>(const AHCommandItem &ci1, const AHCommandItem &ci2); +bool operator>=(const AHCommandItem &ci1, const AHCommandItem &ci2); + +class AHCommandDlg : public QDialog +{ + Q_OBJECT + +public: + AHCommandDlg(PsiAccount*, const Jid& receiver); + static void executeCommand(PsiAccount*, const Jid& j, const QString &node); + +protected slots: + void refreshCommands(); + void listReceived(); + void executeCommand(); + void commandExecuted(); + +private: + PsiAccount* pa_; + QPushButton *pb_execute, *pb_close, *pb_refresh /*,*pb_info*/; + Jid receiver_; + JT_AHCGetList* task_list_; + QComboBox* cb_commands; + QValueList commands_; + BusyWidget *busy; +}; + + +class AHCFormDlg : public QDialog +{ + Q_OBJECT +public: + AHCFormDlg(const AHCommand& r, const Jid& receiver, PsiAccount* account, bool final = false); + +protected: + XData data() const; + +protected slots: + void doPrev(); + void doNext(); + void doComplete(); + void doExecute(); + void doCancel(); + void commandExecuted(); + +private: + QPushButton *pb_prev, *pb_next, *pb_complete, *pb_cancel; + XDataWidget *xdata_; + Jid receiver_; + QString node_; + PsiAccount* account_; + QString sessionId_; + BusyWidget *busy; +}; + +#endif diff -urN psi-gentoo-actual-orig/src/adhoc_fileserver.cpp psi-gentoo-actual/src/adhoc_fileserver.cpp --- src/adhoc_fileserver.cpp 1970-01-01 01:00:00.000000000 +0100 +++ psi-gentoo-actual/src/adhoc_fileserver.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -0,0 +1,90 @@ +/* + * adhoc_fileserver.cpp - Implementation of a personal ad-hoc fileserver + * Copyright (C) 2005 Remko Troncon + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "common.h" +#include "psiaccount.h" +#include "adhoc_fileserver.h" +#include "xmpp_xdata.h" + +using namespace XMPP; + +bool AHFileServer::isAllowed(const Jid& j) const +{ + return manager()->account()->jid().compare(j,false); +} + +AHCommand AHFileServer::execute(const AHCommand& c, const Jid& requester) +{ + // Extract the file + QString file; + if (c.hasData()) { + QString fileName, curDir; + XData::FieldList fl = c.data().fields(); + for (unsigned int i=0; i < fl.count(); i++) { + if (fl[i].var() == "file" && !(fl[i].value().isEmpty())) { + file = fl[i].value().first(); + } + } + } + else { + file = QDir::currentDirPath(); + } + + if (QFileInfo(file).isDir()) { + // Return a form with a filelist + XData form; + form.setTitle(QObject::tr("Choose file")); + form.setInstructions(QObject::tr("Choose a file")); + form.setType(XData::Data_Form); + XData::FieldList fields; + + XData::Field files_field; + files_field.setType(XData::Field::Field_ListSingle); + files_field.setVar("file"); + files_field.setLabel(QObject::tr("File")); + files_field.setRequired(true); + + XData::Field::OptionList file_options; + QDir d(file); + //d.setFilter(QDir::Files|QDir::Hidden|QDir::NoSymLinks); + QStringList l = d.entryList(); + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it ) { + XData::Field::Option file_option; + QFileInfo fi(QDir(file).filePath(*it)); + file_option.label = *it + (fi.isDir() ? QString(" [DIR]") : QString(" (%1 bytes)").arg(QString::number(fi.size()))); + file_option.value = QDir(file).absFilePath(*it); + file_options += file_option; + } + files_field.setOptions(file_options); + fields += files_field; + + form.setFields(fields); + + return AHCommand::formReply(c, form); + } + else { + QStringList l(file); + manager()->account()->sendFiles(requester,l,true); + return AHCommand::completedReply(c); + } +} diff -urN psi-gentoo-actual-orig/src/adhoc_fileserver.h psi-gentoo-actual/src/adhoc_fileserver.h --- src/adhoc_fileserver.h 1970-01-01 01:00:00.000000000 +0100 +++ psi-gentoo-actual/src/adhoc_fileserver.h 2005-11-29 02:53:17.000000000 +0100 @@ -0,0 +1,38 @@ +/* + * adhoc_fileserver.h - Implementation of a personal file server using ad-hoc + * commands + * Copyright (C) 2005 Remko Troncon + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef AHFILESERVER_H +#define AHFILESERVER_H + +#include "adhoc.h" + +class AHFileServer : public AHCommandServer +{ +public: + AHFileServer(AHCServerManager* m) : AHCommandServer(m) { } + virtual QString node() const + { return QString("http://psi.affinix.com/commands/files"); } + virtual bool isAllowed(const Jid&) const; + virtual QString name() const { return QString("Send file"); } + virtual AHCommand execute(const AHCommand& c, const Jid&); +}; + +#endif diff -urN psi-gentoo-actual-orig/src/chatdlg.cpp psi-gentoo-actual/src/chatdlg.cpp --- src/chatdlg.cpp 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/chatdlg.cpp 2005-11-29 02:58:03.000000000 +0100 @@ -86,8 +86,8 @@ QLabel *lb_composing; PsiToolBar *toolbar; - IconAction *act_send, *act_clear, *act_history, *act_info, *act_pgp, *act_icon, *act_file, *act_compact; + IconAction *act_send, *act_clear, *act_history, *act_info, *act_pgp, *act_icon, *act_file, *act_adhoc, *act_compact; IconAction *act_emergencyStatus; // k int pending; @@ -259,6 +259,9 @@ d->act_file = new IconAction( tr( "Send file" ), "psi/upload", tr( "Send file" ), 0, this ); connect( d->act_file, SIGNAL( activated() ), SLOT( doFile() ) ); + d->act_adhoc = new IconAction( tr("Execute command"), "psi/edit", tr("&Execute command"), 0, this ); + connect( d->act_adhoc, SIGNAL( activated() ), SLOT( doAdHoc() ) ); + d->act_pgp = new IconAction( tr( "Toggle encryption" ), "psi/cryptoNo", tr( "Toggle encryption" ), 0, this, 0, true ); d->act_info = new IconAction( tr( "User info" ), "psi/vCard", tr( "User info" ), CTRL+Key_I, this ); @@ -276,6 +279,7 @@ d->act_emergency->addTo( d->toolbar ); if(option.useEmoticons) d->act_icon->addTo( d->toolbar ); d->act_file->addTo( d->toolbar ); + d->act_adhoc->addTo( d->toolbar ); d->act_pgp->addTo( d->toolbar ); d->act_info->addTo( d->toolbar ); d->act_history->addTo( d->toolbar ); @@ -795,6 +799,11 @@ aFile(d->jid); } +void ChatDlg::doAdHoc() +{ + aAdHoc(d->jid, QString::null); +} + void ChatDlg::doClearButton() { int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to clear the chat window?\n(note: does not affect saved history)"), tr("&Yes"), tr("&No")); @@ -1266,6 +1275,7 @@ if(option.useEmoticons) d->act_icon->addTo( d->pm_settings ); d->act_file->addTo( d->pm_settings ); + d->act_adhoc->addTo( d->pm_settings ); d->act_pgp->addTo( d->pm_settings ); d->pm_settings->insertSeparator(); diff -urN psi-gentoo-actual-orig/src/chatdlg.h psi-gentoo-actual/src/chatdlg.h --- src/chatdlg.h 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/chatdlg.h 2005-11-29 02:53:17.000000000 +0100 @@ -52,6 +52,7 @@ void messagesRead(const Jid &); void aSend(const Message &); void aFile(const Jid &); + void aAdHoc(const Jid &, const QString &); void captionChanged(ChatDlg*); void contactIsComposing(ChatDlg*, bool); void unreadMessageUpdate(ChatDlg*, int); @@ -81,6 +82,7 @@ void doClearButton(); void doSend(); void doFile(); + void doAdHoc(); void setKeepOpenFalse(); void setWarnSendFalse(); void updatePGP(); diff -urN psi-gentoo-actual-orig/src/contactview.cpp psi-gentoo-actual/src/contactview.cpp --- src/contactview.cpp 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/contactview.cpp 2005-11-29 03:04:03.000000000 +0100 @@ -1102,6 +1102,7 @@ int at_sendto = 0; QPopupMenu *s2m = new QPopupMenu(&pm); QPopupMenu *c2m = new QPopupMenu(&pm); + QPopupMenu *rc2m = new QPopupMenu(&pm); /*if ( rl.isEmpty() ) { d->cv->qa_send->addTo(&pm); @@ -1132,6 +1133,7 @@ rname = r.name(); s2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++); c2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++); + rc2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++); } } @@ -1199,6 +1201,16 @@ } } + d->cv->qa_adhoc->setIconSet(IconsetFactory::iconPixmap("psi/edit")); + d->cv->qa_adhoc->addTo(&pm); + + if(!isPrivate) + pm.insertItem(tr("Execute command on"), rc2m, 25); + + if(!isPrivate) + if(rl.isEmpty()) + pm.setItemEnabled(25, false); + if(!isAgent && online) { pm.insertSeparator(); pm.insertItem(IconsetFactory::iconPixmap("psi/upload"), tr("Send &file"), 23); @@ -1571,10 +1583,14 @@ connect(w, SIGNAL(setJid(const Jid &, const Status &)), SLOT(setStatusFromDialog(const Jid &, const Status &))); w->show(); } + else if (x == 25) { + if(online) + actionExecuteCommand(u->jid(), QString::null); + } else if(x >= base_sendto && x < base_openurl) { int n = x - base_sendto; - int res = n / 2; - int type = n % 2; + int res = n / 3; + int type = n % 3; QString rname = ""; //if(res > 0) { const UserResource &r = rl[res]; @@ -1591,6 +1607,8 @@ actionSendMessage(j); else if(type == 1) actionOpenChatSpecific(j); + else if (type == 2) + actionExecuteCommandSpecific(j, QString::null); } else if(x >= base_openurl && x < base_hidden) { int n = 0; @@ -1744,6 +1762,12 @@ actionOpenChat(i->u()->jid()); } +void ContactProfile::scExecuteCommand(ContactViewItem *i) +{ + if(i->type() == ContactViewItem::Contact) + actionExecuteCommand(i->u()->jid(), QString::null); +} + void ContactProfile::scAgentSetStatus(ContactViewItem *i, Status &s) { if(i->type() != ContactViewItem::Contact) @@ -2140,6 +2164,8 @@ #endif qa_chat = new IconAction("", "psi/chat", tr("Open &chat window"), QListView::CTRL+QListView::Key_C, this); connect(qa_chat, SIGNAL(activated()), SLOT(doOpenChat())); + qa_adhoc = new IconAction("", tr("E&xecute command"), QListView::CTRL+QListView::Key_X, this); + connect(qa_adhoc, SIGNAL(activated()), SLOT(doExecuteCommand())); qa_hist = new IconAction("", "psi/history", tr("&History"), QListView::CTRL+QListView::Key_H, this); connect(qa_hist, SIGNAL(activated()), SLOT(doHistory())); qa_logon = new IconAction("", tr("&Log on"), QListView::CTRL+QListView::Key_L, this); @@ -2712,6 +2738,14 @@ i->contactProfile()->scOpenChat(i); } +void ContactView::doExecuteCommand() +{ + ContactViewItem *i = (ContactViewItem *)selectedItem(); + if(!i) + return; + i->contactProfile()->scExecuteCommand(i); +} + void ContactView::doHistory() { ContactViewItem *i = (ContactViewItem *)selectedItem(); diff -urN psi-gentoo-actual-orig/src/contactview.h psi-gentoo-actual/src/contactview.h --- src/contactview.h 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/contactview.h 2005-11-29 03:05:23.000000000 +0100 @@ -110,6 +110,8 @@ void actionTest(const Jid &); void actionSendFile(const Jid &); void actionSendFiles(const Jid &, const QStringList &); + void actionExecuteCommand(const Jid &, const QString &); + void actionExecuteCommandSpecific(const Jid &, const QString &); void actionDisco(const Jid &, const QString &); void actionInvite(const Jid &, const QString &); void actionAssignKey(const Jid &); @@ -166,6 +168,7 @@ void scVCard(ContactViewItem *); void scHistory(ContactViewItem *); void scOpenChat(ContactViewItem *); + void scExecuteCommand(ContactViewItem *); void scAgentSetStatus(ContactViewItem *, Status &); void scRemove(ContactViewItem *); void doItemRenamed(ContactViewItem *, const QString &); @@ -195,7 +198,7 @@ void resetAnim(); QTimer *animTimer() const; - IconAction *qa_send, *qa_chat, *qa_ren, *qa_hist, *qa_logon, *qa_recv, *qa_rem, *qa_vcard; + IconAction *qa_send, *qa_chat, *qa_adhoc, *qa_ren, *qa_hist, *qa_logon, *qa_recv, *qa_rem, *qa_vcard; // More properties IconAction *qa_contactSoundMsgOn, *qa_contactSoundMsgOff, *qa_contactSoundMsgDefault; IconAction *qa_contactPopupAvailOn, *qa_contactPopupAvailOff, *qa_contactPopupAvailDefault; @@ -246,6 +249,7 @@ void doContext(); void doSendMessage(); void doOpenChat(); + void doExecuteCommand(); void doHistory(); void doVCard(); void doLogon(); diff -urN psi-gentoo-actual-orig/src/discodlg.cpp psi-gentoo-actual/src/discodlg.cpp --- src/discodlg.cpp 2005-08-21 19:44:28.000000000 +0200 +++ psi-gentoo-actual/src/discodlg.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -790,7 +790,7 @@ IconAction *actBrowse, *actBack, *actForward, *actRefresh, *actStop; // custom actions, that will be added to toolbar and context menu - IconAction *actRegister, *actSearch, *actJoin, *actVCard, *actAdd; + IconAction *actRegister, *actSearch, *actJoin, *actAdHoc, *actVCard, *actAdd; typedef QPtrList HistoryList; HistoryList backHistory, forwardHistory; @@ -898,6 +898,9 @@ actJoin = new IconAction (tr("Join"), "psi/groupChat", tr("&Join"), 0, dlg); connect (actJoin, SIGNAL(activated()), sm, SLOT(map())); sm->setMapping(actJoin, Features::FID_Groupchat); + actAdHoc = new IconAction (tr("Execute command"), "psi/edit", tr("&Execute command"), 0, dlg); + connect (actAdHoc, SIGNAL(activated()), sm, SLOT(map())); + sm->setMapping(actAdHoc, Features::FID_AdHoc); actVCard = new IconAction (tr("vCard"), "psi/vCard", tr("&vCard"), 0, dlg); connect (actVCard, SIGNAL(activated()), sm, SLOT(map())); sm->setMapping(actVCard, Features::FID_VCard); @@ -922,6 +925,7 @@ actRegister->addTo(toolBar); actSearch->addTo(toolBar); actJoin->addTo(toolBar); + actAdHoc->addTo(toolBar); toolBar->addSeparator(); actAdd->addTo(toolBar); @@ -1136,6 +1140,7 @@ actRegister->setEnabled( f.canRegister() ); actSearch->setEnabled( f.canSearch() ); actJoin->setEnabled( f.canGroupchat() ); + actAdHoc->setEnabled( f.canAdHoc() ); actAdd->setEnabled( itemSelected ); actVCard->setEnabled( f.haveVCard() ); } @@ -1163,24 +1168,15 @@ const DiscoItem d = it->item(); const Features &f = d.features(); - // set the prior state of item - // FIXME: causes minor flickering - if ( f.canGroupchat() || f.canRegister() || f.canSearch() ) { - if ( !it->isOpen() ) { - if ( it->isExpandable() || it->childCount() ) - it->setOpen( true ); - } - else { - it->setOpen( false ); - } - } - long id = 0; // trigger default action if ( f.canGroupchat() ) { id = Features::FID_Groupchat; } + else if (f.canAdHoc() ) { + id = Features::FID_AdHoc; + } else { // FIXME: check the category and type for JUD! DiscoItem::Identity ident = d.identities().first(); @@ -1195,8 +1191,20 @@ } } - if ( id > 0 ) + if ( id > 0 ) { + + // set the prior state of item + // FIXME: causes minor flickering + if ( !it->isOpen() ) { + if ( it->isExpandable() || it->childCount() ) + it->setOpen( true ); + } + else { + it->setOpen( false ); + } + emit dlg->featureActivated( Features::feature(id), d.jid(), d.node() ); + } } bool DiscoDlg::Private::eventFilter (QObject *object, QEvent *event) @@ -1254,6 +1262,7 @@ actRegister->addTo(&p); actSearch->addTo(&p); actJoin->addTo(&p); + actAdHoc->addTo(&p); p.insertSeparator(); actAdd->addTo(&p); diff -urN psi-gentoo-actual-orig/src/filetransdlg.cpp psi-gentoo-actual/src/filetransdlg.cpp --- src/filetransdlg.cpp 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/filetransdlg.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -511,7 +511,7 @@ } -FileRequestDlg::FileRequestDlg(const Jid &jid, PsiCon *psi, PsiAccount *pa, const QStringList& files) +FileRequestDlg::FileRequestDlg(const Jid &jid, PsiCon *psi, PsiAccount *pa, const QStringList& files, bool direct) :FileTransUI(0, 0, false, psi_dialog_flags | WDestructiveClose) { d = new Private; @@ -579,6 +579,10 @@ le_fname->setText(QDir::convertSeparators(fi.filePath())); lb_size->setText(tr("%1 byte(s)").arg(fi.size())); // TODO: large file support } + + if (direct) { + doStart(); + } } FileRequestDlg::FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa) diff -urN psi-gentoo-actual-orig/src/filetransdlg.h psi-gentoo-actual/src/filetransdlg.h --- src/filetransdlg.h 2005-11-29 02:44:36.000000000 +0100 +++ psi-gentoo-actual/src/filetransdlg.h 2005-11-29 02:53:17.000000000 +0100 @@ -81,7 +81,7 @@ Q_OBJECT public: FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa); - FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa, const QStringList& files); + FileRequestDlg(const Jid &j, PsiCon *psi, PsiAccount *pa, const QStringList& files, bool direct = false); FileRequestDlg(const QDateTime &ts, FileTransfer *ft, PsiAccount *pa); ~FileRequestDlg(); diff -urN psi-gentoo-actual-orig/src/psiaccount.cpp psi-gentoo-actual/src/psiaccount.cpp --- src/psiaccount.cpp 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/psiaccount.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -43,6 +43,9 @@ #include #include"psicon.h" +#include"adhoc.h" +#include"rc.h" +#include"adhoc_fileserver.h" #include"profiles.h" #include"im.h" //#include"xmpp_client.h" @@ -320,6 +323,9 @@ BlockTransportPopupList *blockTransportPopupList; int userCounter; + // Ad-hoc commands + AHCServerManager* ahcManager; + // Avatars AvatarFactory* avatarFactory; @@ -502,11 +509,25 @@ connect(d->cp, SIGNAL(actionTest(const Jid &)),SLOT(actionTest(const Jid &))); connect(d->cp, SIGNAL(actionSendFile(const Jid &)),SLOT(actionSendFile(const Jid &))); connect(d->cp, SIGNAL(actionSendFiles(const Jid &, const QStringList&)),SLOT(actionSendFiles(const Jid &, const QStringList&))); + connect(d->cp, SIGNAL(actionExecuteCommand(const Jid &, const QString &)),SLOT(actionExecuteCommand(const Jid &, const QString &))); + connect(d->cp, SIGNAL(actionExecuteCommandSpecific(const Jid &, const QString &)),SLOT(actionExecuteCommandSpecific(const Jid &, const QString &))); connect(d->cp, SIGNAL(actionDisco(const Jid &, const QString &)),SLOT(actionDisco(const Jid &, const QString &))); connect(d->cp, SIGNAL(actionInvite(const Jid &, const QString &, const QString &)),SLOT(actionInvite(const Jid &, const QString &, const QString &))); connect(d->cp, SIGNAL(actionAssignKey(const Jid &)),SLOT(actionAssignKey(const Jid &))); connect(d->cp, SIGNAL(actionUnassignKey(const Jid &)),SLOT(actionUnassignKey(const Jid &))); + // Initialize Adhoc Commands server + d->ahcManager = new AHCServerManager(this); + new RCSetStatusServer(d->ahcManager); + new RCForwardServer(d->ahcManager); + new RCSetOptionsServer(d->ahcManager, d->psi); + // !!!!!!!!!! WARNING !!!!!!! + // Uncommenting the following line is a *huge* security risk. Anyone who + // is able to impersonate you in the Jabber protocol (such as your server + // admin) can download _every_ file from your computer you have access to. + // Uncomment this only if you want to do some quick tests ! + //new AHFileServer(d->ahcManager); + // restore cached roster for(Roster::ConstIterator it = acc.roster.begin(); it != acc.roster.end(); ++it) client_rosterItemUpdated(*it); @@ -1555,6 +1575,20 @@ _m.setFrom(jid().domain()); } + // check to see if message was forwarded from another resource + if (jid().compare(_m.from(),false)) + { + AddressList oFrom = _m.addressFind(OriginalFrom); + AddressList oTo = _m.addressFind(OriginalTo); + if ((oFrom.count() > 0) && (oTo.count() > 0)) + { + // might want to store current values in MessageEvent object + // replace out the from and to addresses with the original addresses + _m.setFrom(oFrom[0].jid()); + _m.setTo(oTo[0].jid()); + } + } + // if the sender is already in the queue, then queue this message also QPtrListIterator it(d->messageQueue); for(Message *mi; (mi = it.current()); ++it) { @@ -2138,6 +2172,8 @@ actionSearch(jid); else if ( f.canGroupchat() ) actionJoin(jid, QString::null); + else if ( f.canAdHoc() ) + actionExecuteCommand(jid, node); else if ( f.canDisco() ) actionDisco(jid, node); else if ( f.isGateway() ) @@ -2404,6 +2440,7 @@ connect(c, SIGNAL(aInfo(const Jid &)), SLOT(actionInfo(const Jid &))); connect(c, SIGNAL(aHistory(const Jid &)), SLOT(actionHistory(const Jid &))); connect(c, SIGNAL(aFile(const Jid &)), SLOT(actionSendFile(const Jid &))); + connect(c, SIGNAL(aAdHoc(const Jid &, const QString &)), SLOT(actionExecuteCommandSpecific(const Jid &, const QString &))); connect(d->psi, SIGNAL(emitOptionsUpdate()), c, SLOT(optionsUpdate())); connect(this, SIGNAL(updateContact(const Jid &, bool)), c, SLOT(updateContact(const Jid &, bool))); } @@ -2472,13 +2509,7 @@ w->show(); } -void PsiAccount::actionSendFile(const Jid &j) -{ - QStringList l; - actionSendFiles(j, l); -} - -void PsiAccount::actionSendFiles(const Jid &j, const QStringList& l) +void PsiAccount::sendFiles(const Jid& j, const QStringList& l, bool direct) { Jid j2 = j; if(j.resource().isEmpty()) { @@ -2492,16 +2523,53 @@ if (!l.isEmpty()) { for (QStringList::ConstIterator f = l.begin(); f != l.end(); ++f ) { QStringList fl(*f); - FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, fl); + FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, fl, direct); w->show(); } } else { - FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, l); + FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, l, direct); w->show(); } } +void PsiAccount::actionSendFile(const Jid &j) +{ + QStringList l; + sendFiles(j,l); +} + +void PsiAccount::actionSendFiles(const Jid &j, const QStringList& l) +{ + sendFiles(j, l); +} + +void PsiAccount::actionExecuteCommand(const Jid& j, const QString &node) +{ + printf("Non-specific\n"); + Jid j2 = j; + if(j.resource().isEmpty()) { + UserListItem *u = find(j); + if(u && u->isAvailable()) + j2.setResource((*u->userResourceList().priority()).name()); + } + + actionExecuteCommandSpecific(j2, node); +} + +void PsiAccount::actionExecuteCommandSpecific(const Jid& j, const QString &node) +{ + if (node.isEmpty()) + { + AHCommandDlg *w = new AHCommandDlg(this,j); + w->show(); + } + else + { + AHCommandDlg::executeCommand(this,j,node); + } +} + void PsiAccount::actionDefault(const Jid &j) { UserListItem *u = find(j); @@ -3507,6 +3575,46 @@ openNextEvent(*u); } +int PsiAccount::forwardPendingEvents(const Jid &jid) +{ + QPtrList chatList; + d->eventQueue->extractMessages(&chatList); + + QPtrListIterator it(chatList); + for(PsiEvent *e; (e = it.current()); ++it) { + MessageEvent *me = (MessageEvent *)e; + + // process the message + Message m = me->message(); + + AddressList oFrom = m.addressFind(OriginalFrom); + AddressList oTo = m.addressFind(OriginalTo); + + if (oFrom.count() == 0) + m.addressAdd(Address(OriginalFrom, m.from())); + if (oTo.count() == 0) + m.addressAdd(Address(OriginalTo, m.to())); + + m.setTo(jid); + m.setFrom(""); + + d->client->sendMessage(m); + + // update the eventdlg + UserListItem *u = find(e->jid()); + delete e; + + // update the contact + if(u) + cpUpdate(*u); + + updateReadNext(u->jid()); + + } + + return it.count(); +} + void PsiAccount::updateReadNext(const Jid &j) { // update eventdlg's read-next diff -urN psi-gentoo-actual-orig/src/psiaccount.h psi-gentoo-actual/src/psiaccount.h --- src/psiaccount.h 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/psiaccount.h 2005-11-29 02:53:17.000000000 +0100 @@ -208,6 +208,7 @@ static void getErrorInfo(int err, AdvancedConnector *conn, Stream *stream, QCATLSHandler *tlsHandler, QString *_str, bool *_reconn); void deleteQueueFile(); + void sendFiles(const Jid&, const QStringList&, bool direct = false); signals: void disconnected(); @@ -228,6 +229,7 @@ QString expandAutoStatus(const QString &t); void secondsIdle(int); void openNextEvent(); + int forwardPendingEvents(const Jid &jid); void dj_sendMessage(const Message &, bool log=true); void dj_composeMessage(const Jid &jid, const QString &body); @@ -270,7 +272,9 @@ void actionDisco(const Jid &, const QString &); void actionInvite(const Jid &, const QString &); void actionSendFile(const Jid &); - void actionSendFiles(const Jid &, const QStringList&); + void actionSendFiles(const Jid &, const QStringList &); + void actionExecuteCommand(const Jid &, const QString &); + void actionExecuteCommandSpecific(const Jid &, const QString &); void featureActivated(QString feature, Jid jid, QString node); void actionAssignKey(const Jid &); diff -urN psi-gentoo-actual-orig/src/psievent.cpp psi-gentoo-actual/src/psievent.cpp --- src/psievent.cpp 2005-11-29 02:44:36.000000000 +0100 +++ psi-gentoo-actual/src/psievent.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -650,6 +650,27 @@ emit queueChanged(); } +// this function extracts all messages from the queue, and returns a list of them +void EventQueue::extractMessages(QPtrList *el) +{ + bool changed = false; + + QPtrListIterator it(d->list); + for(EventItem *i; (i = it.current());) { + PsiEvent *e = i->event(); + if(e->type() == PsiEvent::Message) { + el->append(e); + d->list.remove(it); + changed = true; + continue; + } + ++it; + } + + if ( changed ) + emit queueChanged(); +} + void EventQueue::printContent() const { QPtrListIterator it(d->list); diff -urN psi-gentoo-actual-orig/src/psievent.h psi-gentoo-actual/src/psievent.h --- src/psievent.h 2005-11-29 02:44:36.000000000 +0100 +++ psi-gentoo-actual/src/psievent.h 2005-11-29 02:53:17.000000000 +0100 @@ -204,6 +204,7 @@ PsiEvent *peekNext() const; bool hasChats(const Jid &, bool compareRes=true) const; PsiEvent *peekFirstChat(const Jid &, bool compareRes=true) const; + void extractMessages(QPtrList *list); void extractChats(QPtrList *list, const Jid &, bool compareRes=true); void printContent() const; void clear(); diff -urN psi-gentoo-actual-orig/src/rc.cpp psi-gentoo-actual/src/rc.cpp --- src/rc.cpp 1970-01-01 01:00:00.000000000 +0100 +++ psi-gentoo-actual/src/rc.cpp 2005-11-29 02:53:17.000000000 +0100 @@ -0,0 +1,244 @@ +/* + * rc.cpp - Implementation of JEP-146 (Remote Controlling Clients) + * Copyright (C) 2005 Remko Troncon + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "common.h" +#include "iconaction.h" +#include "mainwin.h" +#include "psiaccount.h" +#include "psiactionlist.h" +#include "psicon.h" +#include "rc.h" +#include "xmpp_xdata.h" + +using namespace XMPP; + +bool RCCommandServer::isAllowed(const Jid& j) const +{ + return manager()->account()->jid().compare(j,false); +} + +AHCommand RCSetStatusServer::execute(const AHCommand& c, const Jid&) +{ + // Check if the session ID is correct + //if (c.sessionId() != "") + // return AHCommand::errorReply(c,AHCError::AHCError(AHCError::BadSessionID)); + + if (!c.hasData()) { + // Initial set status form + XData form; + form.setTitle(QObject::tr("Set Status")); + form.setInstructions(QObject::tr("Choose the status and status message")); + form.setType(XData::Data_Form); + XData::FieldList fields; + + XData::Field type_field; + type_field.setType(XData::Field::Field_Hidden); + type_field.setVar("FORM_TYPE"); + type_field.setValue(QStringList("http://jabber.org/protocol/rc")); + type_field.setRequired(false); + fields += type_field; + + XData::Field status_field; + status_field.setType(XData::Field::Field_ListSingle); + status_field.setVar("status"); + status_field.setLabel(QObject::tr("Status")); + status_field.setRequired(true); + switch(makeSTATUS(manager()->account()->status())) { + case STATUS_OFFLINE: status_field.setValue(QStringList("offline")); break; + case STATUS_AWAY: status_field.setValue(QStringList("away")); break; + case STATUS_XA: status_field.setValue(QStringList("xa")); break; + case STATUS_DND: status_field.setValue(QStringList("dnd")); break; + case STATUS_CHAT: status_field.setValue(QStringList("chat")); break; + case STATUS_INVISIBLE: status_field.setValue(QStringList("invisible")); break; + + case STATUS_ONLINE: + default: status_field.setValue(QStringList("online")); break; + } + XData::Field::OptionList status_options; + XData::Field::Option chat_option; + chat_option.label = QObject::tr("Chat"); + chat_option.value = "chat"; + status_options += chat_option; + XData::Field::Option online_option; + online_option.label = QObject::tr("Online"); + online_option.value = "online"; + status_options += online_option; + XData::Field::Option away_option; + away_option.label = QObject::tr("Away"); + away_option.value = "away"; + status_options += away_option; + XData::Field::Option xa_option; + xa_option.label = QObject::tr("Extended Away"); + xa_option.value = "xa"; + status_options += xa_option; + XData::Field::Option dnd_option; + dnd_option.label = QObject::tr("Do Not Disturb"); + dnd_option.value = "dnd"; + status_options += dnd_option; + XData::Field::Option invisible_option; + invisible_option.label = QObject::tr("Invisible"); + invisible_option.value = "invisible"; + status_options += invisible_option; + XData::Field::Option offline_option; + offline_option.label = QObject::tr("Offline"); + offline_option.value = "offline"; + status_options += offline_option; + status_field.setOptions(status_options); + fields += status_field; + + XData::Field priority_field; + priority_field.setType(XData::Field::Field_TextSingle); + priority_field.setLabel(QObject::tr("Priority")); + priority_field.setVar("status-priority"); + priority_field.setRequired(false); + priority_field.setValue(QStringList(QString::number(manager()->account()->status().priority()))); + fields += priority_field; + + XData::Field statusmsg_field; + statusmsg_field.setType(XData::Field::Field_TextMulti); + statusmsg_field.setLabel(QObject::tr("Message")); + statusmsg_field.setVar("status-message"); + statusmsg_field.setRequired(false); + statusmsg_field.setValue(QStringList(manager()->account()->status().status())); + fields += statusmsg_field; + + form.setFields(fields); + + return AHCommand::formReply(c, form); + } + else { + // Set the status + Status s; + bool foundStatus; + XData::FieldList fl = c.data().fields(); + for (unsigned int i=0; i < fl.count(); i++) { + if (fl[i].var() == "status" && !(fl[i].value().isEmpty())) { + foundStatus = true; + QString show = fl[i].value().first(); + if (show == "away" || show == "dnd" || show == "xa" || show == "chat") + s.setShow(show); + else if (show == "offline") + s.setIsAvailable(false); + else if (show == "invisible") + s.setIsInvisible(true); + } + else if (fl[i].var() == "status-message") { + s.setStatus(fl[i].value().first()); + } + else if (fl[i].var() == "status-priority") { + s.setPriority(fl[i].value().first().toInt()); + } + } + if (foundStatus) { + manager()->account()->setStatus(s); + } + return AHCommand::completedReply(c); + } +} + + +AHCommand RCForwardServer::execute(const AHCommand& c, const Jid& j) +{ + int messageCount = manager()->account()->forwardPendingEvents(j); + + XData form; + form.setTitle(QObject::tr("Forward Messages")); + form.setInstructions(QObject::tr("Forwarded %1 messages").arg(messageCount)); + form.setType(XData::Data_Form); + + return AHCommand::completedReply(c, form); +} + + +AHCommand RCSetOptionsServer::execute(const AHCommand& c, const Jid&) +{ + if (!c.hasData()) { + // Initial set options form + XData form; + form.setTitle(QObject::tr("Set Options")); + form.setInstructions(QObject::tr("Set the desired options")); + form.setType(XData::Data_Form); + XData::FieldList fields; + + XData::Field type_field; + type_field.setType(XData::Field::Field_Hidden); + type_field.setVar("FORM_TYPE"); + type_field.setValue(QStringList("http://jabber.org/protocol/rc")); + type_field.setRequired(false); + fields += type_field; + + XData::Field sounds_field; + sounds_field.setType(XData::Field::Field_Boolean); + sounds_field.setLabel(QObject::tr("Play Sounds")); + sounds_field.setVar("sounds"); + sounds_field.setValue(QStringList((useSound ? "1" : "0"))); + sounds_field.setRequired(false); + fields += sounds_field; + + XData::Field auto_offline_field; + auto_offline_field.setType(XData::Field::Field_Boolean); + auto_offline_field.setLabel(QObject::tr("Automatically Go Offline when Idle")); + auto_offline_field.setVar("auto-offline"); + auto_offline_field.setValue(QStringList((option.use_asOffline ? "1" : "0"))); + auto_offline_field.setRequired(false); + fields += auto_offline_field; + + XData::Field auto_auth_field; + auto_auth_field.setType(XData::Field::Field_Boolean); + auto_auth_field.setLabel(QObject::tr("Auto-authorize Contacts")); + auto_auth_field.setVar("auto-auth"); + auto_auth_field.setValue(QStringList((option.autoAuth ? "1" : "0"))); + auto_auth_field.setRequired(false); + fields += auto_auth_field; + + form.setFields(fields); + + return AHCommand::formReply(c, form); + } + else { + // Set the options + XData::FieldList fl = c.data().fields(); + for (unsigned int i=0; i < fl.count(); i++) { + if (fl[i].var() == "sounds") { + QString v = fl[i].value().first(); + IconAction* soundact = psiCon_->actionList()->suitableActions(PsiActionList::ActionsType( PsiActionList::Actions_MainWin | PsiActionList::Actions_Common)).action("menu_play_sounds"); + if (v == "1") + soundact->setOn(true); + else if (v == "0") + soundact->setOn(false); + } + else if (fl[i].var() == "auto-offline") { + QString v = fl[i].value().first(); + if (v == "1") + option.use_asOffline = true; + else if (v == "0") + option.use_asOffline = false; + } + else if (fl[i].var() == "auto-auth") { + QString v = fl[i].value().first(); + if (v == "1") + option.autoAuth = true; + else if (v == "0") + option.autoAuth = false; + } + } + return AHCommand::completedReply(c); + } +} diff -urN psi-gentoo-actual-orig/src/rc.h psi-gentoo-actual/src/rc.h --- src/rc.h 1970-01-01 01:00:00.000000000 +0100 +++ psi-gentoo-actual/src/rc.h 2005-11-29 02:53:17.000000000 +0100 @@ -0,0 +1,69 @@ +/* + * rc.h - Implementation of JEP-146 (Remote Controlling Clients) + * Copyright (C) 2005 Remko Troncon + * + * 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef RC_H +#define RC_H + +#include "adhoc.h" + +class PsiCon; + +class RCCommandServer : public AHCommandServer +{ +public: + RCCommandServer(AHCServerManager* m) : AHCommandServer(m) { } + virtual QString node() const + { return QString("http://jabber.org/protocol/rc#") + rcNode(); } + virtual QString rcNode() const = 0; + virtual bool isAllowed(const Jid&) const; + +}; + +class RCSetStatusServer : public RCCommandServer +{ +public: + RCSetStatusServer(AHCServerManager* m) : RCCommandServer(m) { } + virtual QString name() const { return "Set Status"; } + virtual QString rcNode() const { return "set-status"; } + virtual AHCommand execute(const AHCommand&, const Jid&); +}; + +class RCForwardServer : public RCCommandServer +{ +public: + RCForwardServer(AHCServerManager* m) : RCCommandServer(m) { } + virtual QString name() const { return "Forward Messages"; } + virtual QString rcNode() const { return "forward"; } + virtual AHCommand execute(const AHCommand& c, const Jid&); +}; + +class RCSetOptionsServer : public RCCommandServer +{ +public: + RCSetOptionsServer(AHCServerManager* m, PsiCon* c) : RCCommandServer(m), psiCon_(c) { } + virtual QString name() const { return "Set Options"; } + virtual QString rcNode() const { return "set-options"; } + virtual AHCommand execute(const AHCommand& c, const Jid&); + +private: + PsiCon* psiCon_; +}; + +#endif diff -urN psi-gentoo-actual-orig/src/src.pro psi-gentoo-actual/src/src.pro --- src/src.pro 2005-11-29 02:44:37.000000000 +0100 +++ psi-gentoo-actual/src/src.pro 2005-11-29 02:53:17.000000000 +0100 @@ -134,7 +134,10 @@ $$PSI_CPP/muc_affileditorpriv.h \ $$PSI_CPP/jidvalidator.h \ $$PSI_CPP/psilistview.h \ - $$PSI_CPP/jiddrag.h + $$PSI_CPP/jiddrag.h \ + $$PSI_CPP/adhoc.h \ + $$PSI_CPP/rc.h \ + $$PSI_CPP/adhoc_fileserver.h # Source files SOURCES += \ @@ -184,7 +187,10 @@ $$PSI_CPP/muc_affileditorpriv.cpp \ $$PSI_CPP/jidvalidator.cpp \ $$PSI_CPP/psilistview.cpp \ - $$PSI_CPP/jiddrag.cpp + $$PSI_CPP/jiddrag.cpp \ + $$PSI_CPP/adhoc.cpp \ + $$PSI_CPP/rc.cpp \ + $$PSI_CPP/adhoc_fileserver.cpp mac { contains( DEFINES, HAVE_GROWL ) {