~ubuntu-branches/ubuntu/oneiric/psi/oneiric

« back to all changes in this revision

Viewing changes to iris/jabber/filetransfer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2009-09-25 17:49:51 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090925174951-lvm7kdap82o8xhn3
Tags: 0.13-1
* Updated to upstream version 0.13
* Set Standards-Version to 3.8.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * filetransfer.cpp - File Transfer
3
 
 * Copyright (C) 2004  Justin Karneges
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Lesser General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2.1 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Lesser General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Lesser General Public
16
 
 * License along with this library; if not, write to the Free Software
17
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
 *
19
 
 */
20
 
 
21
 
#include "filetransfer.h"
22
 
 
23
 
#include <qtimer.h>
24
 
#include <q3ptrlist.h>
25
 
#include <qpointer.h>
26
 
#include <qfileinfo.h>
27
 
#include "xmpp_xmlcommon.h"
28
 
#include "s5b.h"
29
 
 
30
 
#define SENDBUFSIZE 65536
31
 
 
32
 
using namespace XMPP;
33
 
 
34
 
// firstChildElement
35
 
//
36
 
// Get an element's first child element
37
 
static QDomElement firstChildElement(const QDomElement &e)
38
 
{
39
 
        for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
40
 
                if(n.isElement())
41
 
                        return n.toElement();
42
 
        }
43
 
        return QDomElement();
44
 
}
45
 
 
46
 
//----------------------------------------------------------------------------
47
 
// FileTransfer
48
 
//----------------------------------------------------------------------------
49
 
class FileTransfer::Private
50
 
{
51
 
public:
52
 
        FileTransferManager *m;
53
 
        JT_FT *ft;
54
 
        Jid peer;
55
 
        QString fname;
56
 
        qlonglong size;
57
 
        qlonglong sent;
58
 
        QString desc;
59
 
        bool rangeSupported;
60
 
        qlonglong rangeOffset, rangeLength, length;
61
 
        QString streamType;
62
 
        bool needStream;
63
 
        QString id, iq_id;
64
 
        S5BConnection *c;
65
 
        Jid proxy;
66
 
        int state;
67
 
        bool sender;
68
 
};
69
 
 
70
 
FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent)
71
 
:QObject(parent)
72
 
{
73
 
        d = new Private;
74
 
        d->m = m;
75
 
        d->ft = 0;
76
 
        d->c = 0;
77
 
        reset();
78
 
}
79
 
 
80
 
FileTransfer::FileTransfer(const FileTransfer& other)
81
 
        : QObject(other.parent())
82
 
{
83
 
        d = new Private;
84
 
        *d = *other.d;
85
 
        d->m = other.d->m;
86
 
        d->ft = 0;
87
 
        d->c = 0;
88
 
        reset();
89
 
 
90
 
        if (d->m->isActive(&other))
91
 
                d->m->link(this);
92
 
}
93
 
 
94
 
FileTransfer::~FileTransfer()
95
 
{
96
 
        reset();
97
 
        delete d;
98
 
}
99
 
 
100
 
FileTransfer *FileTransfer::copy() const
101
 
{
102
 
        return new FileTransfer(*this);
103
 
}
104
 
 
105
 
void FileTransfer::reset()
106
 
{
107
 
        d->m->unlink(this);
108
 
 
109
 
        delete d->ft;
110
 
        d->ft = 0;
111
 
 
112
 
        delete d->c;
113
 
        d->c = 0;
114
 
 
115
 
        d->state = Idle;
116
 
        d->needStream = false;
117
 
        d->sent = 0;
118
 
        d->sender = false;
119
 
}
120
 
 
121
 
void FileTransfer::setProxy(const Jid &proxy)
122
 
{
123
 
        d->proxy = proxy;
124
 
}
125
 
 
126
 
void FileTransfer::sendFile(const Jid &to, const QString &fname, qlonglong size, const QString &desc)
127
 
