diff options
Diffstat (limited to 'ksirc/ksircprocess.cpp')
-rw-r--r-- | ksirc/ksircprocess.cpp | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/ksirc/ksircprocess.cpp b/ksirc/ksircprocess.cpp new file mode 100644 index 00000000..1fff6807 --- /dev/null +++ b/ksirc/ksircprocess.cpp @@ -0,0 +1,663 @@ +/************************************************************************* + + KSircProcess, sirc controller + + $$Id$$ + + KSircProcess cerate and controls toplevel widgets and sirc process'. + Each sirc process has 1 and only 1 KSircProcess to control it. KSirc + process passes all IO to IOController which is it's friend. + + Interface: + + public: + KSircProcess(*server=0L, *parent=0, *name=0) + server: is the name of the server to connect to. It must be + provided or else start sirc will barf. :( + parent: parent window, this _should_ be null + name: name, passed to QObject... + + ~KSirProcess: + kill the sirc process, and iocontrollller, emit delete_toplevel + + getWindowList: + returns the TopList, see bellow. + + Signals: + made_toplevel(server, window) + made a new toplevel window for the "server" we are connected to + with "window" as the title. + + dalete_toplevel(server, window) + delete toplevel with server and called window. If we emit null + as the window name it means to destroy all info about the + server and ksircprocess. + + changeChannel(server, old_name, new_name) + toplevel with old_name has been changed to new_name and all + future refrences will use new_name. + + public slots: + new_toplevel(window): + create a new window with name window. This MAY only change the + name of an existing window that's now idle. + + close_topevel(KsircTopLevel*, window): + deletes all refrences to window and if needed finds a new + default toplevel. + + default_window(KSricTopLevel*): + KSircTopLevel is requesting change to !default. Be carefull + with this one. + + recvChangeChannel(old, new): + window old is changing to new. emit ChangeChannel with server + name added. Without server name we can uniqely id the window. :( + + Implementation: + + Bassic process is to create a new KSircProcess and it takes care of + the rest. It emits signals for each new window and every time a + window is delete so you can update external display (like + servercontroller uses). + + Startup: + + 1. Creates a case insensitive TopList. This is a list of ALL + KSircReceivers under control of this server, and includes such + items as "!all" and "!default". All !name are control windows. + + 2. Forks off a KProcess for sirc and passes it over to IOController + which grabs and control's it's IO. + + 3. It then opens a "!default" window. This will receive all + initial input and such. It WILL change it's name on the first + join. + + 4. The IO broadcast object is created and setup. + + 5. everything is put into run mode. + + + Operation, see code bellow for inline comments. + +*************************************************************************/ + + + +#include "baserules.h" +#include "ksopts.h" +#include "control_message.h" +#include "displayMgr.h" +#include "ioBroadcast.h" +#include "ioDCC.h" +#include "ioDiscard.h" +#include "ioLAG.h" +#include "ioNotify.h" +#include "iocontroller.h" +#include "ksircprocess.h" +#include "objFinder.h" +#include "servercontroller.h" +#include "toplevel.h" +#include "version.h" +#include "KSProgress/ksprogress.h" + +#include <stdlib.h> +#include <time.h> + +#include <qtimer.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <kstandarddirs.h> + + +extern DisplayMgr *displayMgr; + +KSircProcess::KSircProcess( QString &server_id, KSircServer &kss, QObject * parent, const char * name ) + : QObject(parent, name), m_kss(kss), m_serverid(server_id) +{ + + proc = new KProcess(); + +#ifndef NDEBUG + if(getuid() != 0) + proc->setRunPrivileged(true); /* make ksirc run under gdb as a user */ +#endif + + //server = qstrdup(kss.server()); + + QDict<KSircMessageReceiver> nTopList(17, FALSE); + TopList = nTopList; + // TopList.setAutoDelete(TRUE) + + auto_create_really = FALSE; + + // Create the ksopts server structure + ksopts->serverSetup(kss); + + // Setup the environment for KSirc + QString qsNick, qsRealname, qsUserID, qsAltNick; + KConfig *kConfig = kapp->config(); + kConfig->setGroup("StartUp"); + qsNick = ksopts->serv(kss).nick; + qsAltNick = ksopts->serv(kss).altNick; + qsRealname = ksopts->serv(kss).realName; + qsUserID = ksopts->serv(kss).userID; + kdDebug(5008) << "qsNick: " << qsNick << " qsAltNick: " << qsAltNick << " qsRealname: " << qsRealname << "qsUserID: " << qsUserID << endl; + + m_nick = qsNick; + + if((qsNick.isEmpty() == FALSE)){ + proc->setEnvironment("SIRCNICK", qsNick); + } + if((qsAltNick.isEmpty() == FALSE)){ + proc->setEnvironment("BACKUPNICK", qsAltNick); + } + if((qsRealname.isEmpty() == FALSE)){ + proc->setEnvironment("SIRCNAME", qsRealname); + } + if((qsUserID.isEmpty() == FALSE)){ + proc->setEnvironment("SIRCUSER", qsUserID); + kdDebug(5008) << "Set SIRCUSER to: " << qsUserID << endl; + } + + proc->setEnvironment("SIRCLIB", KGlobal::dirs()->findResourceDir("appdata", "ksirc.pl")); + proc->setEnvironment("SIRCWAIT", "1"); + + QString env = locate("appdata", "ksircrc"); + if (!env.isEmpty()) + proc->setEnvironment("SIRCRC", env); + env = locate("appdata", "ksircrc.pl"); + if (!env.isEmpty()) + proc->setEnvironment("SIRCRCPL", env); + + // Setup the proc now, so iocontroller can use it. It's latter + // though. started bellow though. + + proc->setName(QCString(name) + "_kprocess"); + objFinder::insert(proc); +// insertChild(proc); + + // pass the server string using an environment variable, because it might contain + // a password that could be spyed out, as the commandline option is readable to others. + // Fixes 47157. + proc->setEnvironment( "SIRCSERVER", "[" + kss.server() + "]:" + kss.port() + ":" + kss.password()); + + QString sslopt; + if(kss.usessl()) + sslopt = "-S"; + *proc << "perl" << KGlobal::dirs()->findExe("dsirc") << "-8" << "-r" << sslopt; + + // Finally start the iocontroller. + + iocontrol = new KSircIOController(proc, this); + iocontrol->setName(QCString(name) + "_iocontrol"); + + // Create toplevel before iocontroller so it has somewhere to write stuff. + + running_window = TRUE; // True so we do create the default + default_follow_focus = TRUE; + KSircChannel ci(kss.server(), "!no_channel"); + new_toplevel(ci, true); // + TopList.replace("!default", TopList[ci.channel()]); + + running_window = FALSE; // set false so next changes the first name + + // Write default commands, and open default windows. + + TopList.insert("!all", new KSircIOBroadcast(this)); + TopList.insert("!discard", new KSircIODiscard(this)); + + KSircIODCC *dcc = new KSircIODCC(this); + TopList.insert("!dcc", dcc); + dcc = static_cast<KSircIODCC *>( TopList["!dcc"] ); // g++ bug + connect(dcc, SIGNAL(outputLine(QCString)), + iocontrol, SLOT(stdin_write(QCString))); + + KSircIOLAG *lag = new KSircIOLAG(this); + TopList.insert("!lag", lag); + lag = static_cast<KSircIOLAG*>( TopList["!lag"] ); // g++ bug! + connect(lag, SIGNAL(outputLine(QCString)), + iocontrol, SLOT(stdin_write(QCString))); + + KSircIONotify *notify = new KSircIONotify(this); + TopList.insert("!notify", notify); + notify = static_cast<KSircIONotify *>( TopList["!notify"] ); // g++ bug + connect(notify, SIGNAL(notify_online(QString)), + this, SLOT(notify_forw_online(QString))); + connect(notify, SIGNAL(notify_offline(QString)), + this, SLOT(notify_forw_offline(QString))); + + TopList.insert("!base_rules", new KSMBaseRules(this)); + + // Now that all windows are up, start sirc. + + proc->start(KProcess::NotifyOnExit, KProcess::All); + // Intial commands to load ASAP. + // turn on sirc ssfe mode + QCString command = "/eval $ssfe=1\n"; + iocontrol->stdin_write(command); + + command = "/eval $version .= \"+KSIRC/" + QCString(KSIRC_VERSION) + "\"\n"; + iocontrol->stdin_write(command); + command = "/load " + locate("appdata", "filters.pl").local8Bit() + "\n"; + iocontrol->stdin_write(command); + command = "/load " + locate("appdata", "ksirc.pl").local8Bit() + "\n"; + iocontrol->stdin_write(command); + /* + command = "/load " + locate("appdata", "puke.pl") + "\n"; + iocontrol->stdin_write(command); + command = "/load " + locate("appdata", "dcc_status.pm") + "\n"; + iocontrol->stdin_write(command); + */ + command = "/eval $ready = 1\n"; + iocontrol->stdin_write(command); + + + // Load all the filter rules. Must be after /load filtes.pl so all + // the functions are available + + filters_update(); + + // We do this after filters_update() since filters_update loads the + // require notify filters, etc. + + command = "/notify "; + command += ksopts->serv(kss).notifyList.join(" ").latin1(); + command += "\n"; + kdDebug(5008) << "Notify: " << command << endl; + iocontrol->stdin_write(command); + +} + +KSircProcess::~KSircProcess() +{ + cleanup(); +} + +QPtrList<KSircMessageReceiver> KSircProcess::messageReceivers() const +{ + QPtrList<KSircMessageReceiver> res; + res.setAutoDelete( false ); + QDictIterator<KSircMessageReceiver> it( TopList ); + for (; it.current(); ++it ) + if ( it.currentKey() != "!default" && + it.currentKey() != "!no_channel" ) + res.append( it.current() ); + return res; +} + +const QDict<KSircMessageReceiver> &KSircProcess::mrList() const +{ + return TopList; +} + +void KSircProcess::cleanup() +{ + if(TopList["!default"]){ + TopList.remove("!default"); // remove default so we don't delete it twice. + } + + TopList.setAutoDelete(true); + TopList.clear(); + + emit ProcMessage(m_serverid, ProcCommand::procClose, QString()); + + // Do closing down commands, this should release all puke widgets +#if 0 + dsirc does this on SIGTERM (malte) + QString quit_cmd = "/eval &dohooks(\"quit\");\n"; + proc->writeStdin(quit_cmd.ascii(), quit_cmd.length()); + sleep(1); +#endif + if(proc->isRunning()){ + proc->kill(SIGTERM); + } + + delete proc; // Delete process, seems to kill sirc, good. + delete iocontrol; // Take out io controller +// delete []server; + + proc = 0L; + iocontrol = 0L; +// server = 0L; +} + +void KSircProcess::new_toplevel(const KSircChannel &channelInfo, bool safe) +{ + static time_t last_window_open = 0; + static int number_open = 0; + static bool flood_dlg = FALSE; + + if(running_window == FALSE){ // If we're not fully running, reusing + // !default window for next chan. + running_window = TRUE; + // insert and remove is done as a side effect of the control_message call + // TopList.insert(str, TopList["!no_channel"]); + // TopList.remove("!no_channel"); // We're no longer !no_channel + TopList["!no_channel"]->control_message(CHANGE_CHANNEL, channelInfo.server() + "!!!" + channelInfo.channel() + "!!!" + channelInfo.key()); + } + else if(TopList.find(channelInfo.channel()) == 0x0){ // If the window doesn't exist, continue + // If AutoCreate windows is on, let's make sure we're not being flooded. + if(ksopts->autoCreateWin == TRUE && safe == false){ + time_t current_time = time(NULL); + if((channelInfo.channel()[0] != '#' || channelInfo.channel()[0] != '&') && + ((current_time - last_window_open) < 5)){ + if(number_open > 4 && flood_dlg == FALSE){ + flood_dlg = TRUE; + int res = KMessageBox::warningYesNo(0, + i18n("5 Channel windows were opened " + "in less than 5 seconds. Someone " + "may be trying to flood your X server " + "with windows.\n" + "Shall I turn off AutoCreate windows?"), + i18n("Flood Warning"), i18n("Turn Off"), i18n("Keep Enabled")); + switch(res) { + case KMessageBox::Yes: + emit ProcMessage(serverID(), ProcCommand::turnOffAutoCreate, QString()); + } + last_window_open = current_time; + number_open = 0; + } + else{ + // Joining channels can't be a flood, can it? + if(channelInfo.channel()[0] != '#' || channelInfo.channel()[0] != '&') + if(!safe) + number_open++; + } + flood_dlg = FALSE; + } + else{ + last_window_open = current_time; + } + } + + // Create a new toplevel, and add it to the toplist. + // TopList is a list of KSircReceivers so we still need wm. + KSircTopLevel *wm = new KSircTopLevel(this, channelInfo, (serverID() +"_" + channelInfo.channel()).ascii() ); + TopList.insert(channelInfo.channel(), wm); + + // Connect needed signals. For a message window we never want it + // becomming the default so we ignore focusIn events into it. + connect(wm, SIGNAL(outputLine(QCString)), + iocontrol, SLOT(stdin_write(QCString))); + connect(wm, SIGNAL(open_toplevel(const KSircChannel &)), + this,SLOT(new_toplevel (const KSircChannel &))); + connect(wm, SIGNAL(closing(KSircTopLevel *, QString)), + this,SLOT(close_toplevel(KSircTopLevel *, QString))); + connect(wm, SIGNAL(currentWindow(KSircTopLevel *)), + this,SLOT(default_window(KSircTopLevel *))); + connect(wm, SIGNAL(changeChannel(const QString &, const QString &)), + this,SLOT(recvChangeChannel(const QString &, const QString &))); + connect(wm, SIGNAL(destroyed(QObject *)), + this,SLOT(clean_toplevel(QObject *))); + connect( wm, SIGNAL( requestQuit( const QCString& ) ), + SLOT( request_quit( const QCString& ) ) ); + + default_window(wm); // Set it to the default window. + emit ProcMessage(serverID(), ProcCommand::addTopLevel, channelInfo.channel()); + + displayMgr->newTopLevel(wm, TRUE); + displayMgr->setCaption(wm, channelInfo.channel()); +// displayMgr->show(wm); + wm->lineEdit()->setFocus(); // Give focus back to the linee, someone takes it away on new create + } + else { + QWidget *w = dynamic_cast<QWidget *>(TopList.find(channelInfo.channel())); + if(w) + displayMgr->raise(w); + } +} + +void KSircProcess::close_toplevel(KSircTopLevel *wm, QString name) +{ + if(auto_create_really == TRUE) + turn_on_autocreate(); + + kdDebug(5008) << "KSP: get close_toplevel: " << name << endl; + + // the removeTopLevel below also deletes the mditoplevel (in case + // we are using mdi) , which deletes its children, which deletes + // 'wm' , so watch out not to delete twice! (Simon) + QGuardedPtr<KSircTopLevel> guardedwm = wm; + // Do this now or we get junk left on the screen + displayMgr->removeTopLevel(wm); + + while(TopList.remove(name)); // In case multiple copies exist remove them all + + bool isDefault = (wm == TopList["!default"]); + + // Ok, now if we just deleted the default we have a problem, we need + // a new default. BUT don't make the default "!all" or !message. + // So let's go grab a default, and make sure it's not "!" control + // object. + + QDictIterator<KSircMessageReceiver> it(TopList); + for(;it.current() && it.currentKey().startsWith("!"); ++it); + + if (!it.current()) + { + // No top-level windows left. + QCString command = "/quit\n"; // "/signoff" ? + iocontrol->stdin_write(command); // kill sirc + kdDebug(5008) << "KSP closing: " << m_kss.server() << endl; + delete guardedwm; + delete this; // Delete ourself, WARNING MUST RETURN SINCE WE NO + // LONGER EXIST!!!! + return; // ^^^^^^^^^^^^^^^ + } + + if (isDefault) + TopList.replace("!default", it.current()); + + // Let's let em know she's deleted! + if(ksopts->autoCreateWin == TRUE){ + emit ProcMessage(serverID(), ProcCommand::turnOffAutoCreate, QString()); + QTimer::singleShot(5000, this, SLOT(turn_on_autocreate())); + auto_create_really = TRUE; + } + else{ + auto_create_really = FALSE; + } + + delete guardedwm; + emit ProcMessage(serverID(), ProcCommand::deleteTopLevel, name); +} + +void KSircProcess::clean_toplevel(QObject *clean){ + if(!clean){ + qWarning("Passed null to cleaner!!"); + return; + } + bool cont = FALSE; + do{ + cont = FALSE; + QDictIterator<KSircMessageReceiver> it(TopList); + while(it.current() != 0x0){ + if((QObject *)it.current() == clean){ + QString key = it.currentKey(); + while(TopList[key] != 0x0){ + TopList.remove(key); + } + cont = TRUE; + break; + } + ++it; + } + } while(cont == TRUE); +} + +void KSircProcess::request_quit( const QCString& command ) +{ + iocontrol->stdin_write( command ); + // Since removing the toplevels will delete the one that emitted this + // signal as well, we need to defer this a little (malte) + QTimer::singleShot( 0, this, SLOT( do_quit() ) ); +} + +void KSircProcess::do_quit() +{ + for ( QDictIterator< KSircMessageReceiver > it( TopList ); it.current(); ++it ) + { + if ( it.currentKey() == "!default" ) continue; + if ( KSircTopLevel* topLevel = dynamic_cast< KSircTopLevel* >( it.current() ) ) + { + QGuardedPtr< KSircTopLevel > guardedTL = topLevel; + displayMgr->removeTopLevel( topLevel ); + delete guardedTL; + } + else delete it.current(); + } + // cleanup() would otherwise delete them a second time + TopList.clear(); + delete this; +} + +void KSircProcess::default_window(KSircTopLevel *w) +{ + + // + // If we want to track the default as it goes around, change the + // window on focus changes. + // + + if(w && (default_follow_focus == TRUE)) + TopList.replace("!default", w); + +} + +void KSircProcess::recvChangeChannel(const QString &old_chan, const QString &new_chan) +{ + // + // Channel changed name, add our own name and off we go. + // ServerController needs our name so it can have a uniq handle for + // the window name. + // + + if(TopList[old_chan]) { + kdDebug(5008) << "In change channel, found it" << endl; + TopList.insert(new_chan, TopList.take(old_chan)); + } + else { + kdDebug(5008) << "In change channel, didn;t find it" << endl; + } + emit ProcMessage(serverID(), ProcCommand::changeChannel, + old_chan + " " + new_chan); +} + +void KSircProcess::filters_update() +{ + QString command, next_part, key, data; + command = "/crule\n"; + iocontrol->stdin_write(command.ascii()); + QDictIterator<KSircMessageReceiver> it(TopList); + KSircMessageReceiver *cur, *br; + filterRuleList *frl; + filterRule *fr; + cur = TopList["!base_rules"]; + br = cur; + while(cur){ + frl = cur->defaultRules(); + for ( fr=frl->first(); fr != 0; fr=frl->next() ){ + command.truncate(0); + command += "/ksircappendrule DESC=="; + command += fr->desc; + command += " !!! SEARCH=="; + command += fr->search; + command += " !!! FROM=="; + command += fr->from; + command += " !!! TO==\""; + command += fr->to; + command += "\"\n"; + iocontrol->stdin_write(command.local8Bit()); + } + delete frl; + ++it; + cur = it.current(); + if(cur == br){ + ++it; + cur = it.current(); + } + } + KConfig *kConfig = kapp->config(); + kConfig->setGroup("FilterRules"); + int max = kConfig->readNumEntry("Rules", 0); + for(int number = 1; number <= max; number++){ + command.truncate(0); + key.sprintf("name-%d", number); + next_part.sprintf("/ksircappendrule DESC==%s !!! ", kConfig->readEntry(key).ascii()); + command += next_part; + key.sprintf("search-%d", number); + next_part.sprintf("SEARCH==%s !!! ", kConfig->readEntry(key).ascii()); + command += next_part; + key.sprintf("from-%d", number); + next_part.sprintf("FROM==%s !!! ", kConfig->readEntry(key).ascii()); + command += next_part; + key.sprintf("to-%d", number); + next_part.sprintf("TO==\"%s\"\n", kConfig->readEntry(key).ascii()); + command += next_part; + iocontrol->stdin_write(command.ascii()); + } +} + + +void KSircProcess::notify_forw_online(QString nick) +{ + emit ProcMessage(serverID(), ProcCommand::nickOnline, nick); +} + +void KSircProcess::notify_forw_offline(QString nick) +{ + emit ProcMessage(serverID(), ProcCommand::nickOffline, nick); +} + +void KSircProcess::ServMessage(QString dst_server, int command, QString args) +{ + if(dst_server.isEmpty() || (dst_server == serverID())){ + switch(command){ + case ServCommand::updateFilters: + filters_update(); + break; + default: + kdDebug(5008) << "Unkown command: " << command << " to " << command << " args " << args << endl; + break; + } + } +} + +void KSircProcess::turn_on_autocreate() +{ + emit ProcMessage(serverID(), ProcCommand::turnOnAutoCreate, QString()); + auto_create_really = FALSE; +} + +void KSircProcess::setNick(const QString nick) +{ + QString new_nick = nick; + while (!new_nick.isEmpty() && + (new_nick[0].latin1() == '@' || new_nick[0].latin1() == '*')) + new_nick.remove(0, 1); + if(new_nick != m_nick){ + m_nick = new_nick; + /* + * redo the filter rules since they use + * our nick + */ + kdDebug(5008) << "Redo filters" << endl; + filters_update(); + } + +} + +const QString KSircProcess::getNick() const +{ + return m_nick; +} + +#include "ksircprocess.moc" + +// vim: ts=4 sw=4 et |