connwrap - initialize gnutls session in cw_connect
[centerim.git] / src / hooks / ljhook.cc
blobd4269c75553821e3af3058696ea59d1bf46348e0
1 /*
3 * centerim livejournal protocol handling class (sick)
4 * $Id: ljhook.cc,v 1.28 2004/11/11 13:42:05 konst Exp $
6 * Copyright (C) 2003-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 "icqcommon.h"
27 #ifdef BUILD_LJ
29 #include <centerim.h>
30 #include "ljhook.h"
31 #include "rsshook.h"
32 #include "icqface.h"
33 #include "eventmanager.h"
34 #include "icqcontacts.h"
36 #include <sys/utsname.h>
38 ljhook lhook;
40 #define PERIOD_FRIENDS 3600
42 #define NOTIFBUF 512
44 ljhook::ljhook(): abstracthook(livejournal), fonline(false), sdest(0) {
45 fcapabs.insert(hookcapab::nochat);
48 ljhook::~ljhook() {
51 void ljhook::init() {
52 manualstatus = conf->getstatus(proto);
53 httpcli.messageack.connect(this, &ljhook::messageack_cb);
54 httpcli.socket.connect(this, &ljhook::socket_cb);
56 if(conf->getdebug())
57 httpcli.logger.connect(this, &ljhook::logger_cb);
59 journals = vector<string>(1, conf->getourid(proto).nickname);
62 void ljhook::connect() {
63 icqconf::imaccount acc = conf->getourid(proto);
65 baseurl = acc.server + ":" + i2str(acc.port) + "/interface/flat";
66 md5pass = getmd5(acc.password);
68 log(logConnecting);
70 httpcli.setProxyServerHost(conf->gethttpproxyhost());
71 httpcli.setProxyServerPort(conf->gethttpproxyport());
73 if(!conf->gethttpproxyuser().empty()) {
74 httpcli.setProxyServerUser(conf->gethttpproxyuser());
75 httpcli.setProxyServerPasswd(conf->gethttpproxypasswd());
78 HTTPRequestEvent *ev = new HTTPRequestEvent(baseurl, HTTPRequestEvent::POST);
79 ev->addParam("mode", "login");
80 ev->addParam("user", username = acc.nickname);
81 ev->addParam("hpassword", md5pass);
82 ev->addParam("getmoods", "1");
83 ev->addParam("getpickws", "1");
85 struct utsname un;
86 string clientver;
88 if(!uname(&un)) clientver = un.sysname;
89 else clientver = "GNU";
91 clientver += string("-") + PACKAGE + "/" + centerim::version;
93 ev->addParam("clientversion", clientver);
95 self = imcontact(username + "@lj", livejournal);
97 fonline = true;
99 sent[ev] = reqLogin;
100 httpcli.SendEvent(ev);
102 moods = vector<string>(1, "");
103 pictures = vector<string>(1, "");
106 void ljhook::disconnect() {
107 if(fonline) {
108 fonline = false;
109 log(logDisconnected);
110 if(flogged) {
111 flogged = false;
112 icqcontact *c = clist.get(self);
113 if(c) c->setstatus(offline);
118 void ljhook::exectimers() {
119 if(logged())
120 if(timer_current-timer_getfriends >= PERIOD_FRIENDS) {
121 requestfriends();
122 timer_getfriends = timer_current;
126 void ljhook::main() {
127 vector<int>::iterator i;
128 struct timeval tv;
129 int hsock;
130 fd_set rs, ws, es;
132 FD_ZERO(&rs);
133 FD_ZERO(&ws);
134 FD_ZERO(&es);
136 tv.tv_sec = tv.tv_usec = 0;
137 hsock = 0;
139 for(i = rfds.begin(); i != rfds.end(); ++i) {
140 FD_SET(*i, &rs);
141 hsock = max(hsock, *i);
144 for(i = wfds.begin(); i != wfds.end(); ++i) {
145 FD_SET(*i, &ws);
146 hsock = max(hsock, *i);
149 for(i = efds.begin(); i != efds.end(); ++i) {
150 FD_SET(*i, &es);
151 hsock = max(hsock, *i);
154 if(select(hsock+1, &rs, &ws, &es, &tv) > 0) {
155 for(i = rfds.begin(); i != rfds.end(); ++i) {
156 if(FD_ISSET(*i, &rs)) {
157 httpcli.socket_cb(*i, SocketEvent::READ);
158 break;
162 for(i = wfds.begin(); i != wfds.end(); ++i) {
163 if(FD_ISSET(*i, &ws)) {
164 httpcli.socket_cb(*i, SocketEvent::WRITE);
165 break;
169 for(i = efds.begin(); i != efds.end(); ++i) {
170 if(FD_ISSET(*i, &es)) {
171 httpcli.socket_cb(*i, SocketEvent::EXCEPTION);
172 break;
178 void ljhook::getsockets(fd_set &rs, fd_set &ws, fd_set &es, int &hsocket) const {
179 vector<int>::const_iterator i;
181 for(i = rfds.begin(); i != rfds.end(); ++i) {
182 hsocket = max(hsocket, *i);
183 FD_SET(*i, &rs);
186 for(i = wfds.begin(); i != wfds.end(); ++i) {
187 hsocket = max(hsocket, *i);
188 FD_SET(*i, &ws);
191 for(i = efds.begin(); i != efds.end(); ++i) {
192 hsocket = max(hsocket, *i);
193 FD_SET(*i, &es);
197 bool ljhook::isoursocket(fd_set &rs, fd_set &ws, fd_set &es) const {
198 vector<int>::const_iterator i;
200 for(i = rfds.begin(); i != rfds.end(); ++i)
201 if(FD_ISSET(*i, &rs))
202 return true;
204 for(i = wfds.begin(); i != wfds.end(); ++i)
205 if(FD_ISSET(*i, &ws))
206 return true;
208 for(i = efds.begin(); i != efds.end(); ++i)
209 if(FD_ISSET(*i, &es))
210 return true;
212 return false;
215 bool ljhook::online() const {
216 return fonline;
219 bool ljhook::logged() const {
220 return fonline && flogged;
223 bool ljhook::isconnecting() const {
224 return fonline && !flogged;
227 bool ljhook::enabled() const {
228 return true;
231 bool ljhook::send(const imevent &asev) {
232 if(!logged()) return false;
234 bool ret = false;
235 imevent *sev;
237 if(asev.gettype() == imevent::xml) sev = asev.getevent(); else {
238 sev = new imxmlevent(asev.getcontact(), asev.getdirection(), asev.gettext());
239 static_cast<imxmlevent *>(sev)->setfield("_eventkind", "posting");
243 if(sev->gettype() == imevent::xml) {
244 const imxmlevent *m = static_cast<const imxmlevent *> (sev);
245 HTTPRequestEvent *ev = 0;
247 if(m->getfield("_eventkind") == "posting") {
248 ev = new HTTPRequestEvent(baseurl, HTTPRequestEvent::POST);
250 ev->addParam("mode", "postevent");
251 ev->addParam("user", username);
252 ev->addParam("hpassword", md5pass);
253 ev->addParam("event", rusconv("ku", m->gettext()));
254 ev->addParam("ver", "1");
256 time_t t = m->gettimestamp();
257 struct tm *tm = localtime(&t);
259 ev->addParam("year", i2str(tm->tm_year+1900));
260 ev->addParam("mon", i2str(tm->tm_mon+1));
261 ev->addParam("day", i2str(tm->tm_mday));
262 ev->addParam("hour", i2str(tm->tm_hour));
263 ev->addParam("min", i2str(tm->tm_min));
265 ev->addParam("lineendings", "unix");
266 ev->addParam("security", m->getfield("security"));
267 ev->addParam("allowmask", "0");
269 if(!m->field_empty("journal")) ev->addParam("usejournal", m->getfield("journal"));
270 if(!m->field_empty("subject")) ev->addParam("subject", rusconv("ku", m->getfield("subject")));
271 if(!m->field_empty("mood")) ev->addParam("prop_current_mood", rusconv("ku", m->getfield("mood")));
272 if(!m->field_empty("music")) ev->addParam("prop_current_music", rusconv("ku", m->getfield("music")));
273 if(!m->field_empty("taglist")) ev->addParam("prop_taglist", rusconv("ku", m->getfield("taglist")));
274 if(!m->field_empty("picture")) ev->addParam("prop_picture_keyword", m->getfield("picture"));
276 if(!m->field_empty("preformatted")) ev->addParam("prop_opt_preformatted", "1");
277 if(!m->field_empty("nocomments")) ev->addParam("prop_opt_nocomments", "1");
278 if(!m->field_empty("backdated")) ev->addParam("prop_opt_backdated", "1");
279 if(!m->field_empty("noemail")) ev->addParam("prop_opt_noemail", "1");
281 sent[ev] = reqPost;
283 } else if(m->getfield("_eventkind") == "comment") {
284 icqconf::imaccount acc = conf->getourid(proto);
286 string journal = clist.get(sev->getcontact())->getnick();
287 journal.erase(journal.find("@"));
289 ev = new HTTPRequestEvent(acc.server + ":" + i2str(acc.port) + "/talkpost_do.bml", HTTPRequestEvent::POST);
291 ev->addParam("parenttalkid", "0");
292 ev->addParam("itemid", m->getfield("replyto"));
293 ev->addParam("journal", journal);
294 ev->addParam("usertype", "user");
295 ev->addParam("userpost", acc.nickname);
296 ev->addParam("password", acc.password);
297 ev->addParam("do_login", "0");
298 ev->addParam("subject", "");
299 ev->addParam("body", rusconv("ku", sev->gettext()));
300 ev->addParam("subjecticon", "none");
301 ev->addParam("prop_opt_preformatted", "1");
302 ev->addParam("submitpost", "1");
303 ev->addParam("do_spellcheck", "0");
305 sent[ev] = reqPostComment;
308 if(ev) {
309 httpcli.SendEvent(ev);
310 ret = true;
314 delete sev;
315 return ret;
318 void ljhook::sendnewuser(const imcontact &ic) {
319 int npos;
320 icqcontact *c;
322 if(logged())
323 if(ic.pname == rss)
324 if(c = clist.get(ic))
325 if((npos = c->getnick().find("@lj")) != -1) {
326 HTTPRequestEvent *ev = new HTTPRequestEvent(baseurl, HTTPRequestEvent::POST);
328 ev->addParam("mode", "editfriends");
329 ev->addParam("user", username);
330 ev->addParam("hpassword", md5pass);
332 ev->addParam("editfriend_add_1_user", c->getnick().substr(0, npos));
334 sent[ev] = reqAddFriend;
335 httpcli.SendEvent(ev);
339 void ljhook::removeuser(const imcontact &ic) {
340 int npos;
341 icqcontact *c;
343 if(logged())
344 if(conf->getourid(proto).additional["importfriends"] == "1")
345 if(c = clist.get(ic))
346 if((npos = c->getnick().find("@lj")) != -1) {
347 string nick = c->getnick().substr(0, npos);
349 if(c->getworkinfo().homepage == getfeedurl(nick)) {
350 HTTPRequestEvent *ev = new HTTPRequestEvent(baseurl, HTTPRequestEvent::POST);
352 ev->addParam("mode", "editfriends");
353 ev->addParam("user", username);
354 ev->addParam("hpassword", md5pass);
356 ev->addParam((string) "editfriend_delete_" + nick, "1");
358 sent[ev] = reqDelFriend;
359 httpcli.SendEvent(ev);
364 void ljhook::setautostatus(imstatus st) {
365 if(st == offline) {
366 if(getstatus() != offline) disconnect();
368 } else {
369 if(getstatus() == offline) connect();
374 imstatus ljhook::getstatus() const {
375 return flogged ? available : offline;
378 void ljhook::requestinfo(const imcontact &ic) {
379 int npos;
380 icqcontact *c = clist.get(ic);
382 if(c) {
383 icqcontact::moreinfo m = c->getmoreinfo();
384 if((npos = c->getnick().find("@lj")) != -1) {
385 m.homepage = getfeedurl(c->getnick().substr(0, npos));
386 c->setmoreinfo(m);
391 void ljhook::lookup(const imsearchparams &params, verticalmenu &dest) {
392 while(!foundguys.empty()) {
393 delete foundguys.back();
394 foundguys.pop_back();
397 if(params.reverse) {
398 icqcontact *c = clist.get(self);
400 if(c) {
401 string lst = c->getbasicinfo().zip, buf;
402 while(!(buf = getword(lst)).empty()) {
403 imcontact ic(0, rss);
404 ic.nickname = getfeedurl(buf);
405 c = new icqcontact(ic);
407 c->setnick(buf + "@lj");
408 c->setdispnick(c->getnick());
409 c->excludefromlist();
411 dest.additem(conf->getcolor(cp_clist_rss), c, (string) " " + c->getnick());
412 foundguys.push_back(c);
415 face.findready();
416 face.log(_("+ [lj] user lookup finished"));
417 dest.redraw();
420 } else {
421 HTTPRequestEvent *ev = new HTTPRequestEvent(getfeedurl(params.nick));
423 sent[ev] = reqLookup;
424 httpcli.SendEvent(ev);
426 sdest = &dest;
427 lookfor = params.nick;
431 void ljhook::stoplookup() {
432 sdest = 0;
435 void ljhook::requestawaymsg(const imcontact &c) {
438 void ljhook::requestversion(const imcontact &c) {
441 void ljhook::ping(const imcontact &c) {
444 void ljhook::ouridchanged(const icqconf::imaccount &ia) {
447 void ljhook::updatecontact(icqcontact *c) {
450 // ----------------------------------------------------------------------------
452 void ljhook::requestfriends() {
453 HTTPRequestEvent *ev = new HTTPRequestEvent(baseurl, HTTPRequestEvent::POST);
455 ev->addParam("mode", "getfriends");
456 ev->addParam("user", username);
457 ev->addParam("hpassword", md5pass);
458 ev->addParam("includefriendof", "1");
459 ev->addParam("includebdays", "1");
461 sent[ev] = reqGetFriends;
462 httpcli.SendEvent(ev);
465 // ----------------------------------------------------------------------------
467 void ljhook::socket_cb(SocketEvent *ev) {
468 vector<int>::iterator i;
470 if(dynamic_cast<AddSocketHandleEvent*>(ev)) {
471 AddSocketHandleEvent *cev = dynamic_cast<AddSocketHandleEvent*>(ev);
473 if(cev->isRead()) rfds.push_back(cev->getSocketHandle());
474 if(cev->isWrite()) wfds.push_back(cev->getSocketHandle());
475 if(cev->isException()) efds.push_back(cev->getSocketHandle());
477 } else if(dynamic_cast<RemoveSocketHandleEvent*>(ev)) {
478 RemoveSocketHandleEvent *cev = dynamic_cast<RemoveSocketHandleEvent*>(ev);
480 i = find(rfds.begin(), rfds.end(), cev->getSocketHandle());
481 if(i != rfds.end())
482 rfds.erase(i);
484 i = find(wfds.begin(), wfds.end(), cev->getSocketHandle());
485 if(i != wfds.end())
486 wfds.erase(i);
488 i = find(efds.begin(), efds.end(), cev->getSocketHandle());
489 if(i != efds.end())
490 efds.erase(i);
494 void ljhook::messageack_cb(MessageEvent *ev) {
495 HTTPRequestEvent *rev = dynamic_cast<HTTPRequestEvent*>(ev);
497 if(!rev) return;
499 map<HTTPRequestEvent *, RequestType>::iterator ie = sent.find(rev);
501 if(ie == sent.end()) return;
503 int npos, count, i, k;
504 string content = rev->getContent(), pname;
505 map<string, string> params;
506 icqcontact *c;
508 if(ie->second != reqLookup) {
509 if(!ev->isDelivered() || content.empty()) {
510 pname = rev->getHTTPResp();
511 if(pname.empty()) pname = _("cannot connect");
512 face.log(_("+ [lj] HTTP failed: %s"), pname.c_str());
513 disconnect();
514 return;
517 while((npos = content.find("\r")) != -1)
518 content.erase(npos, 1);
520 while((npos = content.find("\n")) != -1) {
521 pname = content.substr(0, npos);
522 content.erase(0, npos+1);
524 if((npos = content.find("\n")) == -1)
525 npos = content.size();
527 params[pname] = content.substr(0, npos);
529 if(npos != content.size()) content.erase(0, npos+1);
530 else content = "";
534 if(isconnecting() && ie->second == reqLogin) {
535 fonline = true;
537 if(params["success"] == "OK") {
538 flogged = true;
539 log(logLogged);
540 face.update();
541 setautostatus(manualstatus);
543 if(!(c = clist.get(self))) {
544 c = clist.addnew(self, false);
545 requestinfo(self);
548 count = atoi(params["mood_count"].c_str());
549 for(i = 1; i < count; i++)
550 moods.push_back(params[(string) "mood_" + i2str(i) + "_name"]);
552 count = atoi(params["pickw_count"].c_str());
553 for(i = 1; i < count; i++)
554 pictures.push_back(params[(string) "pickw_" + i2str(i)]);
556 sort(moods.begin()+1, moods.end());
558 icqcontact::basicinfo bi = c->getbasicinfo();
559 c->setstatus(available);
561 string buf;
562 friendof.clear();
563 while(!(buf = getword(bi.zip)).empty())
564 friendof.push_back(buf);
566 if(!params["message"].empty())
567 em.store(imnotification(self, _("Message from the server: ") + params["message"]));
569 timer_getfriends = 0;
571 } else {
572 face.log(_("+ [lj] login failed: %s"), params["errmsg"].c_str());
574 } else if(ie->second == reqPost) {
575 if(params["success"] == "OK") {
576 face.log(_("+ [lj] posted successully, the id is %s"), params["itemid"].c_str());
577 } else {
578 face.log(_("+ [lj] post error: %s"), params["errmsg"].c_str());
581 } else if(ie->second == reqPostComment) {
583 } else if(ie->second == reqGetFriends) {
584 if(params["success"] == "OK") {
585 journals = vector<string>(1, username);
587 if(conf->getourid(proto).additional["importfriends"] == "1") {
588 count = atoi(params["friend_count"].c_str());
589 } else {
590 count = 0;
593 string username, name, birthday, bd;
594 icqcontact::moreinfo mi;
595 icqcontact::basicinfo bi;
597 for(i = 1; i <= count; i++) {
598 username = params[(string) "friend_" + i2str(i) + "_user"];
599 name = rusconv("uk", params[(string) "friend_" + i2str(i) + "_name"]);
600 birthday = params[(string) "friend_" + i2str(i) + "_birthday"];
602 bool found = false;
604 for(k = 0; k < clist.count && !found; k++) {
605 c = (icqcontact *) clist.at(k);
606 if(c->getdesc().pname == rss) {
607 icqcontact::workinfo wi = c->getworkinfo();
609 if(wi.homepage == getoldfeedurl(username)) {
610 wi.homepage = getfeedurl(username);
611 c->setworkinfo(wi);
614 found = (wi.homepage == getfeedurl(username));
618 if(!found)
619 if(c = clist.addnew(imcontact(0, rss), false)) {
620 icqcontact::workinfo wi = c->getworkinfo();
621 wi.homepage = getfeedurl(username);
622 c->setworkinfo(wi);
624 icqcontact::moreinfo mi = c->getmoreinfo();
625 mi.checkfreq = 120;
626 c->setmoreinfo(mi);
628 c->setnick(username + "@lj");
629 c->setdispnick(c->getnick());
630 c->save();
633 bi = c->getbasicinfo();
634 mi = c->getmoreinfo();
635 bi.email = name;
637 if(!birthday.empty()) {
638 k = 0;
640 while(!(bd = getrword(birthday, "-")).empty())
641 switch(k++) {
642 case 0: mi.birth_day = atoi(bd.c_str()); break;
643 case 1: mi.birth_month = atoi(bd.c_str()); break;
644 case 2: mi.birth_year = atoi(bd.c_str()); break;
649 c->setmoreinfo(mi);
650 c->setbasicinfo(bi);
652 if(params[(string) "friend_" + i2str(i) + "_type"] == "community")
653 journals.push_back(username);
656 count = atoi(params["friendof_count"].c_str());
657 bool foempty = friendof.empty();
659 map<string, string> nfriendof;
660 map<string, string>::const_iterator in;
661 vector<string>::iterator il;
662 char buf[NOTIFBUF];
664 for(i = 1; i <= count; i++) {
665 username = params[(string) "friendof_" + i2str(i) + "_user"];
666 name = rusconv("uk", params[(string) "friendof_" + i2str(i) + "_name"]);
667 nfriendof[username] = name;
670 for(in = nfriendof.begin(); in != nfriendof.end(); ++in)
671 if(find(friendof.begin(), friendof.end(), in->first) == friendof.end()) {
672 friendof.push_back(in->first);
674 if(!foempty) {
675 bd = (string) "http://" + conf->getourid(proto).server + "/users/" + in->first;
677 snprintf(buf, NOTIFBUF, _("The user %s (%s) has added you to his/her friend list\n\nJournal address: %s"),
678 in->first.c_str(), in->second.c_str(), bd.c_str());
680 em.store(imnotification(self, buf));
684 for(il = friendof.begin(); il != friendof.end(); ) {
685 if(nfriendof.find(*il) == nfriendof.end()) {
686 bd = (string) "http://" + conf->getourid(proto).server + "/users/" + *il;
687 snprintf(buf, NOTIFBUF, _("The user %s has removed you from his/her friend list\n\nJournal address: %s"),
688 il->c_str(), bd.c_str());
689 em.store(imnotification(self, buf));
690 friendof.erase(il);
691 il = friendof.begin();
692 } else {
693 ++il;
697 if(c = clist.get(self)) {
698 icqcontact::basicinfo bi = c->getbasicinfo();
699 vector<string>::const_iterator ifo = friendof.begin();
700 bi.zip = "";
702 while(ifo != friendof.end()) {
703 bi.zip += *ifo + " ";
704 ++ifo;
707 c->setbasicinfo(bi);
711 } else if(ie->second == reqDelFriend) {
712 if(params["success"] != "OK") {
713 face.log(_("+ [lj] error deleting friend"));
714 } else {
715 face.log(_("+ [lj] the user has been removed from your friend list"));
718 } else if(ie->second == reqAddFriend) {
719 if(params["success"] != "OK" || !atoi(params["friends_added"].c_str())) {
720 face.log(_("+ [lj] couldn't add friend"));
721 } else {
722 face.log(_("+ [lj] %s was added to friends"), params["friend_1_user"].c_str());
725 } else if(ie->second == reqLookup && sdest) {
726 if(ev->isDelivered() && !content.empty()) {
727 imcontact ic(0, rss);
728 ic.nickname = rev->getURL();
729 c = new icqcontact(ic);
731 c->setnick(lookfor + "@lj");
732 c->setdispnick(c->getnick());
733 c->excludefromlist();
734 rsshook::parsedocument(rev, c);
736 sdest->additem(conf->getcolor(cp_clist_rss), c, (string) " " + c->getnick());
737 foundguys.push_back(c);
740 face.findready();
741 face.log(_("+ [lj] user lookup finished"));
742 sdest->redraw();
743 sdest = 0;
747 if(ie != sent.end()) sent.erase(ie);
750 void ljhook::logger_cb(LogEvent *ev) {
751 switch(ev->getType()) {
752 case LogEvent::PACKET:
753 case LogEvent::DIRECTPACKET:
754 face.log(ev->getMessage());
755 break;
757 default:
758 face.log(ev->getMessage());
759 break;
763 string ljhook::getfeedurl(const string &nick) const {
764 return (string) "http://" + conf->getourid(proto).server + "/users/" + nick + "/data/rss?auth=digest";
767 string ljhook::getoldfeedurl(const string &nick) const {
768 return (string) "http://" + conf->getourid(proto).server + "/users/" + nick + "/rss/";
771 #endif