{
128
 
        d->state = Requesting;
129
 
        d->peer = to;
130
 
        d->fname = fname;
131
 
        d->size = size;
132
 
        d->desc = desc;
133
 
        d->sender = true;
134
 
        d->id = d->m->link(this);
135
 
 
136
 
        d->ft = new JT_FT(d->m->client()->rootTask());
137
 
        connect(d->ft, SIGNAL(finished()), SLOT(ft_finished()));
138
 
        QStringList list;
139
 
        list += "http://jabber.org/protocol/bytestreams";
140
 
        d->ft->request(to, d->id, fname, size, desc, list);
141
 
        d->ft->go(true);
142
 
}
143
 
 
144
 
int FileTransfer::dataSizeNeeded() const
145
 
{
146
 
        int pending = d->c->bytesToWrite();
147
 
        if(pending >= SENDBUFSIZE)
148
 
                return 0;
149
 
        qlonglong left = d->length - (d->sent + pending);
150
 
        int size = SENDBUFSIZE - pending;
151
 
        if((qlonglong)size > left)
152
 
                size = (int)left;
153
 
        return size;
154
 
}
155
 
 
156
 
void FileTransfer::writeFileData(const QByteArray &a)
157
 
{
158
 
        int pending = d->c->bytesToWrite();
159
 
        qlonglong left = d->length - (d->sent + pending);
160
 
        if(left == 0)
161
 
                return;
162
 
 
163
 
        QByteArray block;
164
 
        if((qlonglong)a.size() > left) {
165
 
                block = a;
166
 
                block.resize((uint)left);
167
 
        }
168
 
        else
169
 
                block = a;
170
 
        d->c->write(block);
171
 
}
172
 
 
173
 
Jid FileTransfer::peer() const
174
 
{
175
 
        return d->peer;
176
 
}
177
 
 
178
 
QString FileTransfer::fileName() const
179
 
{
180
 
        return d->fname;
181
 
}
182
 
 
183
 
qlonglong FileTransfer::fileSize() const
184
 
{
185
 
        return d->size;
186
 
}
187
 
 
188
 
QString FileTransfer::description() const
189
 
{
190
 
        return d->desc;
191
 
}
192
 
 
193
 
bool FileTransfer::rangeSupported() const
194
 
{
195
 
        return d->rangeSupported;
196
 
}
197
 
 
198
 
qlonglong FileTransfer::offset() const
199
 
{
200
 
        return d->rangeOffset;
201
 
}
202
 
 
203
 
qlonglong FileTransfer::length() const
204
 
{
205
 
        return d->length;
206
 
}
207
 
 
208
 
void FileTransfer::accept(qlonglong offset, qlonglong length)
209
 
{
210
 
        d->state = Connecting;
211
 
        d->rangeOffset = offset;
212
 
        d->rangeLength = length;
213
 
        if(length > 0)
214
 
                d->length = length;
215
 
        else
216
 
                d->length = d->size;
217
 
        d->streamType = "http://jabber.org/protocol/bytestreams";
218
 
        d->m->con_accept(this);
219
 
}
220
 
 
221
 
void FileTransfer::close()
222
 
{
223
 
        if(d->state == Idle)
224
 
                return;
225
 
        if(d->state == WaitingForAccept)
226
 
                d->m->con_reject(this);
227
 
        else if(d->state == Active)
228
 
                d->c->close();
229
 
        reset();
230
 
}
231
 
 
232
 
S5BConnection *FileTransfer::s5bConnection() const
233
 
{
234
 
        return d->c;
235
 
}
236
 
 
237
 
void FileTransfer::ft_finished()
238
 
