1 /*
2 *
3 * centerim gadu-gadu protocol handling class
4 * $Id: gaduhook.cc,v 1.14 2005/07/08 09:49:17 konst Exp $
5 *
6 * Copyright (C) 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_GADU
28 
29 #include "eventmanager.h"
30 #include "gaduhook.h"
31 #include "icqface.h"
32 #include "imlogger.h"
33 #include "icqcontacts.h"
34 
35 #include "libgadu-config.h"
36 #include "libgadu.h"
37 
38 #include <netdb.h>
39 #include <arpa/inet.h>
40 #include <netinet/in.h>
41 #include <limits.h>
42 
43 #ifdef HAVE_JPEGLIB_H
44 extern "C" {
45 #include <jpeglib.h>
46 }
47 #endif
48 
49 #define PERIOD_PING  50
50 
gg2imstatus(int st)51 static imstatus gg2imstatus(int st) {
52     imstatus imst;
53 
54     switch(st) {
55 	case GG_STATUS_INVISIBLE:
56 	case GG_STATUS_INVISIBLE_DESCR:
57 	    imst = invisible;
58 	    break;
59 
60 	case GG_STATUS_BUSY:
61 	case GG_STATUS_BUSY_DESCR:
62 	    imst = occupied;
63 	    break;
64 
65 	case GG_STATUS_NOT_AVAIL:
66 	case GG_STATUS_NOT_AVAIL_DESCR:
67 	    imst = offline;
68 	    break;
69 
70 	default:
71 	    imst = available;
72 	    break;
73     }
74 
75     return imst;
76 }
77 
imstatus2gg(imstatus st,const string & desc="")78 static int imstatus2gg(imstatus st, const string &desc = "") {
79     int gst;
80 
81     switch(st) {
82 	case invisible:
83 	    gst = desc.empty() ?
84 		GG_STATUS_INVISIBLE : GG_STATUS_INVISIBLE_DESCR;
85 	    break;
86 
87 	case dontdisturb:
88 	case occupied:
89 	case notavail:
90 	case outforlunch:
91 	case away:
92 	    gst = desc.empty() ?
93 		GG_STATUS_BUSY : GG_STATUS_BUSY_DESCR;
94 	    break;
95 
96 	case freeforchat:
97 	case available:
98 	default:
99 	    gst = desc.empty() ?
100 		GG_STATUS_AVAIL : GG_STATUS_AVAIL_DESCR;
101 	    break;
102     }
103 
104     return gst;
105 }
106 
107 // ----------------------------------------------------------------------------
108 
109 gaduhook ghook;
110 
111 gg_pubdir50_t lreq = 0, ireq = 0;
112 
gaduhook()113 gaduhook::gaduhook(): abstracthook(gadu), flogged(false), sess(0) {
114     fcapabs.insert(hookcapab::changepassword);
115     fcapabs.insert(hookcapab::changenick);
116     fcapabs.insert(hookcapab::changedetails);
117     fcapabs.insert(hookcapab::setaway);
118     fcapabs.insert(hookcapab::fetchaway);
119     fcapabs.insert(hookcapab::ssl);
120 }
121 
~gaduhook()122 gaduhook::~gaduhook() {
123 }
124 
init()125 void gaduhook::init() {
126     manualstatus = conf->getstatus(proto);
127 }
128 
connect()129 void gaduhook::connect() {
130     icqconf::imaccount acc = conf->getourid(proto);
131     static struct gg_login_params lp;
132     /* TODO investigate this, auto_ptr will free with delete, but allocated by malloc */
133     static auto_ptr<char> pass(strdup(acc.password.c_str()));
134 
135     memset(&lp, 0, sizeof(lp));
136 
137     struct hostent *he = gethostbyname(acc.server.c_str());
138     struct in_addr addr;
139 
140     if(he) {
141 	memcpy((char *) &addr, he->h_addr, sizeof(addr));
142 	lp.server_addr = addr.s_addr;
143 	lp.server_port = acc.port;
144 
145 	lp.uin = acc.uin;
146 	lp.password = pass.get();
147 	lp.async = 1;
148 
149 	/* TODO investigate this, auto_ptr will free with delete, but allocated by malloc */
150 	static auto_ptr<char> descr(strdup(rusconv("kw", conf->getawaymsg(proto)).c_str()));
151 
152 	lp.status_descr = descr.get();
153 	lp.status = imstatus2gg(manualstatus, descr.get());
154 
155 	lp.tls = acc.additional["ssl"] == "1" ? 1 : 0;
156 
157 	log(logConnecting);
158 	sess = gg_login(&lp);
159 
160 	if(!sess)
161 	    face.log(_("+ [gg] connection failed"));
162 
163     } else {
164 	face.log(_("+ [gg] cannot resolve %s"), acc.server.c_str());
165 
166     }
167 }
168 
cutoff()169 void gaduhook::cutoff() {
170     if(sess) {
171 	gg_change_status(sess, GG_STATUS_NOT_AVAIL);
172 	gg_logoff(sess);
173 	gg_free_session(sess);
174 	sess = 0;
175     }
176 
177     clist.setoffline(proto);
178 }
179 
disconnect()180 void gaduhook::disconnect() {
181     cutoff();
182     log(logDisconnected);
183     logger.putourstatus(proto, getstatus(), offline);
184 }
185 
exectimers()186 void gaduhook::exectimers() {
187     if(logged()) {
188 	if(timer_current-timer_ping > PERIOD_PING) {
189 	    gg_ping(sess);
190 	    timer_ping = timer_current;
191 	}
192     }
193 }
194 
main()195 void gaduhook::main() {
196     int i;
197     fd_set rd, wd;
198     struct timeval tv;
199 
200     struct gg_event *e;
201     struct gg_notify_reply *nr;
202     string text;
203 
204     e = gg_watch_fd(sess);
205 
206     if(e) {
207 	switch(e->type) {
208 	    case GG_EVENT_CONN_SUCCESS:
209 		flogged = true;
210 		time(&timer_ping);
211 		userlistsend();
212 		log(logLogged);
213 		face.update();
214 		break;
215 
216 	    case GG_EVENT_CONN_FAILED:
217 		cutoff();
218 		face.log(_("+ [gg] connection to the server failed"));
219 		break;
220 
221 	    case GG_EVENT_DISCONNECT:
222 		disconnect();
223 		break;
224 
225 	    case GG_EVENT_MSG:
226 		if(e->event.msg.sender && e->event.msg.message) {
227 		    text = rusconv("wk", (const char *) e->event.msg.message);
228 		    em.store(immessage(imcontact(e->event.msg.sender, gadu),
229 			imevent::incoming, text, e->event.msg.time));
230 		}
231 		break;
232 
233 	    case GG_EVENT_NOTIFY:
234 	    case GG_EVENT_NOTIFY_DESCR:
235 		nr = (e->type == GG_EVENT_NOTIFY) ? e->event.notify : e->event.notify_descr.notify;
236 
237 		for(; nr->uin; nr++) {
238 		    char *desc = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : 0;
239 
240 		    usernotify(nr->uin, nr->status, desc,
241 			nr->remote_ip, nr->remote_port,
242 			nr->version);
243 		}
244 		break;
245 
246 	    case GG_EVENT_NOTIFY60:
247 		for(i = 0; e->event.notify60[i].uin; i++)
248 		    usernotify(e->event.notify60[i].uin,
249 			e->event.notify60[i].status,
250 			e->event.notify60[i].descr,
251 			e->event.notify60[i].remote_ip,
252 			e->event.notify60[i].remote_port,
253 			e->event.notify60[i].version);
254 		break;
255 
256 	    case GG_EVENT_STATUS:
257 		userstatuschange(e->event.status.uin, e->event.status.status, e->event.status.descr);
258 		break;
259 
260 	    case GG_EVENT_STATUS60:
261 		userstatuschange(e->event.status60.uin, e->event.status60.status, e->event.status60.descr);
262 		break;
263 
264 	    case GG_EVENT_ACK:
265 		break;
266 
267 	    case GG_EVENT_PONG:
268 		break;
269 
270 	    case GG_EVENT_PUBDIR50_SEARCH_REPLY:
271 		searchdone(e->event.pubdir50);
272 		break;
273 
274 	    case GG_EVENT_PUBDIR50_READ:
275 		break;
276 
277 	    case GG_EVENT_PUBDIR50_WRITE:
278 		break;
279 
280 	    case GG_EVENT_USERLIST:
281 		if(e->event.userlist.type == GG_USERLIST_GET_REPLY) {
282 		    char *p = e->event.userlist.reply;
283 		}
284 		break;
285 	}
286 
287 	gg_free_event(e);
288 
289     } else {
290 	cutoff();
291 	face.log(_("+ [gg] connection lost"));
292 
293     }
294 }
295 
getsockets(fd_set & rfds,fd_set & wfds,fd_set & efds,int & hsocket) const296 void gaduhook::getsockets(fd_set &rfds, fd_set &wfds, fd_set &efds, int &hsocket) const {
297     if(sess && sess->fd != -1) {
298 	if((sess->check & GG_CHECK_READ))
299 	    FD_SET(sess->fd, &rfds);
300 
301 	if((sess->check & GG_CHECK_WRITE))
302 	    FD_SET(sess->fd, &wfds);
303 
304 	hsocket = max(sess->fd, hsocket);
305     }
306 }
307 
isoursocket(fd_set & rfds,fd_set & wfds,fd_set & efds) const308 bool gaduhook::isoursocket(fd_set &rfds, fd_set &wfds, fd_set &efds) const {
309     if(sess && sess->fd != -1) {
310 	return FD_ISSET(sess->fd, &rfds) || FD_ISSET(sess->fd, &wfds);
311     }
312 
313     return false;
314 }
315 
online() const316 bool gaduhook::online() const {
317     return sess;
318 }
319 
logged() const320 bool gaduhook::logged() const {
321     return sess && flogged;
322 }
323 
isconnecting() const324 bool gaduhook::isconnecting() const {
325     return sess && !flogged;
326 }
327 
enabled() const328 bool gaduhook::enabled() const {
329     return true;
330 }
331 
send(const imevent & ev)332 bool gaduhook::send(const imevent &ev) {
333     icqcontact *c = clist.get(ev.getcontact());
334     string text;
335 
336     if(c) {
337 	if(ev.gettype() == imevent::message) {
338 	    const immessage *m = static_cast<const immessage *>(&ev);
339 	    if(m) text = rushtmlconv("kw", m->gettext());
340 
341 	} else if(ev.gettype() == imevent::url) {
342 	    const imurl *m = static_cast<const imurl *>(&ev);
343 	    if(m) text = rushtmlconv("kw", m->geturl()) + "\n\n" + rusconv("kw", m->getdescription());
344 
345 	}
346 
347 	gg_send_message(sess, GG_CLASS_MSG, c->getdesc().uin, (const unsigned char *) text.c_str());
348 	return true;
349     }
350 
351     return false;
352 }
353 
sendnewuser(const imcontact & c)354 void gaduhook::sendnewuser(const imcontact &c) {
355     gg_add_notify(sess, c.uin);
356     requestinfo(c);
357 }
358 
removeuser(const imcontact & c)359 void gaduhook::removeuser(const imcontact &c) {
360     gg_remove_notify(sess, c.uin);
361 }
362 
setautostatus(imstatus st)363 void gaduhook::setautostatus(imstatus st) {
364     if(st == offline) {
365 	if(getstatus() != offline) disconnect();
366 
367     } else {
368 	if(getstatus() != offline) {
369 	    gg_change_status_descr(sess, imstatus2gg(st, conf->getawaymsg(proto)), conf->getawaymsg(proto).c_str());
370 	    logger.putourstatus(proto, getstatus(), st);
371 	} else {
372 	    connect();
373 	}
374 
375     }
376 }
377 
getstatus() const378 imstatus gaduhook::getstatus() const {
379     if(!sess) return offline;
380 
381     if(GG_S_NA(sess->status)) return notavail; else
382     if(GG_S_B(sess->status)) return occupied; else
383     if(GG_S_I(sess->status)) return invisible; else
384 	return available;
385 }
386 
requestinfo(const imcontact & c)387 void gaduhook::requestinfo(const imcontact &c) {
388     if(ireq) gg_pubdir50_free(ireq);
389     ireq = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);
390 
391     gg_pubdir50_add(ireq, GG_PUBDIR50_UIN, i2str(c.uin).c_str());
392     gg_pubdir50(sess, ireq);
393 }
394 
requestawaymsg(const imcontact & ic)395 void gaduhook::requestawaymsg(const imcontact &ic) {
396     icqcontact *c = clist.get(ic);
397 
398     if(c) {
399 	string am = awaymsgs[ic.uin];
400 
401 	if(!am.empty()) {
402 	    em.store(imnotification(ic, (string) _("Away message:") + "\n\n" + am));
403 	} else {
404 	    face.log(_("+ [gg] no away message from %s, %s"),
405 		c->getdispnick().c_str(), ic.totext().c_str());
406 	}
407     }
408 }
409 
regconnect(const string & aserv)410 bool gaduhook::regconnect(const string &aserv) {
411     return true;
412 }
413 
regattempt(unsigned int & auin,const string & apassword,const string & email)414 bool gaduhook::regattempt(unsigned int &auin, const string &apassword, const string &email) {
415     fd_set rd, wr, ex;
416     struct gg_http *th, *rh;
417     struct gg_pubdir *p;
418     struct gg_token *t;
419     bool r = false;
420     string token, tokenid;
421 
422     th = gg_token(0);
423     if(!th) return false;
424 
425     token = handletoken(th);
426     tokenid = ((struct gg_token *) th->data)->tokenid;
427     gg_token_free(th);
428 
429     if((r = !token.empty())) {
430 	rh = gg_register3(email.c_str(), apassword.c_str(), tokenid.c_str(), token.c_str(), 0);
431 	if((r = rh)) {
432 	    auin = ((struct gg_pubdir *) th->data)->uin;
433 	    gg_free_register(rh);
434 	}
435     }
436 
437     return r;
438 }
439 
lookup(const imsearchparams & params,verticalmenu & dest)440 void gaduhook::lookup(const imsearchparams &params, verticalmenu &dest) {
441     searchdest = &dest;
442     while(!foundguys.empty()) {
443 	delete foundguys.back();
444 	foundguys.pop_back();
445     }
446 
447     if(lreq) gg_pubdir50_free(lreq);
448     lreq = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);
449 
450     if(params.uin) gg_pubdir50_add(lreq, GG_PUBDIR50_UIN, i2str(params.uin).c_str());
451     if(!params.nick.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_NICKNAME, params.nick.c_str());
452     if(!params.firstname.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_FIRSTNAME, params.firstname.c_str());
453     if(!params.lastname.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_LASTNAME, params.lastname.c_str());
454     if(!params.city.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_CITY, params.city.c_str());
455 
456     if(params.onlineonly) gg_pubdir50_add(lreq, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE);
457     if(params.gender != genderUnspec) gg_pubdir50_add(lreq, GG_PUBDIR50_GENDER, params.gender == genderMale ? GG_PUBDIR50_GENDER_MALE : GG_PUBDIR50_GENDER_FEMALE);
458 
459     gg_pubdir50(sess, lreq);
460 }
461 
sendupdateuserinfo(const icqcontact & c)462 void gaduhook::sendupdateuserinfo(const icqcontact &c) {
463     gg_pubdir50_t req;
464 
465     if(req = gg_pubdir50_new(GG_PUBDIR50_WRITE)) {
466 	icqcontact::basicinfo bi = c.getbasicinfo();
467 	icqcontact::moreinfo mi = c.getmoreinfo();
468 
469 	gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, c.getnick().c_str());
470 	gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, bi.fname.c_str());
471 	gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, bi.lname.c_str());
472 	gg_pubdir50_add(req, GG_PUBDIR50_CITY, bi.city.c_str());
473 
474 	switch(mi.gender) {
475 	    case genderMale:
476 		gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE);
477 		break;
478 	    case genderFemale:
479 		gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE);
480 		break;
481 	}
482 
483 	gg_pubdir50(sess, req);
484 	gg_pubdir50_free(req);
485     }
486 }
487 
488 // ----------------------------------------------------------------------------
489 
searchdone(void * p)490 void gaduhook::searchdone(void *p) {
491     gg_pubdir50_t sp = (gg_pubdir50_t) p;
492 
493     if(searchdest && lreq) {
494 	for(int i = 0; i < sp->count; i++) {
495 	    icqcontact *c = new icqcontact(imcontact(strtoul(gg_pubdir50_get(sp, i, GG_PUBDIR50_UIN), 0, 0), gadu));
496 	    icqcontact::basicinfo binfo = c->getbasicinfo();
497 
498 	    const char *p = gg_pubdir50_get(sp, i, GG_PUBDIR50_NICKNAME);
499 	    if(p) {
500 		c->setnick(p);
501 		c->setdispnick(c->getnick());
502 	    }
503 
504 	    p = gg_pubdir50_get(sp, i, GG_PUBDIR50_FIRSTNAME);
505 	    if(p) binfo.fname = p;
506 
507 	    p = gg_pubdir50_get(sp, i, GG_PUBDIR50_LASTNAME);
508 	    if(p) binfo.lname = p;
509 
510 	    string line;
511 
512 	    p = gg_pubdir50_get(sp, i, GG_PUBDIR50_STATUS);
513 	    if(p && atoi(p) != GG_STATUS_NOT_AVAIL) line = "o ";
514 		else line = "  ";
515 
516 	    line += c->getnick();
517 	    if(line.size() > 12) line.resize(12);
518 	    else line += string(12-line.size(), ' ');
519 
520 	    line += " " + binfo.fname + " " + binfo.lname;
521 
522 	    c->setbasicinfo(binfo);
523 
524 	    foundguys.push_back(c);
525 	    searchdest->additem(conf->getcolor(cp_clist_gadu), c, line);
526 	}
527 
528 	searchdest->redraw();
529 	face.findready();
530 	log(logSearchFinished, foundguys.size());
531 	searchdest = 0;
532 
533     } else if(ireq) {
534 	icqcontact *c = clist.get(imcontact(strtoul(gg_pubdir50_get(sp, 0, GG_PUBDIR50_UIN), 0, 0), gadu));
535 	string nick, gender;
536 	const char *p = 0;
537 
538 	if(!c) c = clist.get(contactroot);
539 
540 	icqcontact::basicinfo bi = c->getbasicinfo();
541 	icqcontact::moreinfo mi = c->getmoreinfo();
542 
543 	p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_FIRSTNAME);
544 	if(p) bi.fname = p;
545 
546 	p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_LASTNAME);
547 	if(p) bi.lname = p;
548 
549 	p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_NICKNAME);
550 
551 	if(p) {
552 	    nick = p;
553 
554 	    if((c->getnick() == c->getdispnick())
555 	    || (c->getdispnick() == i2str(c->getdesc().uin)))
556 		c->setdispnick(nick);
557 
558 	    c->setnick(nick);
559 
560 	} else {
561 	    c->setdispnick(bi.fname);
562 
563 	}
564 
565 	p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_CITY);
566 	if(p) bi.city = p;
567 
568 	p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_GENDER);
569 	if(p) {
570 	    gender = p;
571 
572 	    if(gender == GG_PUBDIR50_GENDER_MALE) mi.gender = genderMale; else
573 	    if(gender == GG_PUBDIR50_GENDER_FEMALE) mi.gender = genderFemale; else
574 		mi.gender = genderUnspec;
575 	}
576 
577 	c->setbasicinfo(bi);
578 	c->setmoreinfo(mi);
579     }
580 }
581 
userstatuschange(unsigned int uin,int status,const char * desc)582 void gaduhook::userstatuschange(unsigned int uin, int status, const char *desc) {
583     icqcontact *c = clist.get(imcontact(uin, gadu));
584     if(c) {
585 	imstatus ust = gg2imstatus(status);
586 	logger.putonline(c, c->getstatus(), ust);
587 	c->setstatus(ust);
588 
589 	if(desc)
590 	    awaymsgs[uin] = rusconv("wk", desc);
591 	else
592 	    awaymsgs[uin] = "";
593     }
594 }
595 
userlistsend()596 void gaduhook::userlistsend() {
597     int i;
598     vector<uin_t> uins;
599     icqcontact *c;
600 
601     for(i = 0; i < clist.count; i++) {
602 	c = (icqcontact *) clist.at(i);
603 
604 	if(c->getdesc().pname == proto && c->getdesc().uin)
605 	    uins.push_back(c->getdesc().uin);
606     }
607 
608     auto_ptr<uin_t> cuins(new uin_t[uins.size()]);
609     /* TODO: allocated with new[], but will be freed by delete */
610     auto_ptr<char> ctypes(new char[uins.size()]);
611 
612     for(vector<uin_t>::const_iterator iu = uins.begin(); iu != uins.end(); ++iu) {
613 	cuins.get()[iu-uins.begin()] = *iu;
614 	ctypes.get()[iu-uins.begin()] = GG_USER_NORMAL;
615     }
616 
617     gg_notify_ex(sess, cuins.get(), ctypes.get(), uins.size());
618 }
619 
usernotify(unsigned int uin,int status,const char * desc,unsigned int ip,int port,int version)620 void gaduhook::usernotify(unsigned int uin, int status, const char *desc,
621 unsigned int ip, int port, int version) {
622     imcontact ic(uin, gadu);
623     icqcontact *c = clist.get(ic);
624 
625     if(c) {
626 	struct in_addr addr;
627 	addr.s_addr = ntohl(ip);
628 	char *p = inet_ntoa(addr);
629 	if(p) c->setlastip(p);
630 
631 	imstatus ust = gg2imstatus(status);
632 	logger.putonline(c, c->getstatus(), ust);
633 	c->setstatus(ust);
634 
635 	if(desc)
636 	    awaymsgs[ic.uin] = rusconv("wk", desc);
637 	else
638 	    awaymsgs[ic.uin] = "";
639     }
640 }
641 
642 
643 // ----------------------------------------------------------------------------
644 
645 const int token_char_height = 12;
646 const char token_id_char[] = {"0123456789abcdef"};
647 const char token_id[][15] = {
648 "..####..",
649 ".##..##.",
650 "##....##",
651 "##....##",
652 "##....##",
653 "##....##",
654 "##....##",
655 "##....##",
656 "##....##",
657 "##....##",
658 ".##..##.",
659 "..####..",
660 
661 "...##",
662 "..###",
663 "#####",
664 "...##",
665 "...##",
666 "...##",
667 "...##",
668 "...##",
669 "...##",
670 "...##",
671 "...##",
672 "...##",
673 
674 "..####..",
675 ".##..##.",
676 "##....##",
677 "##....##",
678 "......##",
679 ".....##.",
680 "....##..",
681 "...##...",
682 "..##....",
683 ".##.....",
684 "##......",
685 "########",
686 
687 "..####..",
688 ".##..##.",
689 "##....##",
690 "##....##",
691 ".....##.",
692 "...###..",
693 ".....##.",
694 "......##",
695 "##....##",
696 "##....##",
697 ".##..##.",
698 "..####..",
699 
700 ".....##.",
701 "....###.",
702 "...####.",
703 "...#.##.",
704 "..##.##.",
705 "..#..##.",
706 ".##..##.",
707 "##...##.",
708 "########",
709 ".....##.",
710 ".....##.",
711 ".....##.",
712 
713 ".#######",
714 ".##.....",
715 ".##.....",
716 "##......",
717 "######..",
718 "##...##.",
719 "......##",
720 "......##",
721 "......##",
722 "##....##",
723 "##...##.",
724 ".#####..",
725 
726 "..#####.",
727 ".##...##",
728 ".#.....#",
729 "##......",
730 "##.###..",
731 "###..##.",
732 "##....##",
733 "##....##",
734 "##....##",
735 "##....##",
736 ".##..##.",
737 "..####..",
738 
739 "########",
740 "......##",
741 ".....##.",
742 ".....##.",
743 "....##..",
744 "....##..",
745 "...##...",
746 "...##...",
747 "...##...",
748 "..##....",
749 "..##....",
750 "..##....",
751 
752 "..####..",
753 ".##..##.",
754 "##....##",
755 "##....##",
756 ".##..##.",
757 "..####..",
758 ".##..##.",
759 "##....##",
760 "##....##",
761 "##....##",
762 ".##..##.",
763 "..####..",
764 
765 "..####..",
766 ".##..##.",
767 "##....##",
768 "##....##",
769 "##....##",
770 "##....##",
771 ".##..###",
772 "..###.##",
773 "......##",
774 "#.....#.",
775 "##...##.",
776 ".#####..",
777 
778 "........",
779 "........",
780 "........",
781 "........",
782 ".#####..",
783 "##...##.",
784 ".....##.",
785 ".######.",
786 "##...##.",
787 "##...##.",
788 "##...##.",
789 ".####.##",
790 
791 "##.....",
792 "##.....",
793 "##.....",
794 "##.....",
795 "######.",
796 "##...##",
797 "##...##",
798 "##...##",
799 "##...##",
800 "##...##",
801 "##...##",
802 "######.",
803 
804 ".......",
805 ".......",
806 ".......",
807 ".......",
808 ".#####.",
809 "##...##",
810 "##...##",
811 "##.....",
812 "##.....",
813 "##...##",
814 "##...##",
815 ".#####.",
816 
817 ".....##",
818 ".....##",
819 ".....##",
820 ".....##",
821 ".######",
822 "##...##",
823 "##...##",
824 "##...##",
825 "##...##",
826 "##...##",
827 "##...##",
828 ".######",
829 
830 ".......",
831 ".......",
832 ".......",
833 ".......",
834 ".#####.",
835 "##...##",
836 "##...##",
837 "#######",
838 "##.....",
839 "##....#",
840 "##...##",
841 ".#####.",
842 
843 "..###",
844 ".##..",
845 ".##..",
846 ".##..",
847 "#####",
848 ".##..",
849 ".##..",
850 ".##..",
851 ".##..",
852 ".##..",
853 ".##..",
854 ".##.."};
855 
token_check(int nr,int x,int y,const char * ocr,int maxx,int maxy)856 static int token_check(int nr, int x, int y, const char *ocr, int maxx, int maxy) {
857     int i;
858 
859     for(i = nr*token_char_height; i < (nr+1)*token_char_height; i++, y++) {
860 	int j, xx = x;
861 
862 	for(j = 0; token_id[i][j] && j + xx < maxx; j++, xx++) {
863 	    if(token_id[i][j] != ocr[y * (maxx + 1) + xx])
864 		return 0;
865 	}
866     }
867 
868     return 1;
869 }
870 
token_ocr(const char * ocr,int width,int height,int length)871 static char *token_ocr(const char *ocr, int width, int height, int length) {
872     int x, y, count = 0;
873     char *token;
874 
875     token = (char *) malloc(length + 1);
876     memset(token, 0, length + 1);
877 
878     for(x = 0; x < width; x++) {
879 	for(y = 0; y < height - token_char_height; y++) {
880 	    int result = 0, token_part = 0;
881 
882 	    do {
883 		result = token_check(token_part++, x, y, ocr, width, height);
884 	    } while(!result && token_part < 16);
885 
886 	    if(result && count < length)
887 		token[count++] = token_id_char[token_part - 1];
888 	}
889     }
890 
891     if(count == length)
892 	return token;
893 
894     free(token);
895 
896     return NULL;
897 }
898 
handletoken(struct gg_http * h)899 string gaduhook::handletoken(struct gg_http *h) {
900     struct gg_token *t;
901     string fname, r;
902     char *tmpfilep = NULL;
903 
904     if(!h)
905 	return "";
906 
907     if(gg_token_watch_fd(h) || h->state == GG_STATE_ERROR)
908 	return "";
909 
910     if(h->state != GG_STATE_DONE)
911 	return "";
912 
913     if(!(t = (struct gg_token *) h->data) || !h->body)
914 	return "";
915 
916     do {
917 	while (tmpfilep == NULL) {
918 	    char tmpnam[PATH_MAX];
919 	    int tmpfiledes;
920 	    char * tmpdir = getenv("TMPDIR");
921 
922 	    if (!tmpdir)
923 		tmpdir = "/tmp";
924 
925 		strncat(tmpnam, "/gg.token.XXXXXX", sizeof(tmpnam)-strlen(tmpnam)-1);
926 
927 	    if ((tmpfiledes = mkstemp (tmpnam)) == -1) {
928 		tmpfilep = NULL;
929 	    } else {
930 		tmpfilep = tmpnam;
931 		close (tmpfiledes);
932 	    }
933 	    fname = tmpnam;
934 	}
935 
936     } while(!access(fname.c_str(), F_OK));
937 
938     ofstream bf(fname.c_str());
939 
940     if(bf.is_open()) {
941 	bf.write(h->body, h->body_size);
942 	bf.close();
943     } else {
944 	return "";
945     }
946 
947 #ifdef HAVE_LIBJPEG
948 
949     struct jpeg_decompress_struct j;
950     struct jpeg_error_mgr e;
951     JSAMPROW buf[1];
952     int size, i;
953     char *token, *tmp;
954     FILE *f;
955     int ih = 0;
956 
957     if(!(f = fopen(fname.c_str(), "rb")))
958 	return "";
959 
960     j.err = jpeg_std_error(&e);
961     jpeg_create_decompress(&j);
962     jpeg_stdio_src(&j, f);
963     jpeg_read_header(&j, TRUE);
964     jpeg_start_decompress(&j);
965 
966     size = j.output_width * j.output_components;
967     buf[0] = (JSAMPLE *) malloc(size);
968 
969     token = (char *) malloc((j.output_width + 1) * j.output_height);
970 
971     while(j.output_scanline < j.output_height) {
972 	jpeg_read_scanlines(&j, buf, 1);
973 
974 	for(i = 0; i < j.output_width; i++, ih++)
975 	    token[ih] = (buf[0][i*3] + buf[0][i*3+1] + buf[0][i*3+2] < 384) ? '#' : '.';
976 
977 	token[ih++] = 0;
978     }
979 
980     if((tmp = token_ocr(token, j.output_width, j.output_height, t->length))) {
981 	r = tmp;
982 	free(tmp);
983     }
984 
985     free(token);
986 
987     jpeg_finish_decompress(&j);
988     jpeg_destroy_decompress(&j);
989 
990     free(buf[0]);
991     fclose(f);
992 
993     unlink(fname.c_str());
994 
995 #endif
996 
997     return r;
998 }
999 
1000 #endif
1001