~quassel-dev/quassel/i18n-master

« back to all changes in this revision

Viewing changes to src/core/server.cpp

  • Committer: Manuel Nickschas
  • Date: 2007-06-20 01:21:00 UTC
  • Revision ID: git-v1:077d44f36d2f5c730283ef6be839aea7dd073d56
Starting reorganization of files in preparation of separation of client and GUI.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2005-07 by The Quassel Team                             *
 
3
 *   devel@quassel-irc.org                                                 *
 
4
 *                                                                         *
 
5
 *   This program is free software; you can redistribute it and/or modify  *
 
6
 *   it under the terms of the GNU General Public License as published by  *
 
7
 *   the Free Software Foundation; either version 2 of the License, or     *
 
8
 *   (at your option) any later version.                                   *
 
9
 *                                                                         *
 
10
 *   This program is distributed in the hope that it will be useful,       *
 
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
13
 *   GNU General Public License for more details.                          *
 
14
 *                                                                         *
 
15
 *   You should have received a copy of the GNU General Public License     *
 
16
 *   along with this program; if not, write to the                         *
 
17
 *   Free Software Foundation, Inc.,                                       *
 
18
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 
19
 ***************************************************************************/
 
20
 
 
21
#include "util.h"
 
22
#include "global.h"
 
23
#include "server.h"
 
24
#include "message.h"
 
25
 
 
26
#include <QMetaObject>
 
27
#include <QDateTime>
 
28
 
 
29
Server::Server(UserId uid, QString net) : user(uid), network(net) {
 
30
  QString MQUOTE = QString('\020');
 
31
  ctcpMDequoteHash[MQUOTE + '0'] = QString('\000');
 
32
  ctcpMDequoteHash[MQUOTE + 'n'] = QString('\n');
 
33
  ctcpMDequoteHash[MQUOTE + 'r'] = QString('\r');
 
34
  ctcpMDequoteHash[MQUOTE + MQUOTE] = MQUOTE;
 
35
 
 
36
  XDELIM = QString('\001');
 
37
  QString XQUOTE = QString('\134');
 
38
  ctcpXDelimDequoteHash[XQUOTE + XQUOTE] = XQUOTE;
 
39
  ctcpXDelimDequoteHash[XQUOTE + QString('a')] = XDELIM;
 
40
}
 
41
 
 
42
Server::~Server() {
 
43
 
 
44
}
 
45
 
 
46
void Server::run() {
 
47
  connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected()));
 
48
  connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
 
49
  connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
 
50
  connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
 
51
  connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
 
52
 
 
53
  exec();
 
54
}
 
55
 
 
56
void Server::sendState() {
 
57
  VarMap s;
 
58
  VarMap n, t;
 
59
  foreach(QString key, nicks.keys())  { n[key] = nicks[key]; }
 
60
  foreach(QString key, topics.keys()) { t[key] = topics[key];}
 
61
  s["Nicks"] = n;
 
62
  s["Topics"] = t;
 
63
  s["OwnNick"] = ownNick;
 
64
  s["ServerSupports"] = serverSupports;
 
65
  emit serverState(network, s);
 
66
}
 
67
 
 
68
void Server::connectToIrc(QString net) {
 
69
  if(net != network) return; // not me!
 
70
  networkSettings = Global::data(user, "Networks").toMap()[net].toMap();
 
71
  identity = Global::data(user, "Identities").toMap()[networkSettings["Identity"].toString()].toMap();
 
72
  QList<QVariant> servers = networkSettings["Servers"].toList();
 
73
  QString host = servers[0].toMap()["Address"].toString();
 
74
  quint16 port = servers[0].toMap()["Port"].toUInt();
 
75
  displayStatusMsg(QString("Connecting to %1:%2...").arg(host).arg(port));
 
76
  socket.connectToHost(host, port);
 
77
}
 
78
 
 
79
void Server::disconnectFromIrc(QString net) {
 
80
  if(net != network) return; // not me!
 
81
  socket.disconnectFromHost();
 
82
}
 
83
 
 
84
void Server::socketHasData() {
 
85
  while(socket.canReadLine()) {
 
86
    QString s = socket.readLine().trimmed();
 
87
    //qDebug() << "Read" << s;
 
88
    emit recvRawServerMsg(s);
 
89
    //Message *msg = Message::createFromServerString(this, s);
 
90
    handleServerMsg(s);
 
91
  }
 
92
}
 
93
 
 
94
void Server::socketError( QAbstractSocket::SocketError err ) {
 
95
  //qDebug() << "Socket Error!";
 
96
  //emit error(err);
 
97
}
 
98
 
 
99
void Server::socketConnected( ) {
 
100
  emit connected(network);
 
101
  putRawLine(QString("NICK :%1").arg(identity["NickList"].toStringList()[0]));  // FIXME: try more nicks if error occurs
 
102
  putRawLine(QString("USER %1 8 * :%2").arg(identity["Ident"].toString()).arg(identity["RealName"].toString()));
 
103
}
 
104
 
 
105
void Server::socketDisconnected( ) {
 
106
  //qDebug() << "Socket disconnected!";
 
107
  emit disconnected(network);
 
108
  topics.clear();
 
109
  nicks.clear();
 
110
}
 
111
 
 
112
void Server::socketStateChanged(QAbstractSocket::SocketState state) {
 
113
  //qDebug() << "Socket state changed: " << state;
 
114
}
 
