1 /*
2 *
3 * centerim MSN protocol handling class
4 * $Id: msnhook.cc,v 1.91 2005/02/03 02:02:37 konst Exp $
5 *
6 * Copyright (C) 2001-2004 by Konstantin Klyagin <k@thekonst.net>
7 *
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.
12 *
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.
17 *
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
22 *
23 */
24 
25 #include "icqcommon.h"
26 
27 #ifdef BUILD_MSN
28 
29 #include "msnhook.h"
30 #include "icqconf.h"
31 #include "icqface.h"
32 #include "icqcontacts.h"
33 #include "accountmanager.h"
34 #include "eventmanager.h"
35 #include "imlogger.h"
36 #include "connwrap.h"
37 #include "icqgroups.h"
38 
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 #include <arpa/inet.h>
45 
46 msnhook mhook;
47 
nicknormalize(const string & nick)48 static string nicknormalize(const string &nick) {
49     if(nick.find("@") == -1) return nick + "@hotmail.com";
50     return nick;
51 }
52 
nicktodisp(const string & nick)53 static string nicktodisp(const string &nick) {
54     int pos;
55     string r = nick;
56 
57     if((pos = r.find("@")) != -1)
58     if(r.substr(pos+1) == "hotmail.com")
59 	r.erase(pos);
60 
61     return r;
62 }
63 
64 static map<MSN::BuddyStatus, imstatus> convstat;
65 
buddy2stat(MSN::BuddyStatus bst)66 static imstatus buddy2stat(MSN::BuddyStatus bst) {
67     map<MSN::BuddyStatus, imstatus>::const_iterator i = convstat.find(bst);
68     if(i != convstat.end())
69 	return i->second;
70 
71     return offline;
72 }
73 
stat2buddy(imstatus st)74 static MSN::BuddyStatus stat2buddy(imstatus st) {
75     map<MSN::BuddyStatus, imstatus>::const_iterator i = convstat.begin();
76     while(i != convstat.end()) {
77 	if(st == i->second)
78 	    return i->first;
79 	++i;
80     }
81 
82     return MSN::STATUS_AVAILABLE;
83 }
84 
85 // ----------------------------------------------------------------------------
86 
msnhook()87 msnhook::msnhook(): abstracthook(msn), conn(cb) {
88     ourstatus = offline;
89     fonline = false;
90     destroying = false;
91 
92     fcapabs.insert(hookcapab::changedetails);
93     fcapabs.insert(hookcapab::directadd);
94 }
95 
~msnhook()96 msnhook::~msnhook() {
97     if(conn.connectionState() != MSN::NotificationServerConnection::NS_DISCONNECTED) {
98 	destroying = true;
99 	conn.disconnect();
100     }
101 }
102 
init()103 void msnhook::init() {
104     manualstatus = conf->getstatus(msn);
105 
106     convstat[MSN::STATUS_AVAILABLE] = available;
107     convstat[MSN::STATUS_INVISIBLE] = invisible;
108     convstat[MSN::STATUS_BUSY] = dontdisturb;
109     convstat[MSN::STATUS_ONTHEPHONE] = occupied;
110     convstat[MSN::STATUS_AWAY] = notavail;
111     convstat[MSN::STATUS_BERIGHTBACK] = away;
112     convstat[MSN::STATUS_OUTTOLUNCH] = outforlunch;
113     convstat[MSN::STATUS_IDLE] = away;
114 }
115 
connect()116 void msnhook::connect() {
117     icqconf::imaccount account = conf->getourid(msn);
118 
119     log(logConnecting);
120 
121     readinfo = flogged = false;
122     fonline = true;
123 
124     try {
125 	if(conn.connectionState() != MSN::NotificationServerConnection::NS_DISCONNECTED)
126 	    conn.disconnect();
127 
128 	rfds.clear();
129 	wfds.clear();
130 
131 	conn.connect(account.server, account.port, nicknormalize(account.nickname), account.password);
132     } catch(...) {
133     }
134 }
135 
disconnect()136 void msnhook::disconnect() {
137     fonline = false;
138     if(conn.connectionState() != MSN::NotificationServerConnection::NS_DISCONNECTED)
139 	conn.disconnect();
140 	clist.setoffline(mhook.proto);
141     log(logDisconnected);
142 }
143 
exectimers()144 void msnhook::exectimers() {
145     if(logged()) {
146 	if(timer_current-timer_ping > conn.nextPing()) {
147 	    try {
148 		conn.sendPing();
149 		timer_ping = timer_current;
150 	    } catch(...) {
151 	    }
152 	}
153     }
154 }
155 
main()156 void msnhook::main() {
157     vector<int>::const_iterator i;
158     fd_set rs, ws, es;
159     struct timeval tv;
160     int hsock = 0;
161     MSN::Connection *c;
162 
163     FD_ZERO(&rs);
164     FD_ZERO(&ws);
165     FD_ZERO(&es);
166 
167     getsockets(rs, ws, es, hsock);
168     tv.tv_sec = tv.tv_usec = 0;
169 
170     try {
171 	if(select(hsock+1, &rs, &ws, &es, &tv) > 0) {
172 	    for(i = rfds.begin(); i != rfds.end(); ++i)
173 		if(FD_ISSET(*i, &rs)) {
174 		    c = conn.connectionWithSocket(*i);
175 		    if(c) c->dataArrivedOnSocket();
176 		    return;
177 		}
178 
179 	    for(i = wfds.begin(); i != wfds.end(); ++i)
180 		if(FD_ISSET(*i, &ws)) {
181 		    c = conn.connectionWithSocket(*i);
182 
183 		    if(c) {
184 			if(!c->isConnected()) {
185 			    c->socketConnectionCompleted();
186 			} else {
187 			    c->socketIsWritable();
188 			}
189 		    }
190 
191 		    return;
192 		}
193 	}
194     } catch(...) {
195     }
196 }
197 
getsockets(fd_set & rf,fd_set & wf,fd_set & efds,int & hsocket) const198 void msnhook::getsockets(fd_set &rf, fd_set &wf, fd_set &efds, int &hsocket) const {
199     vector<int>::const_iterator i;
200 
201     for(i = rfds.begin(); i != rfds.end(); ++i) {
202 	hsocket = max(hsocket, *i);
203 	FD_SET(*i, &rf);
204     }
205 
206     for(i = wfds.begin(); i != wfds.end(); ++i) {
207 	hsocket = max(hsocket, *i);
208 	FD_SET(*i, &wf);
209     }
210 }
211 
isoursocket(fd_set & rf,fd_set & wf,fd_set & efds) const212 bool msnhook::isoursocket(fd_set &rf, fd_set &wf, fd_set &efds) const {
213     vector<int>::const_iterator i;
214 
215     for(i = rfds.begin(); i != rfds.end(); ++i)
216 	if(FD_ISSET(*i, &rf))
217 	    return true;
218 
219     for(i = wfds.begin(); i != wfds.end(); ++i)
220 	if(FD_ISSET(*i, &wf))
221 	    return true;
222 
223     return false;
224 }
225 
online() const226 bool msnhook::online() const {
227     return fonline;
228 }
229 
logged() const230 bool msnhook::logged() const {
231     return fonline && flogged;
232 }
233 
isconnecting() const234 bool msnhook::isconnecting() const {
235     return fonline && !flogged;
236 }
237 
enabled() const238 bool msnhook::enabled() const {
239     return true;
240 }
241 
send(const imevent & ev)242 bool msnhook::send(const imevent &ev) {
243     string text;
244     string rcpt = nicknormalize(ev.getcontact().nickname);
245 
246     if(ev.gettype() == imevent::message) {
247 	const immessage *m = static_cast<const immessage *>(&ev);
248 	if(m) text = m->gettext();
249 
250     } else if(ev.gettype() == imevent::url) {
251 	const imurl *m = static_cast<const imurl *>(&ev);
252 	if(m) text = m->geturl() + "\n\n" + m->getdescription();
253 
254     } else if(ev.gettype() == imevent::file) {
255 	const imfile *m = static_cast<const imfile *>(&ev);
256 	vector<imfile::record> files = m->getfiles();
257 	vector<imfile::record>::const_iterator ir;
258 
259 	for(ir = files.begin(); ir != files.end(); ++ir) {
260 	    imfile::record r;
261 
262 	    r.fname = ir->fname;
263 	    r.size = ir->size;
264 
265 	    imfile fr(ev.getcontact(), imevent::outgoing, "", vector<imfile::record>(1, r));
266 
267 	    try {
268 		qevent *ctx = new qevent(qevent::qeFile, rcpt, ir->fname);
269 
270 		if(lconn.find(rcpt) != lconn.end()) sendmsn(lconn[rcpt], ctx);
271 		    else conn.requestSwitchboardConnection(ctx);
272 
273 	    } catch(...) {
274 	    }
275 	}
276 
277 	return true;
278     }
279 
280     icqcontact *c = clist.get(ev.getcontact());
281     text = rusconv("ku", text);
282 
283     if(c)
284     if(c->getstatus() != offline || !c->inlist()) {
285 	try {
286 	    qevent *ctx = new qevent(qevent::qeMsg, rcpt, text);
287 
288 	    if(lconn.find(rcpt) != lconn.end()) sendmsn(lconn[rcpt], ctx);
289 		else conn.requestSwitchboardConnection(ctx);
290 
291 	} catch(...) {
292 	}
293 
294 	return true;
295     }
296 
297     return false;
298 }
299 
findgroup(const imcontact & ic,string & gname) const300 int msnhook::findgroup(const imcontact &ic, string &gname) const {
301     int gid = -1;
302     icqcontact *c;
303 
304     if(c = clist.get(ic)) {
305 	vector<icqgroup>::const_iterator ig = find(groups.begin(), groups.end(), c->getgroupid());
306 	if(ig != groups.end()) {
307 	    gname = ig->getname();
308 	    map<int, string>::const_iterator im = mgroups.begin();
309 	    while(im != mgroups.end() && gid == -1) {
310 		if(im->second == ig->getname())
311 		    gid = im->first;
312 		++im;
313 	    }
314 	}
315     }
316 
317     return gid;
318 }
319 
sendnewuser(const imcontact & ic)320 void msnhook::sendnewuser(const imcontact &ic) {
321     if(logged() && !readinfo) {
322 	icqcontact *c;
323 	imcontact icc(nicktodisp(ic.nickname), msn);
324 
325 	if(icc.nickname != ic.nickname)
326 	if(c = clist.get(ic)) {
327 	    c->setdesc(icc);
328 	    c->setnick(icc.nickname);
329 	    c->setdispnick(icc.nickname);
330 	}
331 
332 	int gid;
333 	string gname;
334 
335 	gid = findgroup(ic, gname);
336 
337 	try {
338 	    if(gid == -1) {
339             gid = 0;
340 	    }
341         conn.addToGroup(nicknormalize(ic.nickname), gid);
342 	} catch(...) {
343 	}
344     }
345 
346     requestinfo(ic);
347 }
348 
setautostatus(imstatus st)349 void msnhook::setautostatus(imstatus st) {
350     if(st != offline) {
351 	if(!logged()) {
352 	    connect();
353 	} else {
354 	    logger.putourstatus(msn, ourstatus, st);
355 	    try {
356 		conn.setState(stat2buddy(ourstatus = st));
357 	    } catch(...) {
358 	    }
359 	}
360     } else {
361 	if(getstatus() != offline) {
362 	    disconnect();
363 	}
364     }
365 }
366 
getstatus() const367 imstatus msnhook::getstatus() const {
368     return online() ? ourstatus : offline;
369 }
370 
removeuser(const imcontact & ic)371 void msnhook::removeuser(const imcontact &ic) {
372     removeuser(ic, true);
373 }
374 
removeuser(const imcontact & ic,bool report)375 void msnhook::removeuser(const imcontact &ic, bool report) {
376     int i;
377     bool found;
378     vector<msnbuddy>::const_iterator ib = find(slst["FL"].begin(), slst["FL"].end(), nicknormalize(ic.nickname));
379 
380     if(online() && ib != slst["FL"].end()) {
381 	if(report)
382 	    log(logContactRemove, ic.nickname.c_str());
383 
384 	try {
385 	    conn.removeFromGroup(nicknormalize(ic.nickname), ib->gid);
386 
387 	    for(i = 0, found = false; i < clist.count && !found; i++) {
388 		icqcontact *c = (icqcontact *) clist.at(i);
389 		found = c->getdesc().pname == msn
390 		    && groups.getname(c->getgroupid()) == mgroups[ib->gid];
391 	    }
392 
393 	    if(!found && ib->gid > 0)
394             conn.removeGroup(ib->gid);
395 	} catch(...) {
396 	}
397     }
398 }
399 
requestinfo(const imcontact & ic)400 void msnhook::requestinfo(const imcontact &ic) {
401     icqcontact *c = clist.get(ic);
402     if(!c) c = clist.get(contactroot);
403 
404     icqcontact::moreinfo m = c->getmoreinfo();
405     icqcontact::basicinfo b = c->getbasicinfo();
406 
407     b.email = nicknormalize(ic.nickname);
408     m.homepage = "http://members.msn.com/" + b.email;
409 
410     if(ic.nickname == conf->getourid(msn).nickname)
411 	c->setnick(friendlynicks[ic.nickname]);
412 
413     c->setmoreinfo(m);
414     c->setbasicinfo(b);
415 }
416 
lookup(const imsearchparams & params,verticalmenu & dest)417 void msnhook::lookup(const imsearchparams &params, verticalmenu &dest) {
418     if(params.reverse) {
419 	vector<msnbuddy>::const_iterator i = slst["RL"].begin();
420 
421 	while(i != slst["RL"].end()) {
422 	    icqcontact *c = new icqcontact(imcontact(nicktodisp(i->nick), msn));
423 	    c->setnick(i->friendly);
424 
425 	    dest.additem(conf->getcolor(cp_clist_msn), c, (string) " " + i->nick);
426 	    ++i;
427 	}
428 	face.findready();
429 
430 	face.log(_("+ [msn] reverse users listing finished, %d found"),
431 	    slst["RL"].size());
432 
433 	dest.redraw();
434     }
435 }
436 
getneedsync()437 vector<icqcontact *> msnhook::getneedsync() {
438     int i;
439     vector<icqcontact *> r;
440     bool found;
441 
442     for(i = 0; i < clist.count; i++) {
443 	icqcontact *c = (icqcontact *) clist.at(i);
444 
445 	if(c->getdesc().pname == msn) {
446 	    vector<msnbuddy>::const_iterator fi = slst["FL"].begin();
447 
448 	    for(found = false; fi != slst["FL"].end() && !found; ++fi)
449 		found = c->getdesc().nickname == fi->nick;
450 
451 	    if(!found)
452 		r.push_back(c);
453 	}
454     }
455 
456     return r;
457 }
458 
sendupdateuserinfo(const icqcontact & c)459 void msnhook::sendupdateuserinfo(const icqcontact &c) {
460     try {
461 	conn.setFriendlyName(c.getnick());
462     } catch(...) {
463     }
464 }
465 
checkfriendly(icqcontact * c,const string friendlynick,bool forcefetch)466 void msnhook::checkfriendly(icqcontact *c, const string friendlynick, bool forcefetch) {
467     string oldnick = c->getnick();
468     string newnick = unmime(friendlynick);
469 
470     c->setnick(newnick);
471 
472     if(forcefetch || (oldnick != newnick && c->getdispnick() == oldnick) || oldnick.empty()) {
473 	c->setdispnick(newnick);
474 	face.relaxedupdate();
475     }
476 }
477 
checkinlist(imcontact ic)478 void msnhook::checkinlist(imcontact ic) {
479     icqcontact *c = clist.get(ic);
480     vector<icqcontact *> notremote = getneedsync();
481 
482     if(c)
483     if(c->inlist())
484     if(find(notremote.begin(), notremote.end(), c) != notremote.end())
485 	mhook.sendnewuser(ic);
486 }
487 
knowntransfer(const imfile & fr) const488 bool msnhook::knowntransfer(const imfile &fr) const {
489     return transferinfo.find(fr) != transferinfo.end();
490 }
491 
replytransfer(const imfile & fr,bool accept,const string & localpath)492 void msnhook::replytransfer(const imfile &fr, bool accept, const string &localpath) {
493     if(accept) {
494 	transferinfo[fr].second = localpath;
495 
496 	if(transferinfo[fr].second.substr(transferinfo[fr].second.size()-1) != "/")
497 	    transferinfo[fr].second += "/";
498 
499 	transferinfo[fr].second += justfname(fr.getfiles().begin()->fname);
500 //        msn_filetrans_accept(transferinfo[fr].first, transferinfo[fr].second.c_str());
501 
502     } else {
503 //        msn_filetrans_reject(transferinfo[fr].first);
504 	transferinfo.erase(fr);
505 
506     }
507 }
508 
aborttransfer(const imfile & fr)509 void msnhook::aborttransfer(const imfile &fr) {
510 //    msn_filetrans_reject(transferinfo[fr].first);
511 
512     face.transferupdate(fr.getfiles().begin()->fname, fr,
513 	icqface::tsCancel, 0, 0);
514 
515     transferinfo.erase(fr);
516 }
517 
getfevent(MSN::FileTransferInvitation * fhandle,imfile & fr)518 bool msnhook::getfevent(MSN::FileTransferInvitation *fhandle, imfile &fr) {
519     map<imfile, pair<MSN::FileTransferInvitation *, string> >::const_iterator i = transferinfo.begin();
520 
521     while(i != transferinfo.end()) {
522 	if(i->second.first == fhandle) {
523 	    fr = i->first;
524 	    return true;
525 	}
526 	++i;
527     }
528 
529     return false;
530 }
531 
updatecontact(icqcontact * c)532 void msnhook::updatecontact(icqcontact *c) {
533     string gname, nick = nicknormalize(c->getdesc().nickname);
534     vector<msnbuddy>::const_iterator ib = find(slst["FL"].begin(), slst["FL"].end(), nick);
535 
536     if(ib != slst["FL"].end() && logged() && conf->getgroupmode() != icqconf::nogroups)
537     if(mhook.findgroup(c->getdesc(), gname) != ib->gid) {
538 	try {
539 	    conn.removeFromList("FL", nick.c_str());
540 	} catch(...) {
541 	}
542 
543 	sendnewuser(c->getdesc());
544     }
545 }
546 
renamegroup(const string & oldname,const string & newname)547 void msnhook::renamegroup(const string &oldname, const string &newname) {
548     if(logged()) {
549 	map<int, string>::const_iterator im = mgroups.begin();
550 	while(im != mgroups.end()) {
551 	    if(im->second == oldname) {
552 		try {
553 		    conn.renameGroup(im->first, newname);
554 		} catch(...) {
555 		}
556 
557 		break;
558 	    }
559 	    ++im;
560 	}
561     }
562 }
563 
564 // ----------------------------------------------------------------------------
565 
statusupdate(string buddy,string friendlyname,imstatus status)566 void msnhook::statusupdate(string buddy, string friendlyname, imstatus status) {
567     imcontact ic(nicktodisp(buddy), msn);
568     icqcontact *c = clist.get(ic);
569     bool forcefetch;
570 
571     if(forcefetch = !c)
572 	c = clist.addnew(ic, false);
573 
574     if(!friendlyname.empty())
575 	checkfriendly(c, friendlyname, forcefetch);
576 
577     logger.putonline(ic, c->getstatus(), status);
578     c->setstatus(status);
579 }
580 
581 // ----------------------------------------------------------------------------
582 
sendmsn(MSN::SwitchboardServerConnection * conn,const qevent * ctx)583 void msnhook::sendmsn(MSN::SwitchboardServerConnection *conn, const qevent *ctx) {
584     MSN::FileTransferInvitation *inv;
585 
586     switch(ctx->type) {
587 	case msnhook::qevent::qeMsg:
588 	    conn->sendMessage(ctx->text);
589 	    break;
590 
591 	case msnhook::qevent::qeFile:
592 	    inv = conn->sendFile(ctx->text);
593 //                if(inv) mhook.transferinfo[] = inv;
594 	    break;
595     }
596 
597     delete ctx;
598 }
599 
600 // ----------------------------------------------------------------------------
601 
log(const string & s)602 static void log(const string &s) {
603     if(conf->getdebug())
604 	face.log(s);
605 }
606 
log(int writing,const char * buf)607 void msncallbacks::log(int writing, const char* buf) {
608     string pref = writing ? "OUT" : "IN";
609     ::log((string) "[" + pref + "] " + buf);
610 }
611 
registerSocket(int s,int reading,int writing)612 void msncallbacks::registerSocket(int s, int reading, int writing) {
613     ::log("msncallbacks::registerSocket");
614     if(reading) mhook.rfds.push_back(s);
615     if(writing) mhook.wfds.push_back(s);
616 }
617 
unregisterSocket(int s)618 void msncallbacks::unregisterSocket(int s) {
619     ::log("msncallbacks::unregisterSocket");
620     vector<int>::iterator i;
621 
622     i = find(mhook.rfds.begin(), mhook.rfds.end(), s);
623     if(i != mhook.rfds.end()) mhook.rfds.erase(i);
624 
625     i = find(mhook.wfds.begin(), mhook.wfds.end(), s);
626     if(i != mhook.wfds.end()) mhook.wfds.erase(i);
627 }
628 
gotFriendlyName(MSN::Connection * conn,string friendlyname)629 void msncallbacks::gotFriendlyName(MSN::Connection * conn, string friendlyname) {
630     ::log("msncallbacks::gotFriendlyName");
631     if(!friendlyname.empty())
632 	mhook.friendlynicks[conf->getourid(msn).nickname] = friendlyname;
633 }
634 
gotBuddyListInfo(MSN::NotificationServerConnection * conn,MSN::ListSyncInfo * data)635 void msncallbacks::gotBuddyListInfo(MSN::NotificationServerConnection * conn, MSN::ListSyncInfo * data) {
636     ::log("msncallbacks::gotBuddyListInfo");
637     imcontact ic;
638     bool found;
639 
640     mhook.readinfo = true;
641 
642     std::map<int, MSN::Group>::iterator ig;
643 
644     for(ig = data->groups.begin(); ig != data->groups.end(); ig++) {
645 	mhook.mgroups[ig->second.groupID] = ig->second.name;
646     }
647 
648     std::list<MSN::Buddy> &lst = data->forwardList;
649     std::list<MSN::Buddy>::iterator i;
650 
651     for(i = lst.begin(); i != lst.end(); i++) {
652 	int gid = 0;
653 	if(!i->groups.empty()) gid = (*i->groups.begin())->groupID;
654 	mhook.slst["FL"].push_back(msnbuddy(i->userName, i->friendlyName, gid));
655 
656 	ic = imcontact(nicktodisp(i->userName), msn);
657 	icqcontact *c = clist.get(ic);
658 	if(!c) c = clist.addnew(ic, false);
659 
660 	icqcontact::basicinfo bi = c->getbasicinfo();
661 	icqcontact::workinfo wi = c->getworkinfo();
662 
663 	list<MSN::Buddy::PhoneNumber>::iterator ip = i->phoneNumbers.begin();
664 	for(; ip != i->phoneNumbers.end(); ip++) {
665 	    if(ip->title == "PHH") bi.phone = ip->number; else
666 	    if(ip->title == "PHW") wi.phone = ip->number; else
667 	    if(ip->title == "PHM") bi.cellular = ip->number;
668 	}
669 
670 	c->setbasicinfo(bi);
671 	c->setworkinfo(wi);
672 
673 	for(found = false, ig = data->groups.begin(); ig != data->groups.end() && !found; ++ig) {
674 	    found = ig->second.groupID == gid;
675 	    if(found) clist.updateEntry(ic, ig->second.name);
676 	}
677     }
678 
679     for(lst = data->reverseList, i = lst.begin(); i != lst.end(); ++i)
680 	mhook.slst["RL"].push_back(msnbuddy(i->userName, i->friendlyName));
681 
682     mhook.readinfo = false;
683     mhook.flogged = true;
684 
685     mhook.setautostatus(mhook.manualstatus);
686     mhook.timer_ping = timer_current;
687     mhook.log(abstracthook::logLogged);
688     face.update();
689 }
690 
gotLatestListSerial(MSN::Connection * conn,int serial)691 void msncallbacks::gotLatestListSerial(MSN::Connection * conn, int serial) {
692     ::log("msncallbacks::gotLatestListSerial");
693 }
694 
gotGTC(MSN::Connection * conn,char c)695 void msncallbacks::gotGTC(MSN::Connection * conn, char c) {
696     ::log("msncallbacks::gotGTC");
697 }
698 
gotBLP(MSN::Connection * conn,char c)699 void msncallbacks::gotBLP(MSN::Connection * conn, char c) {
700     ::log("msncallbacks::gotBLP");
701 }
702 
gotNewReverseListEntry(MSN::Connection * conn,MSN::Passport buddy,std::string friendlyname)703 void msncallbacks::gotNewReverseListEntry(MSN::Connection * conn, MSN::Passport buddy, std::string friendlyname) {
704     ::log("msncallbacks::gotNewReverseListEntry");
705 
706     try {
707 	mhook.conn.addToList("AL", buddy);
708     } catch(...) {
709     }
710 
711     imcontact ic(nicktodisp(buddy), msn);
712     mhook.checkinlist(ic);
713     em.store(imnotification(ic, _("The user has added you to his/her contact list")));
714 }
715 
addedListEntry(MSN::Connection * conn,string lst,MSN::Passport buddy,int groupID)716 void msncallbacks::addedListEntry(MSN::Connection * conn, string lst, MSN::Passport buddy, int groupID) {
717     ::log("msncallbacks::addedListEntry");
718     mhook.slst[lst].push_back(msnbuddy(buddy, "", groupID));
719 }
720 
removedListEntry(MSN::Connection * conn,string lst,MSN::Passport buddy,int groupID)721 void msncallbacks::removedListEntry(MSN::Connection * conn, string lst, MSN::Passport buddy, int groupID) {
722     ::log("msncallbacks::removedListEntry");
723     vector<msnbuddy>::iterator i = mhook.slst[lst].begin();
724     while(i != mhook.slst[lst].end()) {
725 	if(i->nick == buddy) {
726 	    mhook.slst[lst].erase(i);
727 	    i = mhook.slst[lst].begin();
728 	} else {
729 	    ++i;
730 	}
731     }
732 }
733 
showError(MSN::Connection * conn,string msg)734 void msncallbacks::showError(MSN::Connection * conn, string msg) {
735     ::log(msg);
736 }
737 
buddyChangedStatus(MSN::Connection * conn,MSN::Passport buddy,string friendlyname,MSN::BuddyStatus state)738 void msncallbacks::buddyChangedStatus(MSN::Connection * conn, MSN::Passport buddy, string friendlyname, MSN::BuddyStatus state) {
739     ::log("msncallbacks::buddyChangedStatus");
740     mhook.statusupdate(buddy, friendlyname, buddy2stat(state));
741 }
742 
buddyOffline(MSN::Connection * conn,MSN::Passport buddy)743 void msncallbacks::buddyOffline(MSN::Connection * conn, MSN::Passport buddy) {
744     ::log("msncallbacks::buddyOffline");
745     mhook.statusupdate(buddy, "", offline);
746 }
747 
gotSwitchboard(MSN::SwitchboardServerConnection * conn,const void * tag)748 void msncallbacks::gotSwitchboard(MSN::SwitchboardServerConnection * conn, const void * tag) {
749     ::log("msncallbacks::gotSwitchboard");
750 
751     if(tag) {
752 	const msnhook::qevent *ctx = static_cast<const msnhook::qevent *>(tag);
753 	conn->inviteUser(ctx->nick);
754     }
755 }
756 
buddyJoinedConversation(MSN::SwitchboardServerConnection * conn,MSN::Passport buddy,std::string friendlyname,int is_initial)757 void msncallbacks::buddyJoinedConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, int is_initial) {
758     ::log("msncallbacks::buddyJoinedConversation");
759     if(conn->auth.tag) {
760 	const msnhook::qevent *ctx = static_cast<const msnhook::qevent *>(conn->auth.tag);
761 	mhook.lconn[ctx->nick] = conn;
762 	mhook.sendmsn(conn, ctx);
763 	conn->auth.tag = 0;
764     }
765 }
766 
buddyLeftConversation(MSN::SwitchboardServerConnection * conn,MSN::Passport buddy)767 void msncallbacks::buddyLeftConversation(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy) {
768     ::log("msncallbacks::buddyLeftConversation");
769 }
770 
gotInstantMessage(MSN::SwitchboardServerConnection * conn,MSN::Passport buddy,std::string friendlyname,MSN::Message * msg)771 void msncallbacks::gotInstantMessage(MSN::SwitchboardServerConnection * conn, MSN::Passport buddy, std::string friendlyname, MSN::Message * msg) {
772     ::log("msncallbacks::gotInstantMessage");
773     imcontact ic(nicktodisp(buddy), msn);
774 
775     mhook.checkinlist(ic);
776 
777     string text = mhook.rusconv("uk", msg->getBody());
778     em.store(immessage(ic, imevent::incoming, text));
779 }
780 
failedSendingMessage(MSN::Connection * conn)781 void msncallbacks::failedSendingMessage(MSN::Connection * conn) {
782     ::log("msncallbacks::failedSendingMessage");
783 }
784 
buddyTyping(MSN::Connection * conn,MSN::Passport buddy,std::string friendlyname)785 void msncallbacks::buddyTyping(MSN::Connection * conn, MSN::Passport buddy, std::string friendlyname) {
786     ::log("msncallbacks::buddyTyping");
787     icqcontact *c = clist.get(imcontact(nicktodisp(buddy), msn));
788     if(c) c->setlasttyping(timer_current);
789 }
790 
gotInitialEmailNotification(MSN::Connection * conn,int unread_inbox,int unread_folders)791 void msncallbacks::gotInitialEmailNotification(MSN::Connection * conn, int unread_inbox, int unread_folders) {
792     ::log("msncallbacks::gotInitialEmailNotification");
793     face.log(_("+ [msn] unread e-mail: %d in inbox, %d in folders"),
794 	unread_inbox, unread_folders);
795 }
796 
gotNewEmailNotification(MSN::Connection * conn,string from,string subject)797 void msncallbacks::gotNewEmailNotification(MSN::Connection * conn, string from, string subject) {
798     ::log("msncallbacks::gotNewEmailNotification");
799     face.log(_("+ [msn] e-mail from %s, %s"), from.c_str(), subject.c_str());
800     clist.get(contactroot)->playsound(imevent::email);
801 }
802 
gotFileTransferInvitation(MSN::Connection * conn,MSN::Passport buddy,std::string friendlyname,MSN::FileTransferInvitation * inv)803 void msncallbacks::gotFileTransferInvitation(MSN::Connection * conn, MSN::Passport buddy, std::string friendlyname, MSN::FileTransferInvitation * inv) {
804     ::log("msncallbacks::gotFileTransferInvitation");
805     if(!mhook.fcapabs.count(hookcapab::files))
806 	return;
807 
808     imfile::record r;
809     r.fname = inv->fileName;
810     r.size = inv->fileSize;
811 
812     imcontact ic(nicktodisp(buddy), msn);
813     mhook.checkinlist(ic);
814 
815     imfile fr(ic, imevent::incoming, "", vector<imfile::record>(1, r));
816 
817     mhook.transferinfo[fr].first = inv;
818     em.store(fr);
819 
820     face.transferupdate(inv->fileName, fr, icqface::tsInit, inv->fileSize, 0);
821 }
822 
fileTransferProgress(MSN::FileTransferInvitation * inv,string status,unsigned long recv,unsigned long total)823 void msncallbacks::fileTransferProgress(MSN::FileTransferInvitation * inv, string status, unsigned long recv, unsigned long total) {
824     ::log("msncallbacks::fileTransferProgress");
825     imfile fr;
826 
827     if(mhook.getfevent(inv, fr)) {
828 	face.transferupdate(fr.getfiles().begin()->fname, fr,
829 	    icqface::tsProgress, total, recv);
830     }
831 }
832 
fileTransferFailed(MSN::FileTransferInvitation * inv,int error,string message)833 void msncallbacks::fileTransferFailed(MSN::FileTransferInvitation * inv, int error, string message) {
834     ::log("msncallbacks::fileTransferFailed");
835     imfile fr;
836 
837     if(mhook.getfevent(inv, fr)) {
838 	face.transferupdate(fr.getfiles().begin()->fname, fr, icqface::tsError, 0, 0);
839 	mhook.transferinfo.erase(fr);
840     }
841 }
842 
fileTransferSucceeded(MSN::FileTransferInvitation * inv)843 void msncallbacks::fileTransferSucceeded(MSN::FileTransferInvitation * inv) {
844     ::log("msncallbacks::fileTransferSucceeded");
845     imfile fr;
846 
847     if(mhook.getfevent(inv, fr)) {
848 	face.transferupdate(fr.getfiles().begin()->fname, fr, icqface::tsFinish, 0, 0);
849 	mhook.transferinfo.erase(fr);
850     }
851 }
852 
gotNewConnection(MSN::Connection * conn)853 void msncallbacks::gotNewConnection(MSN::Connection * conn) {
854     ::log("msncallbacks::gotNewConnection");
855     if(dynamic_cast<MSN::NotificationServerConnection *>(conn))
856 	dynamic_cast<MSN::NotificationServerConnection *>(conn)->synchronizeLists();
857 }
858 
closingConnection(MSN::Connection * conn)859 void msncallbacks::closingConnection(MSN::Connection * conn) {
860     ::log("msncallbacks::closingConnection");
861 
862     MSN::SwitchboardServerConnection *swc;
863 
864     if(swc = dynamic_cast<MSN::SwitchboardServerConnection *>(conn)) {
865 	map<string, MSN::SwitchboardServerConnection *>::const_iterator ic = mhook.lconn.begin();
866 	while(ic != mhook.lconn.end()) {
867 	    if(swc == ic->second) {
868 		mhook.lconn.erase(ic->first);
869 		break;
870 	    }
871 	    ++ic;
872 	}
873 
874     } else if(conn == &mhook.conn) {
875 	if(!mhook.destroying) {
876 	    mhook.rfds.clear();
877 	    mhook.wfds.clear();
878 	    mhook.lconn.clear();
879 	    mhook.slst.clear();
880 
881 	    if(mhook.logged()) {
882 		logger.putourstatus(msn, mhook.getstatus(), mhook.ourstatus = offline);
883 		clist.setoffline(msn);
884 
885 		mhook.fonline = false;
886 		mhook.log(abstracthook::logDisconnected);
887 
888 		face.update();
889 	    }
890 	}
891     }
892 
893     unregisterSocket(conn->sock);
894 }
895 
changedStatus(MSN::Connection * conn,MSN::BuddyStatus state)896 void msncallbacks::changedStatus(MSN::Connection * conn, MSN::BuddyStatus state) {
897     ::log("msncallbacks::changedStatus");
898 }
899 
connectToServer(string server,int port,bool * connected)900 int msncallbacks::connectToServer(string server, int port, bool *connected) {
901     ::log("msncallbacks::connectToServer");
902     struct sockaddr_in sa;
903     struct hostent *hp;
904     int a, s;
905     string msgerr = _("+ [msn] cannot connect: ");
906 
907     hp = gethostbyname(server.c_str());
908     if(!hp) {
909 	face.log(msgerr + _("could not resolve hostname"));
910 	errno = ECONNREFUSED;
911 	return -1;
912     }
913 
914     memset(&sa, 0, sizeof(sa));
915     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
916     sa.sin_family = hp->h_addrtype;
917     sa.sin_port = htons((u_short) port);
918 
919     if((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
920 	return -1;
921 
922     int oldfdArgs = fcntl(s, F_GETFL, 0);
923     fcntl(s, F_SETFL, oldfdArgs | O_NONBLOCK);
924 
925     *connected = false;
926 
927     if(cw_connect(s, (struct sockaddr *) &sa, sizeof sa, 0) < 0) {
928 	if(errno != EINPROGRESS) {
929 	    face.log(msgerr + _("verify the hostname and port"));
930 	    close(s);
931 	    return -1;
932 	}
933     }
934 
935     return s;
936 }
937 
listenOnPort(int port)938 int msncallbacks::listenOnPort(int port) {
939     ::log("msncallbacks::listenOnPort");
940     int s;
941     struct sockaddr_in addr;
942 
943     if((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
944 	return -1;
945 
946     memset(&addr, 0, sizeof(addr));
947     addr.sin_family = AF_INET;
948     addr.sin_port = htons(port);
949 
950     if(bind(s, (sockaddr *) &addr, sizeof(addr)) < 0 || listen(s, 1) < 0) {
951 	close(s);
952 	return -1;
953     }
954 
955     return s;
956 }
957 
getOurIP()958 string msncallbacks::getOurIP() {
959     ::log("msncallbacks::getOurIP");
960     struct hostent *hn;
961     char buf2[1024];
962 
963     gethostname(buf2, 1024);
964     hn = gethostbyname(buf2);
965 
966     return inet_ntoa(*((struct in_addr*) hn->h_addr));
967 }
968 
getSecureHTTPProxy()969 string msncallbacks::getSecureHTTPProxy() {
970     ::log("msncallbacks::getSecureHTTPProxy");
971     return "";
972 }
973 
addedGroup(MSN::Connection * conn,string groupName,int groupID)974 void msncallbacks::addedGroup(MSN::Connection * conn, string groupName, int groupID) {
975     ::log("msncallbacks::addedGroup");
976     int i;
977     icqcontact *c;
978 
979     mhook.mgroups[groupID] = groupName;
980 
981     vector<icqgroup>::const_iterator ig = groups.begin();
982 
983     while(ig != groups.end()) {
984 	if(ig->getname() == groupName) {
985 	    for(i = 0; i < clist.count; i++) {
986 		c = (icqcontact *) clist.at(i);
987 		if(c->getgroupid() == ig->getid())
988 		    mhook.sendnewuser(c->getdesc());
989 	    }
990 	    break;
991 	}
992 	++ig;
993     }
994 }
995 
renamedGroup(MSN::Connection * conn,int groupID,string newGroupName)996 void msncallbacks::renamedGroup(MSN::Connection * conn, int groupID, string newGroupName) {
997     ::log("msncallbacks::renamedGroup");
998     mhook.mgroups[groupID] = newGroupName;
999 }
1000 
removedGroup(MSN::Connection * conn,int groupID)1001 void msncallbacks::removedGroup(MSN::Connection * conn, int groupID) {
1002     ::log("msncallbacks::removedGroup");
1003     mhook.mgroups.erase(groupID);
1004 }
1005 
1006 #endif
1007