{
239
 
        JT_FT *ft = d->ft;
240
 
        d->ft = 0;
241
 
 
242
 
        if(ft->success()) {
243
 
                d->state = Connecting;
244
 
                d->rangeOffset = ft->rangeOffset();
245
 
                d->length = ft->rangeLength();
246
 
                if(d->length == 0)
247
 
                        d->length = d->size - d->rangeOffset;
248
 
                d->streamType = ft->streamType();
249
 
                d->c = d->m->client()->s5bManager()->createConnection();
250
 
                connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
251
 
                connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
252
 
                connect(d->c, SIGNAL(bytesWritten(int)), SLOT(s5b_bytesWritten(int)));
253
 
                connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
254
 
 
255
 
                if(d->proxy.isValid())
256
 
                        d->c->setProxy(d->proxy);
257
 
                d->c->connectToJid(d->peer, d->id);
258
 
                accepted();
259
 
        }
260
 
        else {
261
 
                reset();
262
 
                if(ft->statusCode() == 403)
263
 
                        error(ErrReject);
264
 
                else
265
 
                        error(ErrNeg);
266
 
        }
267
 
}
268
 
 
269
 
void FileTransfer::takeConnection(S5BConnection *c)
270
 
{
271
 
        d->c = c;
272
 
        connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
273
 
        connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
274
 
        connect(d->c, SIGNAL(readyRead()), SLOT(s5b_readyRead()));
275
 
        connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
276
 
        if(d->proxy.isValid())
277
 
                d->c->setProxy(d->proxy);
278
 
        accepted();
279
 
        QTimer::singleShot(0, this, SLOT(doAccept()));
280
 
}
281
 
 
282
 
void FileTransfer::s5b_connected()
283
 
{
284
 
        d->state = Active;
285
 
        connected();
286
 
}
287
 
 
288
 
void FileTransfer::s5b_connectionClosed()
289
 
{
290
 
        reset();
291
 
        error(ErrStream);
292
 
}
293
 
 
294
 
void FileTransfer::s5b_readyRead()
295
 
{
296
 
        QByteArray a = d->c->read();
297
 
        qlonglong need = d->length - d->sent;
298
 
        if((qlonglong)a.size() > need)
299
 
                a.resize((uint)need);
300
 
        d->sent += a.size();
301
 
        if(d->sent == d->length)
302
 
                reset();
303
 
        readyRead(a);
304
 
}
305
 
 
306
 
void FileTransfer::s5b_bytesWritten(int x)
307
 
{
308
 
        d->sent += x;
309
 
        if(d->sent == d->length)
310
 
                reset();
311
 
        bytesWritten(x);
312
 
}
313
 
 
314
 
void FileTransfer::s5b_error(int x)
315
 
{
316
 
        reset();
317
 
        if(x == S5BConnection::ErrRefused || x == S5BConnection::ErrConnect)
318
 
                error(ErrConnect);
319
 
        else if(x == S5BConnection::ErrProxy)
320
 
                error(ErrProxy);
321
 
        else
322
 
                error(ErrStream);
323
 
}
324
 
 
325
 
void FileTransfer::man_waitForAccept(const FTRequest &req)
326
 
{
327
 
        d->state = WaitingForAccept;
328
 
        d->peer = req.from;
329
 
        d->id = req.id;
330
 
        d->iq_id = req.iq_id;
331
 
        d->fname = req.fname;
332
 
        d->size = req.size;
333
 
        d->desc = req.desc;
334
 
        d->rangeSupported = req.rangeSupported;
335
 
}
336
 
 
337
 
void FileTransfer::doAccept()
338
 
{
339
 
        d->c->accept();
340
 
}
341
 
 
342
 
//----------------------------------------------------------------------------
343
 
// FileTransferManager
344
 
//----------------------------------------------------------------------------
345
 
class FileTransferManager::Private
346
 
{
347
 
public:
348
 
        Client *client;
349
 
        Q3PtrList<FileTransfer> list, incoming;
350
 
        JT_PushFT *pft;
351
 
};
352
 
 
353
 
FileTransferManager::FileTransferManager(Client *client)
354
 
:QObject(client)
355
 
{
356
 
        d = new Private;
357
 
        d->client = client;
358
 
 
359
 
        d->pft = new JT_PushFT(d->client->rootTask());
360
 
        connect(d->pft, SIGNAL(incoming(const FTRequest &)), SLOT(pft_incoming(const FTRequest &)));
361
 
}
362
 
 
363
 
