~ubuntu-branches/ubuntu/quantal/psi/quantal

« back to all changes in this revision

Viewing changes to cutestuff/network/httppoll.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
 * httppoll.cpp - HTTP polling proxy
 
3
 * Copyright (C) 2003  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"httppoll.h"
 
22
 
 
23
#include<qstringlist.h>
 
24
#include<qurl.h>
 
25
#include<qtimer.h>
 
26
#include<qguardedptr.h>
 
27
#include<qca.h>
 
28
#include<stdlib.h>
 
29
#include"bsocket.h"
 
30
#include"base64.h"
 
31
 
 
32
#ifdef PROX_DEBUG
 
33
#include<stdio.h>
 
34
#endif
 
35
 
 
36
#define POLL_KEYS 64
 
37
 
 
38
// CS_NAMESPACE_BEGIN
 
39
 
 
40
static QByteArray randomArray(int size)
 
41
{
 
42
        QByteArray a(size);
 
43
        for(int n = 0; n < size; ++n)
 
44
                a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
 
45
        return a;
 
46
}
 
47
 
 
48
//----------------------------------------------------------------------------
 
49
// HttpPoll
 
50
//----------------------------------------------------------------------------
 
51
static QString hpk(int n, const QString &s)
 
52
{
 
53
        if(n == 0)
 
54
                return s;
 
55
        else
 
56
                return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) );
 
57
}
 
58
 
 
59
class HttpPoll::Private
 
60
{
 
61
public:
 
62
        Private() {}
 
63
 
 
64
        HttpProxyPost http;
 
65
        QString host;
 
66
        int port;
 
67
        QString user, pass;
 
68
        QString url;
 
69
        bool use_proxy;
 
70
 
 
71
        QByteArray out;
 
72
 
 
73
        int state;
 
74
        bool closing;
 
75
        QString ident;
 
76
 
 
77
        QTimer *t;
 
78
 
 
79
        QString key[POLL_KEYS];
 
80
        int key_n;
 
81
 
 
82
        int polltime;
 
83
};
 
84
 
 
85
HttpPoll::HttpPoll(QObject *parent)
 
86
:ByteStream(parent)
 
87
{
 
88
        d = new Private;
 
89
 
 
90
        d->polltime = 30;
 
91
        d->t = new QTimer;
 
92
        connect(d->t, SIGNAL(timeout()), SLOT(do_sync()));
 
93
 
 
94
        connect(&d->http, SIGNAL(result()), SLOT(http_result()));
 
95
        connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int)));
 
96
 
 
97
        reset(true);
 
98
}
 
99
 
 
100
HttpPoll::~HttpPoll()
 
101
{
 
102
        reset(true);
 
103
        delete d->t;
 
104
        delete d;
 
105
}
 
106
 
 
107
void HttpPoll::reset(bool clear)
 
108
{
 
109
        if(d->http.isActive())
 
110
                d->http.stop();
 
111
        if(clear)
 
112
                clearReadBuffer();
 
113
        clearWriteBuffer();
 
114
        d->out.resize(0);
 
115
        d->state = 0;
 
116
        d->closing = false;
 
117
        d->t->stop();
 
118
}
 
119
 
 
120
void HttpPoll::setAuth(const QString &user, const QString &pass)
 
121
{
 
122
        d->user = user;
 
123
        d->pass = pass;
 
124
}
 
125
 
 
126
void HttpPoll::connectToUrl(const QString &url)
 
127
{
 
128
        connectToHost("", 0, url);
 
129
}
 
130
 
 
131
void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
 
132
{
 
133
        reset(true);
 
134
 
 
135
        // using proxy?
 
136
        if(!proxyHost.isEmpty()) {
 
137
                d->host = proxyHost;
 
138
                d->port = proxyPort;
 
139
                d->url = url;
 
140
                d->use_proxy = true;
 
141
        }
 
142
        else {
 
143
                QUrl u = url;
 
144
                d->host = u.host();
 
145
                if(u.hasPort())
 
146
                        d->port = u.port();
 
147
                else
 
148
                        d->port = 80;
 
149
                d->url = u.encodedPathAndQuery();
 
150
                d->use_proxy = false;
 
151
        }
 
152
 
 
153
        resetKey();
 
154
        bool last;
 
155
        QString key = getKey(&last);
 
156
 
 
157
#ifdef PROX_DEBUG
 
158
        fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
 
159
        if(d->user.isEmpty())
 
160
                fprintf(stderr, "\n");
 
161
        else
 
162
                fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
 
163
#endif
 
164
        QGuardedPtr<QObject> self = this;
 
165
        syncStarted();
 
166
        if(!self)
 
167
                return;
 
168
 
 
169
        d->state = 1;
 
170
        d->http.setAuth(d->user, d->pass);
 
171
        d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
 
172
}
 
