Now the systrayicon change it's color when a download is in progress. I simply change...
[kdenetwork.git] / ksirc / ksircprocess.cpp
blob1fff680710687aa07c3e7304b0441858e165e312
1 /*************************************************************************
3 KSircProcess, sirc controller
5 $$Id$$
7 KSircProcess cerate and controls toplevel widgets and sirc process'.
8 Each sirc process has 1 and only 1 KSircProcess to control it. KSirc
9 process passes all IO to IOController which is it's friend.
11 Interface:
13 public:
14 KSircProcess(*server=0L, *parent=0, *name=0)
15 server: is the name of the server to connect to. It must be
16 provided or else start sirc will barf. :(
17 parent: parent window, this _should_ be null
18 name: name, passed to QObject...
20 ~KSirProcess:
21 kill the sirc process, and iocontrollller, emit delete_toplevel
23 getWindowList:
24 returns the TopList, see bellow.
26 Signals:
27 made_toplevel(server, window)
28 made a new toplevel window for the "server" we are connected to
29 with "window" as the title.
31 dalete_toplevel(server, window)
32 delete toplevel with server and called window. If we emit null
33 as the window name it means to destroy all info about the
34 server and ksircprocess.
36 changeChannel(server, old_name, new_name)
37 toplevel with old_name has been changed to new_name and all
38 future refrences will use new_name.
40 public slots:
41 new_toplevel(window):
42 create a new window with name window. This MAY only change the
43 name of an existing window that's now idle.
45 close_topevel(KsircTopLevel*, window):
46 deletes all refrences to window and if needed finds a new
47 default toplevel.
49 default_window(KSricTopLevel*):
50 KSircTopLevel is requesting change to !default. Be carefull
51 with this one.
53 recvChangeChannel(old, new):
54 window old is changing to new. emit ChangeChannel with server
55 name added. Without server name we can uniqely id the window. :(
57 Implementation:
59 Bassic process is to create a new KSircProcess and it takes care of
60 the rest. It emits signals for each new window and every time a
61 window is delete so you can update external display (like
62 servercontroller uses).
64 Startup:
66 1. Creates a case insensitive TopList. This is a list of ALL
67 KSircReceivers under control of this server, and includes such
68 items as "!all" and "!default". All !name are control windows.
70 2. Forks off a KProcess for sirc and passes it over to IOController
71 which grabs and control's it's IO.
73 3. It then opens a "!default" window. This will receive all
74 initial input and such. It WILL change it's name on the first
75 join.
77 4. The IO broadcast object is created and setup.
79 5. everything is put into run mode.
82 Operation, see code bellow for inline comments.
84 *************************************************************************/
88 #include "baserules.h"
89 #include "ksopts.h"
90 #include "control_message.h"
91 #include "displayMgr.h"
92 #include "ioBroadcast.h"
93 #include "ioDCC.h"
94 #include "ioDiscard.h"
95 #include "ioLAG.h"
96 #include "ioNotify.h"
97 #include "iocontroller.h"
98 #include "ksircprocess.h"
99 #include "objFinder.h"
100 #include "servercontroller.h"
101 #include "toplevel.h"
102 #include "version.h"
103 #include "KSProgress/ksprogress.h"
105 #include <stdlib.h>
106 #include <time.h>
108 #include <qtimer.h>
109 #include <kapplication.h>
110 #include <kconfig.h>
111 #include <kdebug.h>
112 #include <klocale.h>
113 #include <kmessagebox.h>
114 #include <kprocess.h>
115 #include <kstandarddirs.h>
118 extern DisplayMgr *displayMgr;
120 KSircProcess::KSircProcess( QString &server_id, KSircServer &kss, QObject * parent, const char * name )
121 : QObject(parent, name), m_kss(kss), m_serverid(server_id)
124 proc = new KProcess();
126 #ifndef NDEBUG
127 if(getuid() != 0)
128 proc->setRunPrivileged(true); /* make ksirc run under gdb as a user */
129 #endif
131 //server = qstrdup(kss.server());
133 QDict<KSircMessageReceiver> nTopList(17, FALSE);
134 TopList = nTopList;
135 // TopList.setAutoDelete(TRUE)
137 auto_create_really = FALSE;
139 // Create the ksopts server structure
140 ksopts->serverSetup(kss);
142 // Setup the environment for KSirc
143 QString qsNick, qsRealname, qsUserID, qsAltNick;
144 KConfig *kConfig = kapp->config();
145 kConfig->setGroup("StartUp");
146 qsNick = ksopts->serv(kss).nick;
147 qsAltNick = ksopts->serv(kss).altNick;
148 qsRealname = ksopts->serv(kss).realName;
149 qsUserID = ksopts->serv(kss).userID;
150 kdDebug(5008) << "qsNick: " << qsNick << " qsAltNick: " << qsAltNick << " qsRealname: " << qsRealname << "qsUserID: " << qsUserID << endl;
152 m_nick = qsNick;
154 if((qsNick.isEmpty() == FALSE)){
155 proc->setEnvironment("SIRCNICK", qsNick);
157 if((qsAltNick.isEmpty() == FALSE)){
158 proc->setEnvironment("BACKUPNICK", qsAltNick);
160 if((qsRealname.isEmpty() == FALSE)){
161 proc->setEnvironment("SIRCNAME", qsRealname);
163 if((qsUserID.isEmpty() == FALSE)){
164 proc->setEnvironment("SIRCUSER", qsUserID);
165 kdDebug(5008) << "Set SIRCUSER to: " << qsUserID << endl;
168 proc->setEnvironment("SIRCLIB", KGlobal::dirs()->findResourceDir("appdata", "ksirc.pl"));
169 proc->setEnvironment("SIRCWAIT", "1");
171 QString env = locate("appdata", "ksircrc");
172 if (!env.isEmpty())
173 proc->setEnvironment("SIRCRC", env);
174 env = locate("appdata", "ksircrc.pl");
175 if (!env.isEmpty())
176 proc->setEnvironment("SIRCRCPL", env);
178 // Setup the proc now, so iocontroller can use it. It's latter
179 // though. started bellow though.
181 proc->setName(QCString(name) + "_kprocess");
182 objFinder::insert(proc);
183 // insertChild(proc);
185 // pass the server string using an environment variable, because it might contain
186 // a password that could be spyed out, as the commandline option is readable to others.
187 // Fixes 47157.
188 proc->setEnvironment( "SIRCSERVER", "[" + kss.server() + "]:" + kss.port() + ":" + kss.password());
190 QString sslopt;
191 if(kss.usessl())
192 sslopt = "-S";
193 *proc << "perl" << KGlobal::dirs()->findExe("dsirc") << "-8" << "-r" << sslopt;
195 // Finally start the iocontroller.
197 iocontrol = new KSircIOController(proc, this);
198 iocontrol->setName(QCString(name) + "_iocontrol");
200 // Create toplevel before iocontroller so it has somewhere to write stuff.
202 running_window = TRUE; // True so we do create the default
203 default_follow_focus = TRUE;
204 KSircChannel ci(kss.server(), "!no_channel");
205 new_toplevel(ci, true); //
206 TopList.replace("!default", TopList[ci.channel()]);
208 running_window = FALSE; // set false so next changes the first name
210 // Write default commands, and open default windows.
212 TopList.insert("!all", new KSircIOBroadcast(this));
213 TopList.insert("!discard", new KSircIODiscard(this));
215 KSircIODCC *dcc = new KSircIODCC(this);
216 TopList.insert("!dcc", dcc);
217 dcc = static_cast<KSircIODCC *>( TopList["!dcc"] ); // g++ bug
218 connect(dcc, SIGNAL(outputLine(QCString)),
219 iocontrol, SLOT(stdin_write(QCString)));
221 KSircIOLAG *lag = new KSircIOLAG(this);
222 TopList.insert("!lag", lag);
223 lag = static_cast<KSircIOLAG*>( TopList["!lag"] ); // g++ bug!
224 connect(lag, SIGNAL(outputLine(QCString)),
225 iocontrol, SLOT(stdin_write(QCString)));
227 KSircIONotify *notify = new KSircIONotify(this);
228 TopList.insert("!notify", notify);
229 notify = static_cast<KSircIONotify *>( TopList["!notify"] ); // g++ bug
230 connect(notify, SIGNAL(notify_online(QString)),
231 this, SLOT(notify_forw_online(QString)));
232 connect(notify, SIGNAL(notify_offline(QString)),
233 this, SLOT(notify_forw_offline(QString)));
235 TopList.insert("!base_rules", new KSMBaseRules(this));
237 // Now that all windows are up, start sirc.
239 proc->start(KProcess::NotifyOnExit, KProcess::All);
240 // Intial commands to load ASAP.
241 // turn on sirc ssfe mode
242 QCString command = "/eval $ssfe=1\n";
243 iocontrol->stdin_write(command);
245 command = "/eval $version .= \"+KSIRC/" + QCString(KSIRC_VERSION) + "\"\n";
246 iocontrol->stdin_write(command);
247 command = "/load " + locate("appdata", "filters.pl").local8Bit() + "\n";
248 iocontrol->stdin_write(command);
249 command = "/load " + locate("appdata", "ksirc.pl").local8Bit() + "\n";
250 iocontrol->stdin_write(command);
252 command = "/load " + locate("appdata", "puke.pl") + "\n";
253 iocontrol->stdin_write(command);
254 command = "/load " + locate("appdata", "dcc_status.pm") + "\n";
255 iocontrol->stdin_write(command);
257 command = "/eval $ready = 1\n";
258 iocontrol->stdin_write(command);
261 // Load all the filter rules. Must be after /load filtes.pl so all
262 // the functions are available
264 filters_update();
266 // We do this after filters_update() since filters_update loads the
267 // require notify filters, etc.
269 command = "/notify ";
270 command += ksopts->serv(kss).notifyList.join(" ").latin1();
271 command += "\n";
272 kdDebug(5008) << "Notify: " << command << endl;
273 iocontrol->stdin_write(command);
277 KSircProcess::~KSircProcess()
279 cleanup();
282 QPtrList<KSircMessageReceiver> KSircProcess::messageReceivers() const
284 QPtrList<KSircMessageReceiver> res;
285 res.setAutoDelete( false );
286 QDictIterator<KSircMessageReceiver> it( TopList );
287 for (; it.current(); ++it )
288 if ( it.currentKey() != "!default" &&
289 it.currentKey() != "!no_channel" )
290 res.append( it.current() );
291 return res;
294 const QDict<KSircMessageReceiver> &KSircProcess::mrList() const
296 return TopList;
299 void KSircProcess::cleanup()
301 if(TopList["!default"]){
302 TopList.remove("!default"); // remove default so we don't delete it twice.
305 TopList.setAutoDelete(true);
306 TopList.clear();
308 emit ProcMessage(m_serverid, ProcCommand::procClose, QString());
310 // Do closing down commands, this should release all puke widgets
311 #if 0
312 dsirc does this on SIGTERM (malte)
313 QString quit_cmd = "/eval &dohooks(\"quit\");\n";
314 proc->writeStdin(quit_cmd.ascii(), quit_cmd.length());
315 sleep(1);
316 #endif
317 if(proc->isRunning()){
318 proc->kill(SIGTERM);
321 delete proc; // Delete process, seems to kill sirc, good.
322 delete iocontrol; // Take out io controller
323 // delete []server;
325 proc = 0L;
326 iocontrol = 0L;
327 // server = 0L;
330 void KSircProcess::new_toplevel(const KSircChannel &channelInfo, bool safe)
332 static time_t last_window_open = 0;
333 static int number_open = 0;
334 static bool flood_dlg = FALSE;
336 if(running_window == FALSE){ // If we're not fully running, reusing
337 // !default window for next chan.
338 running_window = TRUE;
339 // insert and remove is done as a side effect of the control_message call
340 // TopList.insert(str, TopList["!no_channel"]);
341 // TopList.remove("!no_channel"); // We're no longer !no_channel
342 TopList["!no_channel"]->control_message(CHANGE_CHANNEL, channelInfo.server() + "!!!" + channelInfo.channel() + "!!!" + channelInfo.key());
344 else if(TopList.find(channelInfo.channel()) == 0x0){ // If the window doesn't exist, continue
345 // If AutoCreate windows is on, let's make sure we're not being flooded.
346 if(ksopts->autoCreateWin == TRUE && safe == false){
347 time_t current_time = time(NULL);
348 if((channelInfo.channel()[0] != '#' || channelInfo.channel()[0] != '&') &&
349 ((current_time - last_window_open) < 5)){
350 if(number_open > 4 && flood_dlg == FALSE){
351 flood_dlg = TRUE;
352 int res = KMessageBox::warningYesNo(0,
353 i18n("5 Channel windows were opened "
354 "in less than 5 seconds. Someone "
355 "may be trying to flood your X server "
356 "with windows.\n"
357 "Shall I turn off AutoCreate windows?"),
358 i18n("Flood Warning"), i18n("Turn Off"), i18n("Keep Enabled"));
359 switch(res) {
360 case KMessageBox::Yes:
361 emit ProcMessage(serverID(), ProcCommand::turnOffAutoCreate, QString());
363 last_window_open = current_time;
364 number_open = 0;
366 else{
367 // Joining channels can't be a flood, can it?
368 if(channelInfo.channel()[0] != '#' || channelInfo.channel()[0] != '&')
369 if(!safe)
370 number_open++;
372 flood_dlg = FALSE;
374 else{
375 last_window_open = current_time;
379 // Create a new toplevel, and add it to the toplist.
380 // TopList is a list of KSircReceivers so we still need wm.
381 KSircTopLevel *wm = new KSircTopLevel(this, channelInfo, (serverID() +"_" + channelInfo.channel()).ascii() );
382 TopList.insert(channelInfo.channel(), wm);
384 // Connect needed signals. For a message window we never want it
385 // becomming the default so we ignore focusIn events into it.
386 connect(wm, SIGNAL(outputLine(QCString)),
387 iocontrol, SLOT(stdin_write(QCString)));
388 connect(wm, SIGNAL(open_toplevel(const KSircChannel &)),
389 this,SLOT(new_toplevel (const KSircChannel &)));
390 connect(wm, SIGNAL(closing(KSircTopLevel *, QString)),
391 this,SLOT(close_toplevel(KSircTopLevel *, QString)));
392 connect(wm, SIGNAL(currentWindow(KSircTopLevel *)),
393 this,SLOT(default_window(KSircTopLevel *)));
394 connect(wm, SIGNAL(changeChannel(const QString &, const QString &)),
395 this,SLOT(recvChangeChannel(const QString &, const QString &)));
396 connect(wm, SIGNAL(destroyed(QObject *)),
397 this,SLOT(clean_toplevel(QObject *)));
398 connect( wm, SIGNAL( requestQuit( const QCString& ) ),
399 SLOT( request_quit( const QCString& ) ) );
401 default_window(wm); // Set it to the default window.
402 emit ProcMessage(serverID(), ProcCommand::addTopLevel, channelInfo.channel());
404 displayMgr->newTopLevel(wm, TRUE);
405 displayMgr->setCaption(wm, channelInfo.channel());
406 // displayMgr->show(wm);
407 wm->lineEdit()->setFocus(); // Give focus back to the linee, someone takes it away on new create
409 else {
410 QWidget *w = dynamic_cast<QWidget *>(TopList.find(channelInfo.channel()));
411 if(w)
412 displayMgr->raise(w);
416 void KSircProcess::close_toplevel(KSircTopLevel *wm, QString name)
418 if(auto_create_really == TRUE)
419 turn_on_autocreate();
421 kdDebug(5008) << "KSP: get close_toplevel: " << name << endl;
423 // the removeTopLevel below also deletes the mditoplevel (in case
424 // we are using mdi) , which deletes its children, which deletes
425 // 'wm' , so watch out not to delete twice! (Simon)
426 QGuardedPtr<KSircTopLevel> guardedwm = wm;
427 // Do this now or we get junk left on the screen
428 displayMgr->removeTopLevel(wm);
430 while(TopList.remove(name)); // In case multiple copies exist remove them all
432 bool isDefault = (wm == TopList["!default"]);
434 // Ok, now if we just deleted the default we have a problem, we need
435 // a new default. BUT don't make the default "!all" or !message.
436 // So let's go grab a default, and make sure it's not "!" control
437 // object.
439 QDictIterator<KSircMessageReceiver> it(TopList);
440 for(;it.current() && it.currentKey().startsWith("!"); ++it);
442 if (!it.current())
444 // No top-level windows left.
445 QCString command = "/quit\n"; // "/signoff" ?
446 iocontrol->stdin_write(command); // kill sirc
447 kdDebug(5008) << "KSP closing: " << m_kss.server() << endl;
448 delete guardedwm;
449 delete this; // Delete ourself, WARNING MUST RETURN SINCE WE NO
450 // LONGER EXIST!!!!
451 return; // ^^^^^^^^^^^^^^^
454 if (isDefault)
455 TopList.replace("!default", it.current());
457 // Let's let em know she's deleted!
458 if(ksopts->autoCreateWin == TRUE){
459 emit ProcMessage(serverID(), ProcCommand::turnOffAutoCreate, QString());
460 QTimer::singleShot(5000, this, SLOT(turn_on_autocreate()));
461 auto_create_really = TRUE;
463 else{
464 auto_create_really = FALSE;
467 delete guardedwm;
468 emit ProcMessage(serverID(), ProcCommand::deleteTopLevel, name);
471 void KSircProcess::clean_toplevel(QObject *clean){
472 if(!clean){
473 qWarning("Passed null to cleaner!!");
474 return;
476 bool cont = FALSE;
478 cont = FALSE;
479 QDictIterator<KSircMessageReceiver> it(TopList);
480 while(it.current() != 0x0){
481 if((QObject *)it.current() == clean){
482 QString key = it.currentKey();
483 while(TopList[key] != 0x0){
484 TopList.remove(key);
486 cont = TRUE;
487 break;
489 ++it;
491 } while(cont == TRUE);
494 void KSircProcess::request_quit( const QCString& command )
496 iocontrol->stdin_write( command );
497 // Since removing the toplevels will delete the one that emitted this
498 // signal as well, we need to defer this a little (malte)
499 QTimer::singleShot( 0, this, SLOT( do_quit() ) );
502 void KSircProcess::do_quit()
504 for ( QDictIterator< KSircMessageReceiver > it( TopList ); it.current(); ++it )
506 if ( it.currentKey() == "!default" ) continue;
507 if ( KSircTopLevel* topLevel = dynamic_cast< KSircTopLevel* >( it.current() ) )
509 QGuardedPtr< KSircTopLevel > guardedTL = topLevel;
510 displayMgr->removeTopLevel( topLevel );
511 delete guardedTL;
513 else delete it.current();
515 // cleanup() would otherwise delete them a second time
516 TopList.clear();
517 delete this;
520 void KSircProcess::default_window(KSircTopLevel *w)
524 // If we want to track the default as it goes around, change the
525 // window on focus changes.
528 if(w && (default_follow_focus == TRUE))
529 TopList.replace("!default", w);
533 void KSircProcess::recvChangeChannel(const QString &old_chan, const QString &new_chan)
536 // Channel changed name, add our own name and off we go.
537 // ServerController needs our name so it can have a uniq handle for
538 // the window name.
541 if(TopList[old_chan]) {
542 kdDebug(5008) << "In change channel, found it" << endl;
543 TopList.insert(new_chan, TopList.take(old_chan));
545 else {
546 kdDebug(5008) << "In change channel, didn;t find it" << endl;
548 emit ProcMessage(serverID(), ProcCommand::changeChannel,
549 old_chan + " " + new_chan);
552 void KSircProcess::filters_update()
554 QString command, next_part, key, data;
555 command = "/crule\n";
556 iocontrol->stdin_write(command.ascii());
557 QDictIterator<KSircMessageReceiver> it(TopList);
558 KSircMessageReceiver *cur, *br;
559 filterRuleList *frl;
560 filterRule *fr;
561 cur = TopList["!base_rules"];
562 br = cur;
563 while(cur){
564 frl = cur->defaultRules();
565 for ( fr=frl->first(); fr != 0; fr=frl->next() ){
566 command.truncate(0);
567 command += "/ksircappendrule DESC==";
568 command += fr->desc;
569 command += " !!! SEARCH==";
570 command += fr->search;
571 command += " !!! FROM==";
572 command += fr->from;
573 command += " !!! TO==\"";
574 command += fr->to;
575 command += "\"\n";
576 iocontrol->stdin_write(command.local8Bit());
578 delete frl;
579 ++it;
580 cur = it.current();
581 if(cur == br){
582 ++it;
583 cur = it.current();
586 KConfig *kConfig = kapp->config();
587 kConfig->setGroup("FilterRules");
588 int max = kConfig->readNumEntry("Rules", 0);
589 for(int number = 1; number <= max; number++){
590 command.truncate(0);
591 key.sprintf("name-%d", number);
592 next_part.sprintf("/ksircappendrule DESC==%s !!! ", kConfig->readEntry(key).ascii());
593 command += next_part;
594 key.sprintf("search-%d", number);
595 next_part.sprintf("SEARCH==%s !!! ", kConfig->readEntry(key).ascii());
596 command += next_part;
597 key.sprintf("from-%d", number);
598 next_part.sprintf("FROM==%s !!! ", kConfig->readEntry(key).ascii());
599 command += next_part;
600 key.sprintf("to-%d", number);
601 next_part.sprintf("TO==\"%s\"\n", kConfig->readEntry(key).ascii());
602 command += next_part;
603 iocontrol->stdin_write(command.ascii());
608 void KSircProcess::notify_forw_online(QString nick)
610 emit ProcMessage(serverID(), ProcCommand::nickOnline, nick);
613 void KSircProcess::notify_forw_offline(QString nick)
615 emit ProcMessage(serverID(), ProcCommand::nickOffline, nick);
618 void KSircProcess::ServMessage(QString dst_server, int command, QString args)
620 if(dst_server.isEmpty() || (dst_server == serverID())){
621 switch(command){
622 case ServCommand::updateFilters:
623 filters_update();
624 break;
625 default:
626 kdDebug(5008) << "Unkown command: " << command << " to " << command << " args " << args << endl;
627 break;
632 void KSircProcess::turn_on_autocreate()
634 emit ProcMessage(serverID(), ProcCommand::turnOnAutoCreate, QString());
635 auto_create_really = FALSE;
638 void KSircProcess::setNick(const QString nick)
640 QString new_nick = nick;
641 while (!new_nick.isEmpty() &&
642 (new_nick[0].latin1() == '@' || new_nick[0].latin1() == '*'))
643 new_nick.remove(0, 1);
644 if(new_nick != m_nick){
645 m_nick = new_nick;
647 * redo the filter rules since they use
648 * our nick
650 kdDebug(5008) << "Redo filters" << endl;
651 filters_update();
656 const QString KSircProcess::getNick() const
658 return m_nick;
661 #include "ksircprocess.moc"
663 // vim: ts=4 sw=4 et