115
 
 
116
QString Server::updateNickFromMask(QString mask) {
 
117
  QString user = userFromMask(mask);
 
118
  QString host = hostFromMask(mask);
 
119
  QString nick = nickFromMask(mask);
 
120
  if(nicks.contains(nick) && !user.isEmpty() && !host.isEmpty()) {
 
121
    VarMap n = nicks[nick];
 
122
    if(n["User"].toString() != user || n["Host"].toString() != host) {
 
123
      if(!n["User"].toString().isEmpty() || !n["Host"].toString().isEmpty())
 
124
        qWarning(QString("Strange: Hostmask for nick %1 has changed!").arg(nick).toAscii());
 
125
      n["User"] = user; n["Host"] = host;
 
126
      nicks[nick] = n;
 
127
      emit nickUpdated(network, nick, n);
 
128
    }
 
129
  }
 
130
  return nick;
 
131
}
 
132
 
 
133
void Server::userInput(QString net, QString buf, QString msg) {
 
134
  if(net != network) return; // not me!
 
135
  //msg = msg.trimmed(); // remove whitespace from start and end
 
136
  if(msg.isEmpty()) return;
 
137
  if(!msg.startsWith('/')) {
 
138
    msg = QString("/SAY ") + msg;
 
139
  }
 
140
  handleUserInput(buf, msg);
 
141
}
 
142
 
 
143
void Server::putRawLine(QString s) {
 
144
//  qDebug() << "SentRaw: " << s;
 
145
  s += "\r\n";
 
146
  socket.write(s.toAscii());
 
147
}
 
148
 
 
149
void Server::putCmd(QString cmd, QStringList params, QString prefix) {
 
150
  QString m;
 
151
  if(!prefix.isEmpty()) m += ":" + prefix + " ";
 
152
  m += cmd.toUpper();
 
153
  for(int i = 0; i < params.size() - 1; i++) {
 
154
    m += " " + params[i];
 
155
  }
 
156
  if(!params.isEmpty()) m += " :" + params.last();
 
157
//  qDebug() << "Sent: " << m;
 
158
  m += "\r\n";
 
159
  socket.write(m.toAscii());
 
160
}
 
161
 
 
162
/** Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */
 
163
void Server::handleServerMsg(QString msg) {
 
164
  try {
 
165
    if(msg.isEmpty()) {
 
166
      qWarning() << "Received empty string from server!";
 
167
      return;
 
168
    }
 
169
    // OK, first we split the raw message into its various parts...
 
170
    QString prefix = "";
 
171
    QString cmd;
 
172
    QStringList params;
 
173
 
 
174
    // check for prefix by checking for a colon as the first char
 
175
    if(msg[0] == ':') {
 
176
      msg.remove(0,1);
 
177
      prefix = msg.section(' ', 0, 0);
 
178
      msg = msg.section(' ', 1);
 
179
    }
 
180
 
 
181
    // next string without a whitespace is the command
 
182
    cmd = msg.section(' ', 0, 0).toUpper();
 
183
    msg = msg.mid(cmd.length());
 
184
 
 
185
    // get the parameters
 
186
    QString trailing = "";
 
187
    if(msg.contains(" :")) {
 
188
      trailing = msg.section(" :", 1);
 
189
      msg = msg.section(" :", 0, 0);
 
190
    }
 
191
    if(!msg.isEmpty()) {
 
192
      params << msg.split(' ', QString::SkipEmptyParts);
 
193
    }
 
194
    if(!trailing.isEmpty()) {
 
195
      params << trailing;
 
196
    }
 
197
 
 
198
    // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this!
 
199
    uint num = cmd.toUInt();
 
200
    if(num > 0) {
 
201
      Q_ASSERT(params.count() > 0); // Violation to RFC
 
202
      params.removeFirst();
 
203
    }
 
204
 
 
205
    // Now we try to find a handler for this message. BTW, I do love the Trolltech guys ;-)
 
206
    QString hname = cmd.toLower();
 
207
    hname[0] = hname[0].toUpper();
 
208
    hname = "handleServer" + hname;
 
209
    if(!QMetaObject::invokeMethod(this, hname.toAscii(), Q_ARG(QString, prefix), Q_ARG(QStringList, params))) {
 
210
      // Ok. Default handler it is.
 
211
      defaultServerHandler(cmd, prefix, params);
 
212
    }
 
213
  } catch(Exception e) {
 
214
    emit displayMsg(Message::Error, "", e.msg());
 
215
  }
 
216
}
 
