summaryrefslogtreecommitdiffstats
path: root/src/modules/ident/libkviident.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/ident/libkviident.cpp')
-rw-r--r--src/modules/ident/libkviident.cpp615
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"