~ubuntu-branches/ubuntu/karmic/kid3/karmic

« back to all changes in this revision

Viewing changes to kid3/httpclient.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Patrick Matthäi
  • Date: 2009-05-20 16:12:30 UTC
  • mfrom: (1.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20090520161230-qetp532r8ydujkz2
Tags: upstream-1.2
Import upstream version 1.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * \file httpclient.cpp
 
3
 * Client to connect to HTTP server.
 
4
 *
 
5
 * \b Project: Kid3
 
6
 * \author Urs Fleisch
 
7
 * \date 30 Dec 2008
 
8
 *
 
9
 * Copyright (C) 2008-2009  Urs Fleisch
 
10
 *
 
11
 * This file is part of Kid3.
 
12
 *
 
13
 * Kid3 is free software; you can redistribute it and/or modify
 
14
 * it under the terms of the GNU General Public License as published by
 
15
 * the Free Software Foundation; either version 2 of the License, or
 
16
 * (at your option) any later version.
 
17
 *
 
18
 * Kid3 is distributed in the hope that it will be useful,
 
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
 * GNU General Public License for more details.
 
22
 *
 
23
 * You should have received a copy of the GNU General Public License
 
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
25
 */
 
26
 
 
27
#include "httpclient.h"
 
28
#include "kid3.h"
 
29
 
 
30
/** Only defined for generation of KDE3 translation files */
 
31
#define FOR_KDE3_PO_1 I18N_NOOP("Data received: %1")
 
32
 
 
33
#if QT_VERSION >= 0x040000
 
34
 
 
35
/**
 
36
 * Constructor.
 
37
 */
 
38
HttpClient::HttpClient() :
 
39
        m_rcvBodyLen(0)
 
40
{
 
41
        m_http = new QHttp();
 
42
        connect(m_http, SIGNAL(stateChanged(int)),
 
43
                                        this, SLOT(slotStateChanged(int)));
 
44
        connect(m_http, SIGNAL(dataReadProgress(int, int)),
 
45
                                        this, SLOT(slotDataReadProgress(int, int)));
 
46
        connect(m_http, SIGNAL(done(bool)),
 
47
                                        this, SLOT(slotDone(bool)));
 
48
        connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader&)),
 
49
                                        this, SLOT(slotResponseHeaderReceived(const QHttpResponseHeader&)));
 
50
}
 
51
 
 
52
/**
 
53
 * Destructor.
 
54
 */
 
55
HttpClient::~HttpClient()
 
56
{
 
57
        m_http->close();
 
58
        m_http->disconnect();
 
59
        delete m_http;
 
60
}
 
61
 
 
62
/**
 
63
 * Called when the connection state changes.
 
64
 *
 
65
 * @param state HTTP connection state
 
66
 */
 
67
void HttpClient::slotStateChanged(int state)
 
68
{
 
69
        switch (state) {
 
70
                case QHttp::HostLookup:
 
71
                        emitProgress(i18n("Ready."), CS_RequestConnection, CS_EstimatedBytes);
 
72
                        break;
 
73
                case QHttp::Connecting:
 
74
                        emitProgress(i18n("Connecting..."), CS_Connecting, CS_EstimatedBytes);
 
75
                        break;
 
76
                case QHttp::Sending:
 
77
                        emitProgress(i18n("Host found..."), CS_HostFound, CS_EstimatedBytes);
 
78
                        break;
 
79
                case QHttp::Reading:
 
80
                        emitProgress(i18n("Request sent..."), CS_RequestSent, CS_EstimatedBytes);
 
81
                        break;
 
82
                case QHttp::Connected:
 
83
                        emitProgress(i18n("Ready."), -1, -1);
 
84
                        break;
 
85
                case QHttp::Unconnected:
 
86
                case QHttp::Closing:
 
87
                default:
 
88
                        ;
 
89
        }
 
90
}
 
91
 
 
92
/**
 
93
 * Called to report connection progress.
 
94
 *
 
95
 * @param done  bytes received
 
96
 * @param total total bytes, 0 if unknown
 
97
 */
 
98
void HttpClient::slotDataReadProgress(int done, int total)
 
99
{
 
100
        emitProgress(KCM_i18n1("Data received: %1", done), done, total);
 
101
}
 
102
 
 
103
/**
 
104
 * Called when the request is finished.
 
105
 *
 
106
 * @param error true if error occurred
 
107
 */
 
108
void HttpClient::slotDone(bool error)
 