217
 
 
218
void Server::defaultServerHandler(QString cmd, QString prefix, QStringList params) {
 
219
  uint num = cmd.toUInt();
 
220
  if(num) {
 
221
    // A lot of server messages don't really need their own handler because they don't do much.
 
222
    // Catch and handle these here.
 
223
    switch(num) {
 
224
      // Welcome, status, info messages. Just display these.
 
225
      case 2: case 3: case 4: case 5: case 251: case 252: case 253: case 254: case 255: case 372: case 375:
 
226
        emit displayMsg(Message::Server, "", params.join(" "), prefix);
 
227
        break;
 
228
      // Server error messages without param, just display them
 
229
      case 409: case 411: case 412: case 422: case 424: case 445: case 446: case 451: case 462:
 
230
      case 463: case 464: case 465: case 466: case 472: case 481: case 483: case 485: case 491: case 501: case 502:
 
231
      case 431: // ERR_NONICKNAMEGIVEN 
 
232
        emit displayMsg(Message::Error, "", params.join(" "), prefix);
 
233
        break;
 
234
      // Server error messages, display them in red. First param will be appended.
 
235
      case 401: case 402: case 403: case 404: case 406: case 408: case 415: case 421: case 442:
 
236
      { QString p = params.takeFirst();
 
237
        emit displayMsg(Message::Error, "", params.join(" ") + " " + p, prefix);
 
238
        break;
 
239
      }
 
240
      // Server error messages which will be displayed with a colon between the first param and the rest
 
241
      case 413: case 414: case 423: case 441: case 444: case 461:
 
242
      case 467: case 471: case 473: case 474: case 475: case 476: case 477: case 478: case 482:
 
243
      case 436: // ERR_NICKCOLLISION
 
244
      { QString p = params.takeFirst();
 
245
        emit displayMsg(Message::Error, "", p + ": " + params.join(" "));
 
246
        break;
 
247
      }
 
248
      // Ignore these commands.
 
249
      case 366: case 376:
 
250
        break;
 
251
 
 
252
      // Everything else will be marked in red, so we can add them somewhere.
 
253
      default:
 
254
        emit displayMsg(Message::Error, "", cmd + " " + params.join(" "), prefix);
 
255
    }
 
256
    //qDebug() << prefix <<":"<<cmd<<params;
 
257
  } else {
 
258
    emit displayMsg(Message::Error, "", QString("Unknown: ") + cmd + " " + params.join(" "), prefix);
 
259
    //qDebug() << prefix <<":"<<cmd<<params;
 
260
  }
 
261
}
 
262
 
 
263
void Server::handleUserInput(QString bufname, QString usrMsg) {
 
264
  try {
 
265
    /* Looks like we don't need core-side buffers...
 
266
    Buffer *buffer = 0;
 
267
    if(!bufname.isEmpty()) {
 
268
      Q_ASSERT(buffers.contains(bufname));
 
269
      buffer = buffers[bufname];
 
270
    }
 
271
    */
 
272
    QString cmd = usrMsg.section(' ', 0, 0).remove(0, 1).toUpper();
 
273
    QString msg = usrMsg.section(' ', 1);
 
274
    QString hname = cmd.toLower();
 
275
    hname[0] = hname[0].toUpper();
 
276
    hname = "handleUser" + hname;
 
277
    if(!QMetaObject::invokeMethod(this, hname.toAscii(), Q_ARG(QString, bufname), Q_ARG(QString, msg))) {
 
278
        // Ok. Default handler it is.
 
279
      defaultUserHandler(bufname, cmd, msg);
 
280
    }
 
281
  } catch(Exception e) {
 
282
    emit displayMsg(Message::Error, "", e.msg());
 
283
  }
 
284
}
 
285
 
 
286
void Server::defaultUserHandler(QString bufname, QString cmd, QString msg) {
 
287
  emit displayMsg(Message::Error, "", QString("Error: %1 %2").arg(cmd).arg(msg));
 
288
 
 
289
}
 
290
 
 
291
QStringList Server::providesUserHandlers() {
 
292
  QStringList userHandlers;
 
293
  for(int i=0; i < metaObject()->methodCount(); i++) {
 
294
    QString methodSignature(metaObject()->method(i).signature());
 
295
    if (methodSignature.startsWith("handleUser")) {
 
296
      methodSignature = methodSignature.section('(',0,0);  // chop the attribute list
 
297
      methodSignature = methodSignature.mid(10); // strip "handleUser"
 
298
      userHandlers << methodSignature;
 
299
    }
 
300
  }
 
301
  return userHandlers;
 
302
}
 
303
 
 
304
QString Server::ctcpDequote(QString message) {
 
305
  QString dequotedMessage;
 
306
  QString messagepart;
 
307
  QHash<QString, QString>::iterator ctcpquote;
 
308
  
 
309
  // copy dequote Message
 
310
  for(int i = 0; i < message.size(); i++) {
 
311
    messagepart = message[i];
 
312
    if(i+1 < message.size()) {
 
313
      for(ctcpquote = ctcpMDequoteHash.begin(); ctcpquote != ctcpMDequoteHash.end(); ++ctcpquote) {
 
314
        if(message.mid(i,2) == ctcpquote.key()) {
 
315
          dequotedMessage += ctcpquote.value();
 
316
          i++;
 
317
          break;
 
318
        }
 
319
      }
 
320
    }
 
321
    dequotedMessage += messagepart;
 
322
  }
 
323
  return dequotedMessage;
 
324
}
 
325
 
 
326
 
 
327
QString Server::ctcpXdelimDequote(QString message) {
 
328
  QString dequotedMessage;
 
329
  QString messagepart;
 
330
  QHash<QString, QString>::iterator xdelimquote;
 
331
 
 
332
  for(int i = 0; i < message.size(); i++) {
 
333
    messagepart = message[i];
 
334
    if(i+1 < message.size()) {
 
335
      for(xdelimquote = ctcpXDelimDequoteHash.begin(); xdelimquote != ctcpXDelimDequoteHash.end(); ++xdelimquote) {
 
336
        if(message.mid(i,2) == xdelimquote.key()) {
 
337
          messagepart = xdelimquote.value();
 
338
          i++;
 
339
          break;
 
340
        }
 
341
      }
 
342
    }
 
343
    dequotedMessage += messagepart;
 
344
  }
 
345
  return dequotedMessage;
 
346
}
 