FileTransferManager::~FileTransferManager()
364
 
{
365
 
        d->incoming.setAutoDelete(true);
366
 
        d->incoming.clear();
367
 
        delete d->pft;
368
 
        delete d;
369
 
}
370
 
 
371
 
Client *FileTransferManager::client() const
372
 
{
373
 
        return d->client;
374
 
}
375
 
 
376
 
FileTransfer *FileTransferManager::createTransfer()
377
 
{
378
 
        FileTransfer *ft = new FileTransfer(this);
379
 
        return ft;
380
 
}
381
 
 
382
 
FileTransfer *FileTransferManager::takeIncoming()
383
 
{
384
 
        if(d->incoming.isEmpty())
385
 
                return 0;
386
 
 
387
 
        FileTransfer *ft = d->incoming.getFirst();
388
 
        d->incoming.removeRef(ft);
389
 
 
390
 
        // move to active list
391
 
        d->list.append(ft);
392
 
        return ft;
393
 
}
394
 
 
395
 
bool FileTransferManager::isActive(const FileTransfer *ft) const
396
 
{
397
 
        return d->list.contains(ft) > 0;
398
 
}
399
 
 
400
 
void FileTransferManager::pft_incoming(const FTRequest &req)
401
 
{
402
 
        bool found = false;
403
 
        for(QStringList::ConstIterator it = req.streamTypes.begin(); it != req.streamTypes.end(); ++it) {
404
 
                if((*it) == "http://jabber.org/protocol/bytestreams") {
405
 
                        found = true;
406
 
                        break;
407
 
                }
408
 
        }
409
 
        if(!found) {
410
 
                d->pft->respondError(req.from, req.iq_id, 400, "No valid stream types");
411
 
                return;
412
 
        }
413
 
        if(!d->client->s5bManager()->isAcceptableSID(req.from, req.id)) {
414
 
                d->pft->respondError(req.from, req.iq_id, 400, "SID in use");
415
 
                return;
416
 
        }
417
 
 
418
 
        FileTransfer *ft = new FileTransfer(this);
419
 
        ft->man_waitForAccept(req);
420
 
        d->incoming.append(ft);
421
 
        incomingReady();
422
 
}
423
 
 
424
 
void FileTransferManager::s5b_incomingReady(S5BConnection *c)
425
 
{
426
 
        Q3PtrListIterator<FileTransfer> it(d->list);
427
 
        FileTransfer *ft = 0;
428
 
        for(FileTransfer *i; (i = it.current()); ++it) {
429
 
                if(i->d->needStream && i->d->peer.compare(c->peer()) && i->d->id == c->sid()) {
430
 
                        ft = i;
431
 
                        break;
432
 
                }
433
 
        }
434
 
        if(!ft) {
435
 
                c->close();
436
 
                delete c;
437
 
                return;
438
 
        }
439
 
        ft->takeConnection(c);
440
 
}
441
 
 
442
 
QString FileTransferManager::link(FileTransfer *ft)
443
 
{
444
 
        d->list.append(ft);
445
 
        return d->client->s5bManager()->genUniqueSID(ft->d->peer);
446
 
}
447
 
 
448
 
void FileTransferManager::con_accept(FileTransfer *ft)
449
 
{
450
 
        ft->d->needStream = true;
451
 
        d->pft->respondSuccess(ft->d->peer, ft->d->iq_id, ft->d->rangeOffset, ft->d->rangeLength, ft->d->streamType);
452
 
}
453
 
 
454
 
void FileTransferManager::con_reject(FileTransfer *ft)
455
 
{
456
 
        d->pft->respondError(ft->d->peer, ft->d->iq_id, 403, "Declined");
457
 
}
458
 
 
459
 
void FileTransferManager::unlink(FileTransfer *ft)
460
 
{
461
 
        d->list.removeRef(ft);
462
 
}
463
 
 
464
 
//----------------------------------------------------------------------------
465
 
// JT_FT
466
 
//----------------------------------------------------------------------------
467
 