173
 
 
174
QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
 
175
{
 
176
        QString str = ident;
 
177
        if(!key.isEmpty()) {
 
178
                str += ';';
 
179
                str += key;
 
180
        }
 
181
        if(!newkey.isEmpty()) {
 
182
                str += ';';
 
183
                str += newkey;
 
184
        }
 
185
        str += ',';
 
186
        QCString cs = str.latin1();
 
187
        int len = cs.length();
 
188
 
 
189
        QByteArray a(len + block.size());
 
190
        memcpy(a.data(), cs.data(), len);
 
191
        memcpy(a.data() + len, block.data(), block.size());
 
192
        return a;
 
193
}
 
194
 
 
195
int HttpPoll::pollInterval() const
 
196
{
 
197
        return d->polltime;
 
198
}
 
199
 
 
200
void HttpPoll::setPollInterval(int seconds)
 
201
{
 
202
        d->polltime = seconds;
 
203
}
 
204
 
 
205
bool HttpPoll::isOpen() const
 
206
{
 
207
        return (d->state == 2 ? true: false);
 
208
}
 
209
 
 
210
void HttpPoll::close()
 
211
{
 
212
        if(d->state == 0 || d->closing)
 
213
                return;
 
214
 
 
215
        if(bytesToWrite() == 0)
 
216
                reset();
 
217
        else
 
218
                d->closing = true;
 
219
}
 
220
 
 
221
void HttpPoll::http_result()
 
222
{
 
223
        // check for death :)
 
224
        QGuardedPtr<QObject> self = this;
 
225
        syncFinished();
 
226
        if(!self)
 
227
                return;
 
228
 
 
229
        // get id and packet
 
230
        QString id;
 
231
        QString cookie = d->http.getHeader("Set-Cookie");
 
232
        int n = cookie.find("ID=");
 
233
        if(n == -1) {
 
234
                reset();
 
235
                error(ErrRead);
 
236
                return;
 
237
        }
 
238
        n += 3;
 
239
        int n2 = cookie.find(';', n);
 
240
        if(n2 != -1)
 
241
                id = cookie.mid(n, n2-n);
 
242
        else
 
243
                id = cookie.mid(n);
 
244
        QByteArray block = d->http.body();
 
245
 
 
246
        // session error?
 
247
        if(id.right(2) == ":0") {
 
248
                if(id == "0:0" && d->state == 2) {
 
249
                        reset();
 
250
                        connectionClosed();
 
251
                        return;
 
252
                }
 
253
                else {
 
254
                        reset();
 
255
                        error(ErrRead);
 
256
                        return;
 
257
                }
 
258
        }
 
259
 
 
260
        d->ident = id;
 
261
        bool justNowConnected = false;
 
262
        if(d->state == 1) {
 
263
                d->state = 2;
 
264
                justNowConnected = true;
 
265
        }
 
266
 
 
267
        // sync up again soon
 
268
        if(bytesToWrite() > 0 || !d->closing)
 
269
                d->t->start(d->polltime * 1000, true);
 
270
 
 
271
        // connecting
 
272
        if(justNowConnected) {
 
273
                connected();
 
274
        }
 
275
        else {
 
276
                if(!d->out.isEmpty()) {
 
277
                        int x = d->out.size();
 
278
                        d->out.resize(0);
 
279
                        takeWrite(x);
 
280
                        bytesWritten(x);
 
281
                }
 
282
        }
 
283
 
 
284
        if(!self)
 
285
                return;
 
286
 
 
287
        if(!block.isEmpty()) {
 
288
                appendRead(block);
 
289
                readyRead();
 
290
        }
 
291
 
 
292
        if(!self)
 
293
                return;
 
294
 
 
295
        if(bytesToWrite() > 0) {
 
296
                do_sync();
 
297
        }
 
298
        else {
 
299
                if(d->closing) {
 
300
                        reset();
 
301
                        delayedCloseFinished();
 
302
                        return;
 
303
                }
 
304
        }
 
305
}
 