109
{
 
110
        if (error) {
 
111
                QHttp::Error err = m_http->error();
 
112
                if (err != QHttp::UnexpectedClose) {
 
113
                        QString msg(i18n("Socket error: "));
 
114
                        switch (err) {
 
115
                                case QHttp::ConnectionRefused:
 
116
                                        msg += i18n("Connection refused");
 
117
                                        break;
 
118
                                case QHttp::HostNotFound:
 
119
                                        msg += i18n("Host not found");
 
120
                                        break;
 
121
                                default:
 
122
                                        msg += m_http->errorString();
 
123
                        }
 
124
                        emitProgress(msg, -1, -1);
 
125
                }
 
126
        }
 
127
        emit bytesReceived(m_http->readAll());
 
128
        if (!error) {
 
129
                emitProgress(i18n("Ready."), CS_EstimatedBytes, CS_EstimatedBytes);
 
130
        }
 
131
}
 
132
 
 
133
/**
 
134
 * Called when the response header is available.
 
135
 *
 
136
 * @param resp HTTP response header
 
137
 */
 
138
void HttpClient::slotResponseHeaderReceived(const QHttpResponseHeader& resp)
 
139
{
 
140
        m_rcvBodyType = resp.contentType();
 
141
        m_rcvBodyLen = resp.contentLength();
 
142
}
 
143
 
 
144
/**
 
145
 * Send a HTTP GET request.
 
146
 *
 
147
 * @param server host name
 
148
 * @param path   path of the URL
 
149
 */
 
150
void HttpClient::sendRequest(const QString& server, const QString& path)
 
151
{
 
152
        m_rcvBodyLen = 0;
 
153
        m_rcvBodyType = "";
 
154
        QString dest;
 
155
        int destPort;
 
156
        splitNamePort(server, dest, destPort);
 
157
        m_http->setHost(dest, destPort);
 
158
        QString proxy, username, password;
 
159
        int proxyPort = 0;
 
160
        if (Kid3App::s_miscCfg.m_useProxy) {
 
161
                splitNamePort(Kid3App::s_miscCfg.m_proxy, proxy, proxyPort);
 
162
        }
 
163
        if (Kid3App::s_miscCfg.m_useProxyAuthentication) {
 
164
                username = Kid3App::s_miscCfg.m_proxyUserName;
 
165
                password = Kid3App::s_miscCfg.m_proxyPassword;
 
166
        }
 
167
        m_http->setProxy(proxy, proxyPort, username, password);
 
168
        m_http->setHost(dest, destPort);
 
169
        m_http->get(path);
 
170
}
 
171
 
 
172
void HttpClient::slotHostFound() {}
 
173
void HttpClient::slotConnected() {}
 
174
void HttpClient::slotConnectionClosed() {}
 
175
void HttpClient::slotReadyRead() {}
 
176
void HttpClient::slotError(int) {}
 
177
 
 
178
#else
 
179
 
 
180
/**
 
181
 * Constructor.
 
182
 */
 
183
HttpClient::HttpClient() :
 
184
        m_rcvIdx(0), m_rcvBodyIdx(0), m_rcvBodyLen(0)
 
185
{
 
186
        m_sock = new QSocket();
 
187
        connect(m_sock, SIGNAL(connectionClosed()),
 
188
                        this, SLOT(slotConnectionClosed()));
 
189
        connect(m_sock, SIGNAL(error(int)),
 
190
                        this, SLOT(slotError(int)));
 
191
        connect(m_sock, SIGNAL(hostFound()),
 
192
                        this, SLOT(slotHostFound()));
 
193
        connect(m_sock, SIGNAL(connected()),
 
194
                        this, SLOT(slotConnected()));
 
195
        connect(m_sock, SIGNAL(readyRead()),
 
196
                        this, SLOT(slotReadyRead()));
 
197
}
 
198
 
 
199
/**
 
200
 * Destructor.
 
201
 */
 
202
HttpClient::~HttpClient()
 
203
{
 
204
        m_sock->close();
 
205
        m_sock->disconnect();
 
206
        delete m_sock;
 
207
}
 
208
 
 
209
/**
 
210
 * Emit a progress signal with bytes received/total bytes.
 
211
 *
 
212
 * @param text state text
 
213
 */
 
214
void HttpClient::emitProgress(const QString& text)
 
215
{
 
216
        emit progress(text, m_rcvIdx - m_rcvBodyIdx, m_rcvBodyLen);
 
217
}
 
218
 
 
219
/**
 
220
 * Display status if host is found.
 
221
 */
 
222
void HttpClient::slotHostFound()
 
223
{
 
224
        emitProgress(i18n("Host found..."), CS_HostFound, CS_EstimatedBytes);
 
225
}
 
