File indexing completed on 2024-06-23 04:03:41

0001 /*
0002  * Copyright (C) 2003  Justin Karneges
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * either version 2
0008    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public
0016  * License along with this library; if not, write to the Free Software
0017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0018  *
0019  */
0020 
0021 #include <QTimer>
0022 
0023 #include "safedelete.h"
0024 #include "xmpp_task.h"
0025 #include "xmpp_client.h"
0026 #include "xmpp_xmlcommon.h"
0027 
0028 using namespace XMPP;
0029 
0030 class Task::TaskPrivate
0031 {
0032 public:
0033     TaskPrivate() {}
0034 
0035     QString id;
0036     bool success;
0037     int statusCode;
0038     QString statusString;
0039     Client *client;
0040     bool insig, deleteme, autoDelete;
0041     bool done;
0042 };
0043 
0044 Task::Task(Task *parent)
0045 :QObject(parent)
0046 {
0047     init();
0048 
0049     d->client = parent->client();
0050     d->id = client()->genUniqueId();
0051     connect(d->client, &Client::disconnected, this, &Task::clientDisconnected);
0052 }
0053 
0054 Task::Task(Client *parent, bool)
0055 :QObject(0)
0056 {
0057     init();
0058 
0059     d->client = parent;
0060     connect(d->client, &Client::disconnected, this, &Task::clientDisconnected);
0061 }
0062 
0063 Task::~Task()
0064 {
0065     delete d;
0066 }
0067 
0068 void Task::init()
0069 {
0070     d = new TaskPrivate;
0071     d->success = false;
0072     d->insig = false;
0073     d->deleteme = false;
0074     d->autoDelete = false;
0075     d->done = false;
0076 }
0077 
0078 Task *Task::parent() const
0079 {
0080     return (Task *)QObject::parent();
0081 }
0082 
0083 Client *Task::client() const
0084 {
0085     return d->client;
0086 }
0087 
0088 QDomDocument *Task::doc() const
0089 {
0090     return client()->doc();
0091 }
0092 
0093 QString Task::id() const
0094 {
0095     return d->id;
0096 }
0097 
0098 bool Task::success() const
0099 {
0100     return d->success;
0101 }
0102 
0103 int Task::statusCode() const
0104 {
0105     return d->statusCode;
0106 }
0107 
0108 const QString & Task::statusString() const
0109 {
0110     return d->statusString;
0111 }
0112 
0113 void Task::go(bool autoDelete)
0114 {
0115     d->autoDelete = autoDelete;
0116 
0117     if (!client() || !&client()->stream()) {
0118         qWarning("Task::go(): attempted to send a task over the broken connection.");
0119         if (autoDelete) {
0120             deleteLater();
0121         }
0122     }
0123     else {
0124         onGo();
0125     }
0126 }
0127 
0128 bool Task::take(const QDomElement &x)
0129 {
0130     const QObjectList p = children();
0131 
0132     // pass along the xml
0133     Task *t;
0134     for(QObjectList::ConstIterator it = p.begin(); it != p.end(); ++it) {
0135         QObject *obj = *it;
0136         if(!obj->inherits("XMPP::Task"))
0137             continue;
0138 
0139         t = static_cast<Task*>(obj);
0140         if(t->take(x))
0141             return true;
0142     }
0143 
0144     return false;
0145 }
0146 
0147 void Task::safeDelete()
0148 {
0149     if(d->deleteme)
0150         return;
0151 
0152     d->deleteme = true;
0153     if(!d->insig)
0154         SafeDelete::deleteSingle(this);
0155 }
0156 
0157 void Task::onGo()
0158 {
0159 }
0160 
0161 void Task::onDisconnect()
0162 {
0163     if(!d->done) {
0164         d->success = false;
0165         d->statusCode = ErrDisc;
0166         d->statusString = tr("Disconnected");
0167 
0168         // delay this so that tasks that react don't block the shutdown
0169         QTimer::singleShot(0, this, &Task::done);
0170     }
0171 }
0172 
0173 void Task::send(const QDomElement &x)
0174 {
0175     client()->send(x);
0176 }
0177 
0178 void Task::setSuccess(int code, const QString &str)
0179 {
0180     if(!d->done) {
0181         d->success = true;
0182         d->statusCode = code;
0183         d->statusString = str;
0184         done();
0185     }
0186 }
0187 
0188 void Task::setError(const QDomElement &e)
0189 {
0190     if(!d->done) {
0191         d->success = false;
0192         getErrorFromElement(e, d->client->streamBaseNS(), &d->statusCode, &d->statusString);
0193         done();
0194     }
0195 }
0196 
0197 void Task::setError(int code, const QString &str)
0198 {
0199     if(!d->done) {
0200         d->success = false;
0201         d->statusCode = code;
0202         d->statusString = str;
0203         done();
0204     }
0205 }
0206 
0207 void Task::done()
0208 {
0209     if(d->done || d->insig)
0210         return;
0211     d->done = true;
0212 
0213     if(d->deleteme || d->autoDelete)
0214         d->deleteme = true;
0215 
0216     d->insig = true;
0217     finished();
0218     d->insig = false;
0219 
0220     if(d->deleteme)
0221         SafeDelete::deleteSingle(this);
0222 }
0223 
0224 void Task::clientDisconnected()
0225 {
0226     onDisconnect();
0227 }
0228 
0229 void Task::debug(const char *fmt, ...)
0230 {
0231     va_list ap;
0232     va_start(ap, fmt);
0233     const QString str = QString::vasprintf(fmt, ap);
0234     va_end(ap);
0235     debug(str);
0236 }
0237 
0238 void Task::debug(const QString &str)
0239 {
0240     client()->debug(QString("%1: ").arg(metaObject()->className()) + str);
0241 }
0242 
0243 
0244 /**
0245  * \brief verifiys a stanza is a IQ reply for this task
0246  *
0247  * it checks that the stanze is form the jid the request was send to and the id and the namespace (if given) match.
0248  *
0249  * it further checks that the sender jid is not empty (except if \a to is our server), it's not from
0250  * our bare jid (except if send to one of our resources or our server)
0251  * \param x the stanza to test
0252  * \param to the Jid this task send a IQ to
0253  * \param id the id of the send IQ
0254  * \param xmlns the expected namespace if the reply (if non empty)
0255  * \return true if it's a valid reply
0256 */
0257 
0258 bool Task::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns)
0259 {
0260     if(x.tagName() != "iq")
0261         return false;
0262 
0263     Jid from(x.attribute("from"));
0264     Jid local = client()->jid();
0265     Jid server = client()->host();
0266 
0267     // empty 'from' ?
0268     if(from.isEmpty()) {
0269         // allowed if we are querying the server
0270         if(!to.isEmpty() && !to.compare(server))
0271             return false;
0272     }
0273     // from ourself?
0274     else if(from.compare(local, false) || from.compare(local.domain(),false)) {
0275         // allowed if we are querying ourself or the server
0276         if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server))
0277             return false;
0278     }
0279     // from anywhere else?
0280     else {
0281         if(!from.compare(to))
0282             return false;
0283     }
0284 
0285     if(!id.isEmpty()) {
0286         if(x.attribute("id") != id)
0287             return false;
0288     }
0289 
0290     if(!xmlns.isEmpty()) {
0291         if(queryNS(x) != xmlns)
0292             return false;
0293     }
0294 
0295     return true;
0296 }
0297