class JT_FT::Private
468
 
{
469
 
public:
470
 
        QDomElement iq;
471
 
        Jid to;
472
 
        qlonglong size, rangeOffset, rangeLength;
473
 
        QString streamType;
474
 
        QStringList streamTypes;
475
 
};
476
 
 
477
 
JT_FT::JT_FT(Task *parent)
478
 
:Task(parent)
479
 
{
480
 
        d = new Private;
481
 
}
482
 
 
483
 
JT_FT::~JT_FT()
484
 
{
485
 
        delete d;
486
 
}
487
 
 
488
 
void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, qlonglong size, const QString &desc, const QStringList &streamTypes)
489
 
{
490
 
        QDomElement iq;
491
 
        d->to = to;
492
 
        iq = createIQ(doc(), "set", to.full(), id());
493
 
        QDomElement si = doc()->createElement("si");
494
 
        si.setAttribute("xmlns", "http://jabber.org/protocol/si");
495
 
        si.setAttribute("id", _id);
496
 
        si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer");
497
 
 
498
 
        QDomElement file = doc()->createElement("file");
499
 
        file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
500
 
        file.setAttribute("name", fname);
501
 
        file.setAttribute("size", QString::number(size));
502
 
        if(!desc.isEmpty()) {
503
 
                QDomElement de = doc()->createElement("desc");
504
 
                de.appendChild(doc()->createTextNode(desc));
505
 
                file.appendChild(de);
506
 
        }
507
 
        QDomElement range = doc()->createElement("range");
508
 
        file.appendChild(range);
509
 
        si.appendChild(file);
510
 
 
511
 
        QDomElement feature = doc()->createElement("feature");
512
 
        feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
513
 
        QDomElement x = doc()->createElement("x");
514
 
        x.setAttribute("xmlns", "jabber:x:data");
515
 
        x.setAttribute("type", "form");
516
 
 
517
 
        QDomElement field = doc()->createElement("field");
518
 
        field.setAttribute("var", "stream-method");
519
 
        field.setAttribute("type", "list-single");
520
 
        for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) {
521
 
                QDomElement option = doc()->createElement("option");
522
 
                QDomElement value = doc()->createElement("value");
523
 
                value.appendChild(doc()->createTextNode(*it));
524
 
                option.appendChild(value);
525
 
                field.appendChild(option);
526
 
        }
527
 
 
528
 
        x.appendChild(field);
529
 
        feature.appendChild(x);
530
 
 
531
 
        si.appendChild(feature);
532
 
        iq.appendChild(si);
533
 
 
534
 
        d->streamTypes = streamTypes;
535
 
        d->size = size;
536
 
        d->iq = iq;
537
 
}
538
 
 
539
 
qlonglong JT_FT::rangeOffset() const
540
 
{
541
 
        return d->rangeOffset;
542
 
}
543
 
 
544
 
qlonglong JT_FT::rangeLength() const
545
 
{
546
 
        return d->rangeLength;
547
 
}
548
 
 
549
 
QString JT_FT::streamType() const
550
 
{
551
 
        return d->streamType;
552
 
}
553
 
 
554
 
void JT_FT::onGo()
555
 
{
556
 
        send(d->iq);
557
 
}
558
 
 
559
 
bool JT_FT::take(const QDomElement &x)
560
 