226
 
 
227
/**
 
228
 * Display status if connection is established.
 
229
 */
 
230
void HttpClient::slotConnected()
 
231
{
 
232
        m_sock->QCM_writeBlock(m_request.QCM_latin1(), m_request.length());
 
233
        emitProgress(i18n("Request sent..."), CS_RequestSent, CS_EstimatedBytes);
 
234
}
 
235
 
 
236
/**
 
237
 * Read the available bytes.
 
238
 */
 
239
void HttpClient::readBytesAvailable()
 
240
{
 
241
        unsigned long len = m_sock->bytesAvailable();
 
242
        if (len > 0) {
 
243
                m_rcvBuf.resize(m_rcvIdx + len);
 
244
                unsigned long bytesRead = m_sock->QCM_readBlock(m_rcvBuf.data() + m_rcvIdx,
 
245
                                                                len);
 
246
                if (bytesRead > 0) {
 
247
                        m_rcvIdx += bytesRead;
 
248
                        if (bytesRead < len) {
 
249
                                m_rcvBuf.resize(m_rcvIdx);
 
250
                        }
 
251
                        if (m_rcvBodyIdx == 0 && m_rcvBodyLen == 0) {
 
252
                                QCString str(m_rcvBuf.data(), m_rcvIdx + 1);
 
253
                                int contentLengthPos = str.QCM_indexOf("Content-Length:");
 
254
                                if (contentLengthPos != -1) {
 
255
                                        contentLengthPos += 15;
 
256
                                        while (str[contentLengthPos] == ' ' ||
 
257
                                               str[contentLengthPos] == '\t') {
 
258
                                                ++contentLengthPos;
 
259
                                        }
 
260
                                        int contentLengthLen = str.QCM_indexOf("\r\n", contentLengthPos);
 
261
                                        if (contentLengthLen > contentLengthPos) {
 
262
                                                contentLengthLen -= contentLengthPos;
 
263
                                        }
 
264
                                        m_rcvBodyLen = str.mid(contentLengthPos, contentLengthLen).toULong();
 
265
                                }
 
266
                                int contentTypePos = str.QCM_indexOf("Content-Type:");
 
267
                                if (contentTypePos != -1) {
 
268
                                        contentTypePos += 13;
 
269
                                        char ch;
 
270
                                        while (contentTypePos < static_cast<int>(m_rcvIdx) &&
 
271
                                               ((ch = str[contentTypePos]) == ' ' || ch == '\t')) {
 
272
                                                ++contentTypePos;
 
273
                                        }
 
274
                                        int contentTypeEnd = contentTypePos;
 
275
                                        while (contentTypeEnd < static_cast<int>(m_rcvIdx) &&
 
276
                                               (ch = str[contentTypeEnd]) != ' ' && ch != '\t' &&
 
277
                                               ch != '\r' && ch != '\n' && ch != ';') {
 
278
                                                ++contentTypeEnd;
 
279
                                        }
 
280
                                        m_rcvBodyType = str.mid(contentTypePos,
 
281
                                                                contentTypeEnd - contentTypePos);
 
282
                                }
 
283
                                int bodyPos = str.QCM_indexOf("\r\n\r\n");
 
284
                                if (bodyPos != -1) {
 
285
                                        m_rcvBodyIdx = bodyPos + 4;
 
286
                                }
 
287
                        }
 
288
                }
 
289
        }
 
290
}
 
291
 
 
292
/**
 
293
 * Read received data when the server has closed the connection.
 
294
 * A bytesReceived() signal is emitted.
 
295
 */
 
296
void HttpClient::slotConnectionClosed()
 
297
{
 
298
        readBytesAvailable();
 
299
        QByteArray body;
 
300
        body.duplicate(m_rcvBuf.data() + m_rcvBodyIdx,
 
301
                       m_rcvBuf.size() - m_rcvBodyIdx);
 
302
        body.resize(m_rcvBuf.size() - m_rcvBodyIdx + 1);
 
303
        body[m_rcvBuf.size() - m_rcvBodyIdx] = '\0';
 
304
        emit bytesReceived(body);
 
305
        m_sock->close();
 
306
        emitProgress(i18n("Ready."), CS_EstimatedBytes, CS_EstimatedBytes);
 
307
}
 
308
 
 
309
/**
 
310
 * Display information about read progress.
 
311
 */
 
312
void HttpClient::slotReadyRead()
 
313
{
 
314
        readBytesAvailable();
 
315
        emitProgress(KCM_i18n1("Data received: %1", m_rcvIdx));
 
316
}
 