347
 
 
348
QStringList Server::parseCtcp(CtcpType ctcptype, QString prefix, QString target, QString message) {
 
349
  QStringList messages;
 
350
  QString ctcp;
 
351
  
 
352
  //lowlevel message dequote
 
353
  QString dequotedMessage = ctcpDequote(message);
 
354
  
 
355
  // extract tagged / extended data
 
356
  while(dequotedMessage.contains(XDELIM)) {
 
357
    messages << dequotedMessage.section(XDELIM,0,0);
 
358
    ctcp = ctcpXdelimDequote(dequotedMessage.section(XDELIM,1,1));
 
359
    dequotedMessage = dequotedMessage.section(XDELIM,2,2);
 
360
    
 
361
    //dispatch the ctcp command
 
362
    QString ctcpcmd = ctcp.section(' ', 0, 0);
 
363
    QString ctcpparam = ctcp.section(' ', 1);
 
364
    
 
365
    QString hname = ctcpcmd.toLower();
 
366
    hname[0] = hname[0].toUpper();
 
367
    hname = "handleCtcp" + hname;
 
368
    if(!QMetaObject::invokeMethod(this, hname.toAscii(), Q_ARG(CtcpType, ctcptype), Q_ARG(QString, prefix), Q_ARG(QString, target), Q_ARG(QString, ctcpparam))) {
 
369
      // Ok. Default handler it is.
 
370
      defaultCtcpHandler(ctcptype, prefix, ctcpcmd, target, ctcpparam);
 
371
    }
 
372
  }
 
373
  if(!dequotedMessage.isEmpty()) {
 
374
    messages << dequotedMessage;
 
375
  }
 
376
  return messages;
 
377
}
 
378
 
 
379
QString Server::ctcpPack(QString ctcpTag, QString message) {
 
380
  return XDELIM + ctcpTag + ' ' + message + XDELIM;
 
381
}
 
382
 
 
383
void Server::ctcpQuery(QString bufname, QString ctcpTag, QString message) {
 
384
  QStringList params;
 
385
  params << bufname << ctcpPack(ctcpTag, message);
 
386
  putCmd("PRIVMSG", params); 
 
387
}
 
388
 
 
389
void Server::ctcpReply(QString bufname, QString ctcpTag, QString message) {
 
390
  QStringList params;
 
391
  params << bufname << ctcpPack(ctcpTag, message);
 
392
  putCmd("NOTICE", params);
 
393
}
 
394
 
 
395
/**********************************************************************************/
 
396
 
 
397
/*
 
398
void Server::handleUser(QString bufname, QString msg) {
 
399
 
 
400
 
 
401
}
 
402
*/
 
403
 
 
404
void Server::handleUserAway(QString bufname, QString msg) {
 
405
  putCmd("AWAY", QStringList(msg));
 
406
}
 
407
 
 
408
void Server::handleUserDeop(QString bufname, QString msg) {
 
409
  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
 
410
  QString m = "-"; for(int i = 0; i < nicks.count(); i++) m += 'o';
 
411
  QStringList params;
 
412
  params << bufname << m << nicks;
 
413
  putCmd("MODE", params);
 
414
}
 
415
 
 
416
void Server::handleUserDevoice(QString bufname, QString msg) {
 
417
  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
 
418
  QString m = "-"; for(int i = 0; i < nicks.count(); i++) m += 'v';
 
419
  QStringList params;
 
420
  params << bufname << m << nicks;
 
421
  putCmd("MODE", params);
 
422
}
 
423
 
 
424
void Server::handleUserInvite(QString bufname, QString msg) {
 
425
  QStringList params;
 
426
  params << msg << bufname;
 
427
  putCmd("INVITE", params);
 
428
}
 
429
 
 
430
void Server::handleUserJoin(QString bufname, QString msg) {
 
431
  putCmd("JOIN", QStringList(msg));
 
432
}
 
433
 
 
434
void Server::handleUserKick(QString bufname, QString msg) {
 
435
  QStringList params;
 
436
  params << bufname << msg.split(' ', QString::SkipEmptyParts);
 
437
  putCmd("KICK", params);
 
438
}
 
439
 
 
440
void Server::handleUserList(QString bufname, QString msg) {
 
441
  putCmd("LIST", msg.split(' ', QString::SkipEmptyParts));
 
442
}
 
443
 
 
444
void Server::handleUserMode(QString bufname, QString msg) {
 
445
  putCmd("MODE", msg.split(' ', QString::SkipEmptyParts));
 
446
}
 
447
 
 
448
// TODO: show privmsgs
 
449
void Server::handleUserMsg(QString bufname, QString msg) {
 
450
  QString nick = msg.section(" ", 0, 0);
 
451
  msg = msg.section(" ", 1);
 
452
  if(nick.isEmpty() || msg.isEmpty()) return;
 
453
  QStringList params;
 
454
  params << nick << msg;
 
455
  putCmd("PRIVMSG", params);
 
456
}
 
457
 
 
458
void Server::handleUserNick(QString bufname, QString msg) {
 
459
  QString nick = msg.section(' ', 0, 0);
 
460
  putCmd("NICK", QStringList(nick));
 
461
}
 
462
 
 
463
void Server::handleUserOp(QString bufname, QString msg) {
 
464
  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
 
465
  QString m = "+"; for(int i = 0; i < nicks.count(); i++) m += 'o';
 
466
  QStringList params;
 
467
  params << bufname << m << nicks;
 
468
  putCmd("MODE", params);
 
469
}
 
470
 
 
471
void Server::handleUserPart(QString bufname, QString msg) {
 
472
  QStringList params;
 
473
  params << bufname << msg;
 
474
  putCmd("PART", params);
 
475
}
 
