aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Szymon Tomasz Stefanek2010-12-29 06:15:31 +0000
committerGravatar Szymon Tomasz Stefanek2010-12-29 06:15:31 +0000
commit132a99871ba98a9ceed37a8e2917d0bb8e7059fc (patch)
treed300592536eb91632c43f3bd8656d464b58401f0 /src
parentMore file splitting (diff)
downloadKVIrc-132a99871ba98a9ceed37a8e2917d0bb8e7059fc.tar.gz
KVIrc-132a99871ba98a9ceed37a8e2917d0bb8e7059fc.tar.bz2
KVIrc-132a99871ba98a9ceed37a8e2917d0bb8e7059fc.zip
More file splitting
git-svn-id: https://svn.kvirc.de/svn/trunk/kvirc@5260 17fca916-40b9-46aa-a4ea-0a15b648b75c
Diffstat (limited to 'src')
-rw-r--r--src/kvilib/net/KviHttpRequestThread.cpp616
-rw-r--r--src/kvilib/net/KviHttpRequestThread.h100
2 files changed, 716 insertions, 0 deletions
diff --git a/src/kvilib/net/KviHttpRequestThread.cpp b/src/kvilib/net/KviHttpRequestThread.cpp
new file mode 100644
index 000000000..21e1d2a9e
--- /dev/null
+++ b/src/kvilib/net/KviHttpRequestThread.cpp
@@ -0,0 +1,616 @@
+//=============================================================================
+//
+// File : KviHttpRequestThread.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 opinion) 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 "KviHttpRequestThread.h"
+#include "KviHttpRequest.h"
+
+#include "KviLocale.h"
+#include "KviNetUtils.h"
+#include "KviError.h"
+#include "KviTimeUtils.h"
+#include "KviDataBuffer.h"
+
+#include "kvi_debug.h"
+#include "kvi_socket.h"
+
+#ifdef COMPILE_SSL_SUPPORT
+ #include "KviSSL.h"
+#endif
+
+//#include <zlib.h>
+
+
+KviHttpRequestThread::KviHttpRequestThread(
+ KviHttpRequest * r,
+ const QString &szHost,
+ const QString &szIp,
+ unsigned short uPort,
+ const QString & szPath,
+ unsigned int uContentOffset,
+ RequestMethod m,
+ const QString &szPostData,
+ bool bUseSSL
+ ) : KviSensitiveThread()
+{
+ m_pRequest = r;
+ m_szHost = szHost;
+ m_szIp = szIp;
+ m_szPath = szPath;
+ m_uPort = uPort > 0 ? uPort : 80;
+ m_uContentOffset = uContentOffset;
+ m_eRequestMethod = m;
+ m_szPostData = szPostData;
+ m_sock = KVI_INVALID_SOCKET;
+ m_bUseSSL = bUseSSL;
+ m_uConnectionTimeout = 60;
+#ifdef COMPILE_SSL_SUPPORT
+ m_pSSL = 0;
+#endif
+}
+
+KviHttpRequestThread::~KviHttpRequestThread()
+{
+}
+
+bool KviHttpRequestThread::processInternalEvents()
+{
+ while(KviThreadEvent *e = dequeueEvent())
+ {
+ switch(e->id())
+ {
+ case KVI_THREAD_EVENT_TERMINATE:
+ {
+ delete e;
+ return false;
+ }
+ break;
+ default:
+ qDebug("Unrecognized event in http thread");
+ delete e;
+ return false;
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool KviHttpRequestThread::failure(const char *error)
+{
+ if(error)
+ {
+ postEvent(m_pRequest,new KviThreadDataEvent<KviCString>(KVI_THREAD_EVENT_ERROR,new KviCString(error)));
+ } /*else {
+ postEvent(m_pRequest,new KviThreadDataEvent<KviCString>(KVI_THREAD_EVENT_ERROR,new KviCString(__tr2qs("Aborted"))));
+ }*/
+ return false;
+}
+
+bool KviHttpRequestThread::selectForWrite(int iTimeoutInSecs)
+{
+
+ kvi_time_t startTime = kvi_unixTime();
+
+ for(;;)
+ {
+ if(!processInternalEvents())
+ {
+ return failure(0);
+ }
+
+ fd_set writeSet;
+
+ FD_ZERO(&writeSet);
+
+ FD_SET(m_sock,&writeSet);
+
+ struct timeval tmv;
+ tmv.tv_sec = 0;
+ tmv.tv_usec = 1000; // we wait 1000 usecs for an event
+
+
+ int nRet = kvi_socket_select(m_sock + 1,0,&writeSet,0,&tmv);
+
+ if(nRet > 0)
+ {
+ if(FD_ISSET(m_sock,&writeSet))
+ {
+ // connected!
+ return true;
+ }
+ } else {
+ if(nRet < 0)
+ {
+ int err = kvi_socket_error();
+#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
+ if((err != EAGAIN) && (err != EINTR) && (err != WSAEWOULDBLOCK))
+#else
+ if((err != EAGAIN) && (err != EINTR))
+#endif
+ {
+ return failure(KviError::getUntranslatedDescription(KviError::translateSystemError(err)));
+ }
+ }
+ }
+
+
+ if((time(0) - startTime) > iTimeoutInSecs)
+ return failure(__tr_no_lookup("Operation timed out"));
+
+ usleep(100000); // 1/10 sec
+ }
+
+ return false;
+}
+
+bool KviHttpRequestThread::sslFailure()
+{
+#ifdef COMPILE_SSL_SUPPORT
+ KviCString buffer;
+ if(m_pSSL->getLastErrorString(buffer))
+ {
+ failure(buffer.ptr());
+ } else {
+ failure(__tr_no_lookup("Unexpected SSL error"));
+ }
+#endif
+ return false;
+}
+
+bool KviHttpRequestThread::connectToRemoteHost()
+{
+ m_sock = kvi_socket_create(KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,0); //tcp
+ if(m_sock == KVI_INVALID_SOCKET)
+ return failure(__tr_no_lookup("Failed to create the socket"));
+
+ if(!kvi_socket_setNonBlocking(m_sock))
+ return failure(__tr_no_lookup("Failed to enter non blocking mode"));
+
+ sockaddr_in saddr;
+
+ if(!KviNetUtils::stringIpToBinaryIp(m_szIp,&(saddr.sin_addr)))
+ return failure(__tr_no_lookup("Invalid target address"));
+
+ saddr.sin_port = htons(m_uPort);
+ saddr.sin_family = AF_INET;
+
+ if(!kvi_socket_connect(m_sock,(struct sockaddr *)&saddr,sizeof(saddr)))
+ {
+ int err = kvi_socket_error();
+ if(!kvi_socket_recoverableConnectError(err))
+ {
+ return failure(KviError::getUntranslatedDescription(KviError::translateSystemError(err)));
+ }
+ }
+
+ // now loop selecting for write
+
+ if(!selectForWrite(m_uConnectionTimeout))
+ return false;
+
+ int sockError;
+ int iSize=sizeof(sockError);
+ if(!kvi_socket_getsockopt(m_sock,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize))sockError = -1;
+ if(sockError != 0)
+ {
+ //failed
+ if(sockError > 0)sockError = KviError::translateSystemError(sockError);
+ else sockError = KviError_unknownError;
+ return failure(KviError::getUntranslatedDescription(sockError));
+ }
+
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_bUseSSL)
+ {
+ m_pSSL = new KviSSL();
+ if(!m_pSSL->initContext(KviSSL::Client))
+ return failure(__tr_no_lookup("Failed to initialize the SSL context"));
+ if(!m_pSSL->initSocket(m_sock))
+ return failure(__tr_no_lookup("Failed to initialize the SSL connection"));
+
+ for(;;)
+ {
+ switch(m_pSSL->connect())
+ {
+ case KviSSL::Success:
+ // done: connected.
+ return true;
+ break;
+ case KviSSL::WantRead:
+ if(!selectForRead(m_uConnectionTimeout))
+ return false;
+ break;
+ case KviSSL::WantWrite:
+ if(!selectForWrite(m_uConnectionTimeout))
+ return false;
+ break;
+ case KviSSL::RemoteEndClosedConnection:
+ return failure(__tr_no_lookup("Remote end has closed the connection"));
+ break;
+ case KviSSL::SSLError:
+ return sslFailure();
+ break;
+ case KviSSL::SyscallError:
+ {
+ // syscall problem
+ int err = kvi_socket_error();
+ if(!kvi_socket_recoverableError(err))
+ {
+ // Declare problems :)
+ return failure(__tr_no_lookup("Unrecoverable SSL error during handshake"));
+ } // else can recover ? (EAGAIN, EINTR ?) ... should select for read or for write
+ }
+ break;
+ default:
+ return sslFailure();
+ break;
+ }
+ }
+
+ // never here
+ return true;
+ }
+#endif
+
+ return true;
+}
+
+
+bool KviHttpRequestThread::sendBuffer(const char * buffer,int bufLen,int iTimeoutInSecs)
+{
+ const char * ptr = buffer;
+ int curLen = bufLen;
+
+ time_t startTime = time(0);
+
+ for(;;)
+ {
+ if(!processInternalEvents())
+ return failure();
+
+ int wrtn;
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ wrtn = m_pSSL->write((char *)ptr,curLen);
+ } else {
+#endif
+ wrtn = kvi_socket_send(m_sock,ptr,curLen);
+#ifdef COMPILE_SSL_SUPPORT
+ }
+#endif
+
+ if(wrtn > 0)
+ {
+ curLen -= wrtn;
+
+ if(curLen <= 0)
+ break;
+
+ ptr += wrtn;
+ } else {
+ if(wrtn < 0)
+ {
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ // ops...might be an SSL error
+ switch(m_pSSL->getProtocolError(wrtn))
+ {
+ case KviSSL::WantWrite:
+ if(!selectForWrite(m_uConnectionTimeout))
+ return false;
+ break;
+ case KviSSL::WantRead:
+ if(!selectForRead(m_uConnectionTimeout))
+ return false;
+ break;
+ case KviSSL::SyscallError:
+ if(wrtn == 0)
+ {
+ return failure(__tr_no_lookup("Remote end has closed the connection"));
+ } else {
+ int iSSLErr = m_pSSL->getLastError(true);
+ if(iSSLErr != 0)
+ {
+ return sslFailure();
+ } else {
+ goto handle_system_error;
+ }
+ }
+ break;
+ case KviSSL::SSLError:
+ return sslFailure();
+ break;
+ default:
+ return sslFailure();
+ break;
+ }
+ } else {
+#endif //COMPILE_SSL_SUPPORT
+
+handle_system_error:
+ int err = kvi_socket_error();
+#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
+ if((err != EAGAIN) && (err != EINTR) && (err != WSAEWOULDBLOCK))
+#else
+ if((err != EAGAIN) && (err != EINTR))
+#endif
+ {
+ return failure(KviError::getUntranslatedDescription(KviError::translateSystemError(err)));
+ }
+#ifdef COMPILE_SSL_SUPPORT
+ }
+#endif
+ }
+ }
+
+ int diff = time(0) - startTime;
+ if(diff > iTimeoutInSecs)
+ return failure(__tr_no_lookup("Operation timed out"));
+
+ usleep(10000);
+ }
+
+ return true;
+}
+
+int KviHttpRequestThread::selectForReadStep()
+{
+ // calls select on the main socket
+ // returns 1 if there is data available for reading
+ // returns 0 if there is no data available but there was no error
+ // returns -1 if there was a critical error (socket closed)
+ fd_set readSet;
+
+ FD_ZERO(&readSet);
+
+ FD_SET(m_sock,&readSet);
+
+ struct timeval tmv;
+ tmv.tv_sec = 0;
+ tmv.tv_usec = 1000; // we wait 1000 usecs for an event
+
+
+ int nRet = kvi_socket_select(m_sock + 1,&readSet,0,0,&tmv);
+
+ if(nRet > 0)
+ {
+ if(FD_ISSET(m_sock,&readSet))
+ {
+ // ok
+ return 1;
+ }
+ } else {
+ if(nRet < 0)
+ {
+ int err = kvi_socket_error();
+#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
+ if((err != EAGAIN) && (err != EINTR) && (err != WSAEWOULDBLOCK))
+#else
+ if((err != EAGAIN) && (err != EINTR))
+#endif
+ {
+ failure(KviError::getUntranslatedDescription(KviError::translateSystemError(err)));
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool KviHttpRequestThread::selectForRead(int iTimeoutInSecs)
+{
+ // waits for some data to arrive on the socket
+ // up to iTimeoutInSecs seconds
+ // returns true if data is available on the socket
+ // or false if there was a select() error or no data
+ // was available in the specified amount of time
+
+ time_t startTime = time(0);
+
+ for(;;)
+ {
+ if(!processInternalEvents())
+ {
+ return failure(); // ensure that the socket is closed
+ }
+
+ int nRet = selectForReadStep();
+
+ if(nRet < 0)return false;
+ if(nRet > 0)return true;
+
+ int diff = time(0) - startTime;
+ if(diff > iTimeoutInSecs)
+ return failure(__tr_no_lookup("Operation timed out (while selecting for read)"));
+
+ usleep(100000); // 1/10 sec
+ }
+
+ return false;
+}
+
+bool KviHttpRequestThread::readDataStep()
+{
+ unsigned char buffer[2048];
+ int readed;
+
+
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ readed = m_pSSL->read((char *)buffer,2048);
+ if(readed <= 0)
+ {
+ // ssl error....?
+ switch(m_pSSL->getProtocolError(readed))
+ {
+ case KviSSL::ZeroReturn:
+ readed = 0;
+ break;
+ case KviSSL::WantRead:
+ return selectForRead(m_uConnectionTimeout);
+ break;
+ case KviSSL::WantWrite:
+ return selectForWrite(m_uConnectionTimeout);
+ break;
+ case KviSSL::SyscallError:
+ {
+ int iE = m_pSSL->getLastError(true);
+ if(iE != 0)return sslFailure();
+ }
+ break;
+ case KviSSL::SSLError:
+ return sslFailure();
+ break;
+ default:
+ return sslFailure();
+ break;
+ }
+ }
+ } else {
+#endif
+ readed = kvi_socket_read(m_sock,buffer,2048);
+#ifdef COMPILE_SSL_SUPPORT
+ }
+#endif
+
+ if(readed > 0)
+ {
+ postEvent(m_pRequest,new KviThreadDataEvent<KviDataBuffer>(KVI_THREAD_EVENT_BINARYDATA,new KviDataBuffer(readed,buffer)));
+ } else {
+ if(readed < 0)
+ {
+ // Read error ?
+ int err = kvi_socket_error();
+#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
+ if((err != EAGAIN) && (err != EINTR) && (err != WSAEWOULDBLOCK))
+#else
+ if((err != EAGAIN) && (err != EINTR))
+#endif
+ {
+ // yes...read error
+ return failure(KviError::getUntranslatedDescription(KviError::translateSystemError(err)));
+ }
+ return selectForRead(m_uConnectionTimeout); // EINTR or EAGAIN...transient problem
+ } else {
+ // readed == 0
+ // Connection closed by remote host
+ postEvent(m_pRequest,new KviThreadEvent(KVI_THREAD_EVENT_SUCCESS));
+ return false;
+ }
+ }
+ return selectForRead(m_uConnectionTimeout);
+}
+
+void KviHttpRequestThread::run()
+{
+ // setup:
+ // nothing needed
+
+ // run:
+ runInternal();
+
+ // cleanup:
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ delete m_pSSL;
+ m_pSSL = 0;
+ }
+#endif
+
+ if(kvi_socket_isValid(m_sock))
+ {
+ kvi_socket_close(m_sock);
+ m_sock = KVI_INVALID_SOCKET;
+ }
+}
+
+void KviHttpRequestThread::runInternal()
+{
+#ifndef COMPILE_SSL_SUPPORT
+ if(m_bUseSSL)
+ {
+ failure(__tr_no_lookup("This KVIrc executable has no SSL support"));
+ return;
+ }
+#endif
+
+ if(!connectToRemoteHost())return;
+
+ postEvent(m_pRequest,new KviThreadEvent(KVI_HTTP_REQUEST_THREAD_EVENT_CONNECTED));
+
+ // FIXME: Other headers ?
+
+ KviCString szMethod;
+ switch(m_eRequestMethod)
+ {
+ case Head: szMethod = "HEAD"; break;
+ case Post: szMethod = "POST"; break;
+ case Get: szMethod = "GET"; break;
+ }
+
+ 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(),KviQString::toUtf8(m_szPath).data(),KviQString::toUtf8(m_szHost).data());
+
+ if(m_uContentOffset > 0)
+ szRequest.append(KviCString::Format,"Range: bytes=%u-\r\n",m_uContentOffset);
+
+ if(m_eRequestMethod == Post)
+ {
+ 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(m_eRequestMethod == Post)
+ {
+ if(!m_szPostData.isEmpty())
+ szRequest.append(m_szPostData);
+ szRequest += "\r\n";
+ }
+
+ //qDebug("SENDING REQUEST:\n%s",szRequest.ptr());
+
+ if(!sendBuffer(szRequest.ptr(),szRequest.len(),60))return;
+
+ // now loop reading data
+ postEvent(m_pRequest,new KviThreadDataEvent<QString>(KVI_HTTP_REQUEST_THREAD_EVENT_REQUESTSENT,new QString(szRequest)));
+
+ for(;;)
+ {
+ if(!readDataStep())return;
+ }
+}
+
diff --git a/src/kvilib/net/KviHttpRequestThread.h b/src/kvilib/net/KviHttpRequestThread.h
new file mode 100644
index 000000000..b1c024c6e
--- /dev/null
+++ b/src/kvilib/net/KviHttpRequestThread.h
@@ -0,0 +1,100 @@
+#ifndef _KviHttpRequestThread_h_
+#define _KviHttpRequestThread_h_
+//=============================================================================
+//
+// File : KviHttpRequestThread.h
+// Creation date : Sat Aug 17 13:43:31 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 opinion) 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 "kvi_settings.h"
+
+#include "KviQString.h"
+#include "KviThread.h"
+#include "kvi_sockettype.h"
+
+#ifdef COMPILE_SSL_SUPPORT
+ class KviSSL;
+#endif //COMPILE_SSL_SUPPORT
+
+class KviHttpRequest;
+
+class KviHttpRequestThread : public KviSensitiveThread
+{
+ friend class KviHttpRequest;
+public:
+ enum RequestMethod { Post, Get, Head };
+protected:
+ KviHttpRequestThread(KviHttpRequest * r,
+ const QString &szHost,
+ const QString &szIp,
+ unsigned short uPort,
+ const QString &szPath,
+ unsigned int uContentOffset,
+ RequestMethod m,
+ const QString &szPostData = QString(),
+ bool bUseSSL = false
+ );
+
+public:
+ ~KviHttpRequestThread();
+protected:
+ KviHttpRequest * m_pRequest;
+
+ QString m_szHost;
+ QString m_szIp;
+ QString m_szPath;
+ unsigned int m_uContentOffset;
+ RequestMethod m_eRequestMethod;
+ QString m_szPostData;
+
+ unsigned short m_uPort;
+ kvi_socket_t m_sock;
+ bool m_bUseSSL;
+ unsigned int m_uConnectionTimeout;
+#ifdef COMPILE_SSL_SUPPORT
+ KviSSL * m_pSSL;
+#endif //COMPILE_SSL_SUPPORT
+protected:
+ void setConnectionTimeout(unsigned int uTimeout)
+ {
+ m_uConnectionTimeout = uTimeout;
+ if(m_uConnectionTimeout < 5)
+ m_uConnectionTimeout = 5; // keep it sane
+ }
+ int selectForReadStep();
+ bool selectForRead(int iTimeoutInSecs);
+ bool readDataStep();
+ bool sendBuffer(const char *buffer,int bufLen,int iTimeoutInSecs);
+ bool failure(const char *error=0);
+ bool sslFailure();
+ bool selectForWrite(int iTimeoutInSecs);
+ bool connectToRemoteHost();
+ bool processInternalEvents();
+ void runInternal();
+ virtual void run();
+}; // class KviHttpRequestThread
+
+
+#define KVI_HTTP_REQUEST_THREAD_EVENT_CONNECTED (KVI_THREAD_USER_EVENT_BASE + 0xCAFE)
+#define KVI_HTTP_REQUEST_THREAD_EVENT_REQUESTSENT (KVI_THREAD_USER_EVENT_BASE + 0xCAFF)
+
+
+#endif //_KviHttpRequestThread_h_