317
 
 
318
/**
 
319
 * Display information about socket error.
 
320
 */
 
321
void HttpClient::slotError(int err)
 
322
{
 
323
        QString msg(i18n("Socket error: "));
 
324
        switch (err) {
 
325
                case QSocket::ErrConnectionRefused:
 
326
                        msg += i18n("Connection refused");
 
327
                        break;
 
328
                case QSocket::ErrHostNotFound:
 
329
                        msg += i18n("Host not found");
 
330
                        break;
 
331
                case QSocket::ErrSocketRead:
 
332
                        msg += i18n("Read failed");
 
333
                        break;
 
334
                default:
 
335
                        msg += QString::number(err);
 
336
        }
 
337
        emitProgress(msg);
 
338
}
 
339
 
 
340
/**
 
341
 * Send a HTTP GET request.
 
342
 *
 
343
 * @param server host name
 
344
 * @param path   path of the URL
 
345
 */
 
346
void HttpClient::sendRequest(const QString& server, const QString& path)
 
347
{
 
348
        QString dest;
 
349
        int destPort;
 
350
        QString destNamePort(getProxyOrDest(server));
 
351
        splitNamePort(destNamePort, dest, destPort);
 
352
        QString serverName;
 
353
        int serverPort;
 
354
        splitNamePort(server, serverName, serverPort);
 
355
        m_request = "GET ";
 
356
        if (dest != serverName) {
 
357
                m_request += "http://";
 
358
                m_request += serverName;
 
359
                if (serverPort != 80) {
 
360
                        m_request += ':';
 
361
                        m_request += QString::number(serverPort);
 
362
                }
 
363
        }
 
364
        m_request += path;
 
365
        m_request += " HTTP/1.1\r\nUser-Agent: Kid3/" VERSION "\r\nHost: ";
 
366
        m_request += serverName;
 
367
        m_request += "\r\nConnection: close\r\n\r\n";
 
368
 
 
369
        m_rcvBuf.resize(0);
 
370
        m_rcvIdx = 0;
 
371
        m_rcvBodyIdx = 0;
 
372
        m_rcvBodyLen = 0;
 
373
        m_rcvBodyType = "";
 
374
 
 
375
        m_sock->connectToHost(dest, destPort);
 
376
        emitProgress(i18n("Connecting..."), CS_Connecting, CS_EstimatedBytes);
 
377
}
 
378
 
 
379
/**
 
380
 * Get string with proxy or destination and port.
 
381
 * If a proxy is set, the proxy is returned, else the real destination.
 
382
 *
 
383
 * @param dst real destination
 
384
 *
 
385
 * @return "destinationname:port".
 
386
 */
 
387
QString HttpClient::getProxyOrDest(const QString& dst)
 
388
{
 
389
        QString dest;
 
390
        if (Kid3App::s_miscCfg.m_useProxy) {
 
391
                dest = Kid3App::s_miscCfg.m_proxy;
 
392
        }
 
393
        if (dest.isEmpty()) {
 
394
                dest = dst;
 
395
        }
 
396
        return dest;
 
397
}
 
398
 
 
399
void HttpClient::slotStateChanged(int) {}
 
400
void HttpClient::slotDataReadProgress(int, int) {}
 
401
void HttpClient::slotDone(bool) {}
 
402
void HttpClient::slotResponseHeaderReceived(const QHttpResponseHeader&) {}
 
403
 
 
404
#endif
 
405
 
 
406
/**
 
407
 * Emit a progress signal with step/total steps.
 
408
 *
 
409
 * @param text       state text
 
410
 * @param step       current step
 
411
 * @param totalSteps total number of steps
 
412
 */
 
413
void HttpClient::emitProgress(const QString& text, int step, int totalSteps)
 
414
{
 
415
        emit progress(text, step, totalSteps);
 
416
}
 
417
 
 
418
/**
 
419
 * Extract name and port from string.
 
420
 *
 
421
 * @param namePort input string with "name:port"
 
422
 * @param name     output string with "name"
 
423
 * @param port     output integer with port
 
424
 */
 
425
void HttpClient::splitNamePort(const QString& namePort,
 
426
                                                                                                                                 QString& name, int& port)
 
427
{
 
428
        int colPos = namePort.QCM_lastIndexOf(':');
 
429
        if (colPos >= 0) {
 
430
                bool ok;
 
431
                port = namePort.mid(colPos + 1).toInt(&ok);
 
432
                if (!ok) port = 80;
 
433
                name = namePort.left(colPos);
 
434
        } else {
 
435
                name = namePort;
 
436
                port = 80;
 
437
        }
 
438
}