476
 
 
477
// TODO: implement queries
 
478
void Server::handleUserQuery(QString bufname, QString msg) {
 
479
  QString nick = msg.section(' ', 0, 0);
 
480
  if(!nick.isEmpty()) emit queryRequested(network, nick);
 
481
}
 
482
 
 
483
void Server::handleUserQuit(QString bufname, QString msg) {
 
484
  putCmd("QUIT", QStringList(msg));
 
485
}
 
486
 
 
487
void Server::handleUserQuote(QString bufname, QString msg) {
 
488
  putRawLine(msg);
 
489
}
 
490
 
 
491
void Server::handleUserSay(QString bufname, QString msg) {
 
492
  if(bufname.isEmpty()) return;  // server buffer
 
493
  QStringList params;
 
494
  params << bufname << msg;
 
495
  putCmd("PRIVMSG", params);
 
496
  if(isChannelName(bufname)) {
 
497
    emit displayMsg(Message::Plain, params[0], msg, ownNick, Message::Self);
 
498
  } else {
 
499
    emit displayMsg(Message::Plain, params[0], msg, ownNick, Message::Self|Message::PrivMsg);
 
500
  }
 
501
}
 
502
 
 
503
void Server::handleUserMe(QString bufname, QString msg) {
 
504
  if(bufname.isEmpty()) return; // server buffer
 
505
  ctcpQuery(bufname, "ACTION", msg);
 
506
  emit displayMsg(Message::Action, bufname, msg, ownNick);
 
507
}
 
508
 
 
509
void Server::handleUserTopic(QString bufname, QString msg) {
 
510
  if(bufname.isEmpty()) return;
 
511
  QStringList params;
 
512
  params << bufname << msg;
 
513
  putCmd("TOPIC", params);
 
514
}
 
515
 
 
516
void Server::handleUserVoice(QString bufname, QString msg) {
 
517
  QStringList nicks = msg.split(' ', QString::SkipEmptyParts);
 
518
  QString m = "+"; for(int i = 0; i < nicks.count(); i++) m += 'v';
 
519
  QStringList params;
 
520
  params << bufname << m << nicks;
 
521
  putCmd("MODE", params);
 
522
}
 
523
 
 
524
/**********************************************************************************/
 
525
 
 
526
void Server::handleServerJoin(QString prefix, QStringList params) {
 
527
  Q_ASSERT(params.count() == 1);
 
528
  QString nick = updateNickFromMask(prefix);
 
529
  emit displayMsg(Message::Join, params[0], params[0], prefix);
 
530
  if(nick == ownNick) {
 
531
  //  Q_ASSERT(!buffers.contains(params[0]));  // cannot join a buffer twice!
 
532
  //  Buffer *buf = new Buffer(params[0]);
 
533
  //  buffers[params[0]] = buf;
 
534
    topics[params[0]] = "";
 
535
    emit topicSet(network, params[0], "");
 
536
  } //else {
 
537
    VarMap n;
 
538
    if(nicks.contains(nick)) {
 
539
      n = nicks[nick];
 
540
      VarMap chans = n["Channels"].toMap();
 
541
      // Q_ASSERT(!chans.keys().contains(params[0])); TODO uncomment
 
542
      chans[params[0]] = VarMap();
 
543
      n["Channels"] = chans;
 
544
      nicks[nick] = n;
 
545
      emit nickUpdated(network, nick, n);
 
546
    } else {
 
547
      VarMap chans;
 
548
      chans[params[0]] = VarMap();
 
549
      n["Channels"] = chans;
 
550
      n["User"] = userFromMask(prefix);
 
551
      n["Host"] = hostFromMask(prefix);
 
552
      nicks[nick] = n;
 
553
      emit nickAdded(network, nick, n);
 
554
    }
 
555
    //emit displayMsg(Message::Join, params[0], params[0], prefix);
 
556
  //}
 
557
}
 
558
 
 
559
void Server::handleServerKick(QString prefix, QStringList params) {
 
560
  QString kicker = updateNickFromMask(prefix);
 
561
  QString nick = params[1];
 
562
  Q_ASSERT(nicks.contains(nick));
 
563
  VarMap n = nicks[nick];
 
564
  VarMap chans = n["Channels"].toMap();
 
565
  Q_ASSERT(chans.contains(params[0]));
 
566
  chans.remove(params[0]);
 
567
  QString msg = nick;
 
568
  if(params.count() > 2) msg = QString("%1 %2").arg(msg).arg(params[2]);
 
569
  emit displayMsg(Message::Kick, params[0], msg, prefix);
 
570
  if(chans.count() > 0) {
 
571
    n["Channels"] = chans;
 
572
    nicks[nick] = n;
 
573
    emit nickUpdated(network, nick, n);
 
574
  } else {
 
575
    nicks.remove(nick);
 
576
    emit nickRemoved(network, nick);
 
577
  }
 
578
  if(nick == ownNick) {
 
579
    topics.remove(params[0]);
 
580
  }
 
581
}
 
