~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: 2005-01-10 17:41:43 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050110174143-ltocv5zapl6blf5d
Tags: 0.9.3-1
* New upstream release
* Cleaned up debian/rules (some things are done by upstream Makefiles now)
* Fixed some lintian warnings:
  - removed executable bit from some .png files
  - moved psi.desktop to /usr/share/applications
* Updated menu files

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