File indexing completed on 2024-12-01 06:51:47

0001 /***************************************************************************
0002                           cunixsocket.cpp  -  UNIX domain socket
0003                              -------------------
0004     begin                : Pi okt 3 2003
0005     copyright            : (C) 2003-2009 by Tomas Mecir
0006     email                : kmuddy@kmuddy.com
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 
0018 #include "cunixsocket.h"
0019 
0020 #include <qsocketnotifier.h>
0021 
0022 #include <fcntl.h>
0023 #include <stdio.h>
0024 #include <stdlib.h>
0025 #include <unistd.h>
0026 #include <sys/socket.h>
0027 #include <sys/types.h>
0028 
0029 #include "cactionmanager.h"
0030 #include "crunninglist.h"
0031 #include "cvariablelist.h"
0032 
0033 cUnixSocket::cUnixSocket (int _sess, cRunningScript *rs) : sess(_sess)
0034 {
0035   script = rs;
0036   
0037   readnotifier = writenotifier = 0;
0038   readCache = writeCache = QString::null;
0039   id = -1;
0040   varlist = 0;
0041   connected = false;
0042   
0043   //first of all, we need a file name
0044   char tmp_template[] = "/tmp/kmXXXXXX";
0045   char *dirname = mkdtemp (tmp_template);
0046   if (dirname != 0)  //only if it didn't fail
0047   {
0048     name = dirname;
0049     name += "/socket";
0050     //now that we have the name, we create a socket and set some parameters
0051     id = socket (AF_UNIX, SOCK_STREAM, 0);
0052     sa.sun_family = AF_UNIX;
0053     strcpy (sa.sun_path, name.toLatin1());
0054     fcntl (id, O_NONBLOCK);
0055     if (bind (id, (const sockaddr *) &sa, sizeof (sa)) == -1)
0056     {
0057       close (id);
0058       id = -1;
0059       unlink (name.toLatin1());
0060       rmdir (dirname);
0061       return;
0062     }
0063 
0064     //it's a listening connect...
0065     listen (id, 1);
0066 
0067     //the notifier will tell us when the connection is here
0068     readnotifier = new QSocketNotifier (id, QSocketNotifier::Read, this);
0069     connect (readnotifier, SIGNAL (activated (int)), this, SLOT (readData (int)));
0070 
0071     //finally, get a pointer to the list of variables
0072     varlist = dynamic_cast<cVariableList *>(cActionManager::self()->object ("variables", sess));
0073   }          
0074 }
0075 
0076 cUnixSocket::~cUnixSocket ()
0077 {
0078   //delete the notifiers
0079   readnotifier->setEnabled (false);
0080   delete readnotifier;
0081   delete writenotifier;
0082 
0083   //close the socket
0084   close (id2);
0085 
0086   //and remove its file
0087   unlink (name.toLatin1());
0088   rmdir (name.left(strlen("/tmp/kmXXXXXX")).toLatin1());
0089 }
0090 
0091 const QString &cUnixSocket::getName ()
0092 {
0093   return name;
0094 }
0095 
0096 void cUnixSocket::readData (int)
0097 {
0098   if (!connected)
0099   {
0100     socklen_t sz = sizeof (sa);
0101     id2 = accept (id, (struct sockaddr *) &sa, &sz);
0102     if (id2 > -1)
0103     {
0104       connected = true;
0105       delete readnotifier;
0106       close (id);
0107 
0108       fcntl (id2, O_NONBLOCK);
0109       
0110       //then we create a pair of notifiers
0111       readnotifier = new QSocketNotifier (id2, QSocketNotifier::Read, this);
0112       writenotifier = new QSocketNotifier (id2, QSocketNotifier::Write, this);
0113       writenotifier->setEnabled (false);
0114       connect (readnotifier, SIGNAL (activated (int)), this, SLOT (readData (int)));
0115       connect (writenotifier, SIGNAL (activated (int)), this, SLOT (writeData (int)));
0116     }
0117     return;
0118   }
0119   
0120   char buffer[201];
0121   int n = read (id2, buffer, 200);
0122   buffer[n] = '\0';
0123   if (n == -1)
0124     return;   //bah
0125   if (n == 0)
0126     readnotifier->setEnabled (false);
0127            //hack: disable the notifier
0128            //(prevents problems on disconnect)
0129   for (int i = 0; i < n; i++)
0130     if (buffer[i] != '\n')
0131       readCache += QChar (buffer[i]);
0132     else
0133     {
0134       QString type = readCache.section (' ', 0, 0, QString::SectionSkipEmpty);
0135       QString data = readCache.section (' ', 1, -1, QString::SectionSkipEmpty);
0136       processRequest (type, data);
0137       readCache = QString::null;
0138     }
0139 }
0140 
0141 void cUnixSocket::writeData (int)
0142 {
0143   int len = writeCache.length();
0144   if (len == 0)
0145     return;   //nothing to write
0146   int n = write (id2, writeCache.toLatin1(), len);
0147   //if n is -1, nothing was written and we'll send the data when the write
0148   //notifier ask us to do so (it may never happen, if the socket was closed
0149   //by the script, but that's not a problem)
0150   if (n > -1)
0151     //remove the part that was successfully sent
0152     writeCache.remove (0, n);
0153                     
0154   if (writeCache.isEmpty()) //nothing more to write
0155     writenotifier->setEnabled (false);
0156 }
0157 
0158 void cUnixSocket::processRequest (const QString &type, const QString &data)
0159 {
0160   //scripts only exist in profile-based connections, so we can assume
0161   //that cConnPrefs object exists
0162   cRunningList *list = dynamic_cast<cRunningList *>(cActionManager::self()->object ("runninglist", sess));
0163   
0164   /*
0165   Request types that are currently supported:
0166    get - get a variable value
0167    set - set a variable value
0168    unset - unset a variable
0169    inc - increase a variable
0170    dec - decrease a variable
0171    request - request a resource
0172    provide - provide a resource
0173    lock - lock a variable
0174    unlock - remove a lock
0175    send - send a command to the MUD
0176   */
0177   if (type == "get")
0178   {
0179     QString value = varlist->getValue (data);
0180     sendResult (value);
0181   }
0182   if (type == "set")
0183   {
0184     QString varname = data.section (' ', 0, 0, QString::SectionSkipEmpty);
0185     QString value = data.section (' ', 1, -1, QString::SectionSkipEmpty);
0186     if (list->canModify (script, varname))
0187     {
0188       varlist->set (varname, value);
0189       sendResult ("OK");
0190     }
0191     else
0192       sendResult ("FAIL");
0193   }
0194   if (type == "unset")
0195   {
0196     if (list->canModify (script, data))
0197     {
0198       varlist->unset (data);
0199       sendResult ("OK");
0200     }
0201     else
0202       sendResult ("FAIL");
0203   }
0204   if (type == "inc")
0205   {
0206     QString varname = data.section (' ', 0, 0, QString::SectionSkipEmpty);
0207     QString value = data.section (' ', 1, 1, QString::SectionSkipEmpty);
0208     bool ok;              
0209     int num = value.toInt (&ok);
0210     if (!ok)    //not a number
0211     {
0212       sendResult ("FAIL");
0213       return;
0214     }
0215     if (num <= 0)  //bad number
0216     {
0217       sendResult ("FAIL");
0218       return;
0219     }
0220 
0221     if (list->canModify (script, varname))
0222     {
0223       varlist->inc (varname, num);
0224       sendResult ("OK");
0225     }
0226     else
0227       sendResult ("FAIL");
0228   }
0229   if (type == "dec")
0230   {
0231     QString varname = data.section (' ', 0, 0, QString::SectionSkipEmpty);
0232     QString value = data.section (' ', 1, 1, QString::SectionSkipEmpty);
0233     bool ok;
0234     int num = value.toInt (&ok);
0235     if (!ok)    //not a number
0236     {
0237       sendResult ("FAIL");
0238       return;
0239     }
0240     if (num <= 0)  //bad number
0241     {
0242       sendResult ("FAIL");
0243       return;
0244     }
0245     if (list->canModify (script, varname))
0246     {
0247       varlist->dec (varname, num);
0248       sendResult ("OK");
0249     }
0250     else
0251       sendResult ("FAIL");
0252   }
0253   if (type == "request")
0254   {
0255     if (list->canModify (script, data))
0256     {
0257       if (varlist->requestResource (data))
0258         sendResult ("OK");
0259       else
0260         sendResult ("FAIL");
0261     }
0262     else
0263       sendResult ("FAIL");
0264   }
0265   if (type == "provide")
0266   {
0267     if (list->canModify (script, data))
0268     {
0269       varlist->provideResource (data);
0270       sendResult ("OK");
0271     }
0272     else
0273       sendResult ("FAIL");
0274   }
0275   if (type == "lock")
0276   {
0277     if (list->requestLock (script, data))
0278       sendResult ("OK");
0279     else
0280       sendResult ("FAIL");
0281   }
0282   if (type == "unlock")
0283   {
0284     list->releaseLock (script, data);
0285     sendResult ("OK");
0286   }
0287   if (type == "send")
0288   {
0289     cActionManager::self()->invokeEvent ("command", sess, data);
0290     
0291     sendResult ("OK");
0292   }
0293 }
0294 
0295 void cUnixSocket::sendResult (const QString &result)
0296 {
0297   writenotifier->setEnabled (true);
0298 
0299   writeCache = result + "\n";
0300   writeData (id);
0301 }
0302 
0303 #include "moc_cunixsocket.cpp"