Updating ChangeLog for 4.22.10
[centerim.git] / src / hooks / irchook.cc
blob8363d9e8831738d4d56e536ea80e218a1ee77b97
1 /*
3 * centerim IRC protocol handling class
4 * $Id: irchook.cc,v 1.80 2004/12/20 00:54:02 konst Exp $
6 * Copyright (C) 2001-2004 by Konstantin Klyagin <k@thekonst.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
25 #include "irchook.h"
27 #ifdef BUILD_IRC
29 #include "icqgroups.h"
30 #include "icqface.h"
31 #include "icqcontacts.h"
32 #include "imlogger.h"
34 #include "accountmanager.h"
35 #include "eventmanager.h"
37 #include "centerim.h"
39 #include <iterator>
40 #include <sstream>
42 #define NOTIFBUF 512
44 // ----------------------------------------------------------------------------
46 irchook irhook;
48 irchook::irchook()
49 : abstracthook(irc), handle(firetalk_create_handle(firetalk_find_protocol("IRC"), 0)),
50 fonline(false), flogged(false), ourstatus(offline)
52 fcapabs.insert(hookcapab::setaway);
53 fcapabs.insert(hookcapab::fetchaway);
54 fcapabs.insert(hookcapab::changenick);
55 fcapabs.insert(hookcapab::optionalpassword);
56 fcapabs.insert(hookcapab::ping);
57 fcapabs.insert(hookcapab::version);
58 fcapabs.insert(hookcapab::files);
59 fcapabs.insert(hookcapab::cltemporary);
60 fcapabs.insert(hookcapab::directadd);
61 fcapabs.insert(hookcapab::conferencing);
62 fcapabs.insert(hookcapab::channelpasswords);
65 irchook::~irchook() {
68 void irchook::init() {
69 int i;
71 manualstatus = conf->getstatus(irc);
73 for(i = 0; i < clist.count; i++) {
74 icqcontact *c = (icqcontact *) clist.at(i);
76 if(c->getdesc().pname == irc)
77 if(ischannel(c))
78 if(c->getbasicinfo().requiresauth) {
79 channels.push_back(channelInfo(c->getdesc().nickname));
80 channels.back().joined = true;
81 channels.back().passwd = c->getbasicinfo().zip;
85 firetalk_register_callback(handle, FC_CONNECTED, &connected);
86 firetalk_register_callback(handle, FC_CONNECTFAILED, &connectfailed);
87 firetalk_register_callback(handle, FC_DISCONNECT, &disconnected);
88 firetalk_register_callback(handle, FC_NEWNICK, &newnick);
89 firetalk_register_callback(handle, FC_IM_GOTINFO, &gotinfo);
90 firetalk_register_callback(handle, FC_IM_GOTCHANNELS, &gotchannels);
91 firetalk_register_callback(handle, FC_IM_GETMESSAGE, &getmessage);
92 firetalk_register_callback(handle, FC_IM_GETACTION, &getaction);
93 firetalk_register_callback(handle, FC_IM_BUDDYONLINE, &buddyonline);
94 firetalk_register_callback(handle, FC_IM_BUDDYOFFLINE, &buddyoffline);
95 firetalk_register_callback(handle, FC_IM_BUDDYAWAY, &buddyaway);
96 firetalk_register_callback(handle, FC_IM_BUDDYUNAWAY, &buddyonline);
97 firetalk_register_callback(handle, FC_CHAT_LISTMEMBER, &listmember);
98 firetalk_register_callback(handle, FC_CHAT_LIST_EXTENDED, &listextended);
99 firetalk_register_callback(handle, FC_CHAT_END_EXTENDED, &endextended);
100 firetalk_register_callback(handle, FC_CHAT_NAMES, &chatnames);
101 firetalk_register_callback(handle, FC_CHAT_GETMESSAGE, &chatmessage);
102 firetalk_register_callback(handle, FC_CHAT_GETACTION, &chataction);
103 firetalk_register_callback(handle, FC_CHAT_JOINED, &chatjoined);
104 firetalk_register_callback(handle, FC_CHAT_LEFT, &chatleft);
105 firetalk_register_callback(handle, FC_CHAT_KICKED, &chatkicked);
106 firetalk_register_callback(handle, FC_CHAT_OPPED, &chatopped);
107 firetalk_register_callback(handle, FC_CHAT_DEOPPED, &chatdeopped);
108 firetalk_register_callback(handle, FC_ERROR, &errorhandler);
109 firetalk_register_callback(handle, FC_IM_USER_NICKCHANGED, &nickchanged);
110 firetalk_register_callback(handle, FC_NEEDPASS, &needpass);
111 firetalk_register_callback(handle, FC_FILE_OFFER, &fileoffer);
112 firetalk_register_callback(handle, FC_FILE_START, &filestart);
113 firetalk_register_callback(handle, FC_FILE_PROGRESS, &fileprogress);
114 firetalk_register_callback(handle, FC_FILE_FINISH, &filefinish);
115 firetalk_register_callback(handle, FC_FILE_ERROR, &fileerror);
116 firetalk_register_callback(handle, FC_CHAT_USER_JOINED, &chatuserjoined);
117 firetalk_register_callback(handle, FC_CHAT_USER_LEFT, &chatuserleft);
118 firetalk_register_callback(handle, FC_CHAT_USER_KICKED, &chatuserkicked);
119 firetalk_register_callback(handle, FC_CHAT_GOTTOPIC, &chatgottopic);
120 firetalk_register_callback(handle, FC_CHAT_USER_OPPED, &chatuseropped);
121 firetalk_register_callback(handle, FC_CHAT_USER_DEOPPED, &chatuserdeopped);
123 firetalk_subcode_register_request_callback(handle, "VERSION", &subrequest);
125 firetalk_subcode_register_reply_callback(handle, "PING", &subreply);
126 firetalk_subcode_register_reply_callback(handle, "VERSION", &subreply);
128 if(conf->getdebug())
129 firetalk_register_callback(handle, FC_LOG, &fclog);
132 void irchook::connect() {
133 icqconf::imaccount acc = conf->getourid(irc);
134 log(logConnecting);
136 firetalk_register_callback(handle, FC_DISCONNECT, 0);
137 firetalk_disconnect(handle);
138 firetalk_register_callback(handle, FC_DISCONNECT, &disconnected);
140 fonline = firetalk_signon(handle, acc.server.c_str(),
141 acc.port, acc.nickname.c_str()) == FE_SUCCESS;
143 if(fonline) {
144 flogged = false;
145 } else {
146 face.log(_("+ [irc] unable to connect to the server"));
150 void irchook::disconnect() {
151 if(fonline) {
152 firetalk_disconnect(handle);
156 void irchook::exectimers() {
159 void irchook::main() {
160 firetalk_select();
163 void irchook::getsockets(fd_set &rfds, fd_set &wfds, fd_set &efds, int &hsocket) const {
164 int *r, *w, *e, *sr, *sw, *se;
166 firetalk_getsockets(firetalk_find_protocol("IRC"), &sr, &sw, &se);
168 for(r = sr; *r; r++) {
169 if(*r > hsocket) hsocket = *r;
170 FD_SET(*r, &rfds);
173 for(w = sw; *w; w++) {
174 if(*w > hsocket) hsocket = *w;
175 FD_SET(*w, &wfds);
178 for(e = se; *e; e++) {
179 if(*e > hsocket) hsocket = *e;
180 FD_SET(*e, &efds);
183 delete sr;
184 delete sw;
185 delete se;
188 bool irchook::isoursocket(fd_set &rfds, fd_set &wfds, fd_set &efds) const {
189 bool res = false;
190 int *r, *w, *e, *sr, *sw, *se;
192 if(online()) {
193 firetalk_getsockets(firetalk_find_protocol("IRC"), &sr, &sw, &se);
195 for(r = sr; *r; r++) res = res || FD_ISSET(*r, &rfds);
196 for(w = sw; *w; w++) res = res || FD_ISSET(*w, &wfds);
197 for(e = se; *e; e++) res = res || FD_ISSET(*e, &efds);
199 delete sr;
200 delete sw;
201 delete se;
204 return res;
207 bool irchook::online() const {
208 return fonline;
211 bool irchook::logged() const {
212 return flogged && fonline;
215 bool irchook::enabled() const {
216 return true;
219 bool irchook::isconnecting() const {
220 return fonline && !flogged;
223 bool irchook::send(const imevent &ev) {
224 icqcontact *c = clist.get(ev.getcontact());
225 string text, cmd;
226 bool result = true;
228 if(c) {
229 if(ev.gettype() == imevent::message) {
230 const immessage *m = static_cast<const immessage *>(&ev);
231 text = rusconv("kw", m->gettext());
233 } else if(ev.gettype() == imevent::url) {
234 const imurl *m = static_cast<const imurl *>(&ev);
235 text = rushtmlconv("kw", m->geturl()) + "\n\n" + rushtmlconv("kw", m->getdescription());
237 } else if(ev.gettype() == imevent::file) {
238 const imfile *m = static_cast<const imfile *>(&ev);
239 vector<imfile::record> files = m->getfiles();
240 vector<imfile::record>::const_iterator ir;
242 for(ir = files.begin(); ir != files.end(); ++ir) {
243 imfile::record r;
244 r.fname = ir->fname;
245 r.size = ir->size;
247 imfile fr(c->getdesc(), imevent::outgoing, "",
248 vector<imfile::record>(1, r));
250 firetalk_file_offer(handle, c->getdesc().nickname.c_str(),
251 ir->fname.c_str(), &transferinfo[fr].first);
254 return true;
257 string original = text;
258 int pos = -1;
260 while (original.length()>0)
262 if ((pos = original.find("\n")) != -1) // split by newlines into separate commands, drop empty ones
264 if (pos>1)
266 text = original.substr(0, pos-1);
267 original.erase(0, pos+1);
269 else
271 original.erase(0, pos+1);
272 continue;
275 else
277 text = original;
278 original.erase();
280 if(text.substr(0, 1) == "/") {
281 text.erase(0, 1);
282 cmd = up(getword(text));
284 if(cmd == "ME") {
285 if(ischannel(c)) return firetalk_chat_send_action(handle,
286 c->getdesc().nickname.c_str(), text.c_str(), 0) == FE_SUCCESS;
287 else return firetalk_im_send_action(handle,
288 c->getdesc().nickname.c_str(), text.c_str(), 0) == FE_SUCCESS;
290 } else if(cmd == "KICK") {
291 text = cmd + " " + c->getdesc().nickname + " " + text;
293 } else if(cmd == "OP") {
294 text = (string) "mode " + c->getdesc().nickname + " +o " + text;
296 } else if(cmd == "DEOP") {
297 text = (string) "mode " + c->getdesc().nickname + " -o " + text;
299 } else if(cmd == "RAW") {
300 // leave text intact
302 } else {
303 text.insert(0, cmd + " ");
307 rawcommand(text);
310 if(ischannel(c)) {
311 if (firetalk_chat_send_message(handle,
312 c->getdesc().nickname.c_str(), text.c_str(), 0) != FE_SUCCESS)
313 result = false;
315 } else {
316 if (firetalk_im_send_message(handle,
317 c->getdesc().nickname.c_str(), text.c_str(), 0) != FE_SUCCESS)
318 result = false;
322 return result;
325 return false;
328 void irchook::sendnewuser(const imcontact &ic) {
329 icqcontact *c;
331 if(online()) {
332 if(!ischannel(ic)) {
333 vector<icqgroup>::const_iterator ig = find(groups.begin(), groups.end(), clist.get(ic)->getgroupid());
334 if(ig != groups.end()) {
335 firetalk_im_add_buddy(handle, ic.nickname.c_str(), ig->getname().c_str(), "");
337 requestinfo(ic);
338 } else {
339 vector<channelInfo> ch = getautochannels();
340 if(find(ch.begin(), ch.end(), ic.nickname) == ch.end()) {
341 ch.push_back(ic.nickname);
342 ch.back().joined = true;
343 if(c = clist.get(ic)) {
344 ch.back().passwd = c->getbasicinfo().zip;
346 setautochannels(ch);
352 void irchook::removeuser(const imcontact &ic) {
353 if(online()) {
354 if(!ischannel(ic)) {
355 firetalk_im_remove_buddy(handle, ic.nickname.c_str());
356 } else {
357 vector<channelInfo> ch = getautochannels();
358 vector<channelInfo>::iterator ich = find(ch.begin(), ch.end(), ic.nickname);
359 if(ich != ch.end()) {
360 ch.erase(ich);
361 setautochannels(ch);
367 void irchook::setautostatus(imstatus st) {
368 if(st != offline) {
369 if(getstatus() == offline) {
370 connect();
371 } else {
372 if(getstatus() != st) {
373 logger.putourstatus(irc, getstatus(), ourstatus = st);
375 switch(st) {
376 case away:
377 case notavail:
378 case outforlunch:
379 case occupied:
380 firetalk_set_away(handle, conf->getawaymsg(irc).c_str(), 0);
381 break;
383 default:
384 firetalk_set_away(handle, 0, 0);
385 break;
389 } else {
390 if(getstatus() != offline) {
391 disconnect();
395 face.update();
398 imstatus irchook::getstatus() const {
399 return (flogged && fonline) ? ourstatus : offline;
402 void irchook::requestinfo(const imcontact &c) {
403 firetalk_im_get_info(handle, c.nickname.c_str());
406 void irchook::sendupdateuserinfo(icqcontact &c, const string &newpass) {
407 const icqcontact::basicinfo &b = c.getbasicinfo();
409 ircname = b.fname;
410 if(!b.lname.empty()) {
411 if(!ircname.empty()) ircname += " ";
412 ircname += b.lname;
416 void irchook::lookup(const imsearchparams &params, verticalmenu &dest) {
417 string rooms = params.room, room;
418 bool ready = true;
419 vector<channelInfo>::iterator ic;
421 searchdest = &dest;
423 emailsub = params.email;
424 namesub = params.firstname;
425 searchsincelast = params.sincelast && !params.room.empty();
427 searchchannels.clear();
428 extlisted.clear();
430 while(!foundguys.empty()) {
431 delete foundguys.back();
432 foundguys.pop_back();
435 if(!params.room.empty()) {
436 smode = Channel;
438 while(!(room = getword(rooms)).empty()) {
439 if(room[0] != '#') room.insert(0, "#");
440 searchchannels.push_back(room);
441 ic = find(irhook.channels.begin(), irhook.channels.end(), room);
443 if(ic == irhook.channels.end()) {
444 irhook.channels.push_back(channelInfo(room));
445 ic = irhook.channels.end()-1;
448 if(!ic->joined) {
449 firetalk_chat_join(handle, room.c_str());
450 ready = false;
453 if(emailsub.empty() && namesub.empty()) {
454 /* if(ic->fetched) {*/
455 ic->nicks.clear();
456 firetalk_chat_listmembers(handle, room.c_str());
457 /* } else {
458 ready = false;
461 } else {
462 ready = false;
463 if(ic->joined) {
464 ic->nicks.clear();
465 firetalk_chat_requestextended(handle, params.room.c_str());
471 if(ready)
472 processnicks();
474 } else if(!params.email.empty()) {
475 smode = Email;
476 searchchannels.push_back("");
477 firetalk_im_searchemail(handle, ("*" + params.email + "*").c_str());
482 void irchook::processnicks() {
483 string dnick;
484 char *nick;
485 vector<string> foundnicks;
486 static vector<string> lastfoundnicks;
487 vector<string>::iterator in, isn;
488 vector<channelInfo>::iterator ir;
489 bool remove;
490 icqcontact *c;
491 int npos;
493 if(!searchdest)
494 return;
496 ir = find(channels.begin(), channels.end(), *searchchannels.begin());
497 if(ir != channels.end())
498 foundnicks = ir->nicks;
500 sort(foundnicks.begin(), foundnicks.end());
502 if(searchchannels.size() > 1) {
503 for(in = foundnicks.begin(); in != foundnicks.end(); ) {
504 remove = false;
506 for(ir = channels.begin(); !remove && (ir != channels.end()); ++ir) {
507 if(find(searchchannels.begin(), searchchannels.end(), ir->name) == searchchannels.end())
508 continue;
509 if(ir->name == *searchchannels.begin())
510 continue;
512 if(find(ir->nicks.begin(), ir->nicks.end(), *in) == ir->nicks.end()) {
515 * Not found in one of other channels. Remove it from the
516 * first channel's list.
519 remove = true;
523 if(remove) {
524 foundnicks.erase(in);
525 in = foundnicks.begin();
526 } else {
527 in++;
532 if(searchsincelast) {
533 vector<string> tnicks;
534 insert_iterator< vector<string> > tins(tnicks, tnicks.begin());
536 set_difference(foundnicks.begin(), foundnicks.end(),
537 lastfoundnicks.begin(), lastfoundnicks.end(), tins);
539 lastfoundnicks = foundnicks;
540 foundnicks = tnicks;
542 } else {
543 lastfoundnicks = foundnicks;
547 for(in = foundnicks.begin(); in != foundnicks.end(); ++in) {
548 dnick = *in;
550 npos = 0;
552 if(!emailsub.empty()) npos = dnick.rfind("<"); else
553 if(!namesub.empty()) npos = dnick.rfind("[");
555 if(npos > 0) dnick.erase(npos-1);
557 c = new icqcontact(imcontact(dnick, irc));
558 c->setdispnick(dnick);
560 searchdest->additem(conf->getcolor(cp_clist_irc), c, (string) " " + *in);
561 foundguys.push_back(c);
564 face.findready();
565 log(logConfMembers, foundguys.size());
567 searchdest->redraw();
568 searchdest = 0;
571 vector<irchook::channelInfo> irchook::getautochannels() const {
572 vector<channelInfo> r;
573 vector<channelInfo>::const_iterator ic;
575 for(ic = channels.begin(); ic != channels.end(); ++ic) {
576 if(find(searchchannels.begin(), searchchannels.end(), ic->name) != searchchannels.end())
577 if(!ic->joined)
578 // A channel used for search
579 continue;
581 r.push_back(*ic);
584 return r;
587 void irchook::setautochannels(vector<channelInfo> &achannels) {
588 bool r;
589 vector<channelInfo>::iterator ic, iac;
593 * First, let's leave the channels we are not on anymore.
597 for(ic = channels.begin(); ic != channels.end(); ++ic) {
598 iac = find(achannels.begin(), achannels.end(), ic->name);
600 if(ic->joined) {
601 r = iac == achannels.end();
602 if(!r) r = !iac->joined;
603 if(r) firetalk_chat_part(irhook.handle, ic->name.c_str());
609 * Now, let's see if there are any new channels we
610 * gotta join to.
614 for(iac = achannels.begin(); iac != achannels.end(); ++iac) {
615 ic = find(channels.begin(), channels.end(), iac->name);
617 if(iac->joined) {
618 r = ic == channels.end();
619 if(!r) r = !ic->joined;
620 string passname = iac->name;
621 if (!iac->passwd.empty())
622 passname += " " + iac->passwd;
623 if(r) firetalk_chat_join(irhook.handle, passname.c_str());
627 channels = achannels;
630 void irchook::requestawaymsg(const imcontact &ic) {
631 em.store(imnotification(ic, string() +
632 _("Away message:") + "\n\n" + awaymessages[ic.nickname]));
635 void irchook::rawcommand(const string &cmd) {
636 int *r, *w, *e, sock;
637 if(online()) {
638 firetalk_getsockets(firetalk_find_protocol("IRC"), &r, &w, &e);
640 if(*r) {
641 write(*r, cmd.c_str(), cmd.size());
642 write(*r, "\n", 1);
645 delete r;
646 delete w;
647 delete e;
651 void irchook::channelfatal(string room, const char *fmt, ...) {
652 va_list ap;
653 char buf[NOTIFBUF];
654 vector<channelInfo>::iterator i;
656 va_start(ap, fmt);
657 vsnprintf(buf, NOTIFBUF, fmt, ap);
658 va_end(ap);
660 if(room.substr(0, 1) != "#")
661 room.insert(0, "#");
663 i = find(channels.begin(), channels.end(), room);
665 if(i != channels.end()) {
666 imcontact cont(room, irc);
667 icqcontact *c = clist.get(cont);
668 if(!c) c = clist.addnew(cont);
669 c->setstatus(offline);
670 i->joined = i->fetched = false;
671 em.store(imnotification(cont, buf));
675 void irchook::ouridchanged(const icqconf::imaccount &ia) {
676 if(logged()) {
677 firetalk_set_nickname(handle, ia.nickname.c_str());
681 void irchook::requestversion(const imcontact &c) {
682 if(logged()) {
683 firetalk_subcode_send_request(handle, c.nickname.c_str(), "VERSION", 0);
687 void irchook::ping(const imcontact &c) {
688 if(logged()) {
689 firetalk_subcode_send_request(handle, c.nickname.c_str(), "PING", 0);
690 time(&pingtime[up(c.nickname)]);
694 bool irchook::knowntransfer(const imfile &fr) const {
695 return transferinfo.find(fr) != transferinfo.end();
698 void irchook::replytransfer(const imfile &fr, bool accept, const string &localpath) {
699 if(accept) {
700 transferinfo[fr].second = localpath;
702 if(transferinfo[fr].second.substr(transferinfo[fr].second.size()-1) != "/")
703 transferinfo[fr].second += "/";
705 transferinfo[fr].second += justfname(fr.getfiles().begin()->fname);
707 firetalk_file_accept(handle, transferinfo[fr].first, 0,
708 transferinfo[fr].second.c_str());
710 } else {
711 firetalk_file_refuse(handle, transferinfo[fr].first);
712 transferinfo.erase(fr);
717 void irchook::aborttransfer(const imfile &fr) {
718 firetalk_file_cancel(handle, transferinfo[fr].first);
720 face.transferupdate(fr.getfiles().begin()->fname, fr,
721 icqface::tsCancel, 0, 0);
723 irhook.transferinfo.erase(fr);
726 bool irchook::getfevent(void *fhandle, imfile &fr) {
727 map<imfile, pair<void *, string> >::const_iterator i = transferinfo.begin();
729 while(i != transferinfo.end()) {
730 if(i->second.first == fhandle) {
731 fr = i->first;
732 return true;
734 ++i;
737 return false;
740 // ----------------------------------------------------------------------------
742 void irchook::userstatus(const string &nickname, imstatus st) {
743 if(!nickname.empty()) {
744 imcontact ic(nickname, irc);
745 icqcontact *c = clist.get(ic);
747 if(c)
748 if(st != c->getstatus()) {
749 logger.putonline(ic, c->getstatus(), st);
750 c->setstatus(st);
755 void irchook::connected(void *conn, void *cli, ...) {
756 irhook.flogged = true;
757 irhook.log(logLogged);
758 face.update();
760 int i;
761 vector<channelInfo>::iterator ic;
762 icqcontact *c;
764 for(i = 0; i < clist.count; i++) {
765 c = (icqcontact *) clist.at(i);
767 if(c->getdesc().pname == irc)
768 if(!ischannel(c)) {
769 vector<icqgroup>::const_iterator ig = find(groups.begin(), groups.end(), clist.get(c)->getgroupid());
770 if(ig != groups.end()) {
771 firetalk_im_add_buddy(irhook.handle, c->getdesc().nickname.c_str() , ig->getname().c_str(), "");
776 for(ic = irhook.channels.begin(); ic != irhook.channels.end(); ++ic) {
777 if(ic->joined) {
778 string passname = ic->name;
779 if (!ic->passwd.empty())
780 passname += " " + ic->passwd;
781 firetalk_chat_join(irhook.handle, passname.c_str());
785 irhook.ourstatus = available;
786 irhook.setautostatus(irhook.manualstatus);
787 irhook.awaymessages.clear();
788 irhook.sentpass = false;
791 void irchook::disconnected(void *conn, void *cli, ...) {
792 irhook.fonline = false;
794 logger.putourstatus(irc, irhook.getstatus(), offline);
795 clist.setoffline(irc);
797 vector<channelInfo>::iterator ic;
798 for(ic = irhook.channels.begin(); ic != irhook.channels.end(); ++ic)
799 ic->fetched = false;
801 irhook.log(logDisconnected);
802 face.update();
805 void irchook::connectfailed(void *connection, void *cli, ...) {
806 va_list ap;
808 va_start(ap, cli);
809 int err = va_arg(ap, int);
810 char *reason = va_arg(ap, char *);
811 va_end(ap);
813 irhook.fonline = false;
815 face.log(_("+ [irc] connect failed: %s"), reason);
816 face.update();
819 void irchook::newnick(void *conn, void *cli, ...) {
820 va_list ap;
822 va_start(ap, cli);
823 char *nick = va_arg(ap, char *);
824 va_end(ap);
826 if(nick)
827 if(strlen(nick)) {
828 icqconf::imaccount acc = conf->getourid(irc);
829 acc.nickname = nick;
830 conf->setourid(acc);
832 face.log(_("+ [irc] nickname was changed successfully"));
836 void irchook::gotinfo(void *conn, void *cli, ...) {
837 int pos;
838 va_list ap;
839 string about, email;
840 icqcontact::basicinfo cbinfo;
842 va_start(ap, cli);
843 char *nick = va_arg(ap, char *);
844 char *info = va_arg(ap, char *);
845 int warning = va_arg(ap, int);
846 int idle = va_arg(ap, int);
847 va_end(ap);
849 if(nick && info)
850 if(strlen(nick) && strlen(info)) {
851 icqcontact *c = clist.get(imcontact(nick, irc));
853 if(!c) c = clist.get(contactroot);
855 about = info;
856 cbinfo = c->getbasicinfo();
858 if((pos = about.find(":")) != -1) {
859 email = about.substr(0, pos);
860 about.erase(0, pos);
862 if(email.substr(0, 1) == "~") email.erase(0, 1);
863 if((pos = about.find_first_not_of(" :")) > 0) about.erase(0, pos);
866 cbinfo.lname = "";
867 cbinfo.email = irhook.rushtmlconv("wk", email);
869 if((pos = about.find(" ")) != -1) {
870 cbinfo.lname = irhook.rushtmlconv("wk", about.substr(pos+1));
871 about.erase(pos);
874 cbinfo.fname = irhook.rushtmlconv("wk", about);
876 c->setnick(nick);
877 c->setbasicinfo(cbinfo);
879 if(c->getstatus() == offline)
880 c->setstatus(available);
884 void irchook::gotchannels(void *conn, void *cli, ...) {
885 va_list ap;
886 string info;
887 icqcontact *c;
889 va_start(ap, cli);
890 char *nick = va_arg(ap, char *);
891 char *channels = va_arg(ap, char *);
892 va_end(ap);
894 if(nick && channels)
895 if(strlen(nick) && strlen(channels)) {
896 c = clist.get(imcontact(nick, irc));
897 if(!c) c = clist.get(contactroot);
899 c->setabout((string) _("On channels: ") + channels);
901 if(c->getstatus() == offline)
902 c->setstatus(available);
906 void irchook::getmessage(void *conn, void *cli, ...) {
907 va_list ap;
909 va_start(ap, cli);
910 char *sender = va_arg(ap, char *);
911 int automessage_flag = va_arg(ap, int);
912 char *message = va_arg(ap, char *);
913 va_end(ap);
915 if(sender && message)
916 if(strlen(sender) && strlen(message)) {
917 /*if(!irhook.sentpass) // NickServ identify should be handled by firetalk and needpass callback
918 if(up(sender) == "NICKSERV") {
919 firetalk_im_send_message(irhook.handle, "NickServ",
920 ((string) "identify " + conf->getourid(irc).additional["nickpass"]).c_str(), 0);
922 irhook.sentpass = true;
925 em.store(immessage(imcontact(sender, irc),
926 imevent::incoming, irhook.rushtmlconv("wk", cuthtml(message, chCutBR | chLeaveLinks))));
930 void irchook::getaction(void *conn, void *cli, ...) {
931 va_list ap;
933 va_start(ap, cli);
934 char *sender = va_arg(ap, char *);
935 int automessage_flag = va_arg(ap, int);
936 char *message = va_arg(ap, char *);
937 va_end(ap);
939 if(sender && message)
940 if(strlen(sender) && strlen(message)) {
941 em.store(immessage(imcontact(sender, irc), imevent::incoming,
942 ((string) "* " + sender + " " +
943 irhook.rushtmlconv("wk", cuthtml(message, chCutBR | chLeaveLinks))).c_str()));
947 void irchook::buddyonline(void *conn, void *cli, ...) {
948 va_list ap;
950 va_start(ap, cli);
951 char *nick = va_arg(ap, char *);
952 va_end(ap);
954 if(nick)
955 if(strlen(nick)) {
956 irhook.userstatus(nick, available);
960 void irchook::buddyoffline(void *conn, void *cli, ...) {
961 va_list ap;
963 va_start(ap, cli);
964 char *nick = va_arg(ap, char *);
965 va_end(ap);
967 if(nick)
968 if(strlen(nick)) {
969 irhook.userstatus(nick, offline);
973 void irchook::buddyaway(void *conn, void *cli, ...) {
974 va_list ap;
976 va_start(ap, cli);
977 char *nick = va_arg(ap, char *);
978 char *msg = va_arg(ap, char *);
979 va_end(ap);
981 if(nick)
982 if(strlen(nick)) {
983 irhook.userstatus(nick, away);
984 if(msg) irhook.awaymessages[nick] = irhook.rushtmlconv("wk", msg);
988 void irchook::listmember(void *connection, void *cli, ...) {
989 va_list ap;
990 vector<channelInfo>::iterator ir;
992 va_start(ap, cli);
993 char *room = va_arg(ap, char *);
994 char *membername = va_arg(ap, char *);
995 int opped = va_arg(ap, int);
996 va_end(ap);
998 if(membername)
999 if(strlen(membername)) {
1000 ir = find(irhook.channels.begin(), irhook.channels.end(), room);
1002 if(ir != irhook.channels.end()) {
1003 ir->nicks.push_back(/*(string) (opped ? "@": "") +*/ membername);
1008 void irchook::fclog(void *connection, void *cli, ...) {
1009 va_list ap;
1011 va_start(ap, cli);
1012 char *msg = va_arg(ap, char *);
1013 va_end(ap);
1015 face.log("irc: %s", msg);
1018 void irchook::chatnames(void *connection, void *cli, ...) {
1019 va_list ap;
1020 vector<channelInfo>::iterator ic;
1021 vector<string>::iterator is;
1022 bool ready = true;
1024 va_start(ap, cli);
1025 char *croom = va_arg(ap, char *);
1026 va_end(ap);
1028 ic = find(irhook.channels.begin(), irhook.channels.end(), croom);
1030 if(ic != irhook.channels.end()) {
1031 ic->fetched = true;
1032 ic->nicks.clear();
1034 if(irhook.searchdest) {
1035 if(irhook.emailsub.empty() && irhook.namesub.empty()) {
1036 firetalk_chat_listmembers(irhook.handle, croom);
1037 if(!ic->joined) firetalk_chat_part(irhook.handle, croom);
1038 } else {
1039 firetalk_chat_requestextended(irhook.handle, croom);
1044 if(irhook.searchdest && irhook.emailsub.empty() && irhook.namesub.empty()) {
1045 for(is = irhook.searchchannels.begin(); is != irhook.searchchannels.end(); ++is) {
1046 ic = find(irhook.channels.begin(), irhook.channels.end(), *is);
1048 if(ic != irhook.channels.end())
1049 if(!ic->fetched) {
1050 ready = false;
1051 break;
1055 if(ready) {
1056 irhook.processnicks();
1061 void irchook::listextended(void *connection, void *cli, ...) {
1062 va_list ap;
1063 string text, email, name;
1064 vector<channelInfo>::iterator ir;
1065 vector<string>::iterator is;
1066 int npos;
1068 va_start(ap, cli);
1069 char *nickname = va_arg(ap, char *);
1070 char *room = va_arg(ap, char *);
1071 char *login = va_arg(ap, char *);
1072 char *hostname = va_arg(ap, char *);
1073 char *net = va_arg(ap, char *);
1074 char *description = va_arg(ap, char *);
1075 va_end(ap);
1077 ir = find(irhook.channels.begin(), irhook.channels.end(), room);
1079 if(irhook.smode == Email) {
1080 ir = find(irhook.channels.begin(), irhook.channels.end(), "");
1082 if(ir == irhook.channels.end()) {
1083 irhook.channels.push_back(channelInfo(""));
1084 ir = irhook.channels.end()-1;
1088 if(ir != irhook.channels.end()) {
1089 name = description;
1090 if((npos = name.find(" ")) != -1)
1091 name.erase(0, npos+1);
1093 email = (string) login + "@" + hostname;
1095 if(irhook.emailsub.empty() || email.find(irhook.emailsub) != -1 || irhook.smode == Email) {
1096 if(irhook.namesub.empty() || name.find(irhook.namesub) != -1) {
1097 text = nickname;
1099 if(!irhook.emailsub.empty()) {
1100 text += " <" + email + ">";
1101 } else if(!irhook.namesub.empty()) {
1102 text += " [" + name + "]";
1105 ir->nicks.push_back(text);
1110 if(find(irhook.extlisted.begin(), irhook.extlisted.end(), room) == irhook.extlisted.end())
1111 irhook.extlisted.push_back(room);
1114 void irchook::endextended(void *connection, void *cli, ...) {
1115 bool ready = true;
1116 vector<string>::iterator is;
1117 vector<channelInfo>::iterator ic;
1119 if(irhook.smode == Channel && !irhook.extlisted.empty()) {
1120 ic = find(irhook.channels.begin(), irhook.channels.end(), irhook.extlisted.back());
1122 if(ic != irhook.channels.end()) {
1123 if(!ic->joined)
1124 firetalk_chat_part(irhook.handle, irhook.extlisted.back().c_str());
1126 for(is = irhook.searchchannels.begin(); ready && is != irhook.searchchannels.end(); ++is)
1127 ready = find(irhook.extlisted.begin(), irhook.extlisted.end(), *is) != irhook.extlisted.end();
1131 ready = ready || irhook.smode == Email;
1133 if(ready) {
1134 irhook.processnicks();
1136 if(irhook.smode == Email) {
1137 ic = find(irhook.channels.begin(), irhook.channels.end(), "");
1138 if(ic != irhook.channels.end()) irhook.channels.erase(ic);
1143 void irchook::chatmessage(void *connection, void *cli, ...) {
1144 va_list ap;
1145 string imsg;
1147 va_start(ap, cli);
1148 char *room = va_arg(ap, char *);
1149 char *from = va_arg(ap, char *);
1150 int automessage = va_arg(ap, int);
1151 char *msg = va_arg(ap, char *);
1152 va_end(ap);
1154 if(clist.get(imcontact(room, irc))) {
1155 imsg = (string) from + ": " + msg;
1156 getmessage(connection, cli, room, automessage, imsg.c_str());
1160 void irchook::chataction(void *connection, void *cli, ...) {
1161 va_list ap;
1162 string imsg;
1164 va_start(ap, cli);
1165 char *room = va_arg(ap, char *);
1166 char *from = va_arg(ap, char *);
1167 int automessage = va_arg(ap, int);
1168 char *msg = va_arg(ap, char *);
1169 va_end(ap);
1171 if(from && msg)
1172 if(strlen(from) && strlen(msg))
1173 if(clist.get(imcontact(room, irc))) {
1174 em.store(immessage(imcontact(room, irc), imevent::incoming,
1175 ((string) "* " + from + " " +
1176 irhook.rushtmlconv("wk", cuthtml(msg, chCutBR | chLeaveLinks))).c_str()));
1181 void irchook::chatjoined(void *connection, void *cli, ...) {
1182 va_list ap;
1184 va_start(ap, cli);
1185 char *room = va_arg(ap, char *);
1186 va_end(ap);
1188 icqcontact *c = clist.get(imcontact(room, irc));
1189 if(c) c->setstatus(available, false);
1192 void irchook::chatleft(void *connection, void *cli, ...) {
1193 va_list ap;
1195 va_start(ap, cli);
1196 char *room = va_arg(ap, char *);
1197 va_end(ap);
1199 icqcontact *c = clist.get(imcontact(room, irc));
1200 if(c) c->setstatus(offline, false);
1203 void irchook::chatkicked(void *connection, void *cli, ...) {
1204 va_list ap;
1206 va_start(ap, cli);
1207 char *room = va_arg(ap, char *);
1208 char *by = va_arg(ap, char *);
1209 char *reason = va_arg(ap, char *);
1210 va_end(ap);
1212 irhook.channelfatal(room, _("Kicked by %s; reason: %s"),
1213 by, irhook.rushtmlconv("wk", reason).c_str());
1216 void irchook::errorhandler(void *connection, void *cli, ...) {
1217 va_list ap;
1218 icqcontact *c;
1220 va_start(ap, cli);
1221 int error = va_arg(ap, int);
1222 char *subject = va_arg(ap, char *);
1223 char *description = va_arg(ap, char *);
1224 va_end(ap);
1226 switch(error) {
1227 case FE_ROOMUNAVAILABLE:
1228 // Cannot join channel
1229 if(subject)
1230 if(strlen(subject))
1231 irhook.channelfatal(subject, "%s", description);
1232 break;
1233 case FE_BADUSER:
1234 // Cannot fetch user's info
1235 if(subject)
1236 if(strlen(subject))
1237 if(c = clist.get(imcontact(subject, irc)))
1238 c->setstatus(offline);
1239 break;
1243 void irchook::nickchanged(void *connection, void *cli, ...) {
1244 va_list ap;
1245 icqcontact *c;
1246 char buf[NOTIFBUF];
1248 va_start(ap, cli);
1249 char *oldnick = va_arg(ap, char *);
1250 char *newnick = va_arg(ap, char *);
1251 va_end(ap);
1253 if(oldnick && newnick)
1254 if(strcmp(oldnick, newnick)) {
1255 if(c = clist.get(imcontact(oldnick, irc))) {
1256 if(!clist.get(imcontact(newnick, irc))) {
1257 if(!c->inlist()) {
1258 if(c->getdispnick() == oldnick) c->setdispnick(newnick);
1259 c->setdesc(imcontact(newnick, irc));
1260 c->setnick(newnick);
1263 } else {
1264 c->setstatus(offline);
1268 snprintf(buf, NOTIFBUF, _("The user has changed their nick from %s to %s"), oldnick, newnick);
1269 em.store(imnotification(c, buf));
1274 void irchook::needpass(void *conn, void *cli, ...) {
1275 va_list ap;
1277 va_start(ap, cli);
1278 char *pass = va_arg(ap, char *);
1279 int size = va_arg(ap, int);
1280 va_end(ap);
1282 if(pass) {
1283 icqconf::imaccount acc = conf->getourid(irc);
1285 if (size == 129) // signon password
1287 if(!acc.password.empty()) {
1288 strncpy(pass, acc.password.c_str(), size-1);
1289 pass[size-1] = 0;
1290 face.log(_("+ [irc] password sent"));
1293 else // NickServ password
1295 irhook.sentpass = true;
1296 if(!acc.additional["nickpass"].empty()) {
1297 strncpy(pass, acc.additional["nickpass"].c_str(), size-1);
1298 pass[size-1] = 0;
1299 face.log(_("+ [irc] nick password sent"));
1305 void irchook::subrequest(void *conn, void *cli, const char * const nick,
1306 const char * const command, const char * const args) {
1308 if(!strcmp(command, "VERSION")) {
1309 ostringstream args_stream;
1310 args_stream << PACKAGE << " " << centerim::version;
1311 firetalk_subcode_send_reply(conn, nick, "VERSION", args_stream.str().c_str());
1315 void irchook::subreply(void *conn, void *cli, const char * const nick,
1316 const char * const command, const char * const args) {
1317 char buf[NOTIFBUF];
1319 if(!strcmp(command, "PING")) {
1320 map<string, time_t>::iterator i = irhook.pingtime.find(up(nick));
1322 if(i != irhook.pingtime.end()) {
1323 snprintf(buf, NOTIFBUF, _("PING reply from the user: %d second(s)"), time(0)-i->second);
1324 em.store(imnotification(imcontact(nick, irc), buf));
1327 } else if(!strcmp(command, "VERSION")) {
1328 snprintf(buf, NOTIFBUF, _("The remote is using %s"), args);
1329 em.store(imnotification(imcontact(nick, irc), buf));
1334 void irchook::fileoffer(void *conn, void *cli, ...) {
1335 va_list ap;
1337 va_start(ap, cli);
1338 void *filehandle = va_arg(ap, void *);
1339 char *from = va_arg(ap, char *);
1340 char *filename = va_arg(ap, char *);
1341 long size = va_arg(ap, long);
1342 va_end(ap);
1344 imfile::record r;
1345 r.fname = filename;
1346 r.size = size;
1348 imfile fr(imcontact(from, irc), imevent::incoming, "",
1349 vector<imfile::record>(1, r));
1351 irhook.transferinfo[fr].first = filehandle;
1352 em.store(fr);
1354 face.transferupdate(filename, fr, icqface::tsInit, size, 0);
1357 void irchook::filestart(void *conn, void *cli, ...) {
1358 va_list ap;
1359 imfile fr;
1361 va_start(ap, cli);
1362 void *filehandle = va_arg(ap, void *);
1363 void *clientfilestruct = va_arg(ap, void *);
1364 va_end(ap);
1366 if(irhook.getfevent(filehandle, fr)) {
1367 face.transferupdate(fr.getfiles().begin()->fname, fr,
1368 icqface::tsStart, 0, 0);
1372 void irchook::fileprogress(void *conn, void *cli, ...) {
1373 va_list ap;
1374 imfile fr;
1376 va_start(ap, cli);
1377 void *filehandle = va_arg(ap, void *);
1378 void *clientfilestruct = va_arg(ap, void *);
1379 long bytes = va_arg(ap, long);
1380 long size = va_arg(ap, long);
1381 va_end(ap);
1383 if(irhook.getfevent(filehandle, fr)) {
1384 face.transferupdate(fr.getfiles().begin()->fname, fr,
1385 icqface::tsProgress, size, bytes);
1389 void irchook::filefinish(void *conn, void *cli, ...) {
1390 va_list ap;
1391 imfile fr;
1393 va_start(ap, cli);
1394 void *filehandle = va_arg(ap, void *);
1395 void *clientfilestruct = va_arg(ap, void *);
1396 long size = va_arg(ap, long);
1397 va_end(ap);
1399 if(irhook.getfevent(filehandle, fr)) {
1400 face.transferupdate(fr.getfiles().begin()->fname, fr,
1401 icqface::tsFinish, size, 0);
1403 irhook.transferinfo.erase(fr);
1407 void irchook::fileerror(void *conn, void *cli, ...) {
1408 va_list ap;
1409 imfile fr;
1411 va_start(ap, cli);
1412 void *filehandle = va_arg(ap, void *);
1413 void *clientfilestruct = va_arg(ap, void *);
1414 int error = va_arg(ap, int);
1415 va_end(ap);
1417 if(irhook.getfevent(filehandle, fr)) {
1418 face.transferupdate(fr.getfiles().begin()->fname, fr,
1419 icqface::tsError, 0, 0);
1421 irhook.transferinfo.erase(fr);
1425 void irchook::chatuserjoined(void *conn, void *cli, ...) {
1426 va_list ap;
1428 va_start(ap, cli);
1429 char *room = va_arg(ap, char *);
1430 char *who = va_arg(ap, char *);
1431 char *email = va_arg(ap, char *);
1432 va_end(ap);
1434 if(conf->getourid(irc).nickname != who) {
1435 string uname = who;
1437 if(email)
1438 if(strlen(email))
1439 uname += (string) " (" + email + ")";
1441 char buf[NOTIFBUF];
1442 snprintf(buf, NOTIFBUF, _("%s has joined."), uname.c_str());
1443 em.store(imnotification(imcontact(room, irc), buf));
1447 void irchook::chatuserleft(void *conn, void *cli, ...) {
1448 va_list ap;
1450 va_start(ap, cli);
1451 char *room = va_arg(ap, char *);
1452 char *who = va_arg(ap, char *);
1453 char *reason = va_arg(ap, char *);
1454 va_end(ap);
1456 if(conf->getourid(irc).nickname != who) {
1457 string text;
1458 string text2;
1459 char buf[NOTIFBUF];
1461 snprintf(buf, NOTIFBUF, _("%s has left"), who); text = buf;
1463 if(reason)
1464 if(strlen(reason)) {
1465 if(strlen(reason) > 450) reason[450] = 0;
1466 text2 = irhook.rushtmlconv( "wk", reason );
1467 snprintf(buf, NOTIFBUF, _("reason: %s"), reason);
1468 text += (string) "; " + buf + ".";
1471 em.store(imnotification(imcontact(room, irc), text));
1475 void irchook::chatuserkicked(void *conn, void *cli, ...) {
1476 va_list ap;
1478 va_start(ap, cli);
1479 char *room = va_arg(ap, char *);
1480 char *who = va_arg(ap, char *);
1481 char *by = va_arg(ap, char *);
1482 char *reason = va_arg(ap, char *);
1483 va_end(ap);
1485 if(conf->getourid(irc).nickname != who) {
1486 string text;
1487 char buf[NOTIFBUF];
1489 snprintf(buf, NOTIFBUF, _("%s has been kicked by %s"), who, by); text = buf;
1491 if(reason)
1492 if(strlen(reason)) {
1493 snprintf(buf, NOTIFBUF, _("reason: %s"), reason);
1494 text += (string) "; " + buf + ".";
1497 em.store(imnotification(imcontact(room, irc), text));
1501 void irchook::chatgottopic(void *conn, void *cli, ...) {
1502 va_list ap;
1504 va_start(ap, cli);
1505 char *room = va_arg(ap, char *);
1506 char *topic = va_arg(ap, char *);
1507 char *author = va_arg(ap, char *);
1508 va_end(ap);
1510 vector<channelInfo>::const_iterator ic = find(irhook.channels.begin(), irhook.channels.end(), room);
1512 if(ic == irhook.channels.end() || !ic->joined)
1513 return;
1515 string text;
1516 char buf[NOTIFBUF];
1517 text = irhook.rushtmlconv( "wk", topic );
1518 snprintf(buf, NOTIFBUF, _("Channel topic now is: %s"), text.c_str());
1519 text = buf;
1521 if(author)
1522 if(strlen(author)) {
1523 snprintf(buf, NOTIFBUF, _("set by %s"), author);
1524 text += (string) "; " + buf + ".";
1527 em.store(imnotification(imcontact(room, irc), text));
1530 void irchook::chatuseropped(void *conn, void *cli, ...) {
1531 va_list ap;
1533 va_start(ap, cli);
1534 char *room = va_arg(ap, char *);
1535 char *who = va_arg(ap, char *);
1536 char *by = va_arg(ap, char *);
1537 va_end(ap);
1539 if(by) {
1540 char buf[NOTIFBUF];
1541 snprintf(buf, NOTIFBUF, _("%s has been opped by %s."), who, by);
1542 em.store(imnotification(imcontact(room, irc), buf));
1546 void irchook::chatuserdeopped(void *conn, void *cli, ...) {
1547 va_list ap;
1549 va_start(ap, cli);
1550 char *room = va_arg(ap, char *);
1551 char *who = va_arg(ap, char *);
1552 char *by = va_arg(ap, char *);
1553 va_end(ap);
1555 if(by) {
1556 char buf[NOTIFBUF];
1557 snprintf(buf, NOTIFBUF, _("%s has been deopped by %s."), who, by);
1558 em.store(imnotification(imcontact(room, irc), buf));
1562 void irchook::chatopped(void *conn, void *cli, ...) {
1563 va_list ap;
1565 va_start(ap, cli);
1566 char *room = va_arg(ap, char *);
1567 char *by = va_arg(ap, char *);
1568 va_end(ap);
1570 char buf[NOTIFBUF];
1571 if(by) snprintf(buf, NOTIFBUF, _("%s has opped us."), by);
1572 else snprintf(buf, NOTIFBUF, _("you are an op here"));
1573 em.store(imnotification(imcontact(room, irc), buf));
1576 void irchook::chatdeopped(void *conn, void *cli, ...) {
1577 va_list ap;
1579 va_start(ap, cli);
1580 char *room = va_arg(ap, char *);
1581 char *by = va_arg(ap, char *);
1582 va_end(ap);
1584 char buf[NOTIFBUF];
1585 snprintf(buf, NOTIFBUF, _("%s has deopped us."), by);
1586 em.store(imnotification(imcontact(room, irc), buf));
1589 // ----------------------------------------------------------------------------
1591 bool irchook::channelInfo::operator != (const string &aname) const {
1592 return up(aname) != up(name);
1595 bool irchook::channelInfo::operator == (const string &aname) const {
1596 return up(aname) == up(name);
1599 #endif