Fix crash on logout
[kdenetwork.git] / ksirc / iocontroller.cpp
bloba451f55dff7eb26f87d55c466c6af36262467454
1 /***********************************************************************
3 IO Controller Object
5 $$Id$$
7 Main io controller. Reads and writes strings to sirc. Input
8 received in the following 2 formats:
10 1. ~window name~<message>
11 2. <message>
13 each is handled diffrently. The window name is extracted from #1 and
14 if the window exists the message portion is sent to it. If the
15 window doesn't exist, or case 2 is found the window is sent to the
16 control window "!default". !default is NOT a constant window but
17 rather follows focus arround. This is the easiest way to solve the
18 problem of output to commands that don't have a fixed destination
19 window. (/whois, /help, etc )
21 Implementation:
23 Friends with KSircProcess allows easy access to TopList. Makes sence
24 and means that IOController has access to TopList, etc. The two work
25 closely together.
27 Variables:
28 holder: used to hold partital lines between writes.
29 *proc: the acutally sirc client process.
30 *ksircproc: the companion ksircprocess GUI controller
31 stdout_notif: access to SocketNotifier, why is this global to the
32 class?
33 counter: existance counter.
35 Functions:
36 public:
37 KSircIOController(KProcess*, KSircProcess*):
38 - Object constructor takes two arguements the KProcess
39 that holds a running copy of sirc.
40 - KSircProcess is saved for access latter to TopList.
41 - The receivedStdout signal from KProcess is connected to
42 stdout_read and the processExited is connected to the sircDied
43 slot.
45 ~KSircIOController: does nothing at this time.
47 public slots:
48 stdout_read(KProcess *, _buffer, buflen):
49 - Called by kprocess when data arrives.
50 - This function does all the parsing and sending of messages
51 to each window.
53 stderr_read(KProcess*, _buffer, buflen):
54 - Should be called for stderr data, not connected, does
55 nothing.
57 stdin_write(QString):
58 - Slot that get's connected to by KSircProcess to each
59 window. QString shold be written un touched! Let the
60 writter figure out what ever he wants to do.
62 sircDied:
63 - Should restart sirc or something....
64 - Becarefull not to get it die->start->die->... etc
66 ***********************************************************************/
68 #include <config.h>
70 #include "ksopts.h"
71 #include "control_message.h"
72 #include "iocontroller.h"
73 #include "ksircprocess.h"
74 #include "messageReceiver.h"
75 #include "ksopts.h"
77 #include <q3listbox.h>
78 #include <qtextcodec.h>
79 //Added by qt3to4:
80 #include <QTextStream>
81 #include <QByteArray>
82 #include <kcharsets.h>
83 #include <kglobal.h>
84 #include <q3popupmenu.h>
86 #include <kdebug.h>
87 #include <kdeversion.h>
88 #include <kprocess.h>
89 #include <kstandarddirs.h>
90 #include <kfiledialog.h>
92 int KSircIOController::counter = 0;
94 KSircIOController::KSircIOController(KProcess *_proc, KSircProcess *_ksircproc)
95 : QObject()
98 counter++;
100 proc = _proc; // save proc
101 ksircproc = _ksircproc; // save ksircproce
103 send_buf = 0x0;
104 m_debugLB = 0;
106 // Connect the data arrived
107 // to sirc receive for adding
108 // the main text window
109 connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
110 this, SLOT(stdout_read(KProcess*, char*, int)));
112 // Connect the stderr data
113 // to sirc receive for adding
114 // the main text window
115 connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)),
116 this, SLOT(stderr_read(KProcess*, char*, int)));
118 connect(proc, SIGNAL(processExited(KProcess *)),
119 this, SLOT(sircDied(KProcess *)));
120 // Notify on sirc dying
121 connect(proc, SIGNAL(wroteStdin(KProcess*)),
122 this, SLOT(procCTS(KProcess*)));
123 proc_CTS = TRUE;
124 #if 0
125 showDebugTraffic(true);
126 #endif
129 void my_print(const char *c){
130 while(*c != 0x0){
131 if(*c & 0x80)
132 fprintf(stderr, "<%02X>", 0xff & *c);
133 else
134 fprintf(stderr, "%c", *c);
135 c++;
137 fprintf(stderr, "\n");
140 void KSircIOController::stdout_read(KProcess *, char *_buffer, int buflen)
145 Main reader reads from sirc and ships it out to the right
146 (hopefully) window.
148 Problem trying to solve:
150 _buffer holds upto 1024 (it seems) block of data. We need to
151 take it, split into lines figure out where each line should go,
152 and ship it there.
154 We watch for broken end of lines, ie partial lines and reattach
155 them to the front on the next read.
157 We also stop all processing in the windows while writting the
158 lines.
160 Implementation:
162 Variables:
163 _buffer original buffer, holds just, icky thing, NOT NULL terminated!
164 buf: new clean just the right size buf that is null terminated.
165 pos, pos2, pos3 used to cut the string up into peices, etc.
166 name: destination window.
167 line: line to ship out.
169 Steps:
170 1. read and copy buffer, make sure it's valid.
171 2. If we're holding a broken line, append it.
172 3. Check for a broken line, and save the end if it is.
173 4. Stop updates in all windows.
174 5. Step through the data and send the lines out to the right
175 window.
176 6. Cleanup and continue.
180 int pos,pos2,pos3;
181 QByteArray name, line;
183 assert(_buffer != 0);
184 assert(buflen > 0);
186 QByteArray buffer(_buffer, buflen+1);
187 //fprintf(stderr, "first print: \n");
188 //my_print(buffer);
189 //kDebug(5008) << "<-- read: " << buffer << endl;
190 name = "!default";
193 if(holder.length() > 0){
194 buffer.prepend(holder);
195 holder.truncate(0);
198 if(buffer[buffer.length()-1] != '\n'){
199 pos = buffer.findRev('\n');
200 if(pos != -1){
201 holder = buffer.right(buffer.length()-(pos+1));
202 buffer.truncate(pos+1);
204 else {
205 /* there is _NO_ linefeeds in this line at all, means we're
206 * only part of a string, buffer it all!!
207 * (lines are linefeed delimeted)
209 holder = buffer;
210 return;
214 pos = pos2 = 0;
216 KSircMessageReceiver * rec = ksircproc->TopList["!all"];
218 if (0 == rec)
220 return;
223 rec->control_message(STOP_UPDATES, "");
224 if(m_debugLB)
225 m_debugLB->setUpdatesEnabled(false);
228 pos2 = buffer.find('\n', pos);
230 if(pos2 == -1)
231 pos2 = buffer.length();
233 line = buffer.mid(pos, pos2 - pos);
234 if(m_debugLB){
235 QString s = QString::fromUtf8(line);
236 m_debugLB->insertItem(s);
239 //kDebug(5008) << "Line: " << line << endl;
241 if((line.length() > 0) && (line[0] == '~')){
242 pos3 = line.find('~', 1);
243 if(pos3 > 0){
244 name = line.mid(1,pos3-1).lower();
245 name = name.lower();
246 line.remove(0, pos3+1);
249 QString enc = KGlobal::charsets()->encodingForName( ksopts->channel["global"]["global"].encoding );
250 QTextCodec *qtc = KGlobal::charsets()->codecForName( enc );
251 QString qsname = qtc->toUnicode(name);
253 char *b = qstrdup(line);
254 kDebug(5008) << "----------------------------------------" << endl;
255 kDebug(5008) << "Line: " << b << endl;
256 fprintf(stderr, "My_print: " ); my_print(b);
257 fprintf(stderr, "fprintf: %s\n", (const char *)b);
258 kDebug(5008) << "Codec: " << qtc->name() << " (" << ksopts->channel["global"]["global"].encoding << ")" << " Name: " << name << " qsname: " << qsname << endl;
259 kDebug(5008) << "Line(de): " << qtc->toUnicode(b) << endl;
260 kDebug(5008) << "----------------------------------------" << endl;
263 if(!(ksircproc->TopList[qsname])){
264 // Ignore ssfe control messages with `
265 // we left channel, don't open a window for a control message
266 bool noticeCreate = true;
267 if(ksopts->autoCreateWinForNotice == false && (line[0] == '-' || line[0] == '*'))
268 noticeCreate = false;
269 if(ksopts->autoCreateWin == TRUE && line[0] != '`' && line[1] != '#' && line[1] != '&' && noticeCreate) {
270 //kDebug(5008) << "Creating window for: " << qsname << " because of: " << line.data() << endl;
271 ksircproc->new_toplevel(KSircChannel(ksircproc->serverName(), qsname));
272 assert(ksircproc->TopList[qsname] != 0x0);
274 else{
275 qsname = "!default";
276 if(line[0] == '`')
277 qsname = "!discard";
281 ksircproc->TopList[qsname]->sirc_receive(line);
284 pos = pos2+1;
285 } while((uint) pos < buffer.length());
287 ksircproc->TopList["!all"]->control_message(RESUME_UPDATES, "");
288 if(m_debugLB){
289 m_debugLB->triggerUpdate(true);
290 m_debugLB->setContentsPos( 0, m_debugLB->contentsHeight()-m_debugLB->visibleHeight());
291 m_debugLB->setUpdatesEnabled(true);
292 m_debugLB->triggerUpdate(false);
298 KSircIOController::~KSircIOController()
300 delete m_debugLB;
303 void KSircIOController::stderr_read(KProcess *p, char *b, int l)
305 stdout_read(p, b, l);
308 void KSircIOController::stdin_write(QByteArray s)
310 if (!proc->isRunning())
312 kDebug(5008) << "writing to a dead process! (" << s << ")\n";
313 return;
316 //kDebug(5008) << "--> wrote: " << s;
317 buffer += s;
318 //fprintf(stderr, "Buffer output: ");
319 //my_print(buffer);
321 if(proc_CTS == TRUE){
322 int len = buffer.length();
323 if(send_buf != 0x0){
324 qWarning("KProcess barfed in all clear signal again");
325 delete[] send_buf;
327 send_buf = new char[len];
328 memcpy(send_buf, buffer.data(), len);
329 if(proc->writeStdin(send_buf, len) == FALSE){
330 kDebug(5008) << "Failed to write but CTS HIGH! Setting low!: " << s << endl;
332 else{
333 if(m_debugLB){
334 QString s = QString::fromUtf8(buffer);
335 m_debugLB->insertItem(s);
336 m_debugLB->setContentsPos( 0, m_debugLB->contentsHeight());
338 buffer.truncate(0);
340 proc_CTS = FALSE;
343 if(buffer.length() > 5000){
344 kDebug(5008) << "IOController: KProcess barfing again!\n";
346 // write(sirc_stdin, s, s.length());
350 void KSircIOController::sircDied(KProcess *process)
352 if ( process->exitStatus() == 0 )
353 return;
354 kDebug(5008) << "IOController: KProcess died!\n";
355 ksircproc->TopList["!all"]->sirc_receive("*E* DSIRC IS DEAD");
356 ksircproc->TopList["!all"]->sirc_receive("*E* KSIRC WINDOW HALTED");
357 ksircproc->TopList["!all"]->sirc_receive( QByteArray( "*E* Tried to run: " ) + KGlobal::dirs()->findExe("dsirc").ascii() + QByteArray( "\n" ) );
358 ksircproc->TopList["!all"]->sirc_receive("*E* DID YOU READ THE INSTALL INTRUCTIONS?");
361 void KSircIOController::procCTS ( KProcess *)
363 proc_CTS = true;
364 delete[] send_buf;
365 send_buf = 0x0;
366 if(!buffer.isEmpty()){
367 QByteArray str = "";
368 stdin_write(str);
372 void KSircIOController::showContextMenuOnDebugWindow(Q3ListBoxItem *, const QPoint &pos)
374 if (!m_debugLB)
375 return;
377 Q3PopupMenu popup(m_debugLB);
378 popup.insertItem("Save Contents to File...", 1);
379 if (popup.exec( pos ) != 1)
380 return;
382 QString path = KFileDialog::getSaveFileName();
383 if (path.isEmpty())
384 return;
386 QFile file(path);
387 if (!file.open(QIODevice::WriteOnly))
388 return;
390 QTextStream stream(&file);
392 for (uint i = 0; i < m_debugLB->count(); ++i)
393 stream << m_debugLB->text(i) << endl;
396 void KSircIOController::showDebugTraffic(bool show)
398 kDebug(5008) << "Got show request: " << show << endl;
399 if(m_debugLB == 0 && show == true){
400 m_debugLB = new Q3ListBox(0x0, QByteArray(this->name()) + "_debugWindow");
401 m_debugLB->resize(600, 300);
402 m_debugLB->show();
403 connect(m_debugLB,SIGNAL(contextMenuRequested(Q3ListBoxItem *,const QPoint &)),
404 this,SLOT(showContextMenuOnDebugWindow(Q3ListBoxItem *,const QPoint &)));
406 else if(m_debugLB != 0 && show == false){
407 delete m_debugLB;
408 m_debugLB = 0x0;
413 bool KSircIOController::isDebugTraffic()
415 if(m_debugLB != 0)
416 return true;
417 else
418 return false;
421 void KSircIOController::appendDebug(QString s)
423 if(m_debugLB){
424 m_debugLB->insertItem(s);
425 m_debugLB->setContentsPos( 0, m_debugLB->contentsHeight()-m_debugLB->visibleHeight());
429 #include "iocontroller.moc"