Updating ChangeLog for 4.22.10
[centerim.git] / src / hooks / gaduhook.cc
blobc272e92d5fb89bd624a5d3da119204e9befa078d
1 /*
3 * centerim gadu-gadu protocol handling class
4 * $Id: gaduhook.cc,v 1.14 2005/07/08 09:49:17 konst Exp $
6 * Copyright (C) 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_GADU
29 #include "eventmanager.h"
30 #include "gaduhook.h"
31 #include "icqface.h"
32 #include "imlogger.h"
33 #include "icqcontacts.h"
35 #include "libgadu-config.h"
36 #include "libgadu.h"
38 #include <netdb.h>
39 #include <arpa/inet.h>
40 #include <netinet/in.h>
41 #include <limits.h>
43 #ifdef HAVE_JPEGLIB_H
44 extern "C" {
45 #include <jpeglib.h>
47 #endif
49 #define PERIOD_PING 50
51 static imstatus gg2imstatus(int st) {
52 imstatus imst;
54 switch(st) {
55 case GG_STATUS_INVISIBLE:
56 case GG_STATUS_INVISIBLE_DESCR:
57 imst = invisible;
58 break;
60 case GG_STATUS_BUSY:
61 case GG_STATUS_BUSY_DESCR:
62 imst = occupied;
63 break;
65 case GG_STATUS_NOT_AVAIL:
66 case GG_STATUS_NOT_AVAIL_DESCR:
67 imst = offline;
68 break;
70 default:
71 imst = available;
72 break;
75 return imst;
78 static int imstatus2gg(imstatus st, const string &desc = "") {
79 int gst;
81 switch(st) {
82 case invisible:
83 gst = desc.empty() ?
84 GG_STATUS_INVISIBLE : GG_STATUS_INVISIBLE_DESCR;
85 break;
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;
96 case freeforchat:
97 case available:
98 default:
99 gst = desc.empty() ?
100 GG_STATUS_AVAIL : GG_STATUS_AVAIL_DESCR;
101 break;
104 return gst;
107 // ----------------------------------------------------------------------------
109 gaduhook ghook;
111 gg_pubdir50_t lreq = 0, ireq = 0;
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);
122 gaduhook::~gaduhook() {
125 void gaduhook::init() {
126 manualstatus = conf->getstatus(proto);
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()));
135 memset(&lp, 0, sizeof(lp));
137 struct hostent *he = gethostbyname(acc.server.c_str());
138 struct in_addr addr;
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;
145 lp.uin = acc.uin;
146 lp.password = pass.get();
147 lp.async = 1;
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()));
152 lp.status_descr = descr.get();
153 lp.status = imstatus2gg(manualstatus, descr.get());
155 lp.tls = acc.additional["ssl"] == "1" ? 1 : 0;
157 log(logConnecting);
158 sess = gg_login(&lp);
160 if(!sess)
161 face.log(_("+ [gg] connection failed"));
163 } else {
164 face.log(_("+ [gg] cannot resolve %s"), acc.server.c_str());
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;
177 clist.setoffline(proto);
180 void gaduhook::disconnect() {
181 cutoff();
182 log(logDisconnected);
183 logger.putourstatus(proto, getstatus(), offline);
186 void gaduhook::exectimers() {
187 if(logged()) {
188 if(timer_current-timer_ping > PERIOD_PING) {
189 gg_ping(sess);
190 timer_ping = timer_current;
195 void gaduhook::main() {
196 int i;
197 fd_set rd, wd;
198 struct timeval tv;
200 struct gg_event *e;
201 struct gg_notify_reply *nr;
202 string text;
204 e = gg_watch_fd(sess);
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;
216 case GG_EVENT_CONN_FAILED:
217 cutoff();
218 face.log(_("+ [gg] connection to the server failed"));
219 break;
221 case GG_EVENT_DISCONNECT:
222 disconnect();
223 break;
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));
231 break;
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;
237 for(; nr->uin; nr++) {
238 char *desc = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : 0;
240 usernotify(nr->uin, nr->status, desc,
241 nr->remote_ip, nr->remote_port,
242 nr->version);
244 break;
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;
256 case GG_EVENT_STATUS:
257 userstatuschange(e->event.status.uin, e->event.status.status, e->event.status.descr);
258 break;
260 case GG_EVENT_STATUS60:
261 userstatuschange(e->event.status60.uin, e->event.status60.status, e->event.status60.descr);
262 break;
264 case GG_EVENT_ACK:
265 break;
267 case GG_EVENT_PONG:
268 break;
270 case GG_EVENT_PUBDIR50_SEARCH_REPLY:
271 searchdone(e->event.pubdir50);
272 break;
274 case GG_EVENT_PUBDIR50_READ:
275 break;
277 case GG_EVENT_PUBDIR50_WRITE:
278 break;
280 case GG_EVENT_USERLIST:
281 if(e->event.userlist.type == GG_USERLIST_GET_REPLY) {
282 char *p = e->event.userlist.reply;
284 break;
287 gg_free_event(e);
289 } else {
290 cutoff();
291 face.log(_("+ [gg] connection lost"));
296 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);
301 if((sess->check & GG_CHECK_WRITE))
302 FD_SET(sess->fd, &wfds);
304 hsocket = max(sess->fd, hsocket);
308 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);
313 return false;
316 bool gaduhook::online() const {
317 return sess;
320 bool gaduhook::logged() const {
321 return sess && flogged;
324 bool gaduhook::isconnecting() const {
325 return sess && !flogged;
328 bool gaduhook::enabled() const {
329 return true;
332 bool gaduhook::send(const imevent &ev) {
333 icqcontact *c = clist.get(ev.getcontact());
334 string text;
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());
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());
347 gg_send_message(sess, GG_CLASS_MSG, c->getdesc().uin, (const unsigned char *) text.c_str());
348 return true;
351 return false;
354 void gaduhook::sendnewuser(const imcontact &c) {
355 gg_add_notify(sess, c.uin);
356 requestinfo(c);
359 void gaduhook::removeuser(const imcontact &c) {
360 gg_remove_notify(sess, c.uin);
363 void gaduhook::setautostatus(imstatus st) {
364 if(st == offline) {
365 if(getstatus() != offline) disconnect();
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();
378 imstatus gaduhook::getstatus() const {
379 if(!sess) return offline;
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;
387 void gaduhook::requestinfo(const imcontact &c) {
388 if(ireq) gg_pubdir50_free(ireq);
389 ireq = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);
391 gg_pubdir50_add(ireq, GG_PUBDIR50_UIN, i2str(c.uin).c_str());
392 gg_pubdir50(sess, ireq);
395 void gaduhook::requestawaymsg(const imcontact &ic) {
396 icqcontact *c = clist.get(ic);
398 if(c) {
399 string am = awaymsgs[ic.uin];
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());
410 bool gaduhook::regconnect(const string &aserv) {
411 return true;
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;
422 th = gg_token(0);
423 if(!th) return false;
425 token = handletoken(th);
426 tokenid = ((struct gg_token *) th->data)->tokenid;
427 gg_token_free(th);
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);
437 return r;
440 void gaduhook::lookup(const imsearchparams &params, verticalmenu &dest) {
441 searchdest = &dest;
442 while(!foundguys.empty()) {
443 delete foundguys.back();
444 foundguys.pop_back();
447 if(lreq) gg_pubdir50_free(lreq);
448 lreq = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);
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());
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);
459 gg_pubdir50(sess, lreq);
462 void gaduhook::sendupdateuserinfo(const icqcontact &c) {
463 gg_pubdir50_t req;
465 if(req = gg_pubdir50_new(GG_PUBDIR50_WRITE)) {
466 icqcontact::basicinfo bi = c.getbasicinfo();
467 icqcontact::moreinfo mi = c.getmoreinfo();
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());
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;
483 gg_pubdir50(sess, req);
484 gg_pubdir50_free(req);
488 // ----------------------------------------------------------------------------
490 void gaduhook::searchdone(void *p) {
491 gg_pubdir50_t sp = (gg_pubdir50_t) p;
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();
498 const char *p = gg_pubdir50_get(sp, i, GG_PUBDIR50_NICKNAME);
499 if(p) {
500 c->setnick(p);
501 c->setdispnick(c->getnick());
504 p = gg_pubdir50_get(sp, i, GG_PUBDIR50_FIRSTNAME);
505 if(p) binfo.fname = p;
507 p = gg_pubdir50_get(sp, i, GG_PUBDIR50_LASTNAME);
508 if(p) binfo.lname = p;
510 string line;
512 p = gg_pubdir50_get(sp, i, GG_PUBDIR50_STATUS);
513 if(p && atoi(p) != GG_STATUS_NOT_AVAIL) line = "o ";
514 else line = " ";
516 line += c->getnick();
517 if(line.size() > 12) line.resize(12);
518 else line += string(12-line.size(), ' ');
520 line += " " + binfo.fname + " " + binfo.lname;
522 c->setbasicinfo(binfo);
524 foundguys.push_back(c);
525 searchdest->additem(conf->getcolor(cp_clist_gadu), c, line);
528 searchdest->redraw();
529 face.findready();
530 log(logSearchFinished, foundguys.size());
531 searchdest = 0;
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;
538 if(!c) c = clist.get(contactroot);
540 icqcontact::basicinfo bi = c->getbasicinfo();
541 icqcontact::moreinfo mi = c->getmoreinfo();
543 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_FIRSTNAME);
544 if(p) bi.fname = p;
546 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_LASTNAME);
547 if(p) bi.lname = p;
549 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_NICKNAME);
551 if(p) {
552 nick = p;
554 if((c->getnick() == c->getdispnick())
555 || (c->getdispnick() == i2str(c->getdesc().uin)))
556 c->setdispnick(nick);
558 c->setnick(nick);
560 } else {
561 c->setdispnick(bi.fname);
565 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_CITY);
566 if(p) bi.city = p;
568 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_GENDER);
569 if(p) {
570 gender = p;
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;
577 c->setbasicinfo(bi);
578 c->setmoreinfo(mi);
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);
589 if(desc)
590 awaymsgs[uin] = rusconv("wk", desc);
591 else
592 awaymsgs[uin] = "";
596 void gaduhook::userlistsend() {
597 int i;
598 vector<uin_t> uins;
599 icqcontact *c;
601 for(i = 0; i < clist.count; i++) {
602 c = (icqcontact *) clist.at(i);
604 if(c->getdesc().pname == proto && c->getdesc().uin)
605 uins.push_back(c->getdesc().uin);
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()]);
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;
617 gg_notify_ex(sess, cuins.get(), ctypes.get(), uins.size());
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);
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);
631 imstatus ust = gg2imstatus(status);
632 logger.putonline(c, c->getstatus(), ust);
633 c->setstatus(ust);
635 if(desc)
636 awaymsgs[ic.uin] = rusconv("wk", desc);
637 else
638 awaymsgs[ic.uin] = "";
643 // ----------------------------------------------------------------------------
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 "..####..",
661 "...##",
662 "..###",
663 "#####",
664 "...##",
665 "...##",
666 "...##",
667 "...##",
668 "...##",
669 "...##",
670 "...##",
671 "...##",
672 "...##",
674 "..####..",
675 ".##..##.",
676 "##....##",
677 "##....##",
678 "......##",
679 ".....##.",
680 "....##..",
681 "...##...",
682 "..##....",
683 ".##.....",
684 "##......",
685 "########",
687 "..####..",
688 ".##..##.",
689 "##....##",
690 "##....##",
691 ".....##.",
692 "...###..",
693 ".....##.",
694 "......##",
695 "##....##",
696 "##....##",
697 ".##..##.",
698 "..####..",
700 ".....##.",
701 "....###.",
702 "...####.",
703 "...#.##.",
704 "..##.##.",
705 "..#..##.",
706 ".##..##.",
707 "##...##.",
708 "########",
709 ".....##.",
710 ".....##.",
711 ".....##.",
713 ".#######",
714 ".##.....",
715 ".##.....",
716 "##......",
717 "######..",
718 "##...##.",
719 "......##",
720 "......##",
721 "......##",
722 "##....##",
723 "##...##.",
724 ".#####..",
726 "..#####.",
727 ".##...##",
728 ".#.....#",
729 "##......",
730 "##.###..",
731 "###..##.",
732 "##....##",
733 "##....##",
734 "##....##",
735 "##....##",
736 ".##..##.",
737 "..####..",
739 "########",
740 "......##",
741 ".....##.",
742 ".....##.",
743 "....##..",
744 "....##..",
745 "...##...",
746 "...##...",
747 "...##...",
748 "..##....",
749 "..##....",
750 "..##....",
752 "..####..",
753 ".##..##.",
754 "##....##",
755 "##....##",
756 ".##..##.",
757 "..####..",
758 ".##..##.",
759 "##....##",
760 "##....##",
761 "##....##",
762 ".##..##.",
763 "..####..",
765 "..####..",
766 ".##..##.",
767 "##....##",
768 "##....##",
769 "##....##",
770 "##....##",
771 ".##..###",
772 "..###.##",
773 "......##",
774 "#.....#.",
775 "##...##.",
776 ".#####..",
778 "........",
779 "........",
780 "........",
781 "........",
782 ".#####..",
783 "##...##.",
784 ".....##.",
785 ".######.",
786 "##...##.",
787 "##...##.",
788 "##...##.",
789 ".####.##",
791 "##.....",
792 "##.....",
793 "##.....",
794 "##.....",
795 "######.",
796 "##...##",
797 "##...##",
798 "##...##",
799 "##...##",
800 "##...##",
801 "##...##",
802 "######.",
804 ".......",
805 ".......",
806 ".......",
807 ".......",
808 ".#####.",
809 "##...##",
810 "##...##",
811 "##.....",
812 "##.....",
813 "##...##",
814 "##...##",
815 ".#####.",
817 ".....##",
818 ".....##",
819 ".....##",
820 ".....##",
821 ".######",
822 "##...##",
823 "##...##",
824 "##...##",
825 "##...##",
826 "##...##",
827 "##...##",
828 ".######",
830 ".......",
831 ".......",
832 ".......",
833 ".......",
834 ".#####.",
835 "##...##",
836 "##...##",
837 "#######",
838 "##.....",
839 "##....#",
840 "##...##",
841 ".#####.",
843 "..###",
844 ".##..",
845 ".##..",
846 ".##..",
847 "#####",
848 ".##..",
849 ".##..",
850 ".##..",
851 ".##..",
852 ".##..",
853 ".##..",
854 ".##.."};
856 static int token_check(int nr, int x, int y, const char *ocr, int maxx, int maxy) {
857 int i;
859 for(i = nr*token_char_height; i < (nr+1)*token_char_height; i++, y++) {
860 int j, xx = x;
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;
868 return 1;
871 static char *token_ocr(const char *ocr, int width, int height, int length) {
872 int x, y, count = 0;
873 char *token;
875 token = (char *) malloc(length + 1);
876 memset(token, 0, length + 1);
878 for(x = 0; x < width; x++) {
879 for(y = 0; y < height - token_char_height; y++) {
880 int result = 0, token_part = 0;
882 do {
883 result = token_check(token_part++, x, y, ocr, width, height);
884 } while(!result && token_part < 16);
886 if(result && count < length)
887 token[count++] = token_id_char[token_part - 1];
891 if(count == length)
892 return token;
894 free(token);
896 return NULL;
899 string gaduhook::handletoken(struct gg_http *h) {
900 struct gg_token *t;
901 string fname, r;
902 char *tmpfilep = NULL;
904 if(!h)
905 return "";
907 if(gg_token_watch_fd(h) || h->state == GG_STATE_ERROR)
908 return "";
910 if(h->state != GG_STATE_DONE)
911 return "";
913 if(!(t = (struct gg_token *) h->data) || !h->body)
914 return "";
916 do {
917 while (tmpfilep == NULL) {
918 char tmpnam[PATH_MAX];
919 int tmpfiledes;
920 char * tmpdir = getenv("TMPDIR");
922 if (!tmpdir)
923 tmpdir = "/tmp";
925 strncat(tmpnam, "/gg.token.XXXXXX", sizeof(tmpnam)-strlen(tmpnam)-1);
927 if ((tmpfiledes = mkstemp (tmpnam)) == -1) {
928 tmpfilep = NULL;
929 } else {
930 tmpfilep = tmpnam;
931 close (tmpfiledes);
933 fname = tmpnam;
936 } while(!access(fname.c_str(), F_OK));
938 ofstream bf(fname.c_str());
940 if(bf.is_open()) {
941 bf.write(h->body, h->body_size);
942 bf.close();
943 } else {
944 return "";
947 #ifdef HAVE_LIBJPEG
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;
957 if(!(f = fopen(fname.c_str(), "rb")))
958 return "";
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);
966 size = j.output_width * j.output_components;
967 buf[0] = (JSAMPLE *) malloc(size);
969 token = (char *) malloc((j.output_width + 1) * j.output_height);
971 while(j.output_scanline < j.output_height) {
972 jpeg_read_scanlines(&j, buf, 1);
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) ? '#' : '.';
977 token[ih++] = 0;
980 if((tmp = token_ocr(token, j.output_width, j.output_height, t->length))) {
981 r = tmp;
982 free(tmp);
985 free(token);
987 jpeg_finish_decompress(&j);
988 jpeg_destroy_decompress(&j);
990 free(buf[0]);
991 fclose(f);
993 unlink(fname.c_str());
995 #endif
997 return r;
1000 #endif