TDE network applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1048 lines
31 KiB

  1. #ifdef HAVE_CONFIG_H
  2. #include <config.h>
  3. #endif
  4. #include "alistbox.h"
  5. #include "chanparser.h"
  6. #include "ksopts.h"
  7. #include "control_message.h"
  8. #include "ssfeprompt.h"
  9. #include "toplevel.h"
  10. #include "ksircprocess.h"
  11. #include "ksview.h"
  12. #include <stdio.h>
  13. #include <tqregexp.h>
  14. #include <tqapplication.h>
  15. #include <tdelocale.h>
  16. #include <tdemessagebox.h>
  17. #include <kdebug.h>
  18. #include <tqptrlist.h>
  19. // Static parser table is "initialized"
  20. TQDict<parseFunc> ChannelParser::parserTable;
  21. ChannelParser::ChannelParser(KSircTopLevel *_top)
  22. {
  23. top = _top;
  24. /*
  25. * Initial helper variables
  26. */
  27. prompt_active = false;
  28. current_item = -1;
  29. top_item = 0;
  30. if(parserTable.isEmpty() == TRUE){
  31. parserTable.setAutoDelete(TRUE);
  32. parserTable.insert("`l`", new parseFunc(&ChannelParser::parseSSFEClear));
  33. parserTable.insert("`s`", new parseFunc(&ChannelParser::parseSSFEStatus));
  34. parserTable.insert("`i`", new parseFunc(&ChannelParser::parseSSFEInit));
  35. parserTable.insert("`t`", new parseFunc(&ChannelParser::parseSSFEMsg));
  36. parserTable.insert("`o`", new parseFunc(&ChannelParser::parseSSFEOut));
  37. parserTable.insert("`p`", new parseFunc(&ChannelParser::parseSSFEPrompt));
  38. parserTable.insert("`P`", new parseFunc(&ChannelParser::parseSSFEPrompt));
  39. parserTable.insert("`R`", new parseFunc(&ChannelParser::parseSSFEReconnect));
  40. // The rest are *** info message
  41. parserTable.insert("***", new parseFunc(&ChannelParser::parseINFOInfo));
  42. parserTable.insert("*E*", new parseFunc(&ChannelParser::parseINFOError));
  43. parserTable.insert("*!*", new parseFunc(&ChannelParser::parseINFONicks)); // Normal
  44. parserTable.insert("*C*", new parseFunc(&ChannelParser::parseINFONicks)); // 1st line
  45. parserTable.insert("*c*", new parseFunc(&ChannelParser::parseINFONicks)); // Last line
  46. parserTable.insert("*#*", new parseFunc(&ChannelParser::parseINFONicks)); // Non enhanced
  47. parserTable.insert("*$*", new parseFunc(&ChannelParser::parseINFONicks)); // Enhanced turned off
  48. parserTable.insert("*>*", new parseFunc(&ChannelParser::parseINFOJoin));
  49. parserTable.insert("*<*", new parseFunc(&ChannelParser::parseINFOPart));
  50. parserTable.insert("*N*", new parseFunc(&ChannelParser::parseINFOChangeNick));
  51. parserTable.insert("*+*", new parseFunc(&ChannelParser::parseINFOMode));
  52. parserTable.insert("*T*", new parseFunc(&ChannelParser::parseINFOTopic));
  53. // End of info message
  54. parserTable.insert("* ", new parseFunc(&ChannelParser::parseCTCPAction));
  55. }
  56. }
  57. parseResult *ChannelParser::parse(TQString string)
  58. {
  59. // for older TQts
  60. parseFunc *pf;
  61. if(string.length() < 3){
  62. return new parseError(string, TQString("Dumb string, too short"));
  63. }
  64. /**
  65. * Start pre-parsing the strings to make them fit the 3 character
  66. * parser codes, etc
  67. */
  68. /*
  69. * SSFE control messages are too long, so we convert the
  70. * messges into a 3 character code, `#ssfe#\S becomes `\S`
  71. */
  72. if ((string[0] == '`') & (string.length() > 7))
  73. {
  74. TQString prefix = "`"+string[7]+"`";
  75. string = prefix + TQString(" ") + string.mid(8).stripWhiteSpace();
  76. }
  77. else if((string[0] == '*') && (string[1] == ' ')) {
  78. string.insert(1, ' ');
  79. }
  80. // Pre-parsing is now complete
  81. pf = parserTable[string.mid(0, 3)];
  82. if(pf != 0x0){
  83. parseResult *result = (this->*(pf->parser))(string);
  84. if (result)
  85. return result;
  86. }
  87. else
  88. {
  89. // debug("No handler for: %s", string.data());
  90. }
  91. // Little bit of past parsing to catch one we've missed
  92. if((string[0] == '*') && (string[2] == '*')) {
  93. string.remove(0, 3);
  94. return new parseSucc(string, ksopts->infoColor, "user|servinfo");
  95. }
  96. // If it's unkown we just fall out of the function
  97. return 0;
  98. }
  99. parseResult * ChannelParser::parseSSFEClear(TQString string)
  100. {
  101. // top->mainw->clear();
  102. top->clearWindow();
  103. // top->mainw->repaint(TRUE);
  104. string.truncate(0);
  105. return new parseSucc(TQString()); // Null string, don't display anything
  106. }
  107. parseResult * ChannelParser::parseSSFEStatus(TQString string)
  108. {
  109. string.remove(0, 12); // strip off the first 12 characters "<junk> [sirc] "
  110. if(string.length() == 0)
  111. return new parseError("", i18n("Unable to parse status string"));
  112. //kdDebug(5008) << "String: " << string << endl;
  113. TQRegExp rx("(\\S+).*\\(*([+-]*[+-\\w\\d]*)\\)*.*on (\\S+) \\((\\S+)\\)");
  114. if(rx.search(string) == -1){
  115. return new parseError("", i18n("Unable to parse status (no known format) string"));
  116. }
  117. TQString nick = rx.cap(1);
  118. TQString modes = rx.cap(2);
  119. TQString chan = rx.cap(3);
  120. TQString chanmode = rx.cap(4);
  121. /*
  122. * fix up modes which may have gotten the (away) part
  123. */
  124. if(modes.contains("away")){
  125. modes = "";
  126. }
  127. bool away = false;
  128. if(string.contains("(away)"))
  129. away = true;
  130. if(away){
  131. chan.prepend(i18n("Away-"));
  132. }
  133. nickListItem *nickItem = top->nicks->item( top->nicks->findNick( nick ) );
  134. if ( nickItem ) {
  135. if(nickItem->away() != away){
  136. nickItem->setAway( away );
  137. top->nicks->viewport()->repaint( top->nicks->itemRect( nickItem ), false );
  138. }
  139. nickItem->forceColour(&ksopts->ownNickColor);
  140. }
  141. top->ksircProcess()->setNick(nick);
  142. if (chanmode.findRev("t") != -1)
  143. top->channelButtons->setProtectMode(true);
  144. else top->channelButtons->setProtectMode(false);
  145. if (chanmode.findRev("m") != -1)
  146. top->channelButtons->setModerateMode(true);
  147. else top->channelButtons->setModerateMode(false);
  148. if (chanmode.findRev("n") != -1)
  149. top->channelButtons->setNooutsideMode(true);
  150. else top->channelButtons->setNooutsideMode(false);
  151. if (chanmode.findRev("i") != -1)
  152. top->channelButtons->setMenuItemMode(0, true);
  153. else top->channelButtons->setMenuItemMode(0, false);
  154. if (chanmode.findRev("s") != -1)
  155. top->channelButtons->setMenuItemMode(3, true);
  156. else top->channelButtons->setMenuItemMode(3, false);
  157. if (modes.findRev("i") != -1)
  158. top->channelButtons->setMenuItemMode(4, true);
  159. else top->channelButtons->setMenuItemMode(4, false);
  160. if (modes.findRev("w") != -1)
  161. top->channelButtons->setMenuItemMode(5, true);
  162. else top->channelButtons->setMenuItemMode(5, false);
  163. if (modes.findRev("s") != -1)
  164. top->channelButtons->setMenuItemMode(6, true);
  165. else top->channelButtons->setMenuItemMode(6, false);
  166. TQString status_line = TQString("%1 (%2) %3 (%4) ").arg(chan).arg(chanmode).arg(nick).arg(modes);
  167. /*
  168. * Go srearching for key and limit messages
  169. */
  170. TQRegExp rxKey("<key: (\\S+)>");
  171. if(rxKey.search(string) >= 0){
  172. top->channelButtons->setMenuItemMode(2, true);
  173. status_line += TQString("<key: %1>").arg(rxKey.cap(1));
  174. }
  175. else {
  176. top->channelButtons->setMenuItemMode(2, false);
  177. }
  178. TQRegExp rxLimit("<limit: (\\S+)>");
  179. if(rxLimit.search(string) >= 0){
  180. top->channelButtons->setMenuItemMode(1, true);
  181. status_line += TQString("<limit: %1>").arg(rxLimit.cap(1));
  182. }
  183. else {
  184. top->channelButtons->setMenuItemMode(1, false);
  185. }
  186. if(ksopts->displayTopic){
  187. if(top->topic().length() > 0)
  188. status_line += "T: " + top->topic();
  189. else
  190. status_line += "T: " + i18n("<No Topic Set>");
  191. }
  192. if(top->caption != status_line){
  193. if(nick[0] == '@' || (nick[0] == '*' && nick[1] == '@')) {
  194. // If we're an op,,
  195. // update the nicks popup menu
  196. top->channelButtons->setButtonsEnabled(true); // set the buttons enabled if were an op
  197. top->opami = TRUE;
  198. } // opami = true sets us to an op
  199. else {
  200. top->channelButtons->setButtonsEnabled(false); // set the buttons enabled if were an op
  201. top->opami = FALSE;
  202. } // FALSE, were not an ops
  203. top->UserUpdateMenu(); // update the menu
  204. top->setCaption(status_line);
  205. top->setIconText(status_line);
  206. if(top->ticker) {
  207. top->ticker->setCaption(status_line);
  208. }
  209. top->caption = status_line; // Make copy so we're not
  210. // constantly changing the title bar
  211. }
  212. return new parseSucc(TQString()); // Null string, don't display anything
  213. }
  214. parseResult * ChannelParser::parseSSFEInit(TQString)
  215. {
  216. return new parseSucc(TQString()); // Null string, don't display anything
  217. }
  218. parseResult * ChannelParser::parseSSFEOut(TQString)
  219. {
  220. return new parseSucc(TQString()); // Null string, don't display anything
  221. }
  222. parseResult * ChannelParser::parseSSFEMsg(TQString string)
  223. {
  224. if(string.length() > 100)
  225. return new parseError(TQString(), i18n("String length for nick is greater than 100 characters. This is unacceptably long."));
  226. int l = string.length();
  227. if (l <= 0)
  228. return new parseError(string, i18n("String not long enough"));
  229. return new parseSucc(TQString()); // Null string, don't display anything
  230. }
  231. parseResult * ChannelParser::parseSSFEPrompt(TQString string)
  232. {
  233. if(prompt_active == FALSE){
  234. TQString prompt, caption;
  235. ssfePrompt *sp;
  236. // Flush the screen.
  237. // First remove the prompt message from the Buffer.
  238. // (it's garunteed to be the first one)
  239. top->LineBuffer.remove( *top->LineBuffer.begin() );
  240. top->Buffer = FALSE;
  241. top->sirc_receive(TQString(""));
  242. // "'[pP]' " gives 4 spaces
  243. if(string.length() < 5)
  244. caption = "";
  245. else
  246. caption = string.mid(3);
  247. prompt_active = TRUE;
  248. // If we use this, then it blows up
  249. // if we haven't popped up on the remote display yet.
  250. KSirc::TextParagIterator it = top->mainw->firstParag();
  251. TQString last;
  252. while(it.atEnd() == FALSE) {
  253. last = it.plainText();
  254. ++it;
  255. }
  256. if(last[0] == '['){ /* strip time stamp */
  257. prompt = last.mid(last.find(' '));
  258. }
  259. else {
  260. prompt = last;
  261. }
  262. sp = new ssfePrompt(prompt, 0);
  263. sp->setCaption(caption);
  264. if(string[1] == 'P')
  265. sp->setPassword(TRUE);
  266. sp->exec();
  267. // cerr << "Entered: " << sp->text() << endl;
  268. prompt = sp->text();
  269. prompt += "\n";
  270. emit top->outputUnicodeLine(prompt);
  271. delete sp;
  272. prompt_active = FALSE;
  273. }
  274. return new parseSucc(TQString()); // Null string, don't display anything
  275. }
  276. parseResult * ChannelParser::parseSSFEReconnect(TQString)
  277. {
  278. if(top->channelInfo().channel()[0] == '#' ||
  279. top->channelInfo().channel()[0] == '&'){
  280. TQString str = "/join " + TQString(top->channelInfo().channel()) + "\n";
  281. emit top->outputUnicodeLine(str);
  282. }
  283. return new parseSucc(TQString()); // Null string, don't display anything
  284. }
  285. parseResult * ChannelParser::parseINFOInfo(TQString string)
  286. {
  287. string.remove(0, 3); // takes off the junk
  288. return new parseSucc(string, ksopts->infoColor, "user|servinfo"); // Null string, don't display anything
  289. }
  290. parseResult * ChannelParser::parseINFOError(TQString string)
  291. {
  292. string.remove(0, 3); // strip the junk
  293. return new parseSucc(string,ksopts->errorColor, "user|error"); // Null string, don't display anything
  294. }
  295. parseResult * ChannelParser::parseINFONicks(TQString in_string)
  296. {
  297. TQString string = in_string;
  298. TQString channel_name;
  299. bool clear_box = FALSE;
  300. // Check to see if it's a continued line
  301. if(string[1] == 'C'){
  302. string[1] = '!';
  303. clear_box = TRUE;
  304. }
  305. if(string[1] == '#'){
  306. string[1] = '!';
  307. clear_box = FALSE;
  308. }
  309. else if(string[1] == 'c'){
  310. if(current_item > 0)
  311. top->nicks->setCurrentItem(current_item);
  312. top->nicks->setTopItem(top_item);
  313. top->nicks->repaint(TRUE);
  314. return new parseSucc(TQString()); // Parsing ok, don't print anything though
  315. }
  316. else if(string[1] == '$'){
  317. top->nicks->clearAdvOps();
  318. //kdDebug(5008) << "Turning off advanced ops" << endl;
  319. return new parseSucc(TQString()); // Parsing ok, don't print anything though
  320. }
  321. // Get the channel name portion of the string
  322. // Search for the first space, since : can be embeded into channel names.
  323. //count = sscanf(string, "*!* Users on %100[^ ]", channelInfo().channel());
  324. // *!* Users on #TEST: boren asj asj_
  325. TQRegExp rx("\\*\\S\\* Users on (\\S+): (.+)");
  326. if(rx.search(string) == -1){
  327. return new parseError(string, i18n("Could not find channel name"));
  328. }
  329. channel_name = rx.cap(1);
  330. if (channel_name.lower() != top->channelInfo().channel().lower()){
  331. string.remove(0,3);
  332. return new parseSucc(string,ksopts->infoColor,"user|misc4");
  333. }
  334. if(clear_box == TRUE){
  335. current_item = top->nicks->currentItem();
  336. top_item = top->nicks->topItem();
  337. top->nicks->clear();
  338. }
  339. //int start = string.find(": ", 0, FALSE); // Find start of nicks
  340. //if (start < 0)
  341. // return new parseError(string, i18n("Could not find start of nicks"));
  342. //
  343. //place_holder = new char[string.length()];
  344. //strcpy(place_holder, string.ascii()+start+2);
  345. //nick = strtok(place_holder, " ");
  346. // while(nick != 0x0){ // While there's nick to go...
  347. TQStringList nicks = TQStringList::split(TQRegExp("\\s+"), rx.cap(2));
  348. for ( TQStringList::Iterator it = nicks.begin(); it != nicks.end(); ++it ) {
  349. TQString nick = *it;
  350. nickListItem *irc = new nickListItem();
  351. bool done = FALSE;
  352. uint i;
  353. for(i = 0; i < nick.length();i++){
  354. switch(nick[0].unicode()){
  355. case '@':
  356. irc->setOp(TRUE);
  357. nick.remove(0,1);
  358. break;
  359. case '+':
  360. irc->setVoice(TRUE);
  361. nick.remove(0,1);
  362. break;
  363. case '#':
  364. irc->setAway(TRUE);
  365. nick.remove(0,1);
  366. break;
  367. case '*':
  368. irc->setIrcOp(TRUE);
  369. nick.remove(0,1);
  370. break;
  371. default:
  372. done = TRUE;
  373. }
  374. if(done == TRUE)
  375. break;
  376. }
  377. if(nick == top->ksircProcess()->getNick()){
  378. irc->forceColour(&ksopts->ownNickColor);
  379. }
  380. irc->setText(nick);
  381. top->nicks->inSort(irc);
  382. }
  383. return new parseSucc(TQString()); // Parsing ok, don't print anything though
  384. }
  385. parseResult * ChannelParser::parseINFOJoin(TQString string)
  386. {
  387. string.remove(0, 4); // strip *>* to save a few compares
  388. // You have joined channel #Linux
  389. TQRegExp rx("You have joined channel (\\S+)");
  390. if(rx.search(string) != -1){
  391. //TQString channel = rx.cap(1).lower();
  392. TQString channel = rx.cap(1);
  393. //kdDebug(5008) << "Channel: " << channel << endl;
  394. if(top->channelInfo().channel() != channel) {
  395. KSircChannel ci(top->channelInfo().server(), channel);
  396. kdDebug(5008) << "Warning: we got a channel join yet me don't belong to it!!! Assuming no key!" << endl;
  397. kdDebug(5008) << "String was: " << string << endl;
  398. kdDebug(5008) << "We think the channel is: " << channel << " we are: " << top->channelInfo().channel()<< endl;
  399. emit top->open_toplevel(ci);
  400. }
  401. return new parseJoinPart(" " + string, ksopts->channelColor, "user|join");
  402. }
  403. // reef-diddy (nenernener@xnet-3B34A9E2.snet.net) has joined channel #aquaria
  404. rx.setPattern("(\\S+) .+ has joined channel (\\S+)");
  405. if(rx.search(string) != -1){
  406. TQString nick = rx.cap(1);
  407. TQString channel = rx.cap(2).lower();
  408. //kdDebug(5008) << "Channel: " << channel << " nick: " << nick << endl;
  409. if(top->channelInfo().channel().lower() != channel){
  410. return new parseWrongChannel(" " + string, ksopts->errorColor, "user|join");
  411. }
  412. // nicks->insertItem(s3, 0); // add the sucker
  413. top->nicks->inSort(nick);
  414. top->addCompleteNick(nick);
  415. highlightNick(string, nick);
  416. return new parseJoinPart(" " + string, ksopts->channelColor, "user|join");
  417. }
  418. return 0; // ??
  419. }
  420. parseResult * ChannelParser::parseINFOPart(TQString string)
  421. {
  422. bool foundNick = false;
  423. TQString pixname = "user|kick";
  424. TQString nick("");
  425. string.remove(0, 4); // clear junk
  426. // Multiple type of parts, a signoff or a /part
  427. // Each get's get nick in a diffrent localtion
  428. // Set we search and find the nick and the remove it from the nick list
  429. // 1. /quit, signoff, nick after "^Singoff: "
  430. // 2. /part, leave the channek, nick after "has left \w+$"
  431. // 3. /kick, kicked off the channel, nick after "kicked off \w+$"
  432. //
  433. // Signoff: looser
  434. TQRegExp rx("Signoff: (\\S+)");
  435. if(rx.search(string) != -1) {
  436. nick = rx.cap(1);
  437. foundNick = true;
  438. pixname = "user|X";
  439. highlightNick(string, nick);
  440. }
  441. /*
  442. * Check for "You" before everyone else or else the next
  443. * case will match it
  444. * You have left channel <channel>
  445. */
  446. rx.setPattern("You have left channel (\\S+)");
  447. if((foundNick == false) && (rx.search(string) != -1)) {
  448. TQString channel = rx.cap(1);
  449. if(top->channelInfo().channel().lower() == channel.lower()) {
  450. TQApplication::postEvent(top, new TQCloseEvent());
  451. // WE'RE DEAD
  452. return new parseSucc(TQString());
  453. }
  454. pixname = "user|part";
  455. }
  456. /*
  457. * Same as above, check your own state first
  458. * You have been kicked off channel <channel>
  459. */
  460. rx.setPattern("You have been kicked off channel (\\S+)");
  461. if((foundNick == false) && (rx.search(string) != -1)) {
  462. TQString channel = rx.cap(1);
  463. if(top->channelInfo().channel().lower() != channel.lower())
  464. return new parseWrongChannel(string, ksopts->errorColor, "user|kick");
  465. if (ksopts->autoRejoin == TRUE)
  466. {
  467. TQString str = TQString("/join %1\n").arg(top->channelInfo().channel());
  468. emit top->outputUnicodeLine(str);
  469. /* if(top->ticker)
  470. top->ticker->show();
  471. else*/
  472. top->show();
  473. }
  474. else
  475. {
  476. if(top->KickWinOpen != false)
  477. return new parseError(" " + string, i18n("Kick window open"));
  478. top->KickWinOpen = true;
  479. int result = KMessageBox::questionYesNo(top, string, i18n("You Have Been Kicked"), i18n("Rejoin"), i18n("Leave"));
  480. if (result == KMessageBox::Yes)
  481. {
  482. TQString str = TQString("/join %1\n").arg(top->channelInfo().channel());
  483. emit top->outputUnicodeLine(str);
  484. /* if(top->ticker)
  485. * top->ticker->show();
  486. * else*/
  487. top->show();
  488. return new parseJoinPart(" " + string, ksopts->channelColor, "user|kick");
  489. }
  490. else
  491. {
  492. // WE'RE DEAD
  493. TQApplication::postEvent(top, new TQCloseEvent());
  494. top->KickWinOpen = false;
  495. }
  496. }
  497. pixname = "user|kick";
  498. }
  499. /*
  500. * <nick> has left channel <channel>
  501. */
  502. rx.setPattern("(\\S+) has left channel (\\S+)");
  503. if((foundNick == false) && (rx.search(string) != -1)) {
  504. nick = rx.cap(1);
  505. TQString channel = rx.cap(2);
  506. // kdDebug(5008) << "Nick: " << nick << " Channel: " << channel << " top: " << top->channelInfo().channel() << endl;
  507. if(top->channelInfo().channel().lower() == channel.lower()) {
  508. foundNick = true;
  509. }
  510. else{
  511. return new parseWrongChannel(TQString());
  512. }
  513. pixname = "user|part";
  514. highlightNick(string, nick);
  515. }
  516. /*
  517. * "<nick> has been kicked off channel <channel>"
  518. */
  519. rx.setPattern("(\\S+) has been kicked off channel (\\S+)");
  520. if((foundNick == false) && (rx.search(string) != -1)) {
  521. nick = rx.cap(1);
  522. TQString channel = rx.cap(2);
  523. if(top->channelInfo().channel().lower() == channel.lower()) {
  524. foundNick = true;
  525. } else {
  526. return new parseWrongChannel(TQString());
  527. }
  528. highlightNick(string, nick);
  529. pixname = "user|kick";
  530. }
  531. if (foundNick) {
  532. top->removeCompleteNick(nick);
  533. int index = top->nicks->findNick(nick);
  534. if(index >= 0){
  535. top->nicks->removeItem(index);
  536. return new parseJoinPart(" " + string, ksopts->channelColor, pixname);
  537. }
  538. else {
  539. return new parseJoinPart(TQString());
  540. }
  541. }
  542. else {
  543. return new parseError(" " + string, i18n("Failed to parse part/kick/leave/quit message"));
  544. }
  545. return 0;
  546. }
  547. parseResult * ChannelParser::parseINFOChangeNick(TQString string)
  548. {
  549. //char old_nick[101], new_nick[101];
  550. TQString old_nick, new_nick;
  551. string.remove(0, 4); // Remove the leading *N* and space
  552. /*
  553. * *N* asj_ is now known as bleh
  554. */
  555. //kdDebug(5008) << "Nick change: " << string << endl;
  556. TQRegExp rx("(\\S+) is now known as (\\S+)");
  557. if(rx.search(string) == -1){
  558. if(string.contains("already taken")){
  559. return new parseSucc(" " + string, ksopts->errorColor, "user|error");
  560. }
  561. return new parseError(i18n("Unable to parse: %1").arg(string), i18n("Unable to parse change nick code"));
  562. }
  563. old_nick = rx.cap(1);
  564. new_nick = rx.cap(2);
  565. // If we have a window open talking to the nick
  566. // Change the nick to the new one.
  567. if((top->channelInfo().channel()[0] != '#' || top->channelInfo().channel()[0] != '&') &&
  568. (top->channelInfo().channel() == old_nick)){
  569. if(!top->ksircProcess()->mrList()[new_nick.lower()]){
  570. top->control_message(CHANGE_CHANNEL, new_nick.lower());
  571. }
  572. }
  573. highlightNick(string, old_nick);
  574. highlightNick(string, new_nick);
  575. // search the list for the nick and remove it
  576. // since the list is source we should do a binary search...
  577. int found = top->nicks->findNick(old_nick);
  578. if(found >= 0){ // If the nick's in the nick list, change it and display the change
  579. // save current selection
  580. int selection = top->nicks->currentItem();
  581. // Get the old item, and create a new one
  582. nickListItem *it = top->nicks->item(found);
  583. nickListItem *irc = new nickListItem(*it);
  584. irc->setText(new_nick);
  585. top->nicks->removeItem(found); // remove old nick
  586. top->nicks->inSort(irc);
  587. top->changeCompleteNick(old_nick, new_nick);
  588. top->nicks->setCurrentItem(selection);
  589. top->nicks->repaint(TRUE);
  590. // We're done, so let's finish up
  591. return new parseSucc(" " + string, ksopts->channelColor, "user|join");
  592. }
  593. else {
  594. if(top->channelInfo().channel() == new_nick ||
  595. top->channelInfo().channel() == old_nick)
  596. return new parseSucc(" " + string, ksopts->channelColor, "user|elipsis");
  597. else
  598. return new parseSucc(TQString());
  599. }
  600. // warning("Toplevel-N: nick change search failed on %s", s3.data());
  601. // return new parseSucc(TQString());
  602. }
  603. class mode_info {
  604. public:
  605. mode_info(bool op, TQChar mode, TQString arg);
  606. bool op() const;
  607. TQChar mode() const;
  608. TQString arg() const;
  609. private:
  610. const bool m_op;
  611. const TQChar m_mode;
  612. const TQString m_arg;
  613. };
  614. mode_info::mode_info(bool op, TQChar mode, TQString arg) :
  615. m_op(op),
  616. m_mode(mode),
  617. m_arg(arg)
  618. {
  619. }
  620. bool mode_info::op() const {
  621. return m_op;
  622. }
  623. TQChar mode_info::mode() const {
  624. return m_mode;
  625. }
  626. TQString mode_info::arg() const {
  627. return m_arg;
  628. }
  629. parseResult * ChannelParser::parseINFOMode(TQString string)
  630. {
  631. // Basic idea here is simple, go through the mode change and
  632. // assign each mode a + or a - and an argument or "" if there is
  633. // none. After that each mode change it looked at to see if
  634. // we should handle it in any special way.
  635. // Strip off leading sirc info
  636. string.remove(0, 4);
  637. /*
  638. * 1k is pretty safe since TDEProcess returns 1 k blocks, and lines don't get split between reads. This is emprical
  639. */
  640. TQString modes, args, channel;
  641. int found = 0;
  642. if(string.find("for user") >= 0)
  643. return new parseSucc(" " + string, ksopts->infoColor, "user|mode");
  644. /*
  645. * We need to 2 scanf's, one for the case of arguments, and one for no args.
  646. */
  647. TQRegExp rx("Mode change \"(\\S+) *([^\"]*)\" on channel (\\S+)");
  648. if(rx.search(string) >= 0){
  649. modes = rx.cap(1);
  650. args = rx.cap(2);
  651. channel = rx.cap(3);
  652. found = 1;
  653. }
  654. rx.setPattern("Mode for channel (\\S+) is \"([^\" ]+)\"");
  655. if(found == 0 &&
  656. rx.search(string) >= 0){
  657. channel = rx.cap(1);
  658. modes = rx.cap(2);
  659. found = 1;
  660. }
  661. rx.setPattern("Your user mode is");
  662. if(found == 0 &&
  663. rx.search(string) >= 0){
  664. /*
  665. * Don't parse user mode requests
  666. */
  667. return new parseSucc(" " + string, ksopts->infoColor, "user|mode");
  668. }
  669. if(found == 0)
  670. return new parseError(" Failed to parse mode change: " + string, TQString());
  671. /*
  672. * op specifie if it's a + or -. tru is + false is -
  673. */
  674. bool op = true;
  675. /*
  676. * arglist is the list of argument
  677. * we use the itirator to tstep through the list
  678. * as need be
  679. */
  680. TQStringList arglist = TQStringList::split(" ", args);
  681. TQStringList::Iterator ai = arglist.begin();
  682. /*
  683. * the ptr list structure contains the parsed contents
  684. */
  685. TQPtrList<const mode_info> pmList;
  686. pmList.setAutoDelete(true);
  687. for(uint pos = 0; pos < modes.length(); pos++){
  688. switch(modes.at(pos).unicode()){
  689. case '+':
  690. op = true;
  691. break;
  692. case '-':
  693. op = false;
  694. break;
  695. case 'l': // Chan limits
  696. /*
  697. * -l doesn't take any arguments, so just add the mode and break
  698. * +l otoh does, so read the argument
  699. */
  700. if(op == false){
  701. pmList.append(new mode_info(op, 'l', TQString()));
  702. break;
  703. }
  704. case 'o': // Op, arg is the nick
  705. case 'v': // Voice, arg is the nick
  706. case 'b': // Ban, arg is mask banned
  707. case 'k': // kcik, arg is nick
  708. if(ai == NULL)
  709. return new parseError(i18n("Unable to parse mode change: %1").arg(string), TQString());
  710. pmList.append(new mode_info(op, modes.at(pos), *ai));
  711. ai++;
  712. break;
  713. case 'i': // Invite only
  714. case 'n': // No message to chan
  715. case 'p': // Private
  716. case 'm': // Moderated
  717. case 's': // Secret
  718. case 't': // Topic setable by ops
  719. case 'R': // (Dalnet) only registered may join
  720. case 'r': // (Dalnet) only registered may join or something
  721. /*
  722. * Mode changes which don't take args
  723. */
  724. pmList.append(new mode_info(op, modes.at(pos), TQString()));
  725. break;
  726. default:
  727. kdDebug(5008) << "Unknown mode change: " << modes.mid(pos, 1) << " Assume no args" << endl;
  728. pmList.append(new mode_info(op, modes.at(pos), TQString()));
  729. }
  730. }
  731. // We have the modes set in mode and arg, now we go though
  732. // looking at each mode seeing if we should handle it.
  733. bool mode_o_plus = false;
  734. bool mode_o_minus = false;
  735. bool mode_b_plus = false;
  736. bool mode_b_minus = false;
  737. TQPtrListIterator<const mode_info> it(pmList);
  738. const mode_info *mi;
  739. while ( (mi = it.current()) != 0 ) {
  740. ++it;
  741. /*
  742. * Look at the second character, it's uniq, check for +,- latter
  743. */
  744. if(mi->mode().unicode() == 'o'){
  745. mode_o_plus = mi->op();
  746. mode_o_minus = !mi->op();
  747. if(top->ksircProcess()->getNick() == mi->arg())
  748. top->channelButtons->setButtonsEnabled(mi->op());
  749. if(mi->arg().length() == 0){
  750. tqWarning("Invalid nick in +/- o mode change");
  751. continue;
  752. }
  753. int offset = top->nicks->findNick(mi->arg());
  754. if(offset >= 0){
  755. nickListItem *irc = new nickListItem();
  756. *irc = *top->nicks->item(offset);
  757. top->nicks->removeItem(offset); // remove old nick
  758. irc->setOp(mi->op());
  759. // add new nick in sorted pass,with colour
  760. top->nicks->inSort(irc);
  761. top->nicks->repaint(TRUE);
  762. }
  763. else{
  764. kdDebug(5008) << "Toplevel+o: nick search failed on " << mi->arg() << endl;
  765. }
  766. }
  767. else if(mi->mode() == 't'){
  768. if(mi->op())
  769. top->channelButtons->setProtectMode(true); // set on
  770. else
  771. top->channelButtons->setProtectMode(false); // set off
  772. }
  773. else if(mi->mode() == 'm'){
  774. if(mi->op())
  775. top->channelButtons->setModerateMode(true); // set on
  776. else
  777. top->channelButtons->setModerateMode(false); // set off
  778. }
  779. else if(mi->mode() == 'n'){
  780. if(mi->op())
  781. top->channelButtons->setNooutsideMode(true); // set on
  782. else
  783. top->channelButtons->setNooutsideMode(false); // set off
  784. }
  785. else if(mi->mode() == 'v'){
  786. bool voice;
  787. if(mi->op())
  788. voice = TRUE;
  789. else
  790. voice = FALSE;
  791. if(mi->arg().length() == 0){
  792. tqWarning("Invalid nick in +-v mode change");
  793. continue;
  794. }
  795. int offset = top->nicks->findNick(mi->arg());
  796. if(offset >= 0){
  797. nickListItem *irc = new nickListItem();
  798. *irc = *top->nicks->item(offset);
  799. top->nicks->removeItem(offset); // remove old nick
  800. irc->setVoice(voice) ;
  801. // add new nick in sorted pass,with colour
  802. top->nicks->inSort(irc);
  803. top->nicks->repaint();
  804. }
  805. }
  806. else if(mi->mode() == 'b'){
  807. if(mi->op())
  808. mode_b_plus = true;
  809. else
  810. mode_b_minus = true;
  811. }
  812. else if(mi->mode() == 'k'){
  813. if(mi->op()){
  814. if(mi->arg().length() == 0){
  815. tqWarning("Invalid +k mode set, no argument!");
  816. continue;
  817. }
  818. top->m_channelInfo.setKey(mi->arg());
  819. }
  820. else {
  821. top->m_channelInfo.setKey(""); /* no key set anymore */
  822. }
  823. }
  824. else{
  825. TQChar c(mi->mode());
  826. TQString m(&c, 1);
  827. TQString o;
  828. if(mi->op())
  829. o = "+";
  830. else
  831. o = "-";
  832. kdDebug(5008) << "Did not handle: " << o << m << " arg: " << mi->arg() << endl;
  833. }
  834. }
  835. /*
  836. * We're all done, so output the message and be done with it
  837. */
  838. TQString pixname = "user|mode";
  839. if(mode_o_plus)
  840. pixname = "user|oplus";
  841. else if(mode_o_minus)
  842. pixname = "user|ominus";
  843. else if(mode_b_plus)
  844. pixname ="user|bplus";
  845. else if(mode_b_minus)
  846. pixname = "user|bminus";
  847. return new parseSucc(" " + string, ksopts->infoColor, pixname);
  848. }
  849. parseResult * ChannelParser::parseCTCPAction(TQString string)
  850. {
  851. string.remove(0, 2); // * <something> use fancy * pixmap. Remove 2, leave one for space after te *
  852. // why? looks cool for dorks
  853. return new parseSucc(string, ksopts->textColor, "user|action");
  854. }
  855. parseResult * ChannelParser::parseINFOTopic(TQString string)
  856. {
  857. int found = 0;
  858. string.remove(0, 4); // Remove the leading *T* and space
  859. //kdDebug(5008) << "Topic parser: " << string << endl;
  860. // Topic for #boo: this is a nice test
  861. TQRegExp rx( "Topic for (\\S+): (.*)" );
  862. if(rx.search( string ) != -1){
  863. TQString channel = rx.cap(1);
  864. TQString topic = rx.cap(2);
  865. topic.replace( TQRegExp( "~~" ), "~" );
  866. /*
  867. * check where it's going.
  868. * topic's maybe for other channels since they have no channel routing
  869. * information, so route it to the right place if need be.
  870. * If we're not on the channnel just fall through and display it
  871. * on our channel, maybe the user asked for a topic of a different channel
  872. */
  873. if(channel.lower() != top->channelInfo().channel().lower()){
  874. if(top->ksircProcess()->mrList()[channel.lower()]){
  875. KSircTopLevel *t = dynamic_cast<KSircTopLevel *>(top->ksircProcess()->mrList()[channel.lower()]);
  876. if(t)
  877. t->setTopic(topic);
  878. }
  879. }
  880. else {
  881. //kdDebug(5008) << "New topic: " << topic << endl;
  882. top->setTopic( topic );
  883. }
  884. found = 1;
  885. }
  886. rx.setPattern("(\\S+) has changed the topic on channel (\\S+) to (.+)");
  887. if(found == 0 && rx.search(string) != -1){
  888. TQString nick = rx.cap(1);
  889. TQString channel = rx.cap(2);
  890. //kdDebug(5008) << "Channel: " << channel << endl;
  891. if(top->channelInfo().channel().lower() == channel.lower()){
  892. TQString topic = rx.cap(3);
  893. //kdDebug(5008) << "Topic: " << topic << endl;
  894. topic.replace(TQRegExp("~~"), "~");
  895. /*
  896. * topic is in double quotes, so remove them
  897. */
  898. top->setTopic( topic.mid(1, topic.length()-2) );
  899. TQString cmd = "/eval &dostatus();\n";
  900. top->sirc_write(cmd);
  901. }
  902. highlightNick(string, nick);
  903. }
  904. return new parseSucc(" " + string, ksopts->infoColor, "user|topic");
  905. }
  906. void ChannelParser::highlightNick(TQString &string, TQString &nick)
  907. {
  908. TQRegExp rx(TQString("(^|\\s+)%1(\\s+|$)").arg(TQRegExp::escape(nick)));
  909. string.replace(rx, "\\1~n" + nick + "~n\\2");
  910. }