306
 
 
307
void HttpPoll::http_error(int x)
 
308
{
 
309
        reset();
 
310
        if(x == HttpProxyPost::ErrConnectionRefused)
 
311
                error(ErrConnectionRefused);
 
312
        else if(x == HttpProxyPost::ErrHostNotFound)
 
313
                error(ErrHostNotFound);
 
314
        else if(x == HttpProxyPost::ErrSocket)
 
315
                error(ErrRead);
 
316
        else if(x == HttpProxyPost::ErrProxyConnect)
 
317
                error(ErrProxyConnect);
 
318
        else if(x == HttpProxyPost::ErrProxyNeg)
 
319
                error(ErrProxyNeg);
 
320
        else if(x == HttpProxyPost::ErrProxyAuth)
 
321
                error(ErrProxyAuth);
 
322
}
 
323
 
 
324
int HttpPoll::tryWrite()
 
325
{
 
326
        if(!d->http.isActive())
 
327
                do_sync();
 
328
        return 0;
 
329
}
 
330
 
 
331
void HttpPoll::do_sync()
 
332
{
 
333
        if(d->http.isActive())
 
334
                return;
 
335
 
 
336
        d->t->stop();
 
337
        d->out = takeWrite(0, false);
 
338
 
 
339
        bool last;
 
340
        QString key = getKey(&last);
 
341
        QString newkey;
 
342
        if(last) {
 
343
                resetKey();
 
344
                newkey = getKey(&last);
 
345
        }
 
346
 
 
347
        QGuardedPtr<QObject> self = this;
 
348
        syncStarted();
 
349
        if(!self)
 
350
                return;
 
351
 
 
352
        d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
 
353
}
 
354
 
 
355
void HttpPoll::resetKey()
 
356
{
 
357
#ifdef PROX_DEBUG
 
358
        fprintf(stderr, "HttpPoll: reset key!\n");
 
359
#endif
 
360
        QByteArray a = randomArray(64);
 
361
        QString str = QString::fromLatin1(a.data(), a.size());
 
362
 
 
363
        d->key_n = POLL_KEYS;
 
364
        for(int n = 0; n < POLL_KEYS; ++n)
 
365
                d->key[n] = hpk(n+1, str);
 
366
}
 
367
 
 
368
const QString & HttpPoll::getKey(bool *last)
 
369
{
 
370
        *last = false;
 
371
        --(d->key_n);
 
372
        if(d->key_n == 0)
 
373
                *last = true;
 
374
        return d->key[d->key_n];
 
375
}
 
376
 
 
377
 
 
378
//----------------------------------------------------------------------------
 
379
// HttpProxyPost
 
380
//----------------------------------------------------------------------------
 
381
static QString extractLine(QByteArray *buf, bool *found)
 
382
{
 
383
        // scan for newline
 
384
        int n;
 
385
        for(n = 0; n < (int)buf->size()-1; ++n) {
 
386
                if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
 
387
                        QCString cstr;
 
388
                        cstr.resize(n+1);
 
389
                        memcpy(cstr.data(), buf->data(), n);
 
390
                        n += 2; // hack off CR/LF
 
391
 
 
392
                        memmove(buf->data(), buf->data() + n, buf->size() - n);
 
393
                        buf->resize(buf->size() - n);
 
394
                        QString s = QString::fromUtf8(cstr);
 
395
 
 
396
                        if(found)
 
397
                                *found = true;
 
398
                        return s;
 
399
                }
 
400
        }
 
401
 
 
402
        if(found)
 
403
                *found = false;
 
404
        return "";
 
405
}
 
406
 
 
407
static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
 
