diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
commit | a6d58bb6052ac8cb01805a48c4ad2f129126116f (patch) | |
tree | dd867a099fcbb263a8009a9fb22695b87855dad6 /src/kvilib/net | |
download | kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip |
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/kvilib/net')
-rw-r--r-- | src/kvilib/net/Makefile.am | 5 | ||||
-rw-r--r-- | src/kvilib/net/kvi_dns.cpp | 450 | ||||
-rw-r--r-- | src/kvilib/net/kvi_dns.h | 142 | ||||
-rw-r--r-- | src/kvilib/net/kvi_http.cpp | 1440 | ||||
-rw-r--r-- | src/kvilib/net/kvi_http.h | 209 | ||||
-rw-r--r-- | src/kvilib/net/kvi_netutils.cpp | 1504 | ||||
-rw-r--r-- | src/kvilib/net/kvi_netutils.h | 104 | ||||
-rw-r--r-- | src/kvilib/net/kvi_socket.cpp | 31 | ||||
-rw-r--r-- | src/kvilib/net/kvi_socket.h | 356 | ||||
-rw-r--r-- | src/kvilib/net/kvi_sockettype.h | 45 | ||||
-rw-r--r-- | src/kvilib/net/kvi_ssl.cpp | 687 | ||||
-rw-r--r-- | src/kvilib/net/kvi_ssl.h | 180 | ||||
-rw-r--r-- | src/kvilib/net/kvi_url.cpp | 164 | ||||
-rw-r--r-- | src/kvilib/net/kvi_url.h | 63 | ||||
-rw-r--r-- | src/kvilib/net/moc_kvi_dns.cpp | 137 | ||||
-rw-r--r-- | src/kvilib/net/moc_kvi_http.cpp | 263 |
16 files changed, 5780 insertions, 0 deletions
diff --git a/src/kvilib/net/Makefile.am b/src/kvilib/net/Makefile.am new file mode 100644 index 00000000..c84487eb --- /dev/null +++ b/src/kvilib/net/Makefile.am @@ -0,0 +1,5 @@ +############################################################################### +# KVirc IRC client Makefile - 16.12.98 Szymon Stefanek <[email protected]> +############################################################################### + +EXTRA_DIST = *.cpp *.h diff --git a/src/kvilib/net/kvi_dns.cpp b/src/kvilib/net/kvi_dns.cpp new file mode 100644 index 00000000..faa2e126 --- /dev/null +++ b/src/kvilib/net/kvi_dns.cpp @@ -0,0 +1,450 @@ +//============================================================================= +// +// File : kvi_dns.cpp +// Creation date : Sat Jul 21 2000 17:19:31 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2000-2007 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. +// +//============================================================================= +#define __KVILIB__ + +#include "kvi_dns.h" +#include "kvi_error.h" +#include "kvi_netutils.h" + +#include <errno.h> + +#ifdef COMPILE_ON_WINDOWS + #include <winsock2.h> + + #ifdef COMPILE_IPV6_SUPPORT + #ifdef WIN2K + #include <ws2ip6.h> + #else + #include <ws2tcpip.h> + //#include <tpipv6.h> + #endif + #endif +#else + #include <sys/types.h> + #include <sys/socket.h> + #include <netdb.h> +#endif + +// this is for FreeBSD +#ifndef EAI_ADDRFAMILY + #define EAI_ADDRFAMILY EAI_FAMILY +#endif + +#ifndef EAI_NODATA + #define EAI_NODATA 0 +#endif + + + +KviDnsResult::KviDnsResult() +{ + m_iError = KviError_success; + m_pHostnameList = new KviPointerList<QString>; + m_pHostnameList->setAutoDelete(true); + m_pIpAddressList = new KviPointerList<QString>; + m_pIpAddressList->setAutoDelete(true); + +} + +KviDnsResult::~KviDnsResult() +{ + delete m_pHostnameList; + delete m_pIpAddressList; +} + +void KviDnsResult::appendHostname(const QString &host) +{ + m_pHostnameList->append(new QString(host)); +} + + +void KviDnsResult::appendAddress(const QString &addr) +{ + m_pIpAddressList->append(new QString(addr)); +} + + + +KviDnsThread::KviDnsThread(KviDns * pDns) +{ + m_pParentDns = pDns; +} + +KviDnsThread::~KviDnsThread() +{ +} + +int KviDnsThread::translateDnsError(int iErr) +{ +#if defined(COMPILE_IPV6_SUPPORT) || !defined(COMPILE_ON_WINDOWS) + + switch(iErr) + { + case EAI_FAMILY: return KviError_unsupportedAddressFamily; break; +#if !defined(COMPILE_ON_WINDOWS) && defined(EAI_ADDRFAMILY) && (EAI_ADDRFAMILY != EAI_FAMILY) + case EAI_ADDRFAMILY: return KviError_unsupportedAddressFamily; break; +#endif +// NOT FreeBSD ARE WE? +#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) +// YARR + case EAI_NODATA: return KviError_validNameButNoIpAddress; break; +#endif + case EAI_FAIL: return KviError_unrecoverableNameserverError; break; + case EAI_AGAIN: return KviError_dnsTemporaneousFault; break; + // this should never happen + case EAI_BADFLAGS: return KviError_dnsInternalErrorBadFlags; break; + case EAI_MEMORY: return KviError_dnsInternalErrorOutOfMemory; break; + // got this when experimenting with protocols + case EAI_SERVICE: return KviError_dnsInternalErrorServiceNotSupported; break; +#ifndef COMPILE_ON_WINDOWS + case EAI_NONAME: return KviError_dnsNoName; break; +#endif + // got this when experimenting with protocols + case EAI_SOCKTYPE: return KviError_dnsInternalErrorUnsupportedSocketType; break; +#ifndef COMPILE_ON_WINDOWS + case EAI_SYSTEM: return -errno; +#endif + } + +#endif + return KviError_dnsQueryFailed; +} + +void KviDnsThread::postDnsError(KviDnsResult * dns,int iErr) +{ + dns->setError(iErr); + KviThreadDataEvent<KviDnsResult> * e = new KviThreadDataEvent<KviDnsResult>(KVI_DNS_THREAD_EVENT_DATA); + e->setData(dns); + postEvent(m_pParentDns,e); +} + +void KviDnsThread::run() +{ + KviDnsResult * dns = new KviDnsResult(); + + dns->setQuery(m_szQuery); + + if(m_szQuery.isEmpty()) + { + postDnsError(dns,KviError_noHostToResolve); + return; + } + +#ifndef COMPILE_IPV6_SUPPORT + if(m_queryType != KviDns::IpV4) + { + if(m_queryType == KviDns::IpV6) + { + postDnsError(dns,KviError_noIpV6Support); + return; + } + m_queryType = KviDns::IpV4; + } +#endif + +#if defined(COMPILE_ON_WINDOWS) && !defined(COMPILE_IPV6_SUPPORT) + + if(m_queryType == KviDns::IpV6) + { + postDnsError(dns,KviError_noIpV6Support); + return; + } + + // gethostbyaddr and gethostbyname are thread-safe on Windoze + struct in_addr inAddr; + struct hostent *pHostEntry = 0; + + + // DIE DIE!....I hope that this stuff will disappear sooner or later :) + + if(KviNetUtils::stringIpToBinaryIp(m_szQuery,&inAddr)) + { + pHostEntry = gethostbyaddr((const char *)&inAddr,sizeof(inAddr),AF_INET); + } else { + pHostEntry = gethostbyname(m_szQuery); + } + + if(!pHostEntry) + { + switch(h_errno) + { + case HOST_NOT_FOUND: dns->setError(KviError_hostNotFound); break; + case NO_ADDRESS: dns->setError(KviError_validNameButNoIpAddress); break; + case NO_RECOVERY: dns->setError(KviError_unrecoverableNameserverError); break; + case TRY_AGAIN: dns->setError(KviError_dnsTemporaneousFault); break; + default: dns->setError(KviError_dnsQueryFailed); break; + } + } else { + dns->appendHostname(pHostEntry->h_name); + QString szIp; + KviNetUtils::binaryIpToStringIp(* ((struct in_addr*)(pHostEntry->h_addr)),szIp); + dns->appendAddress(szIp); + + int idx = 1; + while(pHostEntry->h_addr_list[idx]) + { + QString tmp; + KviNetUtils::binaryIpToStringIp(* ((struct in_addr*)(pHostEntry->h_addr_list[idx])),tmp); + if(tmp.hasData())dns->appendAddress(tmp); + ++idx; + } + if(pHostEntry->h_aliases[0]) + { + dns->appendHostname(QString::fromUtf8(pHostEntry->h_aliases[0])); + if(pHostEntry->h_aliases[1])dns->appendHostname(QString::fromUtf8(pHostEntry->h_aliases[1])); + } + } + + +#else //!COMPILE_ON_WINDOWS || COMPILE_IPV6_SUPPORT + + int retVal; + + +//#ifdef HAVE_GETNAMEINFO + struct sockaddr_in ipv4Addr; + +#ifdef COMPILE_IPV6_SUPPORT + struct sockaddr_in6 ipv6Addr; + bool bIsIpV6Ip = false; +#endif + + bool bIsIpV4Ip = KviNetUtils::stringIpToBinaryIp(m_szQuery,(struct in_addr *)&(ipv4Addr.sin_addr)); + +#ifdef COMPILE_IPV6_SUPPORT + if(!bIsIpV4Ip)bIsIpV6Ip = KviNetUtils::stringIpToBinaryIp_V6(m_szQuery,(struct in6_addr *)&(ipv6Addr.sin6_addr)); +#endif + +//#ifdef HAVE_GETNAMEINFO + +#ifdef COMPILE_IPV6_SUPPORT + if(bIsIpV4Ip || bIsIpV6Ip) + { +#else + if(bIsIpV4Ip) + { +#endif + // use getnameinfo... + char retname[1025]; // should be enough.... + +#ifdef COMPILE_IPV6_SUPPORT + if(bIsIpV4Ip) + { +#endif + ipv4Addr.sin_family = AF_INET; + ipv4Addr.sin_port = 0; + // NI_NAMEREQD as last param ? + retVal = getnameinfo((struct sockaddr *)&ipv4Addr,sizeof(ipv4Addr),retname,1025,0,0,NI_NAMEREQD); +#ifdef COMPILE_IPV6_SUPPORT + } else { + ipv6Addr.sin6_family = AF_INET6; + ipv6Addr.sin6_port = 0; + retVal = getnameinfo((struct sockaddr *)&ipv6Addr,sizeof(ipv6Addr),retname,1025,0,0,NI_NAMEREQD); + } +#endif + + if(retVal != 0)dns->setError(translateDnsError(retVal)); + else { + dns->appendHostname(retname); + dns->appendAddress(m_szQuery); + } + + } else { +//#endif //HAVE_GETNAMEINFO + + +//#ifdef COMPILE_IPV6_SUPPORT +// struct in6_addr in6Addr; +//#endif + struct addrinfo * pRet = 0; + struct addrinfo * pNext; + struct addrinfo hints; + hints.ai_flags = 0; //AI_CANONNAME; <-- for IPV6 it makes cannoname to point to the IP address! +#ifdef COMPILE_IPV6_SUPPORT + hints.ai_family = (m_queryType == KviDns::IpV6) ? PF_INET6 : ((m_queryType == KviDns::IpV4) ? PF_INET : PF_UNSPEC); +#else + hints.ai_family = PF_INET; +#endif + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_addrlen = 0; + hints.ai_canonname = 0; + hints.ai_addr = 0; + hints.ai_next = 0; + + retVal = getaddrinfo(KviQString::toUtf8(m_szQuery).data(),0,&hints,&pRet); + + if(retVal != 0)dns->setError(translateDnsError(retVal)); + else { + dns->appendHostname(pRet->ai_canonname ? QString::fromUtf8(pRet->ai_canonname) : m_szQuery); + QString szIp; +#ifdef COMPILE_IPV6_SUPPORT + if(pRet->ai_family == PF_INET6)KviNetUtils::binaryIpToStringIp_V6(((sockaddr_in6 *)(pRet->ai_addr))->sin6_addr,szIp); + else { +#endif + KviNetUtils::binaryIpToStringIp(((sockaddr_in *)(pRet->ai_addr))->sin_addr,szIp); +#ifdef COMPILE_IPV6_SUPPORT + } +#endif + dns->appendAddress(szIp); + + pNext = pRet->ai_next; + while(pNext) + { + QString tmp; +#ifdef COMPILE_IPV6_SUPPORT + if(pNext->ai_family == PF_INET6)KviNetUtils::binaryIpToStringIp_V6(((sockaddr_in6 *)(pNext->ai_addr))->sin6_addr,tmp); + else { +#endif + KviNetUtils::binaryIpToStringIp(((sockaddr_in *)(pNext->ai_addr))->sin_addr,tmp); +#ifdef COMPILE_IPV6_SUPPORT + } +#endif + if(!tmp.isEmpty())dns->appendAddress(tmp); + + if(pNext->ai_canonname) + { + // FIXME: only of not equal to other names ? + dns->appendHostname(QString::fromUtf8(pNext->ai_canonname)); + } + + pNext = pNext->ai_next; + + } + } + if(pRet)freeaddrinfo(pRet); +//#ifdef HAVE_GETNAMEINFO + } +//#endif //HAVE_GETNAMEINFO + +#endif // !COMPILE_ON_WINDOWS + + + KviThreadDataEvent<KviDnsResult> * e = new KviThreadDataEvent<KviDnsResult>(KVI_DNS_THREAD_EVENT_DATA); + e->setData(dns); + postEvent(m_pParentDns,e); +} + + + + +KviDns::KviDns() +: QObject() +{ + m_pSlaveThread = new KviDnsThread(this); + m_pDnsResult = new KviDnsResult(); + m_pAuxData = 0; + m_state = Idle; +} + +KviDns::~KviDns() +{ + if(m_pSlaveThread)delete m_pSlaveThread; // will eventually terminate it (but it will also block us!!!) + KviThreadManager::killPendingEvents(this); + if(m_pDnsResult)delete m_pDnsResult; + if(m_pAuxData)debug("You're leaking memory man! m_pAuxData is non 0!"); +} + + +bool KviDns::isRunning() const +{ + return (m_state == Busy); +}; + +bool KviDns::lookup(const QString &query,QueryType type) +{ + if(m_state == Busy)return false; + m_pSlaveThread->setQuery(KviQString::trimmed(query),type); + bool bStarted = m_pSlaveThread->start(); + m_state = bStarted ? Busy : Failure; + return bStarted; +} + +int KviDns::error() +{ + if(!m_pDnsResult)return KviError_dnsQueryFailed; + return m_pDnsResult->error(); +} + +KviDnsResult * KviDns::result() +{ + if(!m_pDnsResult)m_pDnsResult = new KviDnsResult(); + return m_pDnsResult; +} + +KviPointerList<QString> * KviDns::hostnameList() +{ + return result()->hostnameList(); +} + +KviPointerList<QString> * KviDns::ipAddressList() +{ + return result()->ipAddressList(); +} + +int KviDns::hostnameCount() +{ + return result()->hostnameList()->count(); +} + +int KviDns::ipAddressCount() +{ + return result()->ipAddressList()->count(); +} + +const QString & KviDns::firstHostname() +{ + QString * pStr = result()->hostnameList()->first(); + if(pStr)return *pStr; + return KviQString::empty; +} + +const QString & KviDns::firstIpAddress() +{ + QString * pStr = result()->ipAddressList()->first(); + if(pStr)return *pStr; + return KviQString::empty; +} + +const QString & KviDns::query() +{ + return result()->query(); +} + +bool KviDns::event(QEvent *e) +{ + if(e->type() == KVI_THREAD_EVENT) + { + if(((KviThreadEvent *)e)->id() == KVI_DNS_THREAD_EVENT_DATA) + { + if(m_pDnsResult)delete m_pDnsResult; + m_pDnsResult = ((KviThreadDataEvent<KviDnsResult> *)e)->getData(); + m_state = (m_pDnsResult->error() == KviError_success) ? Success : Failure; + emit lookupDone(this); + return true; + } // else ops... unknown thread event ? + } + return QObject::event(e); +} + diff --git a/src/kvilib/net/kvi_dns.h b/src/kvilib/net/kvi_dns.h new file mode 100644 index 00000000..3f423c24 --- /dev/null +++ b/src/kvilib/net/kvi_dns.h @@ -0,0 +1,142 @@ +#ifndef _KVI_DNS_H_ +#define _KVI_DNS_H_ + +//============================================================================= +// +// File : kvi_dns.h +// Creation date : Sat Jul 21 2000 13:59:11 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2007 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 "kvi_heapobject.h" +#include "kvi_thread.h" +#include "kvi_qstring.h" + + +class KviDnsThread; // not part of the API + + +class KVILIB_API KviDnsResult : public KviHeapObject +{ + friend class KviDns; + friend class KviDnsThread; +protected: + KviDnsResult(); +public: + ~KviDnsResult(); +protected: + int m_iError; + KviPointerList<QString> * m_pHostnameList; + KviPointerList<QString> * m_pIpAddressList; + QString m_szQuery; +public: + int error(){ return m_iError; }; + // never store nor delete these pointers! + // (these are NEVER 0) + KviPointerList<QString> * hostnameList(){ return m_pHostnameList; }; + KviPointerList<QString> * ipAddressList(){ return m_pIpAddressList; }; + const QString &query(){ return m_szQuery; }; +protected: + void setError(int iError){ m_iError = iError; }; + void setQuery(const QString &query){ m_szQuery = query; }; + void appendHostname(const QString &host); + void appendAddress(const QString &addr); +}; + + + +class KVILIB_API KviDns : public QObject, public KviHeapObject +{ + Q_OBJECT + Q_PROPERTY(bool blockingDelete READ isRunning) +public: + KviDns(); + ~KviDns(); +public: + enum QueryType { IpV4 , IpV6 , Any }; + enum State { Idle , Busy , Failure , Success }; +protected: + void * m_pAuxData; + KviDnsThread * m_pSlaveThread; + KviDnsResult * m_pDnsResult; + State m_state; +public: + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Public interface + // + + // Lookup start + bool lookup(const QString &szQuery,QueryType type); + + // Current object state + State state(){ return m_state; }; + + // Results (return always non null-data..but valid results only if state() == Success or Failure) + int error(); + const QString & firstHostname(); + const QString & firstIpAddress(); + int hostnameCount(); + int ipAddressCount(); + KviPointerList<QString> * hostnameList(); + KviPointerList<QString> * ipAddressList(); + const QString & query(); + bool isRunning() const; + + // Auxiliary data store + void setAuxData(void * pAuxData){ m_pAuxData = pAuxData; }; + void * releaseAuxData(){ void * pData = m_pAuxData; m_pAuxData = 0; return pData; }; +protected: + virtual bool event(QEvent *e); +private: + KviDnsResult * result(); +signals: + void lookupDone(KviDns *); +}; + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// INTERNAL CLASSES +// + +#define KVI_DNS_THREAD_EVENT_DATA (KVI_THREAD_USER_EVENT_BASE + 7432) + +class KviDnsThread : public KviThread +{ + friend class KviDns; +protected: + KviDnsThread(KviDns * pDns); + ~KviDnsThread(); +protected: + QString m_szQuery; + KviDns::QueryType m_queryType; + KviDns * m_pParentDns; +public: + void setQuery(const QString &query,KviDns::QueryType type){ m_szQuery = query; m_queryType = type; }; +protected: + virtual void run(); + int translateDnsError(int iErr); + void postDnsError(KviDnsResult * dns,int iErr); +}; + + +#endif //_KVI_DNS_H_ diff --git a/src/kvilib/net/kvi_http.cpp b/src/kvilib/net/kvi_http.cpp new file mode 100644 index 00000000..2e94abbe --- /dev/null +++ b/src/kvilib/net/kvi_http.cpp @@ -0,0 +1,1440 @@ +//============================================================================= +// +// File : kvi_http.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-2006 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. +// +//============================================================================= + +#define __KVILIB__ + + +#include <qdir.h> +#include <qtimer.h> +//#include <zlib.h> + +#include "kvi_http.h" +#include "kvi_locale.h" +#include "kvi_netutils.h" +#include "kvi_dns.h" +#include "kvi_error.h" +#include "kvi_debug.h" +#include "kvi_socket.h" +#include "kvi_time.h" +#ifdef COMPILE_SSL_SUPPORT + #include "kvi_ssl.h" +#endif + + +#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) + +KviHttpRequest::KviHttpRequest() +: QObject() +{ + m_pDns = 0; + m_pThread = 0; + m_pFile = 0; + m_pPrivateData = 0; + m_bHeaderProcessed = false; + m_pBuffer = new KviDataBuffer(); + + resetStatus(); + resetData(); +} + +KviHttpRequest::~KviHttpRequest() +{ + resetInternalStatus(); + delete m_pBuffer; +} + +void KviHttpRequest::abort() +{ + resetInternalStatus(); + m_szLastError = __tr2qs("Aborted"); + emit terminated(false); +} + +void KviHttpRequest::resetInternalStatus() +{ + if(m_pThread)delete m_pThread; + if(m_pDns)delete m_pDns; + + m_pDns = 0; + m_pThread = 0; + + if(!m_pFile)return; + m_pFile->close(); + delete m_pFile; + m_pFile = 0; + + m_pBuffer->clear(); + m_bHeaderProcessed = false; + + KviThreadManager::killPendingEvents(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 = ""; + m_uMaxContentLength = 0; + m_uContentOffset = 0; + m_bChunkedTransferEncoding = false; + m_bGzip = false; + m_bIgnoreRemainingData = false; + m_uRemainingChunkSize = 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_url.host().isEmpty()) + { + resetInternalStatus(); + m_szLastError = __tr2qs("Invalid URL: Missing hostname"); + return false; + } + + if((!kvi_strEqualCI(m_url.protocol().ptr(),"http")) && (!kvi_strEqualCI(m_url.protocol().ptr(),"https"))) + { + resetInternalStatus(); + m_szLastError=__tr2qs("Unsupported protocol %1").arg(m_url.protocol().ptr()); + return false; + } + + if(kvi_isValidStringIp(m_url.host().ptr())) + { + m_szIp = m_url.host(); + QTimer::singleShot(10,this,SLOT(haveServerIp())); + return true; + } + + return startDnsLookup(); +} + +bool KviHttpRequest::startDnsLookup() +{ + m_pDns = new KviDns(); + connect(m_pDns,SIGNAL(lookupDone(KviDns *)),this,SLOT(dnsLookupDone(KviDns *))); + + if(!m_pDns->lookup(m_url.host().ptr(),KviDns::IpV4)) + { + resetInternalStatus(); + m_szLastError = __tr2qs("Unable to start the DNS lookup"); + return false; + } + + QString tmp; + KviQString::sprintf(tmp,__tr2qs("Looking up host %s"),m_url.host().ptr()); + emit status(tmp); // FIXME + + emit resolvingHost(QString(m_url.host().ptr())); + + return true; +} + +void KviHttpRequest::dnsLookupDone(KviDns *d) +{ + if(d->state() == KviDns::Success) + { + m_szIp = d->firstIpAddress(); + delete m_pDns; + m_pDns = 0; + QString tmp; + KviQString::sprintf(tmp,__tr2qs("Host %s resolved to %Q"),m_url.host().ptr(),&m_szIp); + emit status(tmp); + haveServerIp(); + } else { + int iErr = d->error(); + resetInternalStatus(); + m_szLastError = KviError::getDescription(iErr); + emit terminated(false); + } +} + +void KviHttpRequest::haveServerIp() +{ + unsigned short uPort = m_url.port(); + if(uPort == 0)uPort = 80; + + QString tmp; + KviQString::sprintf(tmp,"%Q:%u",&m_szIp,uPort); + emit contactingHost(tmp); + + if(m_pThread)delete m_pThread; + + m_pThread = new KviHttpRequestThread( + this, + m_url.host().ptr(), + m_szIp, + uPort, + m_url.path().ptr(), + m_uContentOffset, + (m_eProcessingType == HeadersOnly) ? KviHttpRequestThread::Head : (m_szPostData.isEmpty() ? KviHttpRequestThread::Get : KviHttpRequestThread::Post), + m_szPostData, + kvi_strEqualCI(m_url.protocol().ptr(),"https")); + + if(!m_pThread->start()) + { + resetInternalStatus(); + m_szLastError = __tr2qs("Unable to start the request slave thread"); + emit terminated(false); + return; + } + + KviQString::sprintf(tmp,__tr2qs("Contacting host %Q on port %u"),&m_szIp,uPort); + emit status(tmp); +} + +bool KviHttpRequest::event(QEvent *e) +{ + if(e->type() == KVI_THREAD_EVENT) + { + switch(((KviThreadEvent *)e)->id()) + { + case KVI_THREAD_EVENT_BINARYDATA: + { + KviDataBuffer * b = ((KviThreadDataEvent<KviDataBuffer> *)e)->getData(); + processData(b); + delete b; + return true; + } + break; + case KVI_HTTP_REQUEST_THREAD_EVENT_CONNECTED: + emit connectionEstabilished(); + emit status(__tr2qs("Connection established, sending request")); + return true; + break; + case KVI_HTTP_REQUEST_THREAD_EVENT_REQUESTSENT: + { + QString * req = ((KviThreadDataEvent<QString> *)e)->getData(); +#ifdef COMPILE_USE_QT4 + QStringList sl = req->split("\r\n"); +#else + QStringList sl = QStringList::split("\r\n",*req); +#endif + emit requestSent(sl); + delete req; + return true; + } + break; + case KVI_THREAD_EVENT_SUCCESS: + if(!m_pThread && !m_bHeaderProcessed) + { + // the thread has already been deleted + // probably because the response was something like a 404 + // just ignore the event + return true; + } + switch(m_eProcessingType) + { + case WholeFile: + // happens always + emit binaryData(*m_pBuffer); + break; + case Blocks: + // an unprocessed block ?.. should never happend.. but well :D + if(m_pBuffer->size() > 0)emit binaryData(*m_pBuffer); + break; + case Lines: + if(m_pBuffer->size() > 0) + { + // something left in the buffer and has no trailing LF + KviStr tmp((const char *)(m_pBuffer->data()),m_pBuffer->size()); + emit data(tmp); + } + break; + case StoreToFile: + // same as above... should never happen.. but well :D + if(m_pFile && m_pBuffer->size() > 0)m_pFile->writeBlock((const char *)(m_pBuffer->data()),m_pBuffer->size()); + break; + default: + // nothing... just make gcc happy + break; + } + resetInternalStatus(); + m_szLastError = __tr2qs("Success"); + emit terminated(true); + return true; + break; + case KVI_THREAD_EVENT_ERROR: + { + KviStr * err = ((KviThreadDataEvent<KviStr> *)e)->getData(); + m_szLastError = __tr2qs_no_xgettext(err->ptr()); + delete err; + resetInternalStatus(); + emit terminated(false); + return true; + } + break; + case KVI_THREAD_EVENT_MESSAGE: + { + KviStr * msg = ((KviThreadDataEvent<KviStr> *)e)->getData(); + emit status(__tr2qs_no_xgettext(msg->ptr())); + delete msg; + return true; + } + break; + } + } + return QObject::event(e); +} + +void KviHttpRequest::emitLines(KviDataBuffer * pDataBuffer) +{ + int idx = pDataBuffer->find((const unsigned char *)"\n",1); + while(idx != -1) + { + KviStr tmp((const char *)(m_pBuffer->data()),idx); + tmp.stripRight('\r'); + pDataBuffer->remove(idx + 1); + idx = pDataBuffer->find((const unsigned char *)"\n",1); + emit data(tmp); + } +} + +// header += "Accept: "; +// QString acceptHeader = metaData("accept"); +// if (!acceptHeader.isEmpty()) +// header += acceptHeader; +// else +// header += DEFAULT_ACCEPT_HEADER; +// header += "\r\n"; +// +//#ifdef DO_GZIP +// if (m_request.allowCompressedPage) +// header += "Accept-Encoding: x-gzip, x-deflate, gzip, deflate, identity\r\n"; +//#endif +// +// if (!m_request.charsets.isEmpty()) +// header += "Accept-Charset: " + m_request.charsets + "\r\n"; +// +// if (!m_request.languages.isEmpty()) +// header += "Accept-Language: " + m_request.languages + "\r\n"; +// +// +// /* support for virtual hosts and required by HTTP 1.1 */ +// header += "Host: "; +// header += "Pragma: no-cache\r\n"; /* for HTTP/1.0 caches */ +// header += "Cache-control: no-cache\r\n"; /* for HTTP >=1.1 caches */ + +// header += "Referer: "; //Don't try to correct spelling! +// header += m_request.referrer; +// header += "\r\n"; +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_pFile = new KviFile(m_szFileName); + + if(!m_pFile->openForWriting(bAppend)) + { + resetInternalStatus(); + KviQString::sprintf(m_szLastError,__tr2qs("Can't open file \"%Q\" for writing"),&m_szFileName); + emit terminated(false); + return false; + } + + return true; +} + + + + + +bool KviHttpRequest::processHeader(KviStr &szHeader) +{ + int idx = szHeader.findFirstIdx("\r\n"); + KviStr szResponse; + if(idx != -1) + { + szResponse = szHeader.left(idx); + szHeader.cutLeft(idx + 2); + } else { + szResponse = szHeader; + szHeader = ""; + } + + szResponse.stripWhiteSpace(); + + bool bValid = false; + + unsigned int uStatus = 0; + + // check the response value + if(kvi_strEqualCSN(szResponse.ptr(),"HTTP",4)) + { + KviStr szR = szResponse; + szR.cutToFirst(' '); + szR.stripWhiteSpace(); + int idx = szR.findFirstIdx(' '); + KviStr szNumber; + if(idx != -1)szNumber = szR.left(idx); + else szNumber = szR; + bool bOk; + uStatus = szNumber.toUInt(&bOk); + if(bOk)bValid = true; + } + + if(!bValid) + { + // the response is invalid ? + resetInternalStatus(); + m_szLastError=__tr2qs("Invalid HTTP response: %s").arg(szResponse.ptr()); + emit terminated(false); + return false; + } + + QString tmp; + KviQString::sprintf(tmp,__tr2qs("Received HTTP response: %s"),szResponse.ptr()); + + emit status(tmp); + emit receivedResponse(QString(szResponse.ptr())); + + KviPointerList<KviStr> hlist; + hlist.setAutoDelete(true); + + idx = szHeader.findFirstIdx("\r\n"); + while(idx != -1) + { + if(idx > 0) + { + hlist.append(new KviStr(szHeader.ptr(),idx)); + szHeader.cutLeft(idx + 2); + } + idx = szHeader.findFirstIdx("\r\n"); + } + if(szHeader.hasData())hlist.append(new KviStr(szHeader)); + + KviPointerHashTable<const char *,KviStr> hdr(11,false,true); + hdr.setAutoDelete(true); + + for(KviStr * s = hlist.first();s;s = hlist.next()) + { + idx = s->findFirstIdx(":"); + if(idx != -1) + { + KviStr szName = s->left(idx); + s->cutLeft(idx + 1); + s->stripWhiteSpace(); + hdr.replace(szName.ptr(),new KviStr(*s)); + //debug("FOUND HEADER (%s)=(%s)",szName.ptr(),s->ptr()); + } + } + + KviStr * size = hdr.find("Content-length"); + if(size) + { + bool bOk; + m_uTotalSize = size->toUInt(&bOk); + if(!bOk)m_uTotalSize = 0; + } + + KviStr * contentEncoding = hdr.find("Content-encoding"); + if(contentEncoding) + { + m_bGzip = contentEncoding->equalsCI("gzip"); + } + + KviStr * 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; + } + } + + emit header(&hdr); + + // 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: // ??? + // 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) && (uStatus != 206)) + { + // this is not "OK" and not "Partial content" + // Error , redirect or something confusing + if(m_eProcessingType != HeadersOnly) + { + // this is an error then + resetInternalStatus(); + m_szLastError = szResponse.ptr(); + emit terminated(false); + return false; + } // else the server will terminate (it was a HEAD request) + } + + if((m_uMaxContentLength > 0) && (m_uTotalSize > ((unsigned int)m_uMaxContentLength))) + { + resetInternalStatus(); + m_szLastError=__tr2qs("Stream exceeding 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_pBuffer->append(*data); + + int idx = m_pBuffer->find((const unsigned char *)"\r\n\r\n",4); + if(idx == -1) + { + // header not complete + if(m_pBuffer->size() > 4096) + { + resetInternalStatus(); + m_szLastError = __tr2qs("Header too long: exceeded 4096 bytes"); + emit terminated(false); + } + return; + } + KviStr szHeader((const char *)(m_pBuffer->data()),idx); + m_pBuffer->remove(idx + 4); + + if(!processHeader(szHeader))return; + m_bHeaderProcessed = true; + + if(m_eProcessingType == StoreToFile) + { + if(!openFile())return; + } + + m_uReceivedSize = m_pBuffer->size(); + + + // here the header is complete and the eventual remaining data is in m_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_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_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_pFile->writeBlock((const char *)(data->data()),data->size()); + break; + } + + if(((m_uTotalSize > 0) && (m_uReceivedSize > m_uTotalSize)) || ((m_uMaxContentLength > 0) && (m_uReceivedSize > m_uMaxContentLength))) + { + resetInternalStatus(); + m_szLastError=__tr2qs("Stream exceeded expected length"); + emit terminated(false); + } + + return; + } + + // need to append to m_pBuffer and process it + m_pBuffer->append(*data); + } + + // we're processing data in m_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_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 > m_pBuffer->size())uProcessSize = m_pBuffer->size(); + m_uRemainingChunkSize -= uProcessSize; + + switch(m_eProcessingType) + { + case Blocks: + if(m_pBuffer->size() == uProcessSize) + { + // avoid copying to a new buffer + emit binaryData(*m_pBuffer); + } else { + // must copy + KviDataBuffer tmp(uProcessSize,m_pBuffer->data()); + emit binaryData(tmp); + m_pBuffer->remove(uProcessSize); + } + break; + case Lines: + if(m_pBuffer->size() == uProcessSize) + { + // avoid copying to a new buffer + emitLines(m_pBuffer); + } else { + // must copy + KviDataBuffer tmp(uProcessSize,m_pBuffer->data()); + emitLines(&tmp); + m_pBuffer->remove(uProcessSize); + } + break; + case StoreToFile: + m_pFile->writeBlock((const char *)(m_pBuffer->data()),uProcessSize); + m_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_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_pBuffer->remove(2); + } else { + // got a chunk header + KviStr szHeader((const char *)(m_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_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_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_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_pBuffer contains only valid data + switch(m_eProcessingType) + { + case Blocks: + if(m_pBuffer->size() > 0)emit binaryData(*m_pBuffer); + m_pBuffer->clear(); + break; + case Lines: + if(m_pBuffer->size() > 0)emitLines(m_pBuffer); + break; + case StoreToFile: + m_pFile->writeBlock((const char *)(m_pBuffer->data()),m_pBuffer->size()); + m_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("Stream exceeded expected length"); + emit terminated(false); + } + return; +} + + + + + + + + + + + + + + + + +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; +#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: + debug("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<KviStr>(KVI_THREAD_EVENT_ERROR,new KviStr(error))); + } /*else { + postEvent(m_pRequest,new KviThreadDataEvent<KviStr>(KVI_THREAD_EVENT_ERROR,new KviStr(__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(); +#ifdef COMPILE_ON_WINDOWS + 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 + KviStr 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 + + //#warning "This should be a tuneable timeout" + if(!selectForWrite(60))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(60))return false; + break; + case KviSSL::WantWrite: + if(!selectForWrite(60))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(60))return false; + break; + case KviSSL::WantRead: + if(!selectForRead(60))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(); +#ifdef COMPILE_ON_WINDOWS + 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(); +#ifdef COMPILE_ON_WINDOWS + 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(120); + break; + case KviSSL::WantWrite: + return selectForWrite(120); + 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(); +#ifdef COMPILE_ON_WINDOWS + 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(120); // 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(120); +} + +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 ? + + KviStr szMethod; + switch(m_eRequestMethod) + { + case Head: szMethod = "HEAD"; break; + case Post: szMethod = "POST"; break; + case Get: szMethod = "GET"; break; + } + + KviStr szRequest(KviStr::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(KviStr::Format,"Range: bytes=%u-\r\n",m_uContentOffset); + + if(m_eRequestMethod == Post) + { + szRequest.append(KviStr::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"; + } + + //debug("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/kvi_http.h b/src/kvilib/net/kvi_http.h new file mode 100644 index 00000000..1bd6a9d9 --- /dev/null +++ b/src/kvilib/net/kvi_http.h @@ -0,0 +1,209 @@ +#ifndef _KVI_HTTP_H_ +#define _KVI_HTTP_H_ +//============================================================================= +// +// File : kvi_http.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-2007 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. ,59 Temple Place - Suite 33, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_heapobject.h" +#include "kvi_string.h" +#include "kvi_thread.h" +#include "kvi_sockettype.h" +#include "kvi_databuffer.h" +#include "kvi_inttypes.h" +#include "kvi_url.h" + +#include <qobject.h> +#include "kvi_pointerhashtable.h" +#include "kvi_file.h" +#include <qstringlist.h> + +class KviDns; +class KviSSL; +class KviHttpRequestThread; + +// +// This class implements a HTTP protocol client. +// It's able to send GET, POST and HEAD requests, +// download stuff to a file or to a qt SLOT(). +// + +class KVILIB_API KviHttpRequest : public QObject, public KviHeapObject +{ + Q_OBJECT +public: + enum ProcessingType + { + HeadersOnly, // Download headers only (HEAD request) + WholeFile, // Emit the data as whole file (binaryData() is emitted) + Blocks, // Emit the data as blocks (binaryData() is emitted) + Lines, // Emit the data as ASCII text lines (the client must take care of decoding the data) + StoreToFile // Store the data to a file + }; + enum ExistingFileAction + { + Overwrite, // Overwrite existing file + RenameIncoming, // Automatically rename the incoming file + RenameExisting, // Automatically rename the existing file + Resume // Attempt to resume the file (get partial content) + }; +public: + KviHttpRequest(); + ~KviHttpRequest(); +protected: + // data + KviUrl m_url; + QString m_szFileName; + ProcessingType m_eProcessingType; + ExistingFileAction m_eExistingFileAction; + void * m_pPrivateData; + unsigned int m_uMaxContentLength; + unsigned int m_uContentOffset; + QString m_szPostData; + // status + QString m_szLastError; + unsigned int m_uTotalSize; + unsigned int m_uReceivedSize; + // internal status + QString m_szIp; + KviDns * m_pDns; + KviHttpRequestThread * m_pThread; + KviDataBuffer * m_pBuffer; + bool m_bHeaderProcessed; + bool m_bChunkedTransferEncoding; + bool m_bGzip; + unsigned int m_uRemainingChunkSize; + bool m_bIgnoreRemainingData; // used in chunked transfer after the last chunk has been seen + KviFile * m_pFile; +protected: + bool startDnsLookup(); + virtual bool event(QEvent *e); + void processData(KviDataBuffer * data); + bool processHeader(KviStr &szHeader); + bool openFile(); + void emitLines(KviDataBuffer * pDataBuffer); + + void resetStatus(); + void resetData(); + void resetInternalStatus(); +protected slots: + void dnsLookupDone(KviDns *d); + void haveServerIp(); +public: + const KviUrl & url(){ return m_url; }; + ProcessingType processingType(){ return m_eProcessingType; }; + ExistingFileAction existingFileAction(){ return m_eExistingFileAction; }; + const QString &fileName(){ return m_szFileName; }; + void * privateData(){ return m_pPrivateData; }; + unsigned int maxContentLength(){ return m_uMaxContentLength; }; + unsigned int contentOffset(){ return m_uContentOffset; }; + unsigned int totalSize(){ return m_uTotalSize; }; + unsigned int receivedSize(){ return m_uReceivedSize; }; + + void reset(); + + void setPostData(const QString &szPostData){ m_szPostData = szPostData; }; + void setUrl(const KviUrl &u){ m_url = u; }; + void setProcessingType(ProcessingType t){ m_eProcessingType = t; }; + void setExistingFileAction(ExistingFileAction a){ m_eExistingFileAction = a; }; + void setFileName(const QString &szFileName){ m_szFileName = szFileName; }; + void setPrivateData(void * ptr){ m_pPrivateData = ptr; }; + void setMaxContentLength(int uMaxContentLength){ m_uMaxContentLength = uMaxContentLength; }; //0 means unlimited + // this will work regardless of ExistingFileAction : even if the file doesn't exist + void setContentOffset(int uContentOffset){ m_uContentOffset = uContentOffset; }; + + bool start(); + + // this is a shortcut for reset()+setUrl()+setProcessingType()+setFileName()+start() + bool get(const KviUrl &u,ProcessingType p = WholeFile,const QString &szFileName = QString::null); + + const QString & lastError(){ return m_szLastError; }; + + void abort(); +signals: + void resolvingHost(const QString &hostname); + void contactingHost(const QString &ipandport); + void connectionEstabilished(); + void receivedResponse(const QString &response); + + void terminated(bool bSuccess); + + + void status(const QString &message); + void data(const KviStr &data); + void binaryData(const KviDataBuffer &data); + void header(KviPointerHashTable<const char *,KviStr> * hdr); + void requestSent(const QStringList &request); +}; + + +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::null, + 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; +#ifdef COMPILE_SSL_SUPPORT + KviSSL * m_pSSL; +#endif +protected: + 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(); +}; + + +#endif //_KVI_HTTP_H_ diff --git a/src/kvilib/net/kvi_netutils.cpp b/src/kvilib/net/kvi_netutils.cpp new file mode 100644 index 00000000..0cdb8b02 --- /dev/null +++ b/src/kvilib/net/kvi_netutils.cpp @@ -0,0 +1,1504 @@ +//============================================================================= + +// + +// File : kvi_netutlis.cpp + +// Creation date : Sun Jun 18 2000 18:37:27 by Szymon Stefanek + +// + +// This file is part of the KVirc irc client distribution + +// Copyright (C) 1999-2000 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. + +// + +//============================================================================= + +// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH! +// This effect is caused by the combination of broken CVS installation and +// the ugly windows "text mode" files + +#define __KVILIB__ + + + + + +#define _KVI_NETUTILS_CPP_ + + + +#include "kvi_netutils.h" + +#include "kvi_memmove.h" +#include <qstringlist.h> + + +#ifndef COMPILE_ON_WINDOWS + +#include <sys/time.h> // struct timeval + +#endif + + + +#include <sys/types.h> + + + +#include "kvi_qstring.h" + + + +#ifndef COMPILE_ON_WINDOWS + + #include <unistd.h> + + #include <netdb.h> + +#endif + + + +#ifdef COMPILE_GET_INTERFACE_ADDRESS + + #include <sys/ioctl.h> + + #include <net/if.h> + +#endif //COMPILE_GET_INTERFACE_ADDRESS + + + +#ifndef HAVE_INET_ATON + + + + + +// FIXME: #warning "Your system lacks the inet_aton function," + +// FIXME: #warning "you're trying to compile this file without" + +// FIXME: #warning "the config.h created by the configure script," + +// FIXME: #warning "Using own internal implementation of inet_aton." + + + +#include <ctype.h> + + + + + +// Need own inet_aton implementation + + + +// + +// Check whether "cp" is a valid ascii representation + +// of an Internet address and convert to a binary address. + +// Returns 1 if the address is valid, 0 if not. + +// This replaces inet_addr, the return value from which + +// cannot distinguish between failure and a local broadcast address. + +// + +// Original code comes from the ircd source. + +// + + + +bool kvi_stringIpToBinaryIp(const char *szIp,struct in_addr *address) + +{ + + register unsigned long val; + + register int base, n; + + register char c; + + unsigned int parts[4]; + + register unsigned int *pp = parts; + + if(!szIp)return false; + + c = *szIp; + + for(;;){ + + // Collect number up to ``.''. + + // Values are specified as for C: + + // 0x=hex, 0=octal, isdigit=decimal. + + if(!isdigit(c))return false; + + val = 0; + + base = 10; + + if(c == '0'){ + + c = *++szIp; + + if((c == 'x')||(c == 'X'))base = 16, c = *++szIp; + + else base = 8; + + } + + for (;;) { + + if(isascii(c) && isdigit(c)) { + + val = (val * base) + (c - '0'); + + c = *++szIp; + + } else if (base == 16 && isascii(c) && isxdigit(c)) { + + val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A')); + + c = *++szIp; + + } else break; + + } + + if(c == '.'){ + + // Internet format: + + // a.b.c.d + + // a.b.c (with c treated as 16 bits) + + // a.b (with b treated as 24 bits) + + if(pp >= (parts + 3)) return false; + + *pp++ = val; + + c = *++szIp; + + } else break; + + } + + // Check for trailing characters. + + if ((c != '\0') && (!isascii(c) || !isspace(c)))return false; + + // Concact the address according to + + // the number of parts specified. + + n = pp - parts + 1; + + switch (n) { + + case 0: return false; // initial nondigit + + case 1: break; // a -- 32 bits + + case 2: // a.b -- 8.24 bits + + if(val > 0xffffff) return false; + + val |= parts[0] << 24; + + break; + + case 3: // a.b.c -- 8.8.16 bits + + if(val > 0xffff)return false; + + val |= (parts[0] << 24) | (parts[1] << 16); + + break; + + case 4: // a.b.c.d -- 8.8.8.8 bits + + if(val > 0xff)return false; + + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + + break; + + } + + if(address)address->s_addr = htonl(val); + + return true; + +} + + + +#else //!HAVE_INET_ATON + + + +bool kvi_stringIpToBinaryIp(const char *szIp,struct in_addr *address) + +{ + + if(!szIp)return false; + + return (inet_aton(szIp,address) != 0); + +} + + + +#endif //!HAVE_INET_ATON + + + +#ifndef HAVE_INET_NTOA + + + +// FIXME: #warning "Your system lacks the inet_ntoa function," + +// FIXME: #warning "you're trying to compile this file without" + +// FIXME: #warning "the config.h created by the configure script," + +// FIXME: #warning "Using own internal implementation of inet_ntoa." + + + +// + +// Original code comes from the ircd source. + +// + + + +bool kvi_binaryIpToStringIp(struct in_addr in,QString &szBuffer) + +{ + + unsigned char *s = (unsigned char *)∈ + + int a,b,c,d; + + a = (int)*s++; + + b = (int)*s++; + + c = (int)*s++; + + d = (int)*s; + + szBuffer.sprintf("%d.%d.%d.%d", a,b,c,d ); + + return true; + +} + + + +#else //HAVE_INET_NTOA + + + +bool kvi_binaryIpToStringIp(struct in_addr in,QString &szBuffer) + +{ + +// FIXME: #warning "This is NOT thread safe!" + + char * ptr = inet_ntoa(in); + + if(!ptr)return false; + + szBuffer = ptr; + + return true; + +} + + + +#endif //HAVE_INET_NTOA + + + +bool kvi_isValidStringIp(const char *szIp) + +{ + + struct in_addr address; + + if(!szIp)return false; + + if(!isdigit(*szIp))return false; + + return kvi_stringIpToBinaryIp(szIp,&address); + +} + + + + + +#ifdef COMPILE_IPV6_SUPPORT + + + +#ifdef COMPILE_ON_WINDOWS + + + +//#include <stdlib.h> + +//#include <sys/socket.h> + +//#include <arpa/inet.h>/ + +//#include <errno.h> + +//#include "dietfeatures.h" + + + +static unsigned int scan_ip6(const char *s,char ip[16]) + +{ + + unsigned int i; + + unsigned int len=0; + + unsigned long u; + + + + char suffix[16]; + + unsigned int prefixlen=0; + + unsigned int suffixlen=0; + + + + for (i=0; i<16; i++) ip[i]=0; + + + + for (;;) { + + if (*s == ':') { + + len++; + + if (s[1] == ':') { /* Found "::", skip to part 2 */ + + s+=2; + + len++; + + break; + + } + + s++; + + } + + { + + char *tmp; + + u=strtoul(s,&tmp,16); + + i=tmp-s; + + } + + + + if (!i) return 0; + + if (prefixlen==12 && s[i]=='.') { + + /* the last 4 bytes may be written as IPv4 address */ + + if (kvi_stringIpToBinaryIp(s,(struct in_addr*)(ip+12))) + + return i+len; + + else + + return 0; + + } + + ip[prefixlen++] = (u >> 8); + + ip[prefixlen++] = (u & 255); + + s += i; len += i; + + if (prefixlen==16) + + return len; + + } + + + +/* part 2, after "::" */ + + for (;;) { + + if (*s == ':') { + + if (suffixlen==0) + + break; + + s++; + + len++; + + } else if (suffixlen!=0) + + break; + + { + + char *tmp; + + u=strtol(s,&tmp,16); + + i=tmp-s; + + } + + if (!i) { + + if (*s) len--; + + break; + + } + + if (suffixlen+prefixlen<=12 && s[i]=='.') { + + if (kvi_stringIpToBinaryIp(s,(struct in_addr*)(suffix+suffixlen))) { + + suffixlen+=4; + + len+=(unsigned int)strlen(s); + + break; + + } else + + prefixlen=12-suffixlen; /* make end-of-loop test true */ + + } + + suffix[suffixlen++] = (u >> 8); + + suffix[suffixlen++] = (u & 255); + + s += i; len += i; + + if (prefixlen+suffixlen==16) + + break; + + } + + for (i=0; i<suffixlen; i++) + + ip[16-suffixlen+i] = suffix[i]; + + return len; + +} + + + +#ifndef WIN2K + + + +int inet_pton(int AF, const char *CP, void *BUF) { + + int len; + + if (AF==AF_INET) { + + if (!kvi_stringIpToBinaryIp(CP,(struct in_addr*)BUF)) + + return 0; + + } else if (AF==AF_INET6) { + + if (CP[len=scan_ip6(CP,(char *)BUF)]) + + return 0; + + } else { + + errno=WSAEPFNOSUPPORT; + + return -1; + + } + + return 1; + +} + + + +#endif //WIN2K + + + +//#include <sys/socket.h> + +//#include <arpa/inet.h> + + + +//extern char *inet_ntoa_r(struct in_addr in,char* buf); + + + +static const unsigned char V4mappedprefix[12]={0,0,0,0,0,0,0,0,0,0,0xff,0xff}; + + + +static char tohex(char hexdigit) { + + return hexdigit>9?hexdigit+'a'-10:hexdigit+'0'; + +} + + + +static int fmt_xlong(char* s,unsigned int i) + +{ + + char* bak=s; + + *s=tohex((i>>12)&0xf); if (s!=bak || *s!='0') ++s; + + *s=tohex((i>>8)&0xf); if (s!=bak || *s!='0') ++s; + + *s=tohex((i>>4)&0xf); if (s!=bak || *s!='0') ++s; + + *s=tohex(i&0xf); + + return s-bak+1; + +} + + + +static unsigned int i2a(char* dest,unsigned int x) + +{ + + register unsigned int tmp=x; + + register unsigned int len=0; + + if (x>=100) { *dest++=tmp/100+'0'; tmp=tmp%100; ++len; } + + if (x>=10) { *dest++=tmp/10+'0'; tmp=tmp%10; ++len; } + + *dest++=tmp+'0'; + + return len+1; + +} + + + +char *inet_ntoa_r(struct in_addr in,char* buf) + +{ + + unsigned int len; + + unsigned char *ip=(unsigned char*)∈ + + len=i2a(buf,ip[0]); buf[len]='.'; ++len; + + len+=i2a(buf+ len,ip[1]); buf[len]='.'; ++len; + + len+=i2a(buf+ len,ip[2]); buf[len]='.'; ++len; + + len+=i2a(buf+ len,ip[3]); buf[len]=0; + + return buf; + +} + + + + + +unsigned int fmt_ip6(char *s,const char ip[16]) + +{ + + unsigned int len; + + unsigned int i; + + unsigned int temp; + + unsigned int compressing; // 0 not compressing , 1 compressing now , 2 already compressed once + + + + len = 0; + + compressing = 0; + + + + for(int j=0;j<16;j+=2) + + { + + if (j==12 && !memcmp(ip,V4mappedprefix,12)) + + { + + inet_ntoa_r(*(struct in_addr*)(ip+12),s); + + temp=(unsigned int)strlen(s); + + return len+temp; + + } + + temp = ((unsigned long) (unsigned char) ip[j] << 8) + (unsigned long) (unsigned char) ip[j+1]; + + if(temp == 0) + + { + + if(compressing == 0) + + { + + compressing=1; + + if (j==0) + + { + + *s++=':'; + + ++len; + + } + + } + + } else { + + if(compressing == 1) + + { + + compressing=2; // don't do it again + + *s++=':'; ++len; + + } + + i = fmt_xlong(s,temp); + + len += i; + + s += i; + + if (j<14) + + { + + *s++ = ':'; + + ++len; + + } + + } + + } + + if(compressing == 1) + + { + + *s++=':'; + + ++len; + + } + + *s=0; + + return len; + +} + + + +const char* inet_ntop(int AF, const void *CP, char *BUF, size_t LEN) + +{ + + char buf[100]; + + size_t len; + + if (AF==AF_INET) + + { + + inet_ntoa_r(*(struct in_addr*)CP,buf); + + len=strlen(buf); + + } else if (AF==AF_INET6) + + { + + len=fmt_ip6(buf,(char *)CP); + + } else + + return 0; + + if (len<LEN) + + { + + strcpy(BUF,buf); + + return BUF; + + } + + return 0; + +} + + + + + + + + + + + +#endif + + + + + +bool kvi_stringIpToBinaryIp_V6(const char *szIp,struct in6_addr *address) + +{ + + if(!szIp)return false; + + return (inet_pton(AF_INET6,szIp,(void *)address) == 1); + +} + + + +bool kvi_isValidStringIp_V6(const char *szIp) + +{ + + struct in6_addr address; + + if(!szIp)return false; + + return kvi_stringIpToBinaryIp_V6(szIp,&address); + +} + + + + + + + +bool kvi_binaryIpToStringIp_V6(struct in6_addr in,QString &szBuffer) + +{ + + char buf[46]; + + bool bRet = inet_ntop(AF_INET6,(void *)&in,buf,46); + + szBuffer= buf; + + return bRet; + +} + + + +#endif + + + +#include <errno.h> + + + +bool kvi_select(int fd,bool * bCanRead,bool * bCanWrite,int iUSecs) + +{ + + // FIXME: This stuff should DIE! + + fd_set rs; + + fd_set ws; + + FD_ZERO(&rs); + + FD_ZERO(&ws); + + FD_SET(fd,&rs); + + FD_SET(fd,&ws); + + struct timeval tv; + + tv.tv_sec = 0; + + tv.tv_usec = iUSecs; + + int ret = select(fd + 1,&rs,&ws,0,&tv); + + if(ret < 1)return false; // EINTR or ENOSTUFFATALL + + *bCanRead = FD_ISSET(fd,&rs); + + *bCanWrite = FD_ISSET(fd,&ws); + + return true; + +} + + + +namespace KviNetUtils + +{ + + bool stringIpToBinaryIp(const QString &szStringIp,struct in_addr * address) + { +#ifndef HAVE_INET_ATON + QString szAddr = szStringIp.simplifyWhiteSpace(); + Q_UINT32 iAddr=0; + QStringList ipv4 = QStringList::split(".", szAddr, FALSE); + if (ipv4.count() == 4) { + int i = 0; + bool ok = TRUE; + while(ok && i < 4) { + uint byteValue = ipv4[i].toUInt(&ok); + if ( (byteValue > 255) && ok ) + ok = FALSE; + if (ok) + iAddr = (iAddr << 8) + byteValue; + ++i; + } + if (ok) + { + if(address)address->s_addr = htonl(iAddr); + return true; + } + } + return FALSE; +#else //HAVE_INET_ATON + if(szStringIp.isEmpty())return false; + return (inet_aton(KviQString::toUtf8(szStringIp).data(),address) != 0); +#endif //HAVE_INET_ATON + } + + + bool isValidStringIp(const QString &szIp) + + { + + struct in_addr address; + + if(szIp.isEmpty())return false; + + if(!szIp[0].isNumber())return false; + + return stringIpToBinaryIp(szIp,&address); + + } + + + +#ifdef COMPILE_IPV6_SUPPORT + + bool stringIpToBinaryIp_V6(const QString &szStringIp,struct in6_addr * address) + + { + + return (inet_pton(AF_INET6,KviQString::toUtf8(szStringIp).data(),(void *)address) == 1); + + } + + + + bool isValidStringIp_V6(const QString &szIp) + + { + + struct in6_addr address; + + if(szIp.isEmpty())return false; + + return stringIpToBinaryIp_V6(szIp,&address); + + } + + bool binaryIpToStringIp_V6(struct in6_addr in,QString &szBuffer) + { + char buf[46]; + bool bRet = inet_ntop(AF_INET6,(void *)&in,buf,46); + szBuffer= buf; + return bRet; + } + + +#endif //COMPILE_IPV6_SUPPORT + + + + bool binaryIpToStringIp(struct in_addr in,QString &szBuffer) + + { + + char * ptr = inet_ntoa(in); + + if(!ptr)return false; + + szBuffer = ptr; + + return true; + + } + + + + bool isRoutableIpString(const QString &szIpString) + + { + + struct in_addr a; + + if(szIpString.isEmpty())return false; + + stringIpToBinaryIp(szIpString,&a); + + return isRoutableIp((const char *)&a); + + } + + + + bool isRoutableIp(const char * ipaddr) + + { + + if(!ipaddr)return false; + + const unsigned char * ip = (const unsigned char *)ipaddr; + + if(ip[0] == 0)return false; // old-style broadcast + + if(ip[0] == 10)return false; // Class A VPN + + if(ip[0] == 127)return false; // loopback + + if((ip[0] == 172) && (ip[1] >= 16) && (ip[1] <= 31))return false; // Class B VPN + + if((ip[0] == 192) && (ip[1] == 168))return false; // Class C VPN + + if((ip[0] == 169) && (ip[1] == 254))return false; // APIPA + + if((ip[0] == 192) && (ip[1] == 0) && (ip[2] == 2))return false; // Class B VPN + + if(ip[0] >= 224)return false; // class D multicast and class E reserved + + + + return true; + + } + + + + bool getInterfaceAddress(const QString &szInterfaceName,QString &szBuffer) + + { + +#ifdef COMPILE_GET_INTERFACE_ADDRESS + + struct sockaddr *sa; + + struct sockaddr_in *sin; + + struct ifreq ifr; + + int len = szInterfaceName.length(); + + if(len > (IFNAMSIZ - 1))return false; // invalid interface anyway + + + + kvi_memmove(ifr.ifr_name,KviQString::toUtf8(szInterfaceName).data(),len + 1); + + + + int fd = socket(AF_INET,SOCK_STREAM,0); + + if(fd < 0)return false; + + + + if(ioctl(fd,SIOCGIFADDR,&ifr) == -1)return false; // supports only IPV4 ? + + + + close(fd); + + + + sa = (struct sockaddr *)&(ifr.ifr_addr); + + + + if (sa->sa_family != AF_INET) return false; + + sin = (struct sockaddr_in*) sa; + + return binaryIpToStringIp(sin->sin_addr,szBuffer); + + // (this seems to work for AF_INET only anyway) + +#else //!COMPILE_GET_INTERFACE_ADDRESS + + return false; + +#endif //!COMPILE_GET_INTERFACE_ADDRESS + + } + + + + void formatNetworkBandwidthString(QString &szBuffer,unsigned int uBytesPerSec) + + { + + if(uBytesPerSec > (1024 * 1024)) + + { + + unsigned int uMB = uBytesPerSec / (1024 * 1024); + + unsigned int uRem = ((uBytesPerSec % (1024 * 1024)) * 100) / (1024 * 1024); + + KviQString::sprintf(szBuffer,"%u.%u%u MB/s",uMB,uRem / 10,uRem % 10); + + return; + + } + + if(uBytesPerSec >= 1024) + + { + + unsigned int uKB = uBytesPerSec / 1024; + + unsigned int uRem = ((uBytesPerSec % 1024) * 100) / 1024; + + KviQString::sprintf(szBuffer,"%u.%u%u KB/s",uKB,uRem / 10,uRem % 10); + + return; + + } + + KviQString::sprintf(szBuffer,"%u B/s",uBytesPerSec); + + } + + + + + +}; + + + +bool kvi_getInterfaceAddress(const char * ifname,QString &buffer) + +{ + + debug("kvi_getInterfaceAddress is deprecated: use KviNetUtils::getInterfaceAddress"); + + QString szRet; + + bool bRes = KviNetUtils::getInterfaceAddress(QString(ifname),szRet); + + buffer = szRet; + + return bRes; + +} + + + +bool kvi_isRoutableIpString(const char * ipstring) + +{ + + struct in_addr a; + + if(!ipstring)return false; + + kvi_stringIpToBinaryIp(ipstring,&a); + + return kvi_isRoutableIp((const char *)&a); + +} + + + +bool kvi_isRoutableIp(const char * ipaddr) + +{ + + if(!ipaddr)return false; + + const unsigned char * ip = (const unsigned char *)ipaddr; + + if(ip[0] == 0)return false; // old-style broadcast + + if(ip[0] == 10)return false; // Class A VPN + + if(ip[0] == 127)return false; // loopback + + if((ip[0] == 172) && (ip[1] >= 16) && (ip[1] <= 31))return false; // Class B VPN + + if((ip[0] == 192) && (ip[1] == 168))return false; // Class C VPN + + if((ip[0] == 169) && (ip[1] == 254))return false; // APIPA + + if((ip[0] == 192) && (ip[1] == 0) && (ip[2] == 2))return false; // Class B VPN + + if(ip[0] >= 224)return false; // class D multicast and class E reserved + + + + return true; + +} + + + +bool kvi_getLocalHostAddress(QString &buffer) + +{ + + // This will work only on windoze... + + char buf[1024]; + + if(gethostname(buf,1024) != 0)return false; + + struct hostent * h = gethostbyname(buf); + + if(!h)return false; + + QString tmp; + + int i=0; + + while(h->h_addr_list[i]) + + { + + if(kvi_binaryIpToStringIp(*((struct in_addr *)(h->h_addr_list[i])),tmp)) + + { + + if(kvi_isRoutableIp(h->h_addr_list[i])) + + { + + buffer = tmp; + + return true; + + } + + } + + i++; + + } + + buffer = tmp; + + return true; + +} + + + + + + + + + + + +KviSockaddr::KviSockaddr(const char * szIpAddress,kvi_u32_t uPort,bool bIpV6,bool bUdp) + +{ + struct addrinfo hints; + kvi_memset((void *)&hints,0,sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; +#ifdef COMPILE_IPV6_SUPPORT + hints.ai_family = bIpV6 ? PF_INET6 : PF_INET; +#else + hints.ai_family = PF_INET; +#endif + + hints.ai_socktype = bUdp ? SOCK_DGRAM : SOCK_STREAM; + + hints.ai_protocol = 0; + m_pData = 0; + KviStr szPort(KviStr::Format,"%u",uPort); + getaddrinfo(szIpAddress,szPort.ptr(),&hints,(struct addrinfo **)&m_pData); + +} + + +KviSockaddr::KviSockaddr(kvi_u32_t uPort,bool bIpV6,bool bUdp) // passive sockaddr + +{ + struct addrinfo hints; + kvi_memset((void *)&hints,0,sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; +#ifdef COMPILE_IPV6_SUPPORT + hints.ai_family = bIpV6 ? PF_INET6 : PF_INET; +#else + hints.ai_family = PF_INET; +#endif + hints.ai_socktype = bUdp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = 0; + m_pData = 0; + KviStr szPort(KviStr::Format,"%u",uPort); + getaddrinfo(0,szPort.ptr(),&hints,(struct addrinfo **)&m_pData); + +} + + + +KviSockaddr::~KviSockaddr() + +{ + + if(m_pData) + + { + + freeaddrinfo((struct addrinfo *)m_pData); + + m_pData = 0; + + } + +} + + + +struct sockaddr * KviSockaddr::socketAddress() + +{ + + if(!m_pData)return 0; + + return ((struct addrinfo *)m_pData)->ai_addr; + +} + + + +size_t KviSockaddr::addressLength() + +{ + + if(!m_pData)return 0; + + return ((struct addrinfo *)m_pData)->ai_addrlen; + +} + + + +int KviSockaddr::addressFamily() + +{ + + if(!m_pData)return 0; + + return ((struct addrinfo *)m_pData)->ai_family; + +} + + + +bool KviSockaddr::isIpV6() + +{ + + if(!m_pData)return false; + +#ifdef COMPILE_IPV6_SUPPORT + + return false; + +#else + + return (addressFamily() == AF_INET6); + +#endif + +} + + + +kvi_u32_t KviSockaddr::port() + +{ + if(!m_pData)return 0; +#ifdef COMPILE_IPV6_SUPPORT + switch(((struct addrinfo *)m_pData)->ai_family) + { + case AF_INET: + return ntohs(((struct sockaddr_in *)(((struct addrinfo *)m_pData)->ai_addr))->sin_port); + break; + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)(((struct addrinfo *)m_pData)->ai_addr))->sin6_port); + break; + } + return 0; +#else + return ntohs(((struct sockaddr_in *)(((struct addrinfo *)m_pData)->ai_addr))->sin_port); +#endif + +} + + + +bool KviSockaddr::getStringAddress(QString &szBuffer) + +{ + + if(!m_pData)return 0; + +#ifdef COMPILE_IPV6_SUPPORT + + switch(((struct addrinfo *)m_pData)->ai_family) + + { + + case AF_INET: + + return kvi_binaryIpToStringIp(((struct sockaddr_in *)(((struct addrinfo *)m_pData)->ai_addr))->sin_addr,szBuffer); + + break; + + case AF_INET6: + + return kvi_binaryIpToStringIp_V6(((struct sockaddr_in6 *)(((struct addrinfo *)m_pData)->ai_addr))->sin6_addr,szBuffer); + + break; + + } + + return false; + +#else + + return kvi_binaryIpToStringIp(((struct sockaddr_in *)(((struct addrinfo *)m_pData)->ai_addr))->sin_addr,szBuffer); + +#endif + +} + diff --git a/src/kvilib/net/kvi_netutils.h b/src/kvilib/net/kvi_netutils.h new file mode 100644 index 00000000..b43326f0 --- /dev/null +++ b/src/kvilib/net/kvi_netutils.h @@ -0,0 +1,104 @@ +#ifndef _KVI_NETUTILS_H_ +#define _KVI_NETUTILS_H_ + +// +// File : kvi_netutlis.h +// Creation date : Sun Jun 18 2000 18:37:27 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2000 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 "kvi_inttypes.h" + +#ifdef COMPILE_ON_WINDOWS + #include <winsock2.h> + #ifdef COMPILE_IPV6_SUPPORT + #ifdef WIN2K + #include <ws2ip6.h> + #else + #include <ws2tcpip.h> + //#include <tpipv6.h> + #define in6_addr in_addr6 + #endif + #endif +#else + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> //in_addr + #include <arpa/inet.h> //inet_ntoa inet_ntop and inet_pton depend on this one. +#endif + +#include "kvi_string.h" + + +KVILIB_API extern bool kvi_isValidStringIp(const char * szIp); +KVILIB_API extern bool kvi_stringIpToBinaryIp(const char * szIp,struct in_addr * address); +KVILIB_API extern bool kvi_binaryIpToStringIp(struct in_addr in,QString &szBuffer); + +#ifdef COMPILE_IPV6_SUPPORT + KVILIB_API extern bool kvi_isValidStringIp_V6(const char * szIp); + KVILIB_API extern bool kvi_stringIpToBinaryIp_V6(const char * szIp,struct in6_addr * address); + KVILIB_API extern bool kvi_binaryIpToStringIp_V6(struct in6_addr in,QString &szBuffer); +#endif + +class KVILIB_API KviSockaddr +{ +public: + KviSockaddr(const char * szIpAddress,kvi_u32_t uPort,bool bIpV6,bool bUdp = false); + KviSockaddr(kvi_u32_t uPort,bool bIpV6,bool bUdp = false); // passive + ~KviSockaddr(); +private: + void * m_pData; //addrinfo +public: + struct sockaddr * socketAddress(); + size_t addressLength(); + int addressFamily(); + bool isIpV6(); + bool getStringAddress(QString &szBuffer); + kvi_u32_t port(); + +}; + + +KVILIB_API extern bool kvi_select(int fd,bool * bCanRead,bool * bCanWrite,int iUSecs = 0); +KVILIB_API extern bool kvi_getInterfaceAddress(const char * ifname,QString &buffer); + +// Warning : NOT THREAD SAFE! +KVILIB_API extern bool kvi_getLocalHostAddress(QString &buffer); +KVILIB_API extern bool kvi_isRoutableIp(const char * ipaddr); +KVILIB_API extern bool kvi_isRoutableIpString(const char * ipstring); + +namespace KviNetUtils +{ + KVILIB_API bool stringIpToBinaryIp(const QString &szStringIp,struct in_addr * address); + KVILIB_API bool isValidStringIp(const QString &szStringIp); + KVILIB_API bool binaryIpToStringIp(struct in_addr in,QString &szBuffer); + KVILIB_API bool getInterfaceAddress(const QString &szInterfaceName,QString &szBuffer); +#ifdef COMPILE_IPV6_SUPPORT + KVILIB_API bool isValidStringIp_V6(const QString &szStringIp); + KVILIB_API bool stringIpToBinaryIp_V6(const QString &szStringIp,struct in6_addr * address); + KVILIB_API bool binaryIpToStringIp_V6(struct in6_addr in,QString &szBuffer); +#endif + KVILIB_API bool isRoutableIp(const char * ipaddr); + KVILIB_API bool isRoutableIpString(const QString &szIpString); + KVILIB_API void formatNetworkBandwidthString(QString &szBuffer,unsigned int uBytesPerSec); +}; + + +#endif //!_KVI_NETUTILS_H_ diff --git a/src/kvilib/net/kvi_socket.cpp b/src/kvilib/net/kvi_socket.cpp new file mode 100644 index 00000000..9bd3de9e --- /dev/null +++ b/src/kvilib/net/kvi_socket.cpp @@ -0,0 +1,31 @@ +// +// File : kvi_socket.cpp +// Creation date : Thu Sep 20 03:50:24 2001 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2001 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. +// + +#define _KVI_SOCKET_CPP_ + +#define __KVILIB__ + +#include "kvi_socket.h" +#include "kvi_inttypes.h" + +KVILIB_API kvi_u64_t g_uOutgoingTraffic; +KVILIB_API kvi_u64_t g_uIncomingTraffic; diff --git a/src/kvilib/net/kvi_socket.h b/src/kvilib/net/kvi_socket.h new file mode 100644 index 00000000..47d51510 --- /dev/null +++ b/src/kvilib/net/kvi_socket.h @@ -0,0 +1,356 @@ +#ifndef _KVI_SOCKET_H_ +#define _KVI_SOCKET_H_ +//============================================================================= +// +// File : kvi_socket.h +// Creation date : Thu Sep 20 03:50:22 2001 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2001 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. +// +//============================================================================= + +//============================================================================= +// Socket stuff abstraction layer +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_sockettype.h" // <--- this includes <winsock2.h> if needed + +#include <errno.h> + +#include "kvi_inttypes.h" + +//#ifndef _KVI_SOCKET_CPP_ + extern KVILIB_API kvi_u64_t g_uOutgoingTraffic; + extern KVILIB_API kvi_u64_t g_uIncomingTraffic; +//#endif //!_KVI_SOCKET_CPP_ + + + +#ifdef COMPILE_ON_WINDOWS + + #define KVI_INVALID_SOCKET INVALID_SOCKET + +#else + + #include <sys/time.h> + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/tcp.h> + #include <netinet/in.h> + #include <fcntl.h> + #include <unistd.h> + + #define KVI_INVALID_SOCKET (-1) + +#endif + +#ifndef MSG_NOSIGNAL + // At least solaris seems to not have it + #define MSG_NOSIGNAL 0 +#endif + +//#include "kvi_socketcalls.h" + + +//================================================================================================ +// Constants for kvi_socket_create +// + +#define KVI_SOCKET_PF_INET PF_INET +#define KVI_SOCKET_PF_INET6 PF_INET6 +#define KVI_SOCKET_PF_UNIX PF_UNIX + +#define KVI_SOCKET_TYPE_STREAM SOCK_STREAM +#define KVI_SOCKET_TYPE_DGRAM SOCK_DGRAM + +#define KVI_SOCKET_PROTO_TCP 0 + +//================================================================================================ +// kvi_socket_create +// kvi_socket_open +// +// Open a socket of the specified protocol family , type and protocol +// You should always use the KVI_SOCKET_* constants as parameters +// Returns KVI_INVALID_SOCKET if the socket creation has failed. +// The returned socket is in blocking mode! +// + +#define kvi_socket_open kvi_socket_create + +inline kvi_socket_t kvi_socket_create(int pf,int type,int proto) +{ + return (kvi_socket_t)socket(pf,type,proto); +}; + +//================================================================================================ +// kvi_socket_isValid +// +// Check if a socket is valid or not +// + +inline void kvi_socket_flushTrafficCounters() +{ + g_uOutgoingTraffic = 0; + g_uIncomingTraffic = 0; +} + +inline bool kvi_socket_isValid(kvi_socket_t sock) +{ + return (sock != ((kvi_socket_t)(KVI_INVALID_SOCKET))); +} + +//================================================================================================ +// kvi_socket_destroy +// kvi_socket_close +// +// Close a socket...that's all :) +// + +#define kvi_socket_close kvi_socket_destroy + +inline void kvi_socket_destroy(kvi_socket_t sock) +{ +#ifdef COMPILE_ON_WINDOWS + closesocket(sock); +#else + close(sock); +#endif +}; + +//================================================================================================ +// kvi_socket_setNonBlocking +// +// Sets the socket in nonBlocking mode. Obviously returns false in case of failure +// + +inline bool kvi_socket_setNonBlocking(kvi_socket_t sock) +{ +#ifdef COMPILE_ON_WINDOWS + unsigned long arg = 1; + return (ioctlsocket(sock,FIONBIO,(unsigned long FAR *)&arg) == 0); +#else + return (fcntl(sock,F_SETFL,O_NONBLOCK) == 0); +#endif +}; + +//================================================================================================ +// kvi_socket_bind +// +// Standard bind() call on the socket. Returns false in case of failure +// + +inline bool kvi_socket_bind(kvi_socket_t sock,const struct sockaddr * sa,int salen) +{ + return (::bind(sock,sa,salen) == 0); +}; + +//================================================================================================ +// kvi_socket_connect +// +// Starts a connection to the specified remote address +// returns false if the connection can not be started. +// You might take a look at kvi_socket_errno() then. +// + +inline bool kvi_socket_connect(kvi_socket_t sock,const struct sockaddr *sa,int salen) +{ +#ifdef COMPILE_ON_WINDOWS + return (WSAConnect(sock,sa,salen,0,0,0,0) == 0); +#else + return (::connect(sock,sa,salen) == 0); +#endif +}; + +inline bool kvi_socket_recoverableConnectError(int err) +{ +#ifdef COMPILE_ON_WINDOWS + return ((err == WSAEINPROGRESS) || (err == WSAEWOULDBLOCK)); +#else + return (err == EINPROGRESS); +#endif +}; + +inline bool kvi_socket_recoverableError(int err) +{ +#ifdef COMPILE_ON_WINDOWS + return ((err == WSAEWOULDBLOCK) || (err == EINTR) || (err == EAGAIN)); +#else + return ((err == EINTR) || (err = EAGAIN)); +#endif +} + +//================================================================================================ +// kvi_socket_accept +// +// Standard accept() call. Returns KVI_INVALID_SOCKET in case of failure +// You should check kvi_socket_errno() then. +// + +inline kvi_socket_t kvi_socket_accept(kvi_socket_t sock,struct sockaddr *sa,int * salen) +{ +#ifdef COMPILE_ON_WINDOWS + return (kvi_socket_t)::accept(sock,sa,salen); +#else + return (kvi_socket_t)::accept(sock,sa,(socklen_t *)salen); +#endif +}; + +//================================================================================================ +// kvi_socket_listen +// +// Standard listen() call. Returns false in case of failure +// You should check kvi_socket_errno() then. +// + +inline bool kvi_socket_listen(kvi_socket_t sock,int backlog) +{ + return (::listen(sock,backlog) == 0); +}; + +//================================================================================================ +// kvi_socket_select +// +// Standard select() call. This is complex so here is a mini-reminder: +// nhpo is the number of the highest file descriptor in the sets plus one! +// Returns the number of sockets with data available (or space available) +// or something that is less than 0 in case of error. You should check kvi_socket_errno() then. +// + +inline int kvi_socket_select(int nhpo,fd_set *r,fd_set *w,fd_set *e,struct timeval * t) +{ + return ::select(nhpo,r,w,e,t); +}; + +//================================================================================================ +// kvi_socket_send +// kvi_socket_write +// +// Standard send() call. On UNIX ignores SIGPIPE. Returns the number of bytes sent or +// -1 in case of failure. You should check kvi_socket_errno() then. +// + +#define kvi_socket_write kvi_socket_send + +inline int kvi_socket_send(kvi_socket_t sock,const void * buf,int size) +{ + g_uOutgoingTraffic+=size; +#ifdef COMPILE_ON_WINDOWS + return ::send(sock,(const char *)buf,size,0); +#else + return ::send(sock,buf,size,MSG_NOSIGNAL | MSG_DONTWAIT); +#endif +}; + +//================================================================================================ +// kvi_socket_recv +// kvi_socket_read +// +// Standard read() call. On UNIX ignores SIGPIPE. Returns the number of bytes readed or +// -1 in case of failure. You should check kvi_socket_errno() then. +// + +#define kvi_socket_read kvi_socket_recv + +inline int kvi_socket_recv(kvi_socket_t sock,void * buf,int maxlen) +{ + int iReceived; +#ifdef COMPILE_ON_WINDOWS + iReceived = ::recv(sock,(char *)buf,maxlen,0); +#else + iReceived = ::recv(sock,buf,maxlen,MSG_NOSIGNAL); +#endif + g_uIncomingTraffic+=iReceived; + return iReceived; +}; + +//================================================================================================ +// kvi_socket_getsockopt +// +// Standard getsockopt() call. Returns false in case of failure. +// You should check kvi_socket_errno() then. +// + +inline bool kvi_socket_getsockopt(kvi_socket_t sock,int level,int optname,void *optval,int *optlen) +{ +#ifdef COMPILE_ON_WINDOWS + return (::getsockopt(sock,level,optname,(char FAR *)optval,optlen) == 0); +#else + return (::getsockopt(sock,level,optname,optval,(socklen_t *)optlen) == 0); +#endif +} + +//================================================================================================ +// kvi_socket_setsockopt +// +// Standard setsockopt() call. Returns false in case of failure. +// You should check kvi_socket_errno() then. +// + +inline bool kvi_socket_setsockopt(kvi_socket_t sock,int level,int optname,const void *optval,int optlen) +{ +#ifdef COMPILE_ON_WINDOWS + return (::setsockopt(sock,level,optname,(char FAR *)optval,optlen) == 0); +#else + return (::setsockopt(sock,level,optname,optval,optlen) == 0); +#endif +} + + +//================================================================================================ +// kvi_socket_disableNagle +// +// Disables the nagle algorithm (sets TCP_NODELAY) +// + +/* + unused for now +inline bool kvi_socket_disableNagle(kvi_socket_t sock) +{ + int opt = 1; + return kvi_socket_setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,&opt,sizeof(opt)); +}; +*/ + +//================================================================================================ +// kvi_socket_getsockname +// +// Standard getsockname() call. Returns false in case of failure. +// You should check kvi_socket_errno() then. +// + +inline bool kvi_socket_getsockname(kvi_socket_t sock,struct sockaddr * addr,int * addrlen) +{ +#ifdef COMPILE_ON_WINDOWS + return (::getsockname(sock,addr,addrlen) == 0); +#else + return (::getsockname(sock,addr,(socklen_t *)addrlen) == 0); +#endif +} + +inline int kvi_socket_error() +{ +#ifdef COMPILE_ON_WINDOWS + return WSAGetLastError(); +#else + return errno; +#endif +} + + +#endif //_KVI_SOCKET_H_ diff --git a/src/kvilib/net/kvi_sockettype.h b/src/kvilib/net/kvi_sockettype.h new file mode 100644 index 00000000..c8a45743 --- /dev/null +++ b/src/kvilib/net/kvi_sockettype.h @@ -0,0 +1,45 @@ +#ifndef _KVI_SOCKETTYPE_H_ +#define _KVI_SOCKETTYPE_H_ +//============================================================================= +// +// File : kvi_sockettype.h +// Creation date : Thu Sep 20 05:41:46 2001 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2001 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. +// +//============================================================================= + +//============================================================================= +// Socket stuff abstraction layer +//============================================================================= + +#include "kvi_settings.h" + +#ifdef COMPILE_ON_WINDOWS + + #include <winsock2.h> + + typedef SOCKET kvi_socket_t; + +#else + + typedef int kvi_socket_t; + +#endif + +#endif //_KVI_SOCKETTYPE_H_ diff --git a/src/kvilib/net/kvi_ssl.cpp b/src/kvilib/net/kvi_ssl.cpp new file mode 100644 index 00000000..6748e062 --- /dev/null +++ b/src/kvilib/net/kvi_ssl.cpp @@ -0,0 +1,687 @@ +//============================================================================= +// +// File : kvi_ssl.cpp +// Creation date : Mon May 27 2002 21:36:12 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002-2004 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. +// +//============================================================================= + +#define __KVILIB__ + + +#include "kvi_ssl.h" +#include "kvi_locale.h" + +#ifdef COMPILE_SSL_SUPPORT + +#include "kvi_thread.h" +#include "kvi_memmove.h" +#include "kvi_malloc.h" + +#include <openssl/asn1.h> +#include <openssl/err.h> +#include <openssl/dh.h> + +#include <stdio.h> + +static bool g_bSSLInitialized = false; +static KviMutex * g_pSSLMutex = 0; + + +static inline void my_ssl_lock() +{ + g_pSSLMutex->lock(); +} + +static inline void my_ssl_unlock() +{ + g_pSSLMutex->unlock(); +} + + +// THIS PART OF OpenSSL SUCKS + +static DH * dh_512 = 0; +static DH * dh_1024 = 0; +static DH * dh_2048 = 0; +static DH * dh_4096 = 0; + +static unsigned char dh512_p[]={ + 0x90,0x86,0xDD,0x06,0xE8,0x0F,0x10,0x86,0xF0,0x91,0xC5,0x55, + 0x4D,0x6B,0xAF,0x69,0x4F,0x01,0xED,0xF9,0x57,0x8F,0x3B,0xB8, + 0x9C,0x87,0xAE,0x85,0xC1,0xBF,0x57,0xA5,0xD5,0xBA,0x81,0x24, + 0xE7,0x99,0xE3,0xF6,0xCD,0xB4,0x41,0xB7,0x7F,0x6E,0x7B,0xB1, + 0xD2,0xF3,0xE9,0x0F,0xB9,0x0E,0x4D,0xEB,0x9D,0xD4,0xA9,0xE5, + 0x03,0x67,0xA7,0x27 +}; +static unsigned char dh512_g[]={ 0x05 }; + +static unsigned char dh1024_p[]={ + 0xA5,0x4C,0xB9,0xB9,0xC4,0x35,0x88,0x68,0x9B,0x79,0x48,0x6C, + 0x21,0xA7,0x8E,0xE2,0x9C,0xAF,0x2F,0x04,0xBF,0x45,0xBC,0xF5, + 0xAB,0x35,0x86,0xC8,0xBB,0x9B,0x75,0x18,0x7C,0x9B,0xAB,0xE8, + 0x52,0x7F,0x57,0x3E,0xD8,0x65,0x7D,0x2B,0xE1,0x6D,0x3D,0xA5, + 0x32,0xE8,0xA0,0x2B,0x7A,0x58,0x6B,0x47,0x16,0x4E,0xB1,0xFC, + 0x09,0xB7,0x7C,0xC6,0xE9,0x6E,0xC7,0xC7,0xA1,0x42,0x0F,0x4B, + 0x43,0xFB,0x58,0xBA,0xC7,0x66,0xD6,0xCA,0x6B,0xC7,0x45,0x7C, + 0x99,0xE4,0x46,0x02,0x93,0x3F,0x28,0xD2,0xCE,0x0C,0x8A,0xDD, + 0x6A,0x22,0x2E,0xA9,0x9A,0xCA,0x16,0x48,0x4E,0x67,0x4C,0xE9, + 0xC8,0x54,0xCD,0x18,0xC9,0xF3,0x30,0x3A,0x74,0xAB,0xF9,0xAF, + 0xE4,0xA4,0x0D,0x56,0x62,0x28,0x07,0xBF +}; +static unsigned char dh1024_g[]={ 0x05 }; + +static unsigned char dh2048_p[]={ + 0xBF,0x67,0x7B,0x79,0xA5,0x22,0xD3,0xB5,0x0C,0x13,0xE6,0x92, + 0x54,0xFD,0x64,0xBF,0x57,0x25,0xBD,0x02,0x7C,0xFD,0x72,0x97, + 0x82,0xA4,0xA6,0x0A,0xB9,0xE6,0x4B,0xFA,0xBD,0xFA,0x71,0x8A, + 0x2E,0x36,0xF9,0x03,0x58,0x1B,0xB6,0x3A,0xFD,0x15,0xCC,0x87, + 0x5D,0x04,0xF7,0x45,0xE0,0xE2,0x34,0x7F,0x54,0x5F,0x5D,0x14, + 0xD3,0xCA,0x3E,0xFD,0x2A,0x92,0x10,0x89,0xA0,0xB0,0xB4,0xE5, + 0x80,0x05,0x13,0xBE,0xA3,0xD0,0x42,0x4B,0x98,0x44,0x54,0xB3, + 0xE0,0x23,0x26,0xF5,0x6B,0x0E,0x4D,0x2A,0x81,0xB2,0x8A,0x06, + 0xC8,0x00,0x9E,0xAB,0x1B,0x77,0xDC,0x87,0x9C,0x6C,0xD5,0xEE, + 0xB4,0xB4,0xDD,0xDA,0x3F,0x40,0xA3,0xFA,0xC1,0x1E,0xC0,0xA2, + 0x9E,0xB8,0xAC,0x31,0xE8,0x12,0x93,0x9C,0x71,0xF6,0xE7,0xF0, + 0x65,0x7F,0xA5,0x20,0xF7,0x49,0x3D,0xD6,0xF9,0xD3,0xF0,0x3F, + 0xB3,0xF0,0xD0,0x23,0x22,0x82,0xA5,0xDD,0xFB,0xD9,0x9C,0x7D, + 0xE7,0xA0,0x78,0xE8,0xF9,0x02,0x0C,0x2F,0x1D,0x52,0xC7,0x61, + 0xED,0xA0,0xC9,0x06,0x14,0xDF,0xE7,0xB1,0x1E,0x50,0x98,0x4F, + 0x10,0xB9,0x87,0x4C,0x1C,0x9C,0xB3,0xD2,0x98,0x23,0x7C,0x47, + 0xD2,0x3C,0xC5,0x29,0x65,0xC5,0x67,0x4E,0xC0,0x76,0x0F,0x43, + 0x27,0x28,0x89,0x69,0x30,0x7D,0x04,0xFD,0xF7,0x89,0xE5,0xD6, + 0xE6,0x97,0x7D,0xBB,0x54,0x5F,0xB7,0x94,0x1D,0xBC,0x82,0xAB, + 0x9A,0xF5,0x0A,0x0C,0x89,0x68,0xE7,0x0A,0x8C,0x2D,0x0D,0x82, + 0x44,0xA7,0xB8,0xF9,0x0B,0x8E,0xCB,0xA4,0x6A,0xA7,0xEC,0x5F, + 0x0A,0xF8,0x5F,0xE7 +}; +static unsigned char dh2048_g[]={ 0x05 }; + +static unsigned char dh4096_p[]={ + 0xFA,0x14,0x72,0x52,0xC1,0x4D,0xE1,0x5A,0x49,0xD4,0xEF,0x09, + 0x2D,0xC0,0xA8,0xFD,0x55,0xAB,0xD7,0xD9,0x37,0x04,0x28,0x09, + 0xE2,0xE9,0x3E,0x77,0xE2,0xA1,0x7A,0x18,0xDD,0x46,0xA3,0x43, + 0x37,0x23,0x90,0x97,0xF3,0x0E,0xC9,0x03,0x50,0x7D,0x65,0xCF, + 0x78,0x62,0xA6,0x3A,0x62,0x22,0x83,0xA1,0x2F,0xFE,0x79,0xBA, + 0x35,0xFF,0x59,0xD8,0x1D,0x61,0xDD,0x1E,0x21,0x13,0x17,0xFE, + 0xCD,0x38,0x87,0x9E,0xF5,0x4F,0x79,0x10,0x61,0x8D,0xD4,0x22, + 0xF3,0x5A,0xED,0x5D,0xEA,0x21,0xE9,0x33,0x6B,0x48,0x12,0x0A, + 0x20,0x77,0xD4,0x25,0x60,0x61,0xDE,0xF6,0xB4,0x4F,0x1C,0x63, + 0x40,0x8B,0x3A,0x21,0x93,0x8B,0x79,0x53,0x51,0x2C,0xCA,0xB3, + 0x7B,0x29,0x56,0xA8,0xC7,0xF8,0xF4,0x7B,0x08,0x5E,0xA6,0xDC, + 0xA2,0x45,0x12,0x56,0xDD,0x41,0x92,0xF2,0xDD,0x5B,0x8F,0x23, + 0xF0,0xF3,0xEF,0xE4,0x3B,0x0A,0x44,0xDD,0xED,0x96,0x84,0xF1, + 0xA8,0x32,0x46,0xA3,0xDB,0x4A,0xBE,0x3D,0x45,0xBA,0x4E,0xF8, + 0x03,0xE5,0xDD,0x6B,0x59,0x0D,0x84,0x1E,0xCA,0x16,0x5A,0x8C, + 0xC8,0xDF,0x7C,0x54,0x44,0xC4,0x27,0xA7,0x3B,0x2A,0x97,0xCE, + 0xA3,0x7D,0x26,0x9C,0xAD,0xF4,0xC2,0xAC,0x37,0x4B,0xC3,0xAD, + 0x68,0x84,0x7F,0x99,0xA6,0x17,0xEF,0x6B,0x46,0x3A,0x7A,0x36, + 0x7A,0x11,0x43,0x92,0xAD,0xE9,0x9C,0xFB,0x44,0x6C,0x3D,0x82, + 0x49,0xCC,0x5C,0x6A,0x52,0x42,0xF8,0x42,0xFB,0x44,0xF9,0x39, + 0x73,0xFB,0x60,0x79,0x3B,0xC2,0x9E,0x0B,0xDC,0xD4,0xA6,0x67, + 0xF7,0x66,0x3F,0xFC,0x42,0x3B,0x1B,0xDB,0x4F,0x66,0xDC,0xA5, + 0x8F,0x66,0xF9,0xEA,0xC1,0xED,0x31,0xFB,0x48,0xA1,0x82,0x7D, + 0xF8,0xE0,0xCC,0xB1,0xC7,0x03,0xE4,0xF8,0xB3,0xFE,0xB7,0xA3, + 0x13,0x73,0xA6,0x7B,0xC1,0x0E,0x39,0xC7,0x94,0x48,0x26,0x00, + 0x85,0x79,0xFC,0x6F,0x7A,0xAF,0xC5,0x52,0x35,0x75,0xD7,0x75, + 0xA4,0x40,0xFA,0x14,0x74,0x61,0x16,0xF2,0xEB,0x67,0x11,0x6F, + 0x04,0x43,0x3D,0x11,0x14,0x4C,0xA7,0x94,0x2A,0x39,0xA1,0xC9, + 0x90,0xCF,0x83,0xC6,0xFF,0x02,0x8F,0xA3,0x2A,0xAC,0x26,0xDF, + 0x0B,0x8B,0xBE,0x64,0x4A,0xF1,0xA1,0xDC,0xEE,0xBA,0xC8,0x03, + 0x82,0xF6,0x62,0x2C,0x5D,0xB6,0xBB,0x13,0x19,0x6E,0x86,0xC5, + 0x5B,0x2B,0x5E,0x3A,0xF3,0xB3,0x28,0x6B,0x70,0x71,0x3A,0x8E, + 0xFF,0x5C,0x15,0xE6,0x02,0xA4,0xCE,0xED,0x59,0x56,0xCC,0x15, + 0x51,0x07,0x79,0x1A,0x0F,0x25,0x26,0x27,0x30,0xA9,0x15,0xB2, + 0xC8,0xD4,0x5C,0xCC,0x30,0xE8,0x1B,0xD8,0xD5,0x0F,0x19,0xA8, + 0x80,0xA4,0xC7,0x01,0xAA,0x8B,0xBA,0x53,0xBB,0x47,0xC2,0x1F, + 0x6B,0x54,0xB0,0x17,0x60,0xED,0x79,0x21,0x95,0xB6,0x05,0x84, + 0x37,0xC8,0x03,0xA4,0xDD,0xD1,0x06,0x69,0x8F,0x4C,0x39,0xE0, + 0xC8,0x5D,0x83,0x1D,0xBE,0x6A,0x9A,0x99,0xF3,0x9F,0x0B,0x45, + 0x29,0xD4,0xCB,0x29,0x66,0xEE,0x1E,0x7E,0x3D,0xD7,0x13,0x4E, + 0xDB,0x90,0x90,0x58,0xCB,0x5E,0x9B,0xCD,0x2E,0x2B,0x0F,0xA9, + 0x4E,0x78,0xAC,0x05,0x11,0x7F,0xE3,0x9E,0x27,0xD4,0x99,0xE1, + 0xB9,0xBD,0x78,0xE1,0x84,0x41,0xA0,0xDF +}; +static unsigned char dh4096_g[]={ 0x02 }; + +static DH * my_get_dh(int keylength) +{ + DH * dh = 0; + unsigned char * p = 0; + unsigned char * g = 0; + int sp = 0; + int sg = 0; + switch(keylength) + { + case 512: + dh = dh_512; + p = dh512_p; + g = dh512_g; + sp = sizeof(dh512_p); + sg = sizeof(dh512_g); + break; + case 1024: + dh = dh_1024; + p = dh1024_p; + g = dh1024_g; + sp = sizeof(dh1024_p); + sg = sizeof(dh1024_g); + break; + case 2048: + dh = dh_2048; + p = dh2048_p; + g = dh2048_g; + sp = sizeof(dh2048_p); + sg = sizeof(dh2048_g); + break; + case 4096: + dh = dh_4096; + p = dh4096_p; + g = dh4096_g; + sp = sizeof(dh4096_p); + sg = sizeof(dh4096_g); + break; + default: + // What the hell do you want from me ? + debug("OpenSSL is asking for a DH param with keylen %d: no way :D",keylength); + break; + + } + + if(dh)return dh; + dh = DH_new(); + if(!dh)return 0; + dh->p=BN_bin2bn(p,sp,0); + dh->g=BN_bin2bn(g,sg,0); + if((dh->p == 0) || (dh->g == 0)) + { + DH_free(dh); + return 0; + } + return dh; +} + +DH * my_ugly_dh_callback(SSL *s, int is_export, int keylength) +{ + my_ssl_lock(); + DH *dh = my_get_dh(keylength); + my_ssl_unlock(); + return dh; +} + +void KviSSL::globalInit() +{ + if(g_pSSLMutex)return; + g_pSSLMutex = new KviMutex(); +} + +void KviSSL::globalDestroy() +{ + if(!g_pSSLMutex)return; + if(dh_512)DH_free(dh_512); + if(dh_1024)DH_free(dh_1024); + if(dh_2048)DH_free(dh_2048); + if(dh_4096)DH_free(dh_4096); + delete g_pSSLMutex; + g_pSSLMutex = 0; +} + +KviSSL::KviSSL() +{ + my_ssl_lock(); + if(!g_bSSLInitialized) + { + // FIXME: this should be done only if SSL is really needed + SSL_library_init(); + SSL_load_error_strings(); + g_bSSLInitialized = true; + } + my_ssl_unlock(); + m_pSSL = 0; + m_pSSLCtx = 0; +} + +KviSSL::~KviSSL() +{ + shutdown(); +} + +#ifdef COMPILE_ON_WINDOWS + + // On windows we need to override new and delete operators + // to ensure that always the right new/delete pair is called for an object instance + // This bug is present in all the classes exported by a module that + // can be instantiated/destroyed from external modules. + // (this is a well known bug described in Q122675 of MSDN) + + void * KviSSL::operator new(size_t tSize) + { + return kvi_malloc(tSize); + } + + void KviSSL::operator delete(void * p) + { + kvi_free(p); + } + +#endif + +void KviSSL::shutdown() +{ + if(m_pSSL) + { + // At least attempt to shutdown the connection gracefully + SSL_shutdown(m_pSSL); + SSL_free(m_pSSL); + m_pSSL = 0; + } + if(m_pSSLCtx) + { + SSL_CTX_free(m_pSSLCtx); + m_pSSLCtx = 0; + } +} + + +bool KviSSL::initContext(Method m) +{ + if(m_pSSL)return false; + m_pSSLCtx = SSL_CTX_new(m == Client ? SSLv23_client_method() : SSLv23_server_method()); + if(!m_pSSLCtx)return false; + // FIXME: this should be configurable ? + SSL_CTX_set_cipher_list(m_pSSLCtx,"ALL:eNULL"); + SSL_CTX_set_tmp_dh_callback(m_pSSLCtx,my_ugly_dh_callback); + return true; +} + +bool KviSSL::initSocket(kvi_socket_t fd) +{ + if(!m_pSSLCtx)return false; + m_pSSL = SSL_new(m_pSSLCtx); + if(!m_pSSL)return false; + if(!SSL_set_fd(m_pSSL,fd))return false; + return true; + +} + +static int cb(char *buf, int size, int rwflag, void *u) +{ + KviStr * p = (KviStr *)u; + int len = p->len(); + if(len >= size)return 0; + kvi_memmove(buf,p->ptr(),len + 1); +// debug("PASS REQYESTED: %s",p->ptr()); + return len; +} + +KviSSL::Result KviSSL::useCertificateFile(const char * cert,const char * pass) +{ + if(!m_pSSLCtx)return NotInitialized; + m_szPass = pass; + if(m_szPass.len() < 4)m_szPass.append("xxxx"); + X509 * x509 = 0; + + FILE * f = fopen(cert,"r"); + if(!f)return FileIoError; + +// debug("READING CERTIFICATE %s",cert); + if(PEM_read_X509(f,&x509,cb,&m_szPass)) + { + if(!SSL_CTX_use_certificate(m_pSSLCtx,x509)) + { + X509_free(x509); + return SSLError; + } + } + + fclose(f); + return Success; +} + + +KviSSL::Result KviSSL::usePrivateKeyFile(const char * key,const char * pass) +{ + if(!m_pSSLCtx)return NotInitialized; + m_szPass = pass; + if(m_szPass.len() < 4)m_szPass.append("xxxx"); + + EVP_PKEY * k = 0; + + FILE * f = fopen(key,"r"); + if(!f)return FileIoError; + +// debug("READING KEY %s",key); + if(PEM_read_PrivateKey(f,&k,cb,&m_szPass)) + { + if(!SSL_CTX_use_PrivateKey(m_pSSLCtx,k)) + { + EVP_PKEY_free(k); + return SSLError; + } + } + + fclose(f); + return Success; +} + +unsigned long KviSSL::getLastError(bool bPeek) +{ + return bPeek ? ERR_peek_error() : ERR_get_error(); +} + +bool KviSSL::getLastErrorString(KviStr &buffer,bool bPeek) +{ + unsigned long uErr = getLastError(bPeek); + if(uErr != 0) + { + const char * err = ERR_reason_error_string(uErr); + buffer = err ? err : "Unknown error"; + return true; + } + return false; +} + +KviSSL::Result KviSSL::connect() +{ + if(!m_pSSL)return NotInitialized; + int ret = SSL_connect(m_pSSL); + return connectOrAcceptError(ret); +} + +KviSSL::Result KviSSL::accept() +{ + if(!m_pSSL)return NotInitialized; + int ret = SSL_accept(m_pSSL); + return connectOrAcceptError(ret); +} + +KviSSL::Result KviSSL::connectOrAcceptError(int ret) +{ + switch(SSL_get_error(m_pSSL,ret)) + { + case SSL_ERROR_NONE: return Success; break; + case SSL_ERROR_WANT_READ: return WantRead; break; + case SSL_ERROR_WANT_WRITE: return WantWrite; break; + case SSL_ERROR_ZERO_RETURN: return RemoteEndClosedConnection; break; + case SSL_ERROR_WANT_X509_LOOKUP: return ObscureError; break; + case SSL_ERROR_SYSCALL: + { + if(getLastError(true) != 0)return SSLError; + if(ret == 0)return RemoteEndClosedConnection; + return SyscallError; + } + break; + case SSL_ERROR_SSL: return SSLError; break; + default: return UnknownError; break; + } + return UnknownError; +} + +int KviSSL::read(char * buffer,int len) +{ +// if(!m_pSSL)return -1; + return SSL_read(m_pSSL,buffer,len); +} +int KviSSL::write(const char * buffer,int len) +{ +// if(!m_pSSL)return -1; + return SSL_write(m_pSSL,buffer,len); +} + +KviSSL::Result KviSSL::getProtocolError(int ret) +{ + if(!m_pSSL)return NotInitialized; + switch(SSL_get_error(m_pSSL,ret)) + { + case SSL_ERROR_NONE: return Success; break; + case SSL_ERROR_WANT_READ: return WantRead; break; + case SSL_ERROR_WANT_WRITE: return WantWrite; break; + case SSL_ERROR_ZERO_RETURN: return ZeroReturn; break; + case SSL_ERROR_WANT_X509_LOOKUP: return ObscureError; break; + case SSL_ERROR_SYSCALL: return SyscallError; break; + case SSL_ERROR_SSL: return SSLError; break; + default: return UnknownError; break; + } + return UnknownError; +} + +KviSSLCertificate * KviSSL::getPeerCertificate() +{ + if(!m_pSSL)return 0; + X509 * x509 = SSL_get_peer_certificate(m_pSSL); + if(!x509)return 0; + return new KviSSLCertificate(x509); +} + +KviSSLCipherInfo * KviSSL::getCurrentCipherInfo() +{ + if(!m_pSSL)return 0; + SSL_CIPHER * c = SSL_get_current_cipher(m_pSSL); + if(!c)return 0; + return new KviSSLCipherInfo(c); +} + + + +KviSSLCertificate::KviSSLCertificate(X509 * x509) +{ + m_pSubject = new KviPointerHashTable<const char *,KviStr>(17); + m_pSubject->setAutoDelete(true); + m_pIssuer = new KviPointerHashTable<const char *,KviStr>(17); + m_pIssuer->setAutoDelete(true); + m_pX509 = 0; + setX509(x509); +} + +KviSSLCertificate::~KviSSLCertificate() +{ + X509_free(m_pX509); + delete m_pSubject; + delete m_pIssuer; +} + +#ifdef COMPILE_ON_WINDOWS + + // On windows we need to override new and delete operators + // to ensure that always the right new/delete pair is called for an object instance + // This bug is present in all the classes exported by a module that + // can be instantiated/destroyed from external modules. + // (this is a well known bug described in Q122675 of MSDN) + + void * KviSSLCertificate::operator new(size_t tSize) + { + return kvi_malloc(tSize); + } + + void KviSSLCertificate::operator delete(void * p) + { + kvi_free(p); + } + +#endif + +void KviSSLCertificate::setX509(X509 * x509) +{ + if(m_pX509)X509_free(m_pX509); + m_pX509 = x509; + m_iVersion = X509_get_version(x509); + extractSubject(); + extractIssuer(); + extractPubKeyInfo(); + extractSerialNumber(); + extractSignature(); +} + +void KviSSLCertificate::extractSubject() +{ + char buffer[1024]; + char * t = X509_NAME_oneline(X509_get_subject_name(m_pX509),buffer,1024); + if (!t)return; + m_pSubject->clear(); + splitX509String(m_pSubject,t); +} + +void KviSSLCertificate::extractIssuer() +{ + char buffer[1024]; + char * t = X509_NAME_oneline(X509_get_issuer_name(m_pX509),buffer,1024); + if (!t)return; + m_pIssuer->clear(); + splitX509String(m_pIssuer,t); +} + +void KviSSLCertificate::splitX509String(KviPointerHashTable<const char *,KviStr> * dict,const char * t) +{ + KviStr buf = t; + int cnt; + KviStr ** arr = buf.splitToArray('/',50,&cnt); + if(arr) + { + if(cnt > 0) + { + for(int i=0;i<cnt;i++) + { + int idx = arr[i]->findFirstIdx('='); + if(idx != -1) + { + KviStr szTok = arr[i]->left(idx); + arr[i]->cutLeft(idx + 1); + if(szTok.hasData() && arr[i]->hasData()) + { + dict->replace(szTok.ptr(),new KviStr(arr[i]->ptr())); + } + } + } + } + + KviStr::freeArray(arr); + } +} + + +const char * KviSSLCertificate::dictEntry(KviPointerHashTable<const char *,KviStr> * dict,const char * entry) +{ + KviStr * t = dict->find(entry); + if(!t)return __tr("Unknown"); + return t->ptr(); +} + + +/* +void KviSSLCertificate::getPKeyType(int type,KviStr &buffer) +{ + switch(type) + { +#ifndef NO_RSA + case EVP_PKEY_RSA: buffer = "RSA"; break; +#endif +#ifndef NO_DSA + case EVP_PKEY_DSA: buffer = "DSA"; break; +#endif +#ifndef NO_DH + case EVP_PKEY_DH: buffer = "DH"; break; +#endif + case EVP_PKEY_NONE: buffer = "NONE"; break; + } +} +*/ + +void KviSSLCertificate::extractPubKeyInfo() +{ + EVP_PKEY *p = X509_get_pubkey(m_pX509); + if(p) + { + m_iPubKeyBits = EVP_PKEY_bits(p); + m_szPubKeyType = (p->type == NID_undef) ? __tr("Unknown") : OBJ_nid2ln(p->type); +// getPKeyType(p->type,m_szPubKeyType); + } else { + m_iPubKeyBits = 0; + m_szPubKeyType = "None"; + } + +} + +void KviSSLCertificate::extractSerialNumber() +{ + ASN1_INTEGER * i = X509_get_serialNumber(m_pX509); + if(i)m_iSerialNumber = ASN1_INTEGER_get(i); + else m_iSerialNumber = -1; +} + +void KviSSLCertificate::extractSignature() +{ + static char hexdigits[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + //getPKeyType(X509_get_signature_type(m_pX509),m_szSignatureType); + + int i = OBJ_obj2nid(m_pX509->sig_alg->algorithm); + m_szSignatureType = (i == NID_undef) ? __tr("Unknown") : OBJ_nid2ln(i); + + m_szSignatureContents = ""; + + for(i = 0;i < m_pX509->signature->length;i++) + { + if(m_szSignatureContents.hasData())m_szSignatureContents.append(":"); + m_szSignatureContents.append(hexdigits[(m_pX509->signature->data[i] & 0xf0) >> 4]); + m_szSignatureContents.append(hexdigits[(m_pX509->signature->data[i] & 0x0f)]); + } +} + +/* +const char * KviSSLCertificate::verify() +{ + +} +*/ + + +KviSSLCipherInfo::KviSSLCipherInfo(SSL_CIPHER * c) +{ + m_szVersion = SSL_CIPHER_get_version(c); + m_iNumBitsUsed = SSL_CIPHER_get_bits(c,&m_iNumBits); + m_szName = SSL_CIPHER_get_name(c); + char buf[1024]; + m_szDescription = SSL_CIPHER_description(c,buf,1024); +} + +KviSSLCipherInfo::~KviSSLCipherInfo() +{ +} + +#ifdef COMPILE_ON_WINDOWS + + // On windows we need to override new and delete operators + // to ensure that always the right new/delete pair is called for an object instance + // This bug is present in all the classes exported by a module that + // can be instantiated/destroyed from external modules. + // (this is a well known bug described in Q122675 of MSDN) + + void * KviSSLCipherInfo::operator new(size_t tSize) + { + return kvi_malloc(tSize); + } + + void KviSSLCipherInfo::operator delete(void * p) + { + kvi_free(p); + } + +#endif + +#endif //COMPILE_SSL_SUPPORT diff --git a/src/kvilib/net/kvi_ssl.h b/src/kvilib/net/kvi_ssl.h new file mode 100644 index 00000000..5547ecbb --- /dev/null +++ b/src/kvilib/net/kvi_ssl.h @@ -0,0 +1,180 @@ +#ifndef _KVI_SSL_H_ +#define _KVI_SSL_H_ +// +// File : kvi_ssl.h +// Creation date : Mon May 27 2002 21:36:12 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 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" + +#ifdef COMPILE_SSL_SUPPORT + +#include "kvi_string.h" +#include "kvi_sockettype.h" + +#include "kvi_pointerhashtable.h" + +#include <openssl/ssl.h> + + +class KVILIB_API KviSSLCertificate +{ +public: + KviSSLCertificate(X509 * x509); + ~KviSSLCertificate(); +protected: + X509 * m_pX509; + KviPointerHashTable<const char *,KviStr> * m_pSubject; + KviPointerHashTable<const char *,KviStr> * m_pIssuer; + int m_iPubKeyBits; + KviStr m_szPubKeyType; + int m_iSerialNumber; + int m_iVersion; + KviStr m_szSignatureType; + KviStr m_szSignatureContents; +private: + void extractSubject(); + void extractIssuer(); + void extractPubKeyInfo(); + void extractSerialNumber(); + void extractSignature(); + const char * dictEntry(KviPointerHashTable<const char *,KviStr> * dict,const char * entry); + void splitX509String(KviPointerHashTable<const char *,KviStr> * dict,const char * t); +// void getPKeyType(int type,KviStr &buffer); +public: + void setX509(X509 * x509); + + const char * signatureType(){ return m_szSignatureType.ptr(); }; + const char * signatureContents(){ return m_szSignatureContents.ptr(); }; + + const char * subjectCountry(){ return dictEntry(m_pSubject,"C"); }; + const char * subjectStateOrProvince(){ return dictEntry(m_pSubject,"ST"); }; + const char * subjectLocality(){ return dictEntry(m_pSubject,"L"); }; + const char * subjectOrganization(){ return dictEntry(m_pSubject,"O"); }; + const char * subjectOrganizationalUnit(){ return dictEntry(m_pSubject,"OU"); }; + const char * subjectCommonName(){ return dictEntry(m_pSubject,"CN"); }; + + const char * issuerCountry(){ return dictEntry(m_pIssuer,"C"); }; + const char * issuerStateOrProvince(){ return dictEntry(m_pIssuer,"ST"); }; + const char * issuerLocality(){ return dictEntry(m_pIssuer,"L"); }; + const char * issuerOrganization(){ return dictEntry(m_pIssuer,"O"); }; + const char * issuerOrganizationalUnit(){ return dictEntry(m_pIssuer,"OU"); }; + const char * issuerCommonName(){ return dictEntry(m_pIssuer,"CN"); }; + + int publicKeyBits(){ return m_iPubKeyBits; }; + const char * publicKeyType(){ return m_szPubKeyType.ptr(); }; + + int serialNumber(){ return m_iSerialNumber; }; + + int version(){ return m_iVersion; }; +#ifdef COMPILE_ON_WINDOWS + // On windows we need to override new and delete operators + // to ensure that always the right new/delete pair is called for an object instance + // This bug is present in all the classes exported by a module that + // can be instantiated/destroyed from external modules. + // (this is a well known bug described in Q122675 of MSDN) + void * operator new(size_t tSize); + void operator delete(void * p); +#endif +}; + +class KVILIB_API KviSSLCipherInfo +{ +public: + KviSSLCipherInfo(SSL_CIPHER * c); + ~KviSSLCipherInfo(); +protected: + KviStr m_szVersion; + int m_iNumBits; + int m_iNumBitsUsed; + KviStr m_szName; + KviStr m_szDescription; +public: + const char * name(){ return m_szName.ptr(); }; + const char * description(){ return m_szDescription.ptr(); }; + int bits(){ return m_iNumBits; }; + int bitsUsed(){ return m_iNumBitsUsed; }; + const char * version(){ return m_szVersion.ptr(); }; +#ifdef COMPILE_ON_WINDOWS + // On windows we need to override new and delete operators + // to ensure that always the right new/delete pair is called for an object instance + // This bug is present in all the classes exported by a module that + // can be instantiated/destroyed from external modules. + // (this is a well known bug described in Q122675 of MSDN) + void * operator new(size_t tSize); + void operator delete(void * p); +#endif +}; + +#ifdef Success + #undef Success +#endif + + +class KVILIB_API KviSSL +{ +public: + enum Method { Client , Server }; + enum Result { Success , NotInitialized , WantRead , WantWrite , ZeroReturn , FileIoError , + UnknownError , ObscureError , SSLError , SyscallError , RemoteEndClosedConnection }; +public: + KviSSL(); + ~KviSSL(); +public: + SSL * m_pSSL; + SSL_CTX * m_pSSLCtx; + KviStr m_szPass; +public: + static void globalInit(); + static void globalDestroy(); +public: + bool initSocket(kvi_socket_t fd); + bool initContext(KviSSL::Method m); + void shutdown(); + KviSSL::Result connect(); + KviSSL::Result accept(); + int read(char * buffer,int len); + int write(const char * buffer,int len); + // SSL ERRORS + unsigned long getLastError(bool bPeek = false); + bool getLastErrorString(KviStr &buffer,bool bPeek = false); + // Protocol error + KviSSL::Result getProtocolError(int ret); + KviSSLCertificate * getPeerCertificate(); + KviSSLCipherInfo * getCurrentCipherInfo(); + KviSSL::Result useCertificateFile(const char * cert,const char * pass); + KviSSL::Result usePrivateKeyFile(const char * key,const char * pass); +#ifdef COMPILE_ON_WINDOWS + // On windows we need to override new and delete operators + // to ensure that always the right new/delete pair is called for an object instance + // This bug is present in all the classes exported by a module that + // can be instantiated/destroyed from external modules. + // (this is a well known bug described in Q122675 of MSDN) + void * operator new(size_t tSize); + void operator delete(void * p); +#endif +private: + KviSSL::Result connectOrAcceptError(int ret); +}; + + +#endif //COMPILE_SSL_SUPPORT + +#endif //_KVI_SSL_H_ diff --git a/src/kvilib/net/kvi_url.cpp b/src/kvilib/net/kvi_url.cpp new file mode 100644 index 00000000..f980729c --- /dev/null +++ b/src/kvilib/net/kvi_url.cpp @@ -0,0 +1,164 @@ +// +// File : kvi_url.cpp +// Creation date : Sat Aug 17 14:09:18 2002 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 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. +// + + +#define __KVILIB__ + + +#include "kvi_url.h" + +KviUrl::KviUrl() +{ +} + +KviUrl::KviUrl(const KviUrl & u) +{ + *this = u; +} + +KviUrl::KviUrl(const char * szUrl) +{ + m_szUrl = szUrl; + parse(); +} + +KviUrl::KviUrl(const QString &szUrl) +{ + m_szUrl = szUrl; + parse(); +} + +KviUrl::~KviUrl() +{ + +} + +void KviUrl::parse() +{ + m_szProtocol = ""; + m_szHost = ""; + m_szPath = ""; + m_szUser = ""; + m_szPass = ""; + + m_szUrl.stripWhiteSpace(); + + KviStr u = m_szUrl; + + // proto + + kvi_u32_t uDefaultPort = 80; + + int i = u.findFirstIdx(":/"); + if(i != -1) + { + // there is a protocol path + m_szProtocol = u.left(i); + u.cutLeft(i + 2); + u.stripLeft('/'); + u.stripWhiteSpace(); + + // fix the default ports + if(kvi_strEqualCI(m_szProtocol,"https"))uDefaultPort = 443; + else if(kvi_strEqualCI(m_szProtocol,"ftp"))uDefaultPort = 21; + } else { + // no proto... assume http + u.stripLeft('/'); + m_szProtocol = "http"; + } + + m_uPort = uDefaultPort; + + // user and pass + + i = u.findFirstIdx('@'); + + if(i != -1) + { + KviStr szUserPass = u.left(i); + szUserPass.stripWhiteSpace(); + u.cutLeft(i + 1); + + i = szUserPass.findFirstIdx(':'); + if(i != -1) + { + m_szUser = szUserPass.left(i); + szUserPass.cutLeft(i + 1); + m_szPass = szUserPass; + m_szPass.stripWhiteSpace(); + } else { + m_szUser = szUserPass; + } + } + + // host + + i = u.findFirstIdx('/'); + if(i != -1) + { + KviStr h = u.left(i); + u.cutLeft(i + 1); + i = h.findFirstIdx(':'); + if(i != -1) + { + // has a port part + m_szHost = h.left(i); + h.cutLeft(i + 1); + h.stripWhiteSpace(); + bool bOk; + m_uPort = h.toUInt(&bOk); + if(!bOk)m_uPort = uDefaultPort; + } else { + // no port : assume default + m_szHost = h; + } + m_szPath = u; + } else { + m_szHost = u; + } + + m_szHost.stripWhiteSpace(); + m_szPath.stripWhiteSpace(); + if(!m_szPath.firstCharIs('/'))m_szPath.prepend('/'); +} + + +KviUrl & KviUrl::operator=(const char * szUrl) +{ + m_szUrl = szUrl; + parse(); + return *this; +} + +KviUrl & KviUrl::operator=(const KviUrl &u) +{ + m_szUrl = u.m_szUrl; + m_szProtocol = u.m_szProtocol; + m_szHost = u.m_szHost; + m_szPath = u.m_szPath; + m_szUser = u.m_szUser; + m_szPass = u.m_szPass; + m_uPort = u.m_uPort; + return *this; +} + + diff --git a/src/kvilib/net/kvi_url.h b/src/kvilib/net/kvi_url.h new file mode 100644 index 00000000..89adeb9f --- /dev/null +++ b/src/kvilib/net/kvi_url.h @@ -0,0 +1,63 @@ +#ifndef _KVI_URL_H_ +#define _KVI_URL_H_ +// +// File : kvi_url.h +// Creation date : Sat Aug 17 14:09:16 2002 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 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_string.h" +#include "kvi_heapobject.h" +#include "kvi_inttypes.h" + +class KVILIB_API KviUrl : public KviHeapObject +{ +public: + KviUrl(); + KviUrl(const char * szUrl); + KviUrl(const QString &szUrl); + KviUrl(const KviUrl &u); + ~KviUrl(); +protected: + KviStr m_szUrl; + + KviStr m_szProtocol; + KviStr m_szHost; + KviStr m_szPath; + KviStr m_szUser; + KviStr m_szPass; + kvi_u32_t m_uPort; +protected: + void parse(); +public: + const KviStr & url() const { return m_szUrl; }; + const KviStr & protocol() const { return m_szProtocol; }; + const KviStr & host() const { return m_szHost; }; + const KviStr & path() const { return m_szPath; }; + const KviStr & user() const { return m_szUser; }; + const KviStr & pass() const { return m_szPass; }; + kvi_u32_t port() const { return m_uPort; }; + + KviUrl & operator = (const char * szUrl); + KviUrl & operator = (const KviUrl &u); + +}; + + +#endif //_KVI_URL_H_ diff --git a/src/kvilib/net/moc_kvi_dns.cpp b/src/kvilib/net/moc_kvi_dns.cpp new file mode 100644 index 00000000..5b8857a9 --- /dev/null +++ b/src/kvilib/net/moc_kvi_dns.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** KviDns meta object code from reading C++ file 'kvi_dns.h' +** +** Created: Sun Mar 23 20:56:20 2008 +** by: The Qt MOC ($Id: qt/moc_yacc.cpp 3.3.8 edited Feb 2 14:59 $) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#undef QT_NO_COMPAT +#include "kvi_dns.h" +#include <qmetaobject.h> +#include <qapplication.h> + +#include <private/qucomextra_p.h> +#if !defined(Q_MOC_OUTPUT_REVISION) || (Q_MOC_OUTPUT_REVISION != 26) +#error "This file was generated using the moc from 3.3.8. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +#include <qvariant.h> +const char *KviDns::className() const +{ + return "KviDns"; +} + +QMetaObject *KviDns::metaObj = 0; +static QMetaObjectCleanUp cleanUp_KviDns( "KviDns", &KviDns::staticMetaObject ); + +#ifndef QT_NO_TRANSLATION +QString KviDns::tr( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviDns", s, c, QApplication::DefaultCodec ); + else + return QString::fromLatin1( s ); +} +#ifndef QT_NO_TRANSLATION_UTF8 +QString KviDns::trUtf8( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviDns", s, c, QApplication::UnicodeUTF8 ); + else + return QString::fromUtf8( s ); +} +#endif // QT_NO_TRANSLATION_UTF8 + +#endif // QT_NO_TRANSLATION + +QMetaObject* KviDns::staticMetaObject() +{ + if ( metaObj ) + return metaObj; + QMetaObject* parentObject = QObject::staticMetaObject(); + static const QUParameter param_signal_0[] = { + { 0, &static_QUType_ptr, "KviDns", QUParameter::In } + }; + static const QUMethod signal_0 = {"lookupDone", 1, param_signal_0 }; + static const QMetaData signal_tbl[] = { + { "lookupDone(KviDns*)", &signal_0, QMetaData::Private } + }; +#ifndef QT_NO_PROPERTIES + static const QMetaProperty props_tbl[1] = { + { "bool","blockingDelete", 0x12000001, &KviDns::metaObj, 0, -1 } + }; +#endif // QT_NO_PROPERTIES + metaObj = QMetaObject::new_metaobject( + "KviDns", parentObject, + 0, 0, + signal_tbl, 1, +#ifndef QT_NO_PROPERTIES + props_tbl, 1, + 0, 0, +#endif // QT_NO_PROPERTIES + 0, 0 ); + cleanUp_KviDns.setMetaObject( metaObj ); + return metaObj; +} + +void* KviDns::qt_cast( const char* clname ) +{ + if ( !qstrcmp( clname, "KviDns" ) ) + return this; + if ( !qstrcmp( clname, "KviHeapObject" ) ) + return (KviHeapObject*)this; + return QObject::qt_cast( clname ); +} + +#include <qobjectdefs.h> +#include <qsignalslotimp.h> + +// SIGNAL lookupDone +void KviDns::lookupDone( KviDns* t0 ) +{ + if ( signalsBlocked() ) + return; + QConnectionList *clist = receivers( staticMetaObject()->signalOffset() + 0 ); + if ( !clist ) + return; + QUObject o[2]; + static_QUType_ptr.set(o+1,t0); + activate_signal( clist, o ); +} + +bool KviDns::qt_invoke( int _id, QUObject* _o ) +{ + return QObject::qt_invoke(_id,_o); +} + +bool KviDns::qt_emit( int _id, QUObject* _o ) +{ + switch ( _id - staticMetaObject()->signalOffset() ) { + case 0: lookupDone((KviDns*)static_QUType_ptr.get(_o+1)); break; + default: + return QObject::qt_emit(_id,_o); + } + return TRUE; +} +#ifndef QT_NO_PROPERTIES + +bool KviDns::qt_property( int id, int f, QVariant* v) +{ + switch ( id - staticMetaObject()->propertyOffset() ) { + case 0: switch( f ) { + case 1: *v = QVariant( this->isRunning(), 0 ); break; + case 3: case 4: case 5: break; + default: return FALSE; + } break; + default: + return QObject::qt_property( id, f, v ); + } + return TRUE; +} + +bool KviDns::qt_static_property( QObject* , int , int , QVariant* ){ return FALSE; } +#endif // QT_NO_PROPERTIES diff --git a/src/kvilib/net/moc_kvi_http.cpp b/src/kvilib/net/moc_kvi_http.cpp new file mode 100644 index 00000000..7ea9b591 --- /dev/null +++ b/src/kvilib/net/moc_kvi_http.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** KviHttpRequest meta object code from reading C++ file 'kvi_http.h' +** +** Created: Sun Mar 23 20:56:22 2008 +** by: The Qt MOC ($Id: qt/moc_yacc.cpp 3.3.8 edited Feb 2 14:59 $) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#undef QT_NO_COMPAT +#include "kvi_http.h" +#include <qmetaobject.h> +#include <qapplication.h> + +#include <private/qucomextra_p.h> +#if !defined(Q_MOC_OUTPUT_REVISION) || (Q_MOC_OUTPUT_REVISION != 26) +#error "This file was generated using the moc from 3.3.8. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +const char *KviHttpRequest::className() const +{ + return "KviHttpRequest"; +} + +QMetaObject *KviHttpRequest::metaObj = 0; +static QMetaObjectCleanUp cleanUp_KviHttpRequest( "KviHttpRequest", &KviHttpRequest::staticMetaObject ); + +#ifndef QT_NO_TRANSLATION +QString KviHttpRequest::tr( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviHttpRequest", s, c, QApplication::DefaultCodec ); + else + return QString::fromLatin1( s ); +} +#ifndef QT_NO_TRANSLATION_UTF8 +QString KviHttpRequest::trUtf8( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviHttpRequest", s, c, QApplication::UnicodeUTF8 ); + else + return QString::fromUtf8( s ); +} +#endif // QT_NO_TRANSLATION_UTF8 + +#endif // QT_NO_TRANSLATION + +QMetaObject* KviHttpRequest::staticMetaObject() +{ + if ( metaObj ) + return metaObj; + QMetaObject* parentObject = QObject::staticMetaObject(); + static const QUParameter param_slot_0[] = { + { "d", &static_QUType_ptr, "KviDns", QUParameter::In } + }; + static const QUMethod slot_0 = {"dnsLookupDone", 1, param_slot_0 }; + static const QUMethod slot_1 = {"haveServerIp", 0, 0 }; + static const QMetaData slot_tbl[] = { + { "dnsLookupDone(KviDns*)", &slot_0, QMetaData::Protected }, + { "haveServerIp()", &slot_1, QMetaData::Protected } + }; + static const QUParameter param_signal_0[] = { + { "hostname", &static_QUType_QString, 0, QUParameter::In } + }; + static const QUMethod signal_0 = {"resolvingHost", 1, param_signal_0 }; + static const QUParameter param_signal_1[] = { + { "ipandport", &static_QUType_QString, 0, QUParameter::In } + }; + static const QUMethod signal_1 = {"contactingHost", 1, param_signal_1 }; + static const QUMethod signal_2 = {"connectionEstabilished", 0, 0 }; + static const QUParameter param_signal_3[] = { + { "response", &static_QUType_QString, 0, QUParameter::In } + }; + static const QUMethod signal_3 = {"receivedResponse", 1, param_signal_3 }; + static const QUParameter param_signal_4[] = { + { "bSuccess", &static_QUType_bool, 0, QUParameter::In } + }; + static const QUMethod signal_4 = {"terminated", 1, param_signal_4 }; + static const QUParameter param_signal_5[] = { + { "message", &static_QUType_QString, 0, QUParameter::In } + }; + static const QUMethod signal_5 = {"status", 1, param_signal_5 }; + static const QUParameter param_signal_6[] = { + { "data", &static_QUType_ptr, "KviStr", QUParameter::In } + }; + static const QUMethod signal_6 = {"data", 1, param_signal_6 }; + static const QUParameter param_signal_7[] = { + { "data", &static_QUType_ptr, "KviDataBuffer", QUParameter::In } + }; + static const QUMethod signal_7 = {"binaryData", 1, param_signal_7 }; + static const QUParameter param_signal_8[] = { + { "hdr", &static_QUType_ptr, "KviPointerHashTable<const char*,KviStr>", QUParameter::In } + }; + static const QUMethod signal_8 = {"header", 1, param_signal_8 }; + static const QUParameter param_signal_9[] = { + { "request", &static_QUType_varptr, "\x04", QUParameter::In } + }; + static const QUMethod signal_9 = {"requestSent", 1, param_signal_9 }; + static const QMetaData signal_tbl[] = { + { "resolvingHost(const QString&)", &signal_0, QMetaData::Public }, + { "contactingHost(const QString&)", &signal_1, QMetaData::Public }, + { "connectionEstabilished()", &signal_2, QMetaData::Public }, + { "receivedResponse(const QString&)", &signal_3, QMetaData::Public }, + { "terminated(bool)", &signal_4, QMetaData::Public }, + { "status(const QString&)", &signal_5, QMetaData::Public }, + { "data(const KviStr&)", &signal_6, QMetaData::Public }, + { "binaryData(const KviDataBuffer&)", &signal_7, QMetaData::Public }, + { "header(KviPointerHashTable<const char*,KviStr>*)", &signal_8, QMetaData::Public }, + { "requestSent(const QStringList&)", &signal_9, QMetaData::Public } + }; + metaObj = QMetaObject::new_metaobject( + "KviHttpRequest", parentObject, + slot_tbl, 2, + signal_tbl, 10, +#ifndef QT_NO_PROPERTIES + 0, 0, + 0, 0, +#endif // QT_NO_PROPERTIES + 0, 0 ); + cleanUp_KviHttpRequest.setMetaObject( metaObj ); + return metaObj; +} + +void* KviHttpRequest::qt_cast( const char* clname ) +{ + if ( !qstrcmp( clname, "KviHttpRequest" ) ) + return this; + if ( !qstrcmp( clname, "KviHeapObject" ) ) + return (KviHeapObject*)this; + return QObject::qt_cast( clname ); +} + +// SIGNAL resolvingHost +void KviHttpRequest::resolvingHost( const QString& t0 ) +{ + activate_signal( staticMetaObject()->signalOffset() + 0, t0 ); +} + +// SIGNAL contactingHost +void KviHttpRequest::contactingHost( const QString& t0 ) +{ + activate_signal( staticMetaObject()->signalOffset() + 1, t0 ); +} + +// SIGNAL connectionEstabilished +void KviHttpRequest::connectionEstabilished() +{ + activate_signal( staticMetaObject()->signalOffset() + 2 ); +} + +// SIGNAL receivedResponse +void KviHttpRequest::receivedResponse( const QString& t0 ) +{ + activate_signal( staticMetaObject()->signalOffset() + 3, t0 ); +} + +// SIGNAL terminated +void KviHttpRequest::terminated( bool t0 ) +{ + activate_signal_bool( staticMetaObject()->signalOffset() + 4, t0 ); +} + +// SIGNAL status +void KviHttpRequest::status( const QString& t0 ) +{ + activate_signal( staticMetaObject()->signalOffset() + 5, t0 ); +} + +#include <qobjectdefs.h> +#include <qsignalslotimp.h> + +// SIGNAL data +void KviHttpRequest::data( const KviStr& t0 ) +{ + if ( signalsBlocked() ) + return; + QConnectionList *clist = receivers( staticMetaObject()->signalOffset() + 6 ); + if ( !clist ) + return; + QUObject o[2]; + static_QUType_ptr.set(o+1,&t0); + activate_signal( clist, o ); +} + +// SIGNAL binaryData +void KviHttpRequest::binaryData( const KviDataBuffer& t0 ) +{ + if ( signalsBlocked() ) + return; + QConnectionList *clist = receivers( staticMetaObject()->signalOffset() + 7 ); + if ( !clist ) + return; + QUObject o[2]; + static_QUType_ptr.set(o+1,&t0); + activate_signal( clist, o ); +} + +// SIGNAL header +void KviHttpRequest::header( KviPointerHashTable<const char*,KviStr>* t0 ) +{ + if ( signalsBlocked() ) + return; + QConnectionList *clist = receivers( staticMetaObject()->signalOffset() + 8 ); + if ( !clist ) + return; + QUObject o[2]; + static_QUType_ptr.set(o+1,t0); + activate_signal( clist, o ); +} + +// SIGNAL requestSent +void KviHttpRequest::requestSent( const QStringList& t0 ) +{ + if ( signalsBlocked() ) + return; + QConnectionList *clist = receivers( staticMetaObject()->signalOffset() + 9 ); + if ( !clist ) + return; + QUObject o[2]; + static_QUType_varptr.set(o+1,&t0); + activate_signal( clist, o ); +} + +bool KviHttpRequest::qt_invoke( int _id, QUObject* _o ) +{ + switch ( _id - staticMetaObject()->slotOffset() ) { + case 0: dnsLookupDone((KviDns*)static_QUType_ptr.get(_o+1)); break; + case 1: haveServerIp(); break; + default: + return QObject::qt_invoke( _id, _o ); + } + return TRUE; +} + +bool KviHttpRequest::qt_emit( int _id, QUObject* _o ) +{ + switch ( _id - staticMetaObject()->signalOffset() ) { + case 0: resolvingHost((const QString&)static_QUType_QString.get(_o+1)); break; + case 1: contactingHost((const QString&)static_QUType_QString.get(_o+1)); break; + case 2: connectionEstabilished(); break; + case 3: receivedResponse((const QString&)static_QUType_QString.get(_o+1)); break; + case 4: terminated((bool)static_QUType_bool.get(_o+1)); break; + case 5: status((const QString&)static_QUType_QString.get(_o+1)); break; + case 6: data((const KviStr&)*((const KviStr*)static_QUType_ptr.get(_o+1))); break; + case 7: binaryData((const KviDataBuffer&)*((const KviDataBuffer*)static_QUType_ptr.get(_o+1))); break; + case 8: header((KviPointerHashTable<const char*,KviStr>*)static_QUType_ptr.get(_o+1)); break; + case 9: requestSent((const QStringList&)*((const QStringList*)static_QUType_ptr.get(_o+1))); break; + default: + return QObject::qt_emit(_id,_o); + } + return TRUE; +} +#ifndef QT_NO_PROPERTIES + +bool KviHttpRequest::qt_property( int id, int f, QVariant* v) +{ + return QObject::qt_property( id, f, v); +} + +bool KviHttpRequest::qt_static_property( QObject* , int , int , QVariant* ){ return FALSE; } +#endif // QT_NO_PROPERTIES |