//============================================================================= // // File : KviHttpRequest.cpp // Creation date : Sat Aug 17 13:43:32 2002 GMT by Szymon Stefanek // // This file is part of the KVIrc IRC client distribution // Copyright (C) 2002-2010 Szymon Stefanek (pragma at kvirc dot net) // // This program is FREE software. You can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the HOPE that it will be USEFUL, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #include "KviHttpRequest.h" #include "KviCString.h" #include "KviDataBuffer.h" #include "KviDnsResolver.h" #include "KviError.h" #include "KviFile.h" #include "KviLocale.h" #include "KviNetUtils.h" #include "KviTimeUtils.h" #include "kvi_debug.h" #include #ifdef COMPILE_SSL_SUPPORT #include #endif #include #include #include #include #include //#include class KviHttpRequestPrivate { public: QTcpSocket * pSocket; bool bIsSSL; KviDataBuffer * pBuffer; QTimer * pConnectTimeoutTimer; KviFile * pFile; unsigned short uPort; }; KviHttpRequest::KviHttpRequest() : QObject() { m_p = new KviHttpRequestPrivate(); m_p->pSocket = nullptr; m_p->bIsSSL = false; m_p->pConnectTimeoutTimer = nullptr; m_p->pBuffer = new KviDataBuffer(); m_p->pFile = nullptr; m_pPrivateData = nullptr; m_bHeaderProcessed = false; m_uConnectionTimeout = 60; m_bFollowRedirects = true; m_uRedirectCount = 0; m_uMaximumRedirectCount = 20; resetStatus(); resetData(); } KviHttpRequest::~KviHttpRequest() { resetInternalStatus(); KVI_ASSERT(!(m_p->pSocket)); KVI_ASSERT(!(m_p->pFile)); if(m_p->pBuffer) delete m_p->pBuffer; delete m_p; } void KviHttpRequest::abort() { resetInternalStatus(); m_szLastError = __tr2qs("Aborted"); emit terminated(false); } void KviHttpRequest::resetInternalStatus() { if(m_p->pConnectTimeoutTimer) { delete m_p->pConnectTimeoutTimer; m_p->pConnectTimeoutTimer = nullptr; } if(m_p->pSocket) closeSocket(); if(m_p->pFile) { m_p->pFile->close(); delete m_p->pFile; m_p->pFile = nullptr; } m_p->pBuffer->clear(); m_bHeaderProcessed = false; //KviThreadManager::killPendingEvents(this); //QApplication::removePostedEvents(this); } void KviHttpRequest::resetStatus() { m_szLastError = __tr2qs("No request"); m_uTotalSize = 0; m_uReceivedSize = 0; } void KviHttpRequest::resetData() { m_szFileName = ""; m_eProcessingType = WholeFile; m_eExistingFileAction = RenameIncoming; m_url = QString(""); m_connectionUrl = QString(""); m_uMaxContentLength = 0; m_uContentOffset = 0; m_bChunkedTransferEncoding = false; m_bGzip = false; m_bIgnoreRemainingData = false; m_uRemainingChunkSize = 0; m_uRedirectCount = 0; } void KviHttpRequest::reset() { resetStatus(); resetData(); resetInternalStatus(); } bool KviHttpRequest::get(const KviUrl & u, ProcessingType p, const QString & szFileName) { reset(); setUrl(u); setProcessingType(p); setFileName(szFileName); return start(); } bool KviHttpRequest::start() { // ensure that the file is closed resetInternalStatus(); resetStatus(); if(m_eProcessingType == StoreToFile) { if(m_szFileName.isEmpty()) { m_szLastError = __tr2qs("No filename specified for the \"StoreToFile\" processing type"); return false; } if((m_eExistingFileAction == Resume) && (m_uContentOffset == 0)) { // determine the content offset automatically if(KviFile::exists(m_szFileName)) { // we check it QFileInfo fi(m_szFileName); m_uContentOffset = fi.size(); } } } if(m_connectionUrl.host().isEmpty()) { resetInternalStatus(); m_szLastError = __tr2qs("Invalid URL: missing hostname"); return false; } m_p->bIsSSL = KviQString::equalCI(m_connectionUrl.protocol(), "https"); if(!KviQString::equalCI(m_connectionUrl.protocol(), "http") && !m_p->bIsSSL) { resetInternalStatus(); m_szLastError = __tr2qs("Unsupported protocol %1").arg(m_connectionUrl.protocol()); return false; } return doConnect(); } void KviHttpRequest::closeSocket() { if(!m_p->pSocket) return; QObject::disconnect(m_p->pSocket, SIGNAL(connected()), this, SLOT(slotSocketConnected())); QObject::disconnect(m_p->pSocket, SIGNAL(disconnected()), this, SLOT(slotSocketDisconnected())); QObject::disconnect(m_p->pSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError))); m_p->pSocket->abort(); m_p->pSocket->close(); // This can be called from a socket handler slot m_p->pSocket->deleteLater(); m_p->pSocket = nullptr; } void KviHttpRequest::slotSocketDisconnected() { switch(m_eProcessingType) { case WholeFile: // happens always emit binaryData(*m_p->pBuffer); break; case Blocks: // an unprocessed block ?.. should never happened.. but well :D if(m_p->pBuffer->size() > 0) emit binaryData(*m_p->pBuffer); break; case Lines: if(m_p->pBuffer->size() > 0) { // something left in the buffer and has no trailing LF KviCString tmp((const char *)(m_p->pBuffer->data()), m_p->pBuffer->size()); emit data(tmp); } break; case StoreToFile: // same as above... should never happen.. but well :D if(m_p->pFile && m_p->pBuffer->size() > 0) m_p->pFile->write((const char *)(m_p->pBuffer->data()), m_p->pBuffer->size()); break; default: // nothing... just make gcc happy break; } resetInternalStatus(); m_szLastError = __tr2qs("Success"); emit terminated(true); } void KviHttpRequest::slotSocketConnected() { if(m_p->pConnectTimeoutTimer) { delete m_p->pConnectTimeoutTimer; m_p->pConnectTimeoutTimer = nullptr; } emit connectionEstablished(); emit status( __tr2qs("Connected to %1:%2: sending request") .arg(m_p->pSocket->peerAddress().toString()) .arg(m_p->pSocket->peerPort())); KviCString szMethod; bool bIsPost = false; if(m_eProcessingType == HeadersOnly) szMethod = "HEAD"; else if(m_szPostData.isEmpty()) szMethod = "GET"; else { szMethod = "POST"; bIsPost = true; } KviCString szRequest( KviCString::Format, "%s %s HTTP/1.1\r\n" "Host: %s\r\n" "Connection: Close\r\n" "User-Agent: KVIrc-http-slave/1.0.0\r\n" "Accept: */*\r\n", szMethod.ptr(), m_connectionUrl.path().toUtf8().data(), m_connectionUrl.host().toUtf8().data()); if(m_uContentOffset > 0) szRequest.append(KviCString::Format, "Range: bytes=%u-\r\n", m_uContentOffset); if(bIsPost) { szRequest.append(KviCString::Format, "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %u\r\n" "Cache-control: no-cache\r\n" "Pragma: no-cache\r\n", m_szPostData.length()); } szRequest += "\r\n"; if(bIsPost) { if(!m_szPostData.isEmpty()) szRequest.append(m_szPostData); szRequest += "\r\n"; } // FIXME: Handle this better! int written = m_p->pSocket->write(szRequest.ptr(), szRequest.len()); if(written < szRequest.len()) { m_szLastError = __tr2qs("Socket write error"); resetInternalStatus(); emit terminated(false); } // FIXME: Handle this better QString req = QString::fromLatin1(szRequest.ptr()); QStringList sl = req.split("\r\n"); emit requestSent(sl); // now wait for the response // FIXME: Handle read timeouts! } void KviHttpRequest::slotSocketReadDataReady() { KVI_ASSERT(m_p->pSocket); int iBytes = m_p->pSocket->bytesAvailable(); if(iBytes <= 0) { // assume connection closed ? slotSocketDisconnected(); return; } // FIXME: Avoid double-buffering here! KviDataBuffer oBuffer(iBytes); int iRead = m_p->pSocket->read((char *)(oBuffer.data()), iBytes); if(iRead < iBytes) { // hum.... what here ? if(iRead < 1) { slotSocketDisconnected(); return; } // FIXME oBuffer.resize(iRead); } processData(&oBuffer); } void KviHttpRequest::slotSocketError(QAbstractSocket::SocketError eError) { KVI_ASSERT(m_p->pSocket); if(eError == QAbstractSocket::RemoteHostClosedError) { slotSocketDisconnected(); return; } m_szLastError = m_p->pSocket->errorString(); resetInternalStatus(); emit terminated(false); } void KviHttpRequest::slotConnectionTimedOut() { resetInternalStatus(); m_szLastError = __tr2qs("Connection timed out"); emit terminated(false); } void KviHttpRequest::slotSocketHostResolved() { emit contactingHost(QString::fromLatin1("%1:%2").arg(m_connectionUrl.host()).arg(m_p->uPort)); emit status(__tr2qs("Contacting host %1 on port %2").arg(m_connectionUrl.host()).arg(m_p->uPort)); } bool KviHttpRequest::doConnect() { m_p->uPort = m_connectionUrl.port(); if(m_p->uPort == 0) m_p->uPort = m_p->bIsSSL ? 443 : 80; if(m_p->pSocket) closeSocket(); #ifdef COMPILE_SSL_SUPPORT m_p->pSocket = m_p->bIsSSL ? new QSslSocket() : new QTcpSocket(); #else m_p->pSocket = new QTcpSocket(); #endif QObject::connect(m_p->pSocket, SIGNAL(connected()), this, SLOT(slotSocketConnected())); QObject::connect(m_p->pSocket, SIGNAL(disconnected()), this, SLOT(slotSocketDisconnected())); QObject::connect(m_p->pSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError))); QObject::connect(m_p->pSocket, SIGNAL(readyRead()), this, SLOT(slotSocketReadDataReady())); QObject::connect(m_p->pSocket, SIGNAL(hostFound()), this, SLOT(slotSocketHostResolved())); emit resolvingHost(m_connectionUrl.host()); #ifdef COMPILE_SSL_SUPPORT if(m_p->bIsSSL) { static_cast(m_p->pSocket)->setProtocol(QSsl::AnyProtocol); static_cast(m_p->pSocket)->connectToHostEncrypted(m_connectionUrl.host(), m_p->uPort); } else { m_p->pSocket->connectToHost(m_connectionUrl.host(), m_p->uPort); } #else m_p->pSocket->connectToHost(m_connectionUrl.host(), m_p->uPort); #endif if(m_p->pConnectTimeoutTimer) { delete m_p->pConnectTimeoutTimer; m_p->pConnectTimeoutTimer = nullptr; } m_p->pConnectTimeoutTimer = new QTimer(); m_p->pConnectTimeoutTimer->setSingleShot(true); QObject::connect(m_p->pConnectTimeoutTimer, SIGNAL(timeout()), this, SLOT(slotConnectionTimedOut())); m_p->pConnectTimeoutTimer->start(m_uConnectionTimeout * 1000); return true; } void KviHttpRequest::emitLines(KviDataBuffer * pDataBuffer) { int idx = pDataBuffer->find((const unsigned char *)"\n", 1); while(idx != -1) { KviCString tmp((const char *)(m_p->pBuffer->data()), idx); tmp.stripRight('\r'); pDataBuffer->remove(idx + 1); idx = pDataBuffer->find((const unsigned char *)"\n", 1); emit data(tmp); } } bool KviHttpRequest::openFile() { if(m_eProcessingType != StoreToFile) return true; bool bAppend = false; // take action when the file is existing if(KviFile::exists(m_szFileName)) { switch(m_eExistingFileAction) { case Resume: { bAppend = true; } break; case RenameIncoming: { int i = 0; QString tmp = m_szFileName; do { i++; m_szFileName = tmp + QString(".kvirnm-%1").arg(i); } while(KviFile::exists(m_szFileName)); } break; case RenameExisting: { int i = 0; QString tmp; do { i++; tmp = m_szFileName + QString(".kvirnm-%1").arg(i); } while(KviFile::exists(tmp)); QDir d; if(!d.rename(m_szFileName, tmp)) { // fail :( resetInternalStatus(); m_szLastError = __tr2qs("Failed to rename the existing file, please rename manually and retry"); emit terminated(false); return false; } } break; case Overwrite: default: // nothing break; } } m_p->pFile = new KviFile(m_szFileName); if(!m_p->pFile->open(QFile::WriteOnly | (bAppend ? QFile::Append : QFile::Truncate))) { resetInternalStatus(); m_szLastError = QString(__tr2qs("Can't open file \"%1\" for writing")).arg(m_szFileName); emit terminated(false); return false; } return true; } bool KviHttpRequest::processHeader(KviCString & szHeader) { int idx = szHeader.findFirstIdx("\r\n"); KviCString szResponse; if(idx != -1) { szResponse = szHeader.left(idx); szHeader.cutLeft(idx + 2); } else { szResponse = szHeader; szHeader = ""; } szResponse.trim(); bool bValid = false; unsigned int uStatus = 0; // check the response value if(kvi_strEqualCSN(szResponse.ptr(), "HTTP", 4)) { KviCString szR = szResponse; szR.cutToFirst(' '); szR.trim(); int idx = szR.findFirstIdx(' '); KviCString szNumber; if(idx != -1) szNumber = szR.left(idx); else szNumber = szR; bool bOk; uStatus = szNumber.toUInt(&bOk); if(bOk) bValid = true; } QString szUniResponse = QString::fromUtf8(szResponse.ptr()); if(!bValid) { // the response is invalid ? resetInternalStatus(); m_szLastError = __tr2qs("Invalid HTTP response: %1").arg(szUniResponse); emit terminated(false); return false; } emit status(__tr2qs("Received HTTP response: %1").arg(szUniResponse)); KviPointerList hlist; hlist.setAutoDelete(true); idx = szHeader.findFirstIdx("\r\n"); while(idx != -1) { if(idx > 0) { hlist.append(new KviCString(szHeader.ptr(), idx)); szHeader.cutLeft(idx + 2); } idx = szHeader.findFirstIdx("\r\n"); } if(szHeader.hasData()) hlist.append(new KviCString(szHeader)); KviPointerHashTable hdr(11, false, true); hdr.setAutoDelete(true); for(KviCString * s = hlist.first(); s; s = hlist.next()) { idx = s->findFirstIdx(":"); if(idx != -1) { KviCString szName = s->left(idx); s->cutLeft(idx + 1); s->trim(); hdr.replace(szName.ptr(), new KviCString(*s)); //qDebug("FOUND HEADER (%s)=(%s)",szName.ptr(),s->ptr()); } } KviCString * size = hdr.find("Content-length"); if(size) { bool bOk; m_uTotalSize = size->toUInt(&bOk); if(!bOk) m_uTotalSize = 0; } KviCString * contentEncoding = hdr.find("Content-encoding"); if(contentEncoding) { m_bGzip = contentEncoding->equalsCI("gzip"); } KviCString * transferEncoding = hdr.find("Transfer-Encoding"); if(transferEncoding) { if(kvi_strEqualCI(transferEncoding->ptr(), "chunked")) { // be prepared to handle the chunked transfer encoding as required by HTTP/1.1 m_bChunkedTransferEncoding = true; m_uRemainingChunkSize = 0; } } // check the status // case 200: // OK // case 206: // Partial content // case 100: // Continue // case 101: // Switching protocols // case 201: // Created // case 202: // Accepted // case 203: // Non-Authoritative Information // case 204: // No content // case 205: // Reset content // case 300: // Multiple choices // case 301: // Moved permanently // case 302: // Found // case 303: // See Other // case 304: // Not modified // case 305: // Use Proxy // case 306: // Switch Proxy // case 307: // Temporary Redirect // case 400: // Bad request // case 401: // Unauthorized // case 402: // Payment Required // case 403: // Forbidden // case 404: // Not found // case 405: // Method not allowed // case 406: // Not acceptable // case 407: // Proxy authentication required // case 408: // Request timeout // case 409: // Conflict // case 410: // Gone // case 411: // Length required // case 412: // Precondition failed // case 413: // Request entity too large // case 414: // Request-URI Too Long // case 415: // Unsupported media type // case 416: // Requested range not satisfiable // case 417: // Expectation Failed // case 500: // Internal server error // case 501: // Not implemented // case 502: // Bad gateway // case 503: // Service unavailable // case 504: // Gateway timeout // case 505: // HTTP Version not supported if( (uStatus != 200) && // OK (uStatus != 206) // Partial content ) { // This is not "OK" and not "Partial content" // Error, redirect or something confusing if(m_eProcessingType != HeadersOnly) { switch(uStatus) { case 301: // Moved permanently case 302: // Found case 303: // See Other case 307: // Temporary Redirect { if(!m_bFollowRedirects) { resetInternalStatus(); m_szLastError = szResponse.ptr(); emit terminated(false); return false; } m_uRedirectCount++; if(m_uRedirectCount > m_uMaximumRedirectCount) { resetInternalStatus(); m_szLastError = __tr2qs("Too many redirects"); emit terminated(false); return false; } KviCString * headerLocation = hdr.find("Location"); if(!headerLocation || headerLocation->isEmpty()) { resetInternalStatus(); m_szLastError = __tr2qs("Bad redirect"); emit terminated(false); return false; } KviUrl url; QString location(headerLocation->ptr()); if(location.startsWith('/')) { // relative redirect, use the old url and only update the path url = m_connectionUrl; url.setPath(location); } else { // absolute redirect url.setUrl(location); } if( (url.url() == m_connectionUrl.url()) || (url.url() == m_url.url())) { resetInternalStatus(); m_szLastError = __tr2qs("Redirect loop"); emit terminated(false); return false; } m_connectionUrl = url; emit status(__tr2qs("Following Redirect to %1").arg(url.url())); if(!start()) emit terminated(false); return false; // will exit the call stack } break; break; default: // assume error resetInternalStatus(); m_szLastError = szResponse.ptr(); emit terminated(false); return false; break; } // this is an error then } // else the server will terminate (it was a HEAD request) } emit receivedResponse(szUniResponse); emit header(&hdr); if((m_uMaxContentLength > 0) && (m_uTotalSize > ((unsigned int)m_uMaxContentLength))) { resetInternalStatus(); m_szLastError = __tr2qs("The amount of received data exceeds the maximum length"); emit terminated(false); return false; } // fixme: could check for data type etc... return true; } #define BUFFER_SIZE 32768 void KviHttpRequest::processData(KviDataBuffer * data) { // unsigned char obuffer[BUFFER_SIZE]; if(m_bChunkedTransferEncoding && m_bIgnoreRemainingData) { // In chunked transfer encoding mode there may be additional headers // after the last chunk of data. We simply ignore them. return; } if(!m_bHeaderProcessed) { // time to process the header m_p->pBuffer->append(*data); int idx = m_p->pBuffer->find((const unsigned char *)"\r\n\r\n", 4); if(idx == -1) { // header not complete if(m_p->pBuffer->size() > 4096) { resetInternalStatus(); m_szLastError = __tr2qs("Header too long: exceeded 4096 bytes"); emit terminated(false); } return; } KviCString szHeader((const char *)(m_p->pBuffer->data()), idx); m_p->pBuffer->remove(idx + 4); if(!processHeader(szHeader)) return; m_bHeaderProcessed = true; if(m_eProcessingType == StoreToFile) { if(!openFile()) return; } m_uReceivedSize = m_p->pBuffer->size(); // here the header is complete and the eventual remaining data is in m_p->pBuffer. data has been already used. } else { // header already processed m_uReceivedSize += data->size(); // here the header is complete and some data *might* be already in m_p->pBuffer. data is unused yet. // Optimisation: If the transfer is NOT chunked (so we don't have to parse it) // and the requested processing type is either Blocks or StoreToFile // then we just can avoid to copy the data to m_p->pBuffer. // This is a good optimisation since for large files we can save allocating // space for and moving megabytes of data... if((!m_bChunkedTransferEncoding) && ((m_eProcessingType == Blocks) || (m_eProcessingType == StoreToFile))) { switch(m_eProcessingType) { case Blocks: emit binaryData(*data); break; case StoreToFile: m_p->pFile->write((const char *)(data->data()), data->size()); break; default: break; } if(((m_uTotalSize > 0) && (m_uReceivedSize > m_uTotalSize)) || ((m_uMaxContentLength > 0) && (m_uReceivedSize > m_uMaxContentLength))) { resetInternalStatus(); m_szLastError = __tr2qs("The amount of received data exceeds expected length"); emit terminated(false); } return; } // need to append to m_p->pBuffer and process it m_p->pBuffer->append(*data); } // we're processing data in m_p->pBuffer here if(m_bChunkedTransferEncoding) { // The transfer encoding is chunked: the buffer contains // chunks of data with an initial header composed // of a hexadecimal length, an optional bullshit and a single CRLF // The transfer terminates when we read a last chunk of size 0 // that may be followed by optional headers... // This sux :) while(m_p->pBuffer->size() > 0) // <-- note that we may exit from this loop also for other conditions (there is a goto below) { // we process chunks of parts of chunks at a time. if(m_uRemainingChunkSize > 0) { // process the current chunk data unsigned int uProcessSize = m_uRemainingChunkSize; if(uProcessSize > (unsigned int)m_p->pBuffer->size()) uProcessSize = m_p->pBuffer->size(); m_uRemainingChunkSize -= uProcessSize; switch(m_eProcessingType) { case Blocks: if((unsigned int)m_p->pBuffer->size() == uProcessSize) { // avoid copying to a new buffer emit binaryData(*m_p->pBuffer); } else { // must copy KviDataBuffer tmp(uProcessSize, m_p->pBuffer->data()); emit binaryData(tmp); m_p->pBuffer->remove(uProcessSize); } break; case Lines: if((unsigned int)m_p->pBuffer->size() == uProcessSize) { // avoid copying to a new buffer emitLines(m_p->pBuffer); } else { // must copy KviDataBuffer tmp(uProcessSize, m_p->pBuffer->data()); emitLines(&tmp); m_p->pBuffer->remove(uProcessSize); } break; case StoreToFile: m_p->pFile->write((const char *)(m_p->pBuffer->data()), uProcessSize); m_p->pBuffer->remove(uProcessSize); break; default: // nothing.. just make gcc happy break; } // now either the buffer is empty or there is another chunk header: continue looping } else { // We're looking for the beginning of a chunk now. // Note that we might be at the end of a previous chunk that has a CRLF terminator // we need to skip it. int crlf = m_p->pBuffer->find((const unsigned char *)"\r\n", 2); if(crlf != -1) { if(crlf == 0) { // This is a plain CRLF at the beginning of the buffer BEFORE a chunk header. // It comes from the previous chunk terminator. Skip it. m_p->pBuffer->remove(2); } else { // got a chunk header KviCString szHeader((const char *)(m_p->pBuffer->data()), crlf); szHeader.cutFromFirst(' '); // now szHeader should contain a hexadecimal chunk length... (why the hell it is hex and not decimal ????) QString szHexHeader = szHeader.ptr(); bool bOk; m_uRemainingChunkSize = szHexHeader.toLong(&bOk, 16); if(!bOk) { resetInternalStatus(); m_szLastError = __tr2qs("Protocol error: invalid chunk size"); emit terminated(false); return; } m_p->pBuffer->remove(crlf + 2); if(m_uRemainingChunkSize == 0) { // this is the last chunk of data. It may be followed by optional headers // but we actually don't need them (since we're surely not in HEAD mode) m_bIgnoreRemainingData = true; m_p->pBuffer->clear(); goto check_stream_length; } } // the rest is valid data of a non-zero chunk: continue looping } else { // chunk header not complete if(m_p->pBuffer->size() > 4096) { resetInternalStatus(); m_szLastError = __tr2qs("Chunk header too long: exceeded 4096 bytes"); emit terminated(false); return; } goto check_stream_length; } } } } else { // the transfer encoding is not chunked: m_p->pBuffer contains only valid data switch(m_eProcessingType) { case Blocks: if(m_p->pBuffer->size() > 0) emit binaryData(*m_p->pBuffer); m_p->pBuffer->clear(); break; case Lines: if(m_p->pBuffer->size() > 0) emitLines(m_p->pBuffer); break; case StoreToFile: m_p->pFile->write((const char *)(m_p->pBuffer->data()), m_p->pBuffer->size()); m_p->pBuffer->clear(); break; default: // nothing.. just make gcc happy break; } } check_stream_length: if(((m_uTotalSize > 0) && (m_uReceivedSize > m_uTotalSize)) || ((m_uMaxContentLength > 0) && (m_uReceivedSize > m_uMaxContentLength))) { resetInternalStatus(); m_szLastError = __tr2qs("The amount of received data exceeds expected length"); emit terminated(false); } return; } #if 0 bool KviHttpRequest::startDnsLookup() { m_pDns = new KviDnsResolver(); connect(m_pDns,SIGNAL(lookupDone(KviDnsResolver *)),this,SLOT(dnsLookupDone(KviDnsResolver *))); if(!m_pDns->lookup(m_connectionUrl.host(),KviDnsResolver::IPv4)) { resetInternalStatus(); m_szLastError = __tr2qs("Unable to start the DNS lookup"); return false; } QString tmp; tmp = QString(__tr2qs("Looking up host %1")).arg(m_connectionUrl.host()); emit status(tmp); // FIXME return true; } void KviHttpRequest::dnsLookupDone(KviDnsResolver *d) { if(d->state() == KviDnsResolver::Success) { m_szIp = d->firstIpAddress(); delete m_pDns; m_pDns = nullptr; QString tmp; tmp = QString(__tr2qs("Host %1 resolved to %2")).arg(m_connectionUrl.host(),m_szIp); emit status(tmp); haveServerIp(); } else { int iErr = d->error(); resetInternalStatus(); m_szLastError = KviError::getDescription(iErr); emit terminated(false); } } #endif