{
561
 
        if(!iqVerify(x, d->to, id()))
562
 
                return false;
563
 
 
564
 
        if(x.attribute("type") == "result") {
565
 
                QDomElement si = firstChildElement(x);
566
 
                if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") {
567
 
                        setError(900, "");
568
 
                        return true;
569
 
                }
570
 
 
571
 
                QString id = si.attribute("id");
572
 
 
573
 
                qlonglong range_offset = 0;
574
 
                qlonglong range_length = 0;
575
 
 
576
 
                QDomElement file = si.elementsByTagName("file").item(0).toElement();
577
 
                if(!file.isNull()) {
578
 
                        QDomElement range = file.elementsByTagName("range").item(0).toElement();
579
 
                        if(!range.isNull()) {
580
 
                                int x;
581
 
                                bool ok;
582
 
                                if(range.hasAttribute("offset")) {
583
 
                                        x = range.attribute("offset").toLongLong(&ok);
584
 
                                        if(!ok || x < 0) {
585
 
                                                setError(900, "");
586
 
                                                return true;
587
 
                                        }
588
 
                                        range_offset = x;
589
 
                                }
590
 
                                if(range.hasAttribute("length")) {
591
 
                                        x = range.attribute("length").toLongLong(&ok);
592
 
                                        if(!ok || x < 0) {
593
 
                                                setError(900, "");
594
 
                                                return true;
595
 
                                        }
596
 
                                        range_length = x;
597
 
                                }
598
 
                        }
599
 
                }
600
 
 
601
 
                if(range_offset > d->size || (range_length > (d->size - range_offset))) {
602
 
                        setError(900, "");
603
 
                        return true;
604
 
                }
605
 
 
606
 
                QString streamtype;
607
 
                QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
608
 
                if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
609
 
                        QDomElement x = feature.elementsByTagName("x").item(0).toElement();
610
 
                        if(!x.isNull() && x.attribute("type") == "submit") {
611
 
                                QDomElement field = x.elementsByTagName("field").item(0).toElement();
612
 
                                if(!field.isNull() && field.attribute("var") == "stream-method") {
613
 
                                        QDomElement value = field.elementsByTagName("value").item(0).toElement();
614
 
                                        if(!value.isNull())
615
 
                                                streamtype = value.text();
616
 
                                }
617
 
                        }
618
 
                }
619
 
 
620
 
                // must be one of the offered streamtypes
621
 
                bool found = false;
622
 
                for(QStringList::ConstIterator it = d->streamTypes.begin(); it != d->streamTypes.end(); ++it) {
623
 
                        if((*it) == streamtype) {
624
 
                                found = true;
625
 
                                break;
626
 
                        }
627
 
                }
628
 
                if(!found)
629
 
                        return true;
630
 
 
631
 
                d->rangeOffset = range_offset;
632
 
                d->rangeLength = range_length;
633
 
                d->streamType = streamtype;
634
 
                setSuccess();
635
 
        }
636
 
        else {
637
 
                setError(x);
638
 
        }
639
 
 
640
 
        return true;
641
 
}
642
 
 
643
 
//----------------------------------------------------------------------------
644
 
// JT_PushFT
645
 
//----------------------------------------------------------------------------
646
 
JT_PushFT::JT_PushFT(Task *parent)
647
 
:Task(parent)
648
 
{
649
 
}
650
 
 
651
 
JT_PushFT::~JT_PushFT()
652
 
{
653
 
}
654
 
 
655
 
void JT_PushFT::respondSuccess(const Jid &to, const QString &id, qlonglong rangeOffset, qlonglong rangeLength, const QString &streamType)
656
 
{
657
 
        QDomElement iq = createIQ(doc(), "result", to.full(), id);
658
 
        QDomElement si = doc()->createElement("si");
659
 
        si.setAttribute("xmlns", "http://jabber.org/protocol/si");
660
 
 
661
 
        if(rangeOffset != 0 || rangeLength != 0) {
662
 
                QDomElement file = doc()->createElement("file");
663
 
                file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
664
 
                QDomElement range = doc()->createElement("range");
665
 
                if(rangeOffset > 0)
666
 
                        range.setAttribute("offset", QString::number(rangeOffset));
667
 
                if(rangeLength > 0)
668
 
                        range.setAttribute("length", QString::number(rangeLength));
669
 
                file.appendChild(range);
670
 
                si.appendChild(file);
671
 
        }
672
 
 
673
 
        QDomElement feature = doc()->createElement("feature");
674
 
        feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
675
 
        QDomElement x = doc()->createElement("x");
676
 
        x.setAttribute("xmlns", "jabber:x:data");
677
 
        x.setAttribute("type", "submit");
678
 
 
679
 
        QDomElement field = doc()->createElement("field");
680
 
        field.setAttribute("var", "stream-method");
681
 
        QDomElement value = doc()->createElement("value");
682
 
        value.appendChild(doc()->createTextNode(streamType));
683
 
        field.appendChild(value);
684
 
 
685
 
        x.appendChild(field);
686
 
        feature.appendChild(x);
687
 
 
688
 
        si.appendChild(feature);
689
 
        iq.appendChild(si);
690
 
        send(iq);
691
 
}
692
 
 
693
 