408
{
 
409
        int n = line.find(' ');
 
410
        if(n == -1)
 
411
                return false;
 
412
        if(proto)
 
413
                *proto = line.mid(0, n);
 
414
        ++n;
 
415
        int n2 = line.find(' ', n);
 
416
        if(n2 == -1)
 
417
                return false;
 
418
        if(code)
 
419
                *code = line.mid(n, n2-n).toInt();
 
420
        n = n2+1;
 
421
        if(msg)
 
422
                *msg = line.mid(n);
 
423
        return true;
 
424
}
 
425
 
 
426
class HttpProxyPost::Private
 
427
{
 
428
public:
 
429
        Private() {}
 
430
 
 
431
        BSocket sock;
 
432
        QByteArray postdata, recvBuf, body;
 
433
        QString url;
 
434
        QString user, pass;
 
435
        bool inHeader;
 
436
        QStringList headerLines;
 
437
        bool asProxy;
 
438
        QString host;
 
439
};
 
440
 
 
441
HttpProxyPost::HttpProxyPost(QObject *parent)
 
442
:QObject(parent)
 
443
{
 
444
        d = new Private;
 
445
        connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
 
446
        connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
 
447
        connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
 
448
        connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
 
449
        reset(true);
 
450
}
 
451
 
 
452
HttpProxyPost::~HttpProxyPost()
 
453
{
 
454
        reset(true);
 
455
        delete d;
 
456
}
 
457
 
 
458
void HttpProxyPost::reset(bool clear)
 
459
{
 
460
        if(d->sock.state() != BSocket::Idle)
 
461
                d->sock.close();
 
462
        d->recvBuf.resize(0);
 
463
        if(clear)
 
464
                d->body.resize(0);
 
465
}
 
466
 
 
467
void HttpProxyPost::setAuth(const QString &user, const QString &pass)
 
468
{
 
469
        d->user = user;
 
470
        d->pass = pass;
 
471
}
 
472
 
 
473
bool HttpProxyPost::isActive() const
 
474
{
 
475
        return (d->sock.state() == BSocket::Idle ? false: true);
 
476
}
 
477
 
 
478
void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
 
479
{
 
480
        reset(true);
 
481
 
 
482
        d->host = proxyHost;
 
483
        d->url = url;
 
484
        d->postdata = data;
 
485
        d->asProxy = asProxy;
 
486
 
 
487
#ifdef PROX_DEBUG
 
488
        fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
 
489
        if(d->user.isEmpty())
 
490
                fprintf(stderr, "\n");
 
491
        else
 
492
                fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
 
493
#endif
 
494
        d->sock.connectToHost(proxyHost, proxyPort);
 
495
}
 
496
 
 
497
void HttpProxyPost::stop()
 
498
{
 
499
        reset();
 
500
}
 
501
 
 
502
QByteArray HttpProxyPost::body() const
 
503
{
 
504
        return d->body;
 
505
}
 
506
 
 
507
QString HttpProxyPost::getHeader(const QString &var) const
 
508
{
 
509
        for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) {
 
510
                const QString &s = *it;
 
511
                int n = s.find(": ");
 
512
                if(n == -1)
 
513
                        continue;
 
514
                QString v = s.mid(0, n);
 
515
                if(v == var)
 
516
                        return s.mid(n+2);
 
517
        }
 
518
        return "";
 
519
}
 
520
 
 
521
void HttpProxyPost::sock_connected()
 
522
{
 
523
#ifdef PROX_DEBUG
 
524
        fprintf(stderr, "HttpProxyPost: Connected\n");
 
525
#endif
 
526
        d->inHeader = true;
 
527
        d->headerLines.clear();
 
528
 
 
529
        QUrl u = d->url;
 
530
 
 
531
        // connected, now send the request
 
532
        QString s;
 
533
        s += QString("POST ") + d->url + " HTTP/1.0\r\n";
 
534
        if(d->asProxy) {
 
535
                if(!d->user.isEmpty()) {
 
536
                        QString str = d->user + ':' + d->pass;
 
537
                        s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
 
538
                }
 
539
                s += "Proxy-Connection: Keep-Alive\r\n";
 
540
                s += "Pragma: no-cache\r\n";
 
541
                s += QString("Host: ") + u.host() + "\r\n";
 
542
        }
 
543
        else {
 
544
                s += QString("Host: ") + d->host + "\r\n";
 
545
        }
 
546
        s += "Content-Type: application/x-www-form-urlencoded\r\n";
 
547
        s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
 
548
        s += "\r\n";
 
549
 
 
550
        // write request
 
551
        QCString cs = s.utf8();
 
552
        QByteArray block(cs.length());
 
553
        memcpy(block.data(), cs.data(), block.size());
 
554
        d->sock.write(block);
 
555
 
 
556
        // write postdata
 
557
        d->sock.write(d->postdata);
 
558
}
 