582
 
 
583
void Server::handleServerMode(QString prefix, QStringList params) {
 
584
  if(isChannelName(params[0])) {
 
585
    // TODO only channel-user modes supported by now
 
586
    QString prefixes = serverSupports["PrefixModes"].toString();
 
587
    QString modes = params[1];
 
588
    int p = 2;
 
589
    int m = 0;
 
590
    bool add = true;
 
591
    while(m < modes.length()) {
 
592
      if(modes[m] == '+') { add = true; m++; continue; }
 
593
      if(modes[m] == '-') { add = false; m++; continue; }
 
594
      if(prefixes.contains(modes[m])) {  // it's a user channel mode
 
595
        Q_ASSERT(params.count() > m);
 
596
        QString nick = params[p++];
 
597
        if(nicks.contains(nick)) {  // sometimes, a server might try to set a MODE on a nick that is no longer there
 
598
          VarMap n = nicks[nick]; VarMap clist = n["Channels"].toMap(); VarMap chan = clist[params[0]].toMap();
 
599
          QString mstr = chan["Mode"].toString();
 
600
          add ? mstr += modes[m] : mstr.remove(modes[m]);
 
601
          chan["Mode"] = mstr; clist[params[0]] = chan; n["Channels"] = clist; nicks[nick] = n;
 
602
          emit nickUpdated(network, nick, n);
 
603
        }
 
604
        m++;
 
605
      } else {
 
606
        // TODO add more modes
 
607
        m++;
 
608
      }
 
609
    }
 
610
    emit displayMsg(Message::Mode, params[0], params.join(" "), prefix);
 
611
  } else {
 
612
    //Q_ASSERT(nicks.contains(params[0]));
 
613
    //VarMap n = nicks[params[0]].toMap();
 
614
    //QString mode = n["Mode"].toString();
 
615
    emit displayMsg(Message::Mode, "", params.join(" "));
 
616
  }
 
617
}
 
618
 
 
619
void Server::handleServerNick(QString prefix, QStringList params) {
 
620
  QString oldnick = updateNickFromMask(prefix);
 
621
  QString newnick = params[0];
 
622
  VarMap v = nicks.take(oldnick);
 
623
  nicks[newnick] = v;
 
624
  VarMap chans = v["Channels"].toMap();
 
625
  foreach(QString c, chans.keys()) {
 
626
    if(oldnick != ownNick) { emit displayMsg(Message::Nick, c, newnick, prefix); }
 
627
    else { emit displayMsg(Message::Nick, c, newnick, newnick); }
 
628
  }
 
629
  emit nickRenamed(network, oldnick, newnick);
 
630
  if(oldnick == ownNick) {
 
631
    ownNick = newnick;
 
632
    emit ownNickSet(network, newnick);
 
633
  }
 
634
}
 
635
 
 
636
void Server::handleServerNotice(QString prefix, QStringList params) {
 
637
  //Message msg(Message::Notice, params[1], prefix);
 
638
  if(currentServer.isEmpty() || prefix == currentServer) emit displayMsg(Message::Server, "", params[1], prefix);
 
639
  else emit displayMsg(Message::Notice, "", params[1], prefix);
 
640
}
 
641
 
 
642
void Server::handleServerPart(QString prefix, QStringList params) {
 
643
  QString nick = updateNickFromMask(prefix);
 
644
  Q_ASSERT(nicks.contains(nick));
 
645
  VarMap n = nicks[nick];
 
646
  VarMap chans = n["Channels"].toMap();
 
647
  Q_ASSERT(chans.contains(params[0]));
 
648
  chans.remove(params[0]);
 
649
  QString msg;
 
650
  if(params.count() > 1) msg = params[1];
 
651
  emit displayMsg(Message::Part, params[0], msg, prefix);
 
652
  if(chans.count() > 0) {
 
653
    n["Channels"] = chans;
 
654
    nicks[nick] = n;
 
655
    emit nickUpdated(network, nick, n);
 
656
  } else {
 
657
    nicks.remove(nick);
 
658
    emit nickRemoved(network, nick);
 
659
  }
 
660
  if(nick == ownNick) {
 
661
    Q_ASSERT(topics.contains(params[0]));
 
662
    topics.remove(params[0]);
 
663
  }
 
664
}
 
665
 
 
666
void Server::handleServerPing(QString prefix, QStringList params) {
 
667
  putCmd("PONG", params);
 
668
}
 
669
 
 
670
void Server::handleServerPrivmsg(QString prefix, QStringList params) {
 
671
  updateNickFromMask(prefix);
 
672
  Q_ASSERT(params.count() >= 2);
 
673
  if(params.count()<2) emit displayMsg(Message::Plain, params[0], "", prefix);
 
674
  else {
 
675
    // it's possible to pack multiple privmsgs into one param using ctcp
 
676
    QStringList messages = parseCtcp(Server::CtcpQuery, prefix, params[0], params[1]);
 
677
    if(params[0].toLower() == ownNick.toLower()) {  // Freenode sends nickname in lower case!
 
678
      foreach(QString message, messages) {
 
679
        if(!message.isEmpty()) {
 
680
          emit displayMsg(Message::Plain, "", message, prefix, Message::PrivMsg);
 
681
        }
 
682
      }
 
683
      
 
684
    } else {
 
685
      //qDebug() << prefix << params;
 
686
      Q_ASSERT(isChannelName(params[0]));  // should be channel!
 
687
      foreach(QString message, messages) {
 
688
        if(!message.isEmpty()) {
 
689
          emit displayMsg(Message::Plain, params[0], message, prefix);
 
690
        }
 
691
      }
 
692
    }
 
693
  }
 
694
}
 
