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/modules/ident/libkviident.cpp | |
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/modules/ident/libkviident.cpp')
-rw-r--r-- | src/modules/ident/libkviident.cpp | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/src/modules/ident/libkviident.cpp b/src/modules/ident/libkviident.cpp new file mode 100644 index 00000000..c8f4f194 --- /dev/null +++ b/src/modules/ident/libkviident.cpp @@ -0,0 +1,615 @@ +// +// File : libkviident.cpp +// Creation date : Tue Oct 2 18:22:04 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. +// + +#include "kvi_module.h" + +#include "libkviident.h" + +#include "kvi_socket.h" +#include "kvi_app.h" +#include "kvi_out.h" +#include "kvi_netutils.h" +#include "kvi_locale.h" +#include "kvi_window.h" + +#include "kvi_options.h" +#include "kvi_defaults.h" + +#define KVI_IDENT_THREAD_EVENT_EXITING KVI_THREAD_USER_EVENT_BASE + 111 +#define KVI_IDENT_THREAD_EVENT_EXITING_ON_REQUEST KVI_THREAD_USER_EVENT_BASE + 112 + + +// FIXME: Should have a timeout on the requests!!! + +static KviIdentDaemon * g_pIdentDaemon = 0; +static KviIdentSentinel * g_pIdentSentinel = 0; + +extern KVIRC_API int g_iIdentDaemonRunningUsers; + +void startIdentService() +{ +// debug("Stargin"); + if(!g_pIdentDaemon)g_pIdentDaemon = new KviIdentDaemon(); + if(!g_pIdentDaemon->isRunning())g_pIdentDaemon->start(); + while(g_pIdentDaemon->isStartingUp()) + { +#ifdef COMPILE_ON_WINDOWS + Sleep(10); +#else + usleep(100); +#endif + } +// debug("Service started"); +} + +void stopIdentService() +{ +// debug("Stopping"); + if(g_pIdentDaemon)delete g_pIdentDaemon; + g_pIdentDaemon = 0; +// debug("Stopped"); +} + +KviIdentSentinel::KviIdentSentinel() +: QObject(0) +{ +} + +KviIdentSentinel::~KviIdentSentinel() +{ + KviThreadManager::killPendingEvents(this); +} + +bool KviIdentSentinel::event(QEvent *e) +{ + if(KVI_OPTION_UINT(KviOption_uintIdentdOutputMode)==KviIdentdOutputMode::Quiet || !g_pActiveWindow) + return QObject::event(e); + + KviWindow * pTarget = KVI_OPTION_UINT(KviOption_uintIdentdOutputMode)==KviIdentdOutputMode::ToActiveWindow ? + (KviWindow *)g_pActiveWindow : (KviWindow *)g_pApp->activeConsole(); + + if(e->type() == KVI_THREAD_EVENT) + { + if(((KviThreadEvent *)e)->id() == KVI_THREAD_EVENT_DATA) + { + KviIdentMessageData * d = ((KviThreadDataEvent<KviIdentMessageData> *)e)->getData(); + if(pTarget) + { + if(d->szHost.hasData()) + { + if(d->szAux.hasData()) + { + if(_OUTPUT_PARANOIC) + pTarget->output(KVI_OUT_IDENT,__tr("%s (%s) (%s:%u)"),d->szMessage.ptr(),d->szAux.ptr(),d->szHost.ptr(),d->uPort); + else + pTarget->output(KVI_OUT_IDENT,__tr("%s (%s)"),d->szMessage.ptr(),d->szAux.ptr()); + } else { + if(_OUTPUT_PARANOIC) + pTarget->output(KVI_OUT_IDENT,__tr("%s (%s:%u)"),d->szMessage.ptr(),d->szHost.ptr(),d->uPort); + else + pTarget->output(KVI_OUT_IDENT,__tr("%s"),d->szMessage.ptr()); + } + } else { + pTarget->output(KVI_OUT_IDENT,__tr("[IDENT]: %s"),d->szMessage.ptr()); + } + } + delete d; + } else if(((KviThreadEvent *)e)->id() == KVI_IDENT_THREAD_EVENT_EXITING) + { + if(_OUTPUT_VERBOSE) + if(pTarget)pTarget->outputNoFmt(KVI_OUT_IDENT,__tr("Shutting down identd service (spontaneous action)")); + stopIdentService(); + } else if(((KviThreadEvent *)e)->id() == KVI_IDENT_THREAD_EVENT_EXITING_ON_REQUEST) + { + if(_OUTPUT_VERBOSE) + if(pTarget)pTarget->outputNoFmt(KVI_OUT_IDENT,__tr("Shutting down identd service (requested action)")); + } + + return true; + } + + return QObject::event(e); +} + + +KviIdentRequest::KviIdentRequest(kvi_socket_t sock,const char * host,kvi_u32_t uPort) +{ + m_sock = sock; + m_szHost = host; + m_uPort = uPort; + m_tStart = time(0); +} + +KviIdentRequest::~KviIdentRequest() +{ + kvi_socket_close(m_sock); +} + + + +KviIdentDaemon::KviIdentDaemon() +: KviSensitiveThread() +{ +// debug("Thread constructor"); + m_szUser = KVI_OPTION_STRING(KviOption_stringIdentdUser); + if(m_szUser.isEmpty())m_szUser = "kvirc"; + m_uPort = KVI_OPTION_UINT(KviOption_uintIdentdPort); +#ifdef COMPILE_IPV6_SUPPORT + m_bEnableIpV6 = KVI_OPTION_BOOL(KviOption_boolIdentdEnableIpV6); +#else + m_bEnableIpV6 = false; +#endif + m_bIpV6ContainsIpV4 = KVI_OPTION_BOOL(KviOption_boolIdentdIpV6ContainsIpV4); +// debug("Thread constructor done"); +} + +KviIdentDaemon::~KviIdentDaemon() +{ +// debug("Thread destructor"); + terminate(); + g_iIdentDaemonRunningUsers = 0; + + g_pIdentDaemon = 0; +// debug("Destructor gone"); +} + +void KviIdentDaemon::postMessage(const char * message,KviIdentRequest * r,const char * szAux) +{ + KviThreadDataEvent<KviIdentMessageData> * e = new KviThreadDataEvent<KviIdentMessageData>(KVI_THREAD_EVENT_DATA); + + KviIdentMessageData * d = new KviIdentMessageData; + + d->szMessage = message; + if(szAux)d->szAux = szAux; + + if(r) + { + d->szHost = r->m_szHost; + d->uPort = r->m_uPort; + } + + e->setData(d); + postEvent(g_pIdentSentinel,e); +} + +void KviIdentDaemon::run() +{ +// debug("RUN STARTED"); + m_sock = KVI_INVALID_SOCKET; + m_sock6 = KVI_INVALID_SOCKET; + bool bEventPosted = false; + + m_pRequestList = new KviPointerList<KviIdentRequest>; + m_pRequestList->setAutoDelete(true); + + KviPointerList<KviIdentRequest> dying; + dying.setAutoDelete(false); + +#ifdef COMPILE_IPV6_SUPPORT + // If we have enabled ipv6 and we have to use a single socket: this one is IPV6 + // otherwise this one is IPV4 + KviSockaddr sa(m_uPort,m_bEnableIpV6 && m_bIpV6ContainsIpV4); +#else + KviSockaddr sa(m_uPort,false); +#endif + + KviIdentRequest * r; + +#ifdef COMPILE_IPV6_SUPPORT + m_sock = kvi_socket_create((m_bEnableIpV6 && m_bIpV6ContainsIpV4) ? KVI_SOCKET_PF_INET6 : KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP); +#else + m_sock = kvi_socket_create(KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP); +#endif + + if(m_sock == KVI_INVALID_SOCKET) + { + postMessage(__tr("Can't start the ident service : socket() failed"),0); + goto exit_thread; + } + + if(!kvi_socket_setNonBlocking(m_sock)) + { + postMessage(__tr("Can't start the ident service : async setting failed"),0); + goto exit_thread; + } + + if(!sa.socketAddress()) + { + postMessage(__tr("Can't enable the ident service : can't setup the listen address"),0); + goto exit_thread; + } + + if(!kvi_socket_bind(m_sock,sa.socketAddress(),((int)(sa.addressLength())))) + { + postMessage(__tr("Can't start the ident service : bind() failed"),0); + goto exit_thread; + } + + if(!kvi_socket_listen(m_sock,128)) + { + postMessage(__tr("Can't start the ident service : listen() failed"),0); + goto exit_thread; + } + + +#ifdef COMPILE_IPV6_SUPPORT + if(m_bEnableIpV6 && (!m_bIpV6ContainsIpV4)) + { + // Need to start the IPV6 socket too + KviSockaddr sa6(m_uPort,true); + m_sock6 = kvi_socket_create(KVI_SOCKET_PF_INET6,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP); + + if(m_sock6 == KVI_INVALID_SOCKET) + { + postMessage(__tr("Can't start the ident service on IpV6 : socket() failed"),0); + goto ipv6_failure; + } + + if(!kvi_socket_setNonBlocking(m_sock6)) + { + postMessage(__tr("Can't start the ident service on IpV6 : async setting failed"),0); + kvi_socket_close(m_sock6); + m_sock6 = KVI_INVALID_SOCKET; + goto ipv6_failure; + } + + if(!sa6.socketAddress()) + { + postMessage(__tr("Can't enable the ident service on IpV6 : can't setup the listen address"),0); + kvi_socket_close(m_sock6); + m_sock6 = KVI_INVALID_SOCKET; + goto ipv6_failure; + } + + if(!kvi_socket_bind(m_sock6,sa6.socketAddress(),((int)(sa6.addressLength())))) + { + postMessage(__tr("Can't start the ident service on IpV6 : bind() failed"),0); + kvi_socket_close(m_sock6); + m_sock6 = KVI_INVALID_SOCKET; + goto ipv6_failure; + + } + + if(!kvi_socket_listen(m_sock6,128)) + { + postMessage(__tr("Can't start the ident service on IpV6 : listen() failed"),0); + kvi_socket_close(m_sock6); + m_sock6 = KVI_INVALID_SOCKET; + goto ipv6_failure; + } + + } +#endif + +ipv6_failure: + +#ifdef COMPILE_IPV6_SUPPORT + if(m_bEnableIpV6) + { + if(m_sock6 != KVI_INVALID_SOCKET) { + if(_OUTPUT_PARANOIC) + postMessage(__tr("Starting identd service (IpV4/V6 on separate namespaces)"),0); + } else { + if(_OUTPUT_PARANOIC) + postMessage(__tr("Starting identd service (IpV4/V6 in IpV6 namespace)"),0); + } + + } else { + if(_OUTPUT_PARANOIC) + postMessage(__tr("Starting identd service (IpV4)"),0); + } +#else //!COMPILE_IPV6_SUPPORT + if(_OUTPUT_PARANOIC) + postMessage(__tr("Service startup (IpV4)"),0); +#endif //!COMPILE_IPV6_SUPPORT + + + for(;;) + { + + if(KviThreadEvent * e = dequeueEvent()) + { + // This can be ONLY a terminate event + delete e; + goto exit_on_request; + } + + struct timeval t; + t.tv_sec = 0; + t.tv_usec = 10000; + + int nmax = 0; + + fd_set rs; + FD_ZERO(&rs); + + if(m_sock != KVI_INVALID_SOCKET) + { + FD_SET(m_sock,&rs); + if(((unsigned int)m_sock) > ((unsigned int)nmax))nmax = m_sock; + } +#ifdef COMPILE_IPV6_SUPPORT + if(m_sock6 != KVI_INVALID_SOCKET) + { + FD_SET(m_sock6,&rs); + if(((unsigned int)m_sock6) > ((unsigned int)nmax))nmax = m_sock6; + } +#endif + + for(r = m_pRequestList->first();r;r = m_pRequestList->next()) + { + FD_SET(r->m_sock,&rs); + if(((unsigned int)r->m_sock) > ((unsigned int)nmax))nmax = r->m_sock; + } + + // FIXME: SO_REUSEADDR ? + + + int ret = kvi_socket_select(nmax + 1,&rs,0,0,&t); + + + if(ret == 0)msleep(100); + else { + + + if(m_sock != KVI_INVALID_SOCKET) + { + if(FD_ISSET(m_sock,&rs)) + { +#ifdef COMPILE_IPV6_SUPPORT + KviSockaddr satmp(0,m_bEnableIpV6 && m_bIpV6ContainsIpV4); +#else + KviSockaddr satmp(0,false); +#endif + int salen = (int)satmp.addressLength(); + + kvi_socket_t t = kvi_socket_accept(m_sock,satmp.socketAddress(),&salen); + if(t != KVI_INVALID_SOCKET) + { + QString szHost; + if(!satmp.getStringAddress(szHost))szHost = "unknown"; + KviIdentRequest * r = new KviIdentRequest(t,szHost.utf8().data(),satmp.port()); + m_pRequestList->append(r); + postMessage(__tr("Identd accepting connection"),r); + } + } + } + +#ifdef COMPILE_IPV6_SUPPORT + if(m_sock6 != KVI_INVALID_SOCKET) + { + if(FD_ISSET(m_sock6,&rs)) + { + KviSockaddr satmp(0,true); + int salen = (int)satmp.addressLength(); + + kvi_socket_t t = kvi_socket_accept(m_sock6,satmp.socketAddress(),&salen); + if(t != KVI_INVALID_SOCKET) + { + QString szHost; + if(!satmp.getStringAddress(szHost))szHost = "unknown"; + KviIdentRequest * r = new KviIdentRequest(t,szHost.utf8().data(),satmp.port()); + m_pRequestList->append(r); + postMessage(__tr("Identd accepting connection"),r); + } + } + } +#endif + + for(r = m_pRequestList->first();r;r = m_pRequestList->next()) + { + if(FD_ISSET(r->m_sock,&rs)) + { + char buffer[1025]; + int readed = kvi_socket_recv(r->m_sock,buffer,1024); + if(readed > 0) + { + buffer[readed] = '\0'; + r->m_szData.append(buffer); + } else { + // error ? + if(readed < 0) + { + int err = kvi_socket_error(); + if(!kvi_socket_recoverableConnectError(err)) + { + postMessage(__tr("Identd socket error : dropping connection"),r); + dying.append(r); + } + } else { + // connection closed + postMessage(__tr("Identd connection closed by remote host"),r); + dying.append(r); + } + } + } + } + + for(r = m_pRequestList->first();r;r = m_pRequestList->next()) + { + + int idx = r->m_szData.findFirstIdx('\n'); + + if(idx != -1) + { + // Ok...parse the request + KviStr szReq = r->m_szData.left(idx); + r->m_szData.cutLeft(idx + 1); + szReq.stripWhiteSpace(); + + if(szReq.hasData()) + { + postMessage(__tr("Identd processing request"),r,szReq.ptr()); + + if(kvi_strEqualCI("VERSION",szReq.ptr())) + { + KviStr reply("Quad-Echelon 7.12-r-244"); + kvi_socket_write(r->m_sock,reply.ptr(),reply.len()); + } else { + KviStr reply(KviStr::Format,"%s : USERID : UNIX : %s\r\n",szReq.ptr(),m_szUser.ptr()); + kvi_socket_write(r->m_sock,reply.ptr(),reply.len()); + } + + dying.append(r); + + } else { + + postMessage(__tr("Empty request (EOT ?)"),r,szReq.ptr()); + + dying.append(r); + } + + } else { + // debug("Data is : (%s)",r->m_szData.ptr()); + if(r->m_szData.len() > 1024) + { + // request too long...kill it + dying.append(r); + postMessage(__tr("Dropping connection (request too long)"),r); + } + } + + } + } + + time_t curTime = time(0); + + for(r = m_pRequestList->first();r;r = m_pRequestList->next()) + { + if((unsigned int)(curTime - r->m_tStart) > 30) + { + postMessage(__tr("Timed out while waiting for the request : dropping connection"),r); + dying.append(r); + } + } + + for(KviIdentRequest * ir = dying.first();ir;ir = dying.next()) + m_pRequestList->removeRef(ir); + + dying.clear(); + + } + + + +exit_on_request: + + postEvent(g_pIdentSentinel,new KviThreadEvent(KVI_IDENT_THREAD_EVENT_EXITING_ON_REQUEST)); + bEventPosted = true; + +exit_thread: + + if(!bEventPosted) + postEvent(g_pIdentSentinel,new KviThreadEvent(KVI_IDENT_THREAD_EVENT_EXITING)); + + + if(m_sock != KVI_INVALID_SOCKET)kvi_socket_close(m_sock); + if(m_sock6 != KVI_INVALID_SOCKET)kvi_socket_close(m_sock6); + delete m_pRequestList; + m_pRequestList = 0; + +// debug("RUN EXITING"); +} + + +/* + @doc: ident.start + @type: + command + @title: + ident.start + @short: + Starts the builtin ident service + @syntax: + ident.start + @description: + Starts the builtin ident service.[br] + WARNING: the kvirc ident service is just a partial implementation + of the RFC specifications. You should use is ONLY if you can't get + any other ident daemon running on your machine.[br] +*/ + +static bool ident_kvs_cmd_start(KviKvsModuleCommandCall * c) +{ + if(!g_iIdentDaemonRunningUsers) + startIdentService(); + g_iIdentDaemonRunningUsers++; + return true; +} + +/* + @doc: ident.stop + @type: + command + @title: + ident.stop + @short: + Stops the ident service + @syntax: + ident.stop + @description: + Stops the ident service + @seealso: + [cmd]ident.start[/cmd] +*/ + +static bool ident_kvs_cmd_stop(KviKvsModuleCommandCall * c) +{ + if(g_iIdentDaemonRunningUsers) g_iIdentDaemonRunningUsers--; + if(!g_iIdentDaemonRunningUsers) stopIdentService(); + return true; +} + +static bool ident_module_init(KviModule *m) +{ + g_pIdentSentinel = new KviIdentSentinel(); + KVSM_REGISTER_SIMPLE_COMMAND(m,"start",ident_kvs_cmd_start); + KVSM_REGISTER_SIMPLE_COMMAND(m,"stop",ident_kvs_cmd_stop); + return true; +} + +static bool ident_module_cleanup(KviModule *m) +{ + stopIdentService(); + delete g_pIdentSentinel; + g_pIdentSentinel = 0; + + return true; +} + +static bool ident_module_can_unload(KviModule *m) +{ + return !g_pIdentDaemon; +} + +KVIRC_MODULE( + "Ident", // module name + "1.0.0", // module version + "Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)", // author & (C) + "Ident service", + ident_module_init, + ident_module_can_unload, + 0, + ident_module_cleanup +) + +#include "libkviident.moc" |