559
 
 
560
void HttpProxyPost::sock_connectionClosed()
 
561
{
 
562
        d->body = d->recvBuf.copy();
 
563
        reset();
 
564
        result();
 
565
}
 
566
 
 
567
void HttpProxyPost::sock_readyRead()
 
568
{
 
569
        QByteArray block = d->sock.read();
 
570
        ByteStream::appendArray(&d->recvBuf, block);
 
571
 
 
572
        if(d->inHeader) {
 
573
                // grab available lines
 
574
                while(1) {
 
575
                        bool found;
 
576
                        QString line = extractLine(&d->recvBuf, &found);
 
577
                        if(!found)
 
578
                                break;
 
579
                        if(line.isEmpty()) {
 
580
                                d->inHeader = false;
 
581
                                break;
 
582
                        }
 
583
                        d->headerLines += line;
 
584
                }
 
585
 
 
586
                // done with grabbing the header?
 
587
                if(!d->inHeader) {
 
588
                        QString str = d->headerLines.first();
 
589
                        d->headerLines.remove(d->headerLines.begin());
 
590
 
 
591
                        QString proto;
 
592
                        int code;
 
593
                        QString msg;
 
594
                        if(!extractMainHeader(str, &proto, &code, &msg)) {
 
595
#ifdef PROX_DEBUG
 
596
                                fprintf(stderr, "HttpProxyPost: invalid header!\n");
 
597
#endif
 
598
                                reset(true);
 
599
                                error(ErrProxyNeg);
 
600
                                return;
 
601
                        }
 
602
                        else {
 
603
#ifdef PROX_DEBUG
 
604
                                fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
 
605
                                for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
 
606
                                        fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
 
607
#endif
 
608
                        }
 
609
 
 
610
                        if(code == 200) { // OK
 
611
#ifdef PROX_DEBUG
 
612
                                fprintf(stderr, "HttpProxyPost: << Success >>\n");
 
613
#endif
 
614
                        }
 
615
                        else {
 
616
                                int err;
 
617
                                QString errStr;
 
618
                                if(code == 407) { // Authentication failed
 
619
                                        err = ErrProxyAuth;
 
620
                                        errStr = tr("Authentication failed");
 
621
                                }
 
622
                                else if(code == 404) { // Host not found
 
623
                                        err = ErrHostNotFound;
 
624
                                        errStr = tr("Host not found");
 
625
                                }
 
626
                                else if(code == 403) { // Access denied
 
627
                                        err = ErrProxyNeg;
 
628
                                        errStr = tr("Access denied");
 
629
                                }
 
630
                                else if(code == 503) { // Connection refused
 
631
                                        err = ErrConnectionRefused;
 
632
                                        errStr = tr("Connection refused");
 
633
                                }
 
634
                                else { // invalid reply
 
635
                                        err = ErrProxyNeg;
 
636
                                        errStr = tr("Invalid reply");
 
637
                                }
 
638
 
 
639
#ifdef PROX_DEBUG
 
640
                                fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
 
641
#endif
 
642
                                reset(true);
 
643
                                error(err);
 
644
                                return;
 
645
                        }
 
646
                }
 
647
        }
 
648
}
 
649
 
 
650
void HttpProxyPost::sock_error(int x)
 
651
{
 
652
#ifdef PROX_DEBUG
 
653
        fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
 
654
#endif
 
655
        reset(true);
 
656
        if(x == BSocket::ErrHostNotFound)
 
657
                error(ErrProxyConnect);
 
658
        else if(x == BSocket::ErrConnectionRefused)
 
659
                error(ErrProxyConnect);
 
660
        else if(x == BSocket::ErrRead)
 
661
                error(ErrProxyNeg);
 
662
}
 
663
 
 
664
// CS_NAMESPACE_END