695
 
 
696
void Server::handleServerQuit(QString prefix, QStringList params) {
 
697
  QString nick = updateNickFromMask(prefix);
 
698
  Q_ASSERT(nicks.contains(nick));
 
699
  VarMap chans = nicks[nick]["Channels"].toMap();
 
700
  QString msg;
 
701
  if(params.count()) msg = params[0];
 
702
  foreach(QString c, chans.keys()) {
 
703
    emit displayMsg(Message::Quit, c, msg, prefix);
 
704
  }
 
705
  nicks.remove(nick);
 
706
  emit nickRemoved(network, nick);
 
707
}
 
708
 
 
709
void Server::handleServerTopic(QString prefix, QStringList params) {
 
710
  QString nick = updateNickFromMask(prefix);
 
711
  Q_ASSERT(nicks.contains(nick));
 
712
  topics[params[0]] = params[1];
 
713
  emit topicSet(network, params[0], params[1]);
 
714
  emit displayMsg(Message::Server, params[0], tr("%1 has changed topic for %2 to: \"%3\"").arg(nick).arg(params[0]).arg(params[1]));
 
715
}
 
716
 
 
717
/* RPL_WELCOME */
 
718
void Server::handleServer001(QString prefix, QStringList params) {
 
719
  // there should be only one param: "Welcome to the Internet Relay Network <nick>!<user>@<host>"
 
720
  currentServer = prefix;
 
721
  ownNick = params[0].section(' ', -1, -1).section('!', 0, 0);
 
722
  VarMap n;
 
723
  n["Channels"] = VarMap();
 
724
  nicks[ownNick] = n;
 
725
  emit ownNickSet(network, ownNick);
 
726
  emit nickAdded(network, ownNick, VarMap());
 
727
  emit displayMsg(Message::Server, "", params[0], prefix);
 
728
  // send performlist
 
729
  QStringList performList = networkSettings["Perform"].toString().split( "\n" );
 
730
  int count = performList.count();
 
731
  for(int a = 0; a < count; a++) {
 
732
    if(!performList[a].isEmpty() ) {
 
733
      userInput(network, "", performList[a]);
 
734
    }
 
735
  }
 
736
}
 
737
 
 
738
/* RPL_ISUPPORT */
 
739
// TODO Complete 005 handling, also use sensible defaults for non-sent stuff
 
740
void Server::handleServer005(QString prefix, QStringList params) {
 
741
  //qDebug() << prefix << params;
 
742
  params.removeLast();
 
743
  foreach(QString p, params) {
 
744
    QString key = p.section("=", 0, 0);
 
745
    QString val = p.section("=", 1);
 
746
    serverSupports[key] = val;
 
747
    // handle some special cases
 
748
    if(key == "PREFIX") {
 
749
      VarMap foo; QString modes, prefixes;
 
750
      Q_ASSERT(val.contains(')') && val.startsWith('('));
 
751
      int m = 1, p;
 
752
      for(p = 2; p < val.length(); p++) if(val[p] == ')') break;
 
753
      p++;
 
754
      for(; val[m] != ')'; m++, p++) {
 
755
        Q_ASSERT(p < val.length());
 
756
        foo[QString(val[m])] = QString(val[p]);
 
757
        modes += val[m]; prefixes += val[p];
 
758
      }
 
759
      serverSupports["PrefixModes"] = modes; serverSupports["Prefixes"] = prefixes;
 
760
      serverSupports["ModePrefixMap"] = foo;
 
761
    }
 
762
  }
 
763
}
 
764
 
 
765
 
 
766
/* RPL_NOTOPIC */
 
767
void Server::handleServer331(QString prefix, QStringList params) {
 
768
  topics[params[0]] = "";
 
769
  emit topicSet(network, params[0], "");
 
770
  emit displayMsg(Message::Server, params[0], tr("No topic is set for %1.").arg(params[0]));
 
771
}
 
772
 
 
773
/* RPL_TOPIC */
 
774
void Server::handleServer332(QString prefix, QStringList params) {
 
775
  topics[params[0]] = params[1];
 
776
  emit topicSet(network, params[0], params[1]);
 
777
  emit displayMsg(Message::Server, params[0], tr("Topic for %1 is \"%2\"").arg(params[0]).arg(params[1]));
 
778
}
 
779
 
 
780
/* Topic set by... */
 
781
void Server::handleServer333(QString prefix, QStringList params) {
 
782
  emit displayMsg(Message::Server, params[0],
 
783
                    tr("Topic set by %1 on %2").arg(params[1]).arg(QDateTime::fromTime_t(params[2].toUInt()).toString()));
 
784
}
 
785
 
 
786
/* RPL_NAMREPLY */
 
787
void Server::handleServer353(QString prefix, QStringList params) {
 
788
  params.removeFirst(); // = or *
 
789
  QString buf = params.takeFirst();
 
790
  QString prefixes = serverSupports["Prefixes"].toString();
 
791
  foreach(QString nick, params[0].split(' ')) {
 
792
    QString mode = "", pfx = "";
 
793
    if(prefixes.contains(nick[0])) {
 
794
      pfx = nick[0];
 
795
      for(int i = 0;; i++)
 
796
        if(prefixes[i] == nick[0]) { mode = serverSupports["PrefixModes"].toString()[i]; break; }
 
797
      nick.remove(0,1);
 
798
    }
 
799
    VarMap c; c["Mode"] = mode; c["Prefix"] = pfx;
 
800
    if(nicks.contains(nick)) {
 
801
      VarMap n = nicks[nick];
 
802
      VarMap chans = n["Channels"].toMap();
 
803
      chans[buf] = c;
 
804
      n["Channels"] = chans;
 
805
      nicks[nick] = n;
 
806
      emit nickUpdated(network, nick, n);
 
807
    } else {
 
808
      VarMap n; VarMap c; VarMap chans;
 
809
      c["Mode"] = mode;
 
810
      chans[buf] = c;
 
811
      n["Channels"] = chans;
 
812
      nicks[nick] = n;
 
813
      emit nickAdded(network, nick, n);
 
814
    }
 
815
  }
 
816
}
 