void JT_PushFT::respondError(const Jid &to, const QString &id, int code, const QString &str)
694
 
{
695
 
        QDomElement iq = createIQ(doc(), "error", to.full(), id);
696
 
        QDomElement err = textTag(doc(), "error", str);
697
 
        err.setAttribute("code", QString::number(code));
698
 
        iq.appendChild(err);
699
 
        send(iq);
700
 
}
701
 
 
702
 
bool JT_PushFT::take(const QDomElement &e)
703
 
{
704
 
        // must be an iq-set tag
705
 
        if(e.tagName() != "iq")
706
 
                return false;
707
 
        if(e.attribute("type") != "set")
708
 
                return false;
709
 
 
710
 
        QDomElement si = firstChildElement(e);
711
 
        if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si")
712
 
                return false;
713
 
        if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer")
714
 
                return false;
715
 
 
716
 
        Jid from(e.attribute("from"));
717
 
        QString id = si.attribute("id");
718
 
 
719
 
        QDomElement file = si.elementsByTagName("file").item(0).toElement();
720
 
        if(file.isNull())
721
 
                return true;
722
 
 
723
 
        QString fname = file.attribute("name");
724
 
        if(fname.isEmpty()) {
725
 
                respondError(from, id, 400, "Bad file name");
726
 
                return true;
727
 
        }
728
 
 
729
 
        // ensure kosher
730
 
        {
731
 
                QFileInfo fi(fname);
732
 
                fname = fi.fileName();
733
 
        }
734
 
 
735
 
        bool ok;
736
 
        qlonglong size = file.attribute("size").toLongLong(&ok);
737
 
        if(!ok || size < 0) {
738
 
                respondError(from, id, 400, "Bad file size");
739
 
                return true;
740
 
        }
741
 
 
742
 
        QString desc;
743
 
        QDomElement de = file.elementsByTagName("desc").item(0).toElement();
744
 
        if(!de.isNull())
745
 
                desc = de.text();
746
 
 
747
 
        bool rangeSupported = false;
748
 
        QDomElement range = file.elementsByTagName("range").item(0).toElement();
749
 
        if(!range.isNull())
750
 
                rangeSupported = true;
751
 
 
752
 
        QStringList streamTypes;
753
 
        QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
754
 
        if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
755
 
                QDomElement x = feature.elementsByTagName("x").item(0).toElement();
756
 
                if(!x.isNull() /*&& x.attribute("type") == "form"*/) {
757
 
                        QDomElement field = x.elementsByTagName("field").item(0).toElement();
758
 
                        if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") {
759
 
                                QDomNodeList nl = field.elementsByTagName("option");
760
 
                                for(int n = 0; n < nl.count(); ++n) {
761
 
                                        QDomElement e = nl.item(n).toElement();
762
 
                                        QDomElement value = e.elementsByTagName("value").item(0).toElement();
763
 
                                        if(!value.isNull())
764
 
                                                streamTypes += value.text();
765
 
                                }
766
 
                        }
767
 
                }
768
 
        }
769
 
 
770
 
        FTRequest r;
771
 
        r.from = from;
772
 
        r.iq_id = e.attribute("id");
773
 
        r.id = id;
774
 
        r.fname = fname;
775
 
        r.size = size;
776
 
        r.desc = desc;
777
 
        r.rangeSupported = rangeSupported;
778
 
        r.streamTypes = streamTypes;
779
 
 
780
 
        incoming(r);
781
 
        return true;
782
 
}