/************************************************************************* 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 TQObject... ~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 TDEProcess 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 #include #include #include #include #include #include #include #include #include extern DisplayMgr *displayMgr; KSircProcess::KSircProcess( TQString &server_id, KSircServer &kss, TQObject * parent, const char * name ) : TQObject(parent, name), m_kss(kss), m_serverid(server_id) { proc = new TDEProcess(); #ifndef NDEBUG if(getuid() != 0) proc->setRunPrivileged(true); /* make ksirc run under gdb as a user */ #endif //server = tqstrdup(kss.server()); TQDict 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 TQString qsNick, qsRealname, qsUserID, qsAltNick; TDEConfig *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", TDEGlobal::dirs()->findResourceDir("appdata", "ksirc.pl")); proc->setEnvironment("SIRCWAIT", "1"); TQString 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(TQCString(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()); TQString sslopt; if(kss.usessl()) sslopt = "-S"; *proc << "perl" << TDEGlobal::dirs()->findExe("dsirc") << "-8" << "-r" << sslopt; // Finally start the iocontroller. iocontrol = new KSircIOController(proc, this); iocontrol->setName(TQCString(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( TopList["!dcc"] ); // g++ bug connect(dcc, TQT_SIGNAL(outputLine(TQCString)), iocontrol, TQT_SLOT(stdin_write(TQCString))); KSircIOLAG *lag = new KSircIOLAG(this); TopList.insert("!lag", lag); lag = static_cast( TopList["!lag"] ); // g++ bug! connect(lag, TQT_SIGNAL(outputLine(TQCString)), iocontrol, TQT_SLOT(stdin_write(TQCString))); KSircIONotify *notify = new KSircIONotify(this); TopList.insert("!notify", notify); notify = static_cast( TopList["!notify"] ); // g++ bug connect(notify, TQT_SIGNAL(notify_online(TQString)), this, TQT_SLOT(notify_forw_online(TQString))); connect(notify, TQT_SIGNAL(notify_offline(TQString)), this, TQT_SLOT(notify_forw_offline(TQString))); TopList.insert("!base_rules", new KSMBaseRules(this)); // Now that all windows are up, start sirc. proc->start(TDEProcess::NotifyOnExit, TDEProcess::All); // Intial commands to load ASAP. // turn on sirc ssfe mode TQCString command = "/eval $ssfe=1\n"; iocontrol->stdin_write(command); command = "/eval $version .= \"+KSIRC/" + TQCString(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(); } TQPtrList KSircProcess::messageReceivers() const { TQPtrList res; res.setAutoDelete( false ); TQDictIterator it( TopList ); for (; it.current(); ++it ) if ( it.currentKey() != "!default" && it.currentKey() != "!no_channel" ) res.append( it.current() ); return res; } const TQDict &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, TQString()); // Do closing down commands, this should release all puke widgets #if 0 dsirc does this on SIGTERM (malte) TQString 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, TQString()); } 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, TQT_SIGNAL(outputLine(TQCString)), iocontrol, TQT_SLOT(stdin_write(TQCString))); connect(wm, TQT_SIGNAL(open_toplevel(const KSircChannel &)), this,TQT_SLOT(new_toplevel (const KSircChannel &))); connect(wm, TQT_SIGNAL(closing(KSircTopLevel *, TQString)), this,TQT_SLOT(close_toplevel(KSircTopLevel *, TQString))); connect(wm, TQT_SIGNAL(currentWindow(KSircTopLevel *)), this,TQT_SLOT(default_window(KSircTopLevel *))); connect(wm, TQT_SIGNAL(changeChannel(const TQString &, const TQString &)), this,TQT_SLOT(recvChangeChannel(const TQString &, const TQString &))); connect(wm, TQT_SIGNAL(destroyed(TQObject *)), this,TQT_SLOT(clean_toplevel(TQObject *))); connect( wm, TQT_SIGNAL( requestQuit( const TQCString& ) ), TQT_SLOT( request_quit( const TQCString& ) ) ); 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 { TQWidget *w = dynamic_cast(TopList.find(channelInfo.channel())); if(w) displayMgr->raise(w); } } void KSircProcess::close_toplevel(KSircTopLevel *wm, TQString 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) TQGuardedPtr 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. TQDictIterator it(TopList); for(;it.current() && it.currentKey().startsWith("!"); ++it); if (!it.current()) { // No top-level windows left. TQCString 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, TQString()); TQTimer::singleShot(5000, this, TQT_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(TQObject *clean){ if(!clean){ tqWarning("Passed null to cleaner!!"); return; } bool cont = FALSE; do{ cont = FALSE; TQDictIterator it(TopList); while(it.current() != 0x0){ if((TQObject *)it.current() == clean){ TQString key = it.currentKey(); while(TopList[key] != 0x0){ TopList.remove(key); } cont = TRUE; break; } ++it; } } while(cont == TRUE); } void KSircProcess::request_quit( const TQCString& 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) TQTimer::singleShot( 0, this, TQT_SLOT( do_quit() ) ); } void KSircProcess::do_quit() { for ( TQDictIterator< KSircMessageReceiver > it( TopList ); it.current(); ++it ) { if ( it.currentKey() == "!default" ) continue; if ( KSircTopLevel* topLevel = dynamic_cast< KSircTopLevel* >( it.current() ) ) { TQGuardedPtr< 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 TQString &old_chan, const TQString &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() { TQString command, next_part, key, data; command = "/crule\n"; iocontrol->stdin_write(command.ascii()); TQDictIterator 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(); } } TDEConfig *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(TQString nick) { emit ProcMessage(serverID(), ProcCommand::nickOnline, nick); } void KSircProcess::notify_forw_offline(TQString nick) { emit ProcMessage(serverID(), ProcCommand::nickOffline, nick); } void KSircProcess::ServMessage(TQString dst_server, int command, TQString 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, TQString()); auto_create_really = FALSE; } void KSircProcess::setNick(const TQString nick) { TQString 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 TQString KSircProcess::getNick() const { return m_nick; } #include "ksircprocess.moc" // vim: ts=4 sw=4 et