817
 
 
818
/* ERR_ERRONEUSNICKNAME */
 
819
void Server::handleServer432(QString prefix, QStringList params) {
 
820
  if(params.size() < 2) {
 
821
    // handle unreal-ircd bug, where unreal ircd doesnt supply a TARGET in ERR_ERRONEUSNICKNAME during registration phase:
 
822
    // nick @@@
 
823
    // :irc.scortum.moep.net 432  @@@ :Erroneous Nickname: Illegal characters
 
824
    // correct server reply:
 
825
    // :irc.scortum.moep.net 432 * @@@ :Erroneous Nickname: Illegal characters
 
826
    emit displayMsg(Message::Error, "", tr("There is a nickname in your identity's nicklist which contains illegal characters"));
 
827
    emit displayMsg(Message::Error, "", tr("Due to a bug in Unreal IRCd (and maybe other irc-servers too) we're unable to determine the erroneous nick"));
 
828
    emit displayMsg(Message::Error, "", tr("Please use: /nick <othernick> to continue or clean up your nicklist"));
 
829
  } else {
 
830
    QString errnick = params[0];
 
831
    emit displayMsg(Message::Error, "", tr("Nick %1 contains illegal characters").arg(errnick));
 
832
    // if there is a problem while connecting to the server -> we handle it
 
833
    // TODO rely on another source...
 
834
    if(currentServer.isEmpty()) {
 
835
      QStringList desiredNicks = identity["NickList"].toStringList();
 
836
      int nextNick = desiredNicks.indexOf(errnick) + 1;
 
837
      if (desiredNicks.size() > nextNick) {
 
838
        putCmd("NICK", QStringList(desiredNicks[nextNick]));
 
839
      } else {
 
840
        emit displayMsg(Message::Error, "", tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"));
 
841
      }
 
842
    }
 
843
  }
 
844
}
 
845
 
 
846
/* ERR_NICKNAMEINUSE */
 
847
void Server::handleServer433(QString prefix, QStringList params) {
 
848
  QString errnick = params[0];
 
849
  emit displayMsg(Message::Error, "", tr("Nick %1 is already taken").arg(errnick));
 
850
  // if there is a problem while connecting to the server -> we handle it
 
851
  // TODO rely on another source...
 
852
  if(currentServer.isEmpty()) {
 
853
    QStringList desiredNicks = identity["NickList"].toStringList();
 
854
    int nextNick = desiredNicks.indexOf(errnick) + 1;
 
855
    if (desiredNicks.size() > nextNick) {
 
856
      putCmd("NICK", QStringList(desiredNicks[nextNick]));
 
857
    } else {
 
858
      emit displayMsg(Message::Error, "", tr("No free and valid nicks in nicklist found. use: /nick <othernick> to continue"));
 
859
    }
 
860
  }
 
861
}
 
862
 
 
863
/***********************************************************************************/
 
864
// CTCP HANDLER
 
865
 
 
866
void Server::handleCtcpAction(CtcpType ctcptype, QString prefix, QString target, QString param) {
 
867
  emit displayMsg(Message::Action, target, param, prefix);
 
868
}
 
869
 
 
870
void Server::handleCtcpPing(CtcpType ctcptype, QString prefix, QString target, QString param) {
 
871
  if(ctcptype == CtcpQuery) {
 
872
    ctcpReply(nickFromMask(prefix), "PING", param);
 
873
    emit displayMsg(Message::Server, "", tr("Received CTCP PING request by %1").arg(prefix));
 
874
  } else {
 
875
    // display ping answer
 
876
  }
 
877
}
 
878
 
 
879
void Server::handleCtcpVersion(CtcpType ctcptype, QString prefix, QString target, QString param) {
 
880
  if(ctcptype == CtcpQuery) {
 
881
    // FIXME use real Info about quassel :)
 
882
    //ctcpReply(nickFromMask(prefix), "VERSION", QString("Quassel:pre Release:*nix"));
 
883
    ctcpReply(nickFromMask(prefix), "VERSION", QString("Quassel IRC (Pre-Release) - http://www.quassel-irc.org"));
 
884
    emit displayMsg(Message::Server, "", tr("Received CTCP VERSION request by %1").arg(prefix));
 
885
  } else {
 
886
    // TODO display Version answer
 
887
  }
 
888
}
 
889
 
 
890
void Server::defaultCtcpHandler(CtcpType ctcptype, QString prefix, QString cmd, QString target, QString param) {
 
891
  emit displayMsg(Message::Error, "", tr("Received unknown CTCP %1 by %2").arg(cmd).arg(prefix));
 
892
}
 
893
 
 
894
 
 
895
/***********************************************************************************/
 
896
 
 
897
/* Exception classes for message handling */
 
898
Server::ParseError::ParseError(QString cmd, QString prefix, QStringList params) {
 
899
  _msg = QString("Command Parse Error: ") + cmd + params.join(" ");
 
900
 
 
901
}
 
902
 
 
903
Server::UnknownCmdError::UnknownCmdError(QString cmd, QString prefix, QStringList params) {
 
904
  _msg = QString("Unknown Command: ") + cmd;
 
905
 
 
906
}