Checked couplig of malloc with free, new with delete and new[] with delete[]
[centerim.git] / src / hooks / gaduhook.cc
blob9462c8ae164236439a9b8f23f39b22b326f96969
1 /*
3 * centericq 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 <konst@konst.org.ua>
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"
34 #include "libgadu-config.h"
35 #include "libgadu.h"
37 #include <netdb.h>
38 #include <arpa/inet.h>
39 #include <netinet/in.h>
41 #ifdef HAVE_JPEGLIB_H
42 extern "C" {
43 #include <jpeglib.h>
45 #endif
47 #define PERIOD_PING 50
49 static imstatus gg2imstatus(int st) {
50 imstatus imst;
52 switch(st) {
53 case GG_STATUS_INVISIBLE:
54 case GG_STATUS_INVISIBLE_DESCR:
55 imst = invisible;
56 break;
58 case GG_STATUS_BUSY:
59 case GG_STATUS_BUSY_DESCR:
60 imst = occupied;
61 break;
63 case GG_STATUS_NOT_AVAIL:
64 case GG_STATUS_NOT_AVAIL_DESCR:
65 imst = offline;
66 break;
68 default:
69 imst = available;
70 break;
73 return imst;
76 static int imstatus2gg(imstatus st, const string &desc = "") {
77 int gst;
79 switch(st) {
80 case invisible:
81 gst = desc.empty() ?
82 GG_STATUS_INVISIBLE : GG_STATUS_INVISIBLE_DESCR;
83 break;
85 case dontdisturb:
86 case occupied:
87 case notavail:
88 case away:
89 gst = desc.empty() ?
90 GG_STATUS_BUSY : GG_STATUS_BUSY_DESCR;
91 break;
93 case freeforchat:
94 case available:
95 default:
96 gst = desc.empty() ?
97 GG_STATUS_AVAIL : GG_STATUS_AVAIL_DESCR;
98 break;
101 return gst;
104 // ----------------------------------------------------------------------------
106 gaduhook ghook;
108 gg_pubdir50_t lreq = 0, ireq = 0;
110 gaduhook::gaduhook(): abstracthook(gadu), flogged(false), sess(0) {
111 fcapabs.insert(hookcapab::changepassword);
112 fcapabs.insert(hookcapab::changenick);
113 fcapabs.insert(hookcapab::changedetails);
114 fcapabs.insert(hookcapab::setaway);
115 fcapabs.insert(hookcapab::fetchaway);
116 fcapabs.insert(hookcapab::ssl);
119 gaduhook::~gaduhook() {
122 void gaduhook::init() {
123 manualstatus = conf.getstatus(proto);
126 void gaduhook::connect() {
127 icqconf::imaccount acc = conf.getourid(proto);
128 static struct gg_login_params lp;
129 /* TODO investigate this, auto_ptr will free with delete, but allocated by malloc */
130 static auto_ptr<char> pass(strdup(acc.password.c_str()));
132 memset(&lp, 0, sizeof(lp));
134 struct hostent *he = gethostbyname(acc.server.c_str());
135 struct in_addr addr;
137 if(he) {
138 memcpy((char *) &addr, he->h_addr, sizeof(addr));
139 lp.server_addr = addr.s_addr;
140 lp.server_port = acc.port;
142 lp.uin = acc.uin;
143 lp.password = pass.get();
144 lp.async = 1;
146 /* TODO investigate this, auto_ptr will free with delete, but allocated by malloc */
147 static auto_ptr<char> descr(strdup(rusconv("kw", conf.getawaymsg(proto)).c_str()));
149 lp.status_descr = descr.get();
150 lp.status = imstatus2gg(manualstatus, descr.get());
152 lp.tls = acc.additional["ssl"] == "1" ? 1 : 0;
154 log(logConnecting);
155 sess = gg_login(&lp);
157 if(!sess)
158 face.log(_("+ [gg] connection failed"));
160 } else {
161 face.log(_("+ [gg] cannot resolve %s"), acc.server.c_str());
166 void gaduhook::cutoff() {
167 if(sess) {
168 gg_change_status(sess, GG_STATUS_NOT_AVAIL);
169 gg_logoff(sess);
170 gg_free_session(sess);
171 sess = 0;
174 clist.setoffline(proto);
177 void gaduhook::disconnect() {
178 cutoff();
179 log(logDisconnected);
180 logger.putourstatus(proto, getstatus(), offline);
183 void gaduhook::exectimers() {
184 if(logged()) {
185 if(timer_current-timer_ping > PERIOD_PING) {
186 gg_ping(sess);
187 timer_ping = timer_current;
192 void gaduhook::main() {
193 int i;
194 fd_set rd, wd;
195 struct timeval tv;
197 struct gg_event *e;
198 struct gg_notify_reply *nr;
199 string text;
201 e = gg_watch_fd(sess);
203 if(e) {
204 switch(e->type) {
205 case GG_EVENT_CONN_SUCCESS:
206 flogged = true;
207 time(&timer_ping);
208 userlistsend();
209 log(logLogged);
210 face.update();
211 break;
213 case GG_EVENT_CONN_FAILED:
214 cutoff();
215 face.log(_("+ [gg] connection to the server failed"));
216 break;
218 case GG_EVENT_DISCONNECT:
219 disconnect();
220 break;
222 case GG_EVENT_MSG:
223 if(e->event.msg.sender && e->event.msg.message) {
224 text = rusconv("wk", (const char *) e->event.msg.message);
225 em.store(immessage(imcontact(e->event.msg.sender, gadu),
226 imevent::incoming, text, e->event.msg.time));
228 break;
230 case GG_EVENT_NOTIFY:
231 case GG_EVENT_NOTIFY_DESCR:
232 nr = (e->type == GG_EVENT_NOTIFY) ? e->event.notify : e->event.notify_descr.notify;
234 for(; nr->uin; nr++) {
235 char *desc = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : 0;
237 usernotify(nr->uin, nr->status, desc,
238 nr->remote_ip, nr->remote_port,
239 nr->version);
241 break;
243 case GG_EVENT_NOTIFY60:
244 for(i = 0; e->event.notify60[i].uin; i++)
245 usernotify(e->event.notify60[i].uin,
246 e->event.notify60[i].status,
247 e->event.notify60[i].descr,
248 e->event.notify60[i].remote_ip,
249 e->event.notify60[i].remote_port,
250 e->event.notify60[i].version);
251 break;
253 case GG_EVENT_STATUS:
254 userstatuschange(e->event.status.uin, e->event.status.status, e->event.status.descr);
255 break;
257 case GG_EVENT_STATUS60:
258 userstatuschange(e->event.status60.uin, e->event.status60.status, e->event.status60.descr);
259 break;
261 case GG_EVENT_ACK:
262 break;
264 case GG_EVENT_PONG:
265 break;
267 case GG_EVENT_PUBDIR50_SEARCH_REPLY:
268 searchdone(e->event.pubdir50);
269 break;
271 case GG_EVENT_PUBDIR50_READ:
272 break;
274 case GG_EVENT_PUBDIR50_WRITE:
275 break;
277 case GG_EVENT_USERLIST:
278 if(e->event.userlist.type == GG_USERLIST_GET_REPLY) {
279 char *p = e->event.userlist.reply;
281 break;
284 gg_free_event(e);
286 } else {
287 cutoff();
288 face.log(_("+ [gg] connection lost"));
293 void gaduhook::getsockets(fd_set &rfds, fd_set &wfds, fd_set &efds, int &hsocket) const {
294 if(sess && sess->fd != -1) {
295 if((sess->check & GG_CHECK_READ))
296 FD_SET(sess->fd, &rfds);
298 if((sess->check & GG_CHECK_WRITE))
299 FD_SET(sess->fd, &wfds);
301 hsocket = max(sess->fd, hsocket);
305 bool gaduhook::isoursocket(fd_set &rfds, fd_set &wfds, fd_set &efds) const {
306 if(sess && sess->fd != -1) {
307 return FD_ISSET(sess->fd, &rfds) || FD_ISSET(sess->fd, &wfds);
310 return false;
313 bool gaduhook::online() const {
314 return sess;
317 bool gaduhook::logged() const {
318 return sess && flogged;
321 bool gaduhook::isconnecting() const {
322 return sess && !flogged;
325 bool gaduhook::enabled() const {
326 return true;
329 bool gaduhook::send(const imevent &ev) {
330 icqcontact *c = clist.get(ev.getcontact());
331 string text;
333 if(c) {
334 if(ev.gettype() == imevent::message) {
335 const immessage *m = static_cast<const immessage *>(&ev);
336 if(m) text = rushtmlconv("kw", m->gettext());
338 } else if(ev.gettype() == imevent::url) {
339 const imurl *m = static_cast<const imurl *>(&ev);
340 if(m) text = rushtmlconv("kw", m->geturl()) + "\n\n" + rusconv("kw", m->getdescription());
344 gg_send_message(sess, GG_CLASS_MSG, c->getdesc().uin, (const unsigned char *) text.c_str());
345 return true;
348 return false;
351 void gaduhook::sendnewuser(const imcontact &c) {
352 gg_add_notify(sess, c.uin);
353 requestinfo(c);
356 void gaduhook::removeuser(const imcontact &c) {
357 gg_remove_notify(sess, c.uin);
360 void gaduhook::setautostatus(imstatus st) {
361 if(st == offline) {
362 if(getstatus() != offline) disconnect();
364 } else {
365 if(getstatus() != offline) {
366 gg_change_status_descr(sess, imstatus2gg(st, conf.getawaymsg(proto)), conf.getawaymsg(proto).c_str());
367 logger.putourstatus(proto, getstatus(), st);
368 } else {
369 connect();
375 imstatus gaduhook::getstatus() const {
376 if(!sess) return offline;
378 if(GG_S_NA(sess->status)) return notavail; else
379 if(GG_S_B(sess->status)) return occupied; else
380 if(GG_S_I(sess->status)) return invisible; else
381 return available;
384 void gaduhook::requestinfo(const imcontact &c) {
385 if(ireq) gg_pubdir50_free(ireq);
386 ireq = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);
388 gg_pubdir50_add(ireq, GG_PUBDIR50_UIN, i2str(c.uin).c_str());
389 gg_pubdir50(sess, ireq);
392 void gaduhook::requestawaymsg(const imcontact &ic) {
393 icqcontact *c = clist.get(ic);
395 if(c) {
396 string am = awaymsgs[ic.uin];
398 if(!am.empty()) {
399 em.store(imnotification(ic, (string) _("Away message:") + "\n\n" + am));
400 } else {
401 face.log(_("+ [gg] no away message from %s, %s"),
402 c->getdispnick().c_str(), ic.totext().c_str());
407 bool gaduhook::regconnect(const string &aserv) {
408 return true;
411 bool gaduhook::regattempt(unsigned int &auin, const string &apassword, const string &email) {
412 fd_set rd, wr, ex;
413 struct gg_http *th, *rh;
414 struct gg_pubdir *p;
415 struct gg_token *t;
416 bool r = false;
417 string token, tokenid;
419 th = gg_token(0);
420 if(!th) return false;
422 token = handletoken(th);
423 tokenid = ((struct gg_token *) th->data)->tokenid;
424 gg_token_free(th);
426 if((r = !token.empty())) {
427 rh = gg_register3(email.c_str(), apassword.c_str(), tokenid.c_str(), token.c_str(), 0);
428 if((r = rh)) {
429 auin = ((struct gg_pubdir *) th->data)->uin;
430 gg_free_register(rh);
434 return r;
437 void gaduhook::lookup(const imsearchparams &params, verticalmenu &dest) {
438 searchdest = &dest;
439 while(!foundguys.empty()) {
440 delete foundguys.back();
441 foundguys.pop_back();
444 if(lreq) gg_pubdir50_free(lreq);
445 lreq = gg_pubdir50_new(GG_PUBDIR50_SEARCH_REQUEST);
447 if(params.uin) gg_pubdir50_add(lreq, GG_PUBDIR50_UIN, i2str(params.uin).c_str());
448 if(!params.nick.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_NICKNAME, params.nick.c_str());
449 if(!params.firstname.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_FIRSTNAME, params.firstname.c_str());
450 if(!params.lastname.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_LASTNAME, params.lastname.c_str());
451 if(!params.city.empty()) gg_pubdir50_add(lreq, GG_PUBDIR50_CITY, params.city.c_str());
453 if(params.onlineonly) gg_pubdir50_add(lreq, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE);
454 if(params.gender != genderUnspec) gg_pubdir50_add(lreq, GG_PUBDIR50_GENDER, params.gender == genderMale ? GG_PUBDIR50_GENDER_MALE : GG_PUBDIR50_GENDER_FEMALE);
456 gg_pubdir50(sess, lreq);
459 void gaduhook::sendupdateuserinfo(const icqcontact &c) {
460 gg_pubdir50_t req;
462 if(req = gg_pubdir50_new(GG_PUBDIR50_WRITE)) {
463 icqcontact::basicinfo bi = c.getbasicinfo();
464 icqcontact::moreinfo mi = c.getmoreinfo();
466 gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, c.getnick().c_str());
467 gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, bi.fname.c_str());
468 gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, bi.lname.c_str());
469 gg_pubdir50_add(req, GG_PUBDIR50_CITY, bi.city.c_str());
471 switch(mi.gender) {
472 case genderMale:
473 gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE);
474 break;
475 case genderFemale:
476 gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE);
477 break;
480 gg_pubdir50(sess, req);
481 gg_pubdir50_free(req);
485 // ----------------------------------------------------------------------------
487 void gaduhook::searchdone(void *p) {
488 gg_pubdir50_t sp = (gg_pubdir50_t) p;
490 if(searchdest && lreq) {
491 for(int i = 0; i < sp->count; i++) {
492 icqcontact *c = new icqcontact(imcontact(strtoul(gg_pubdir50_get(sp, i, GG_PUBDIR50_UIN), 0, 0), gadu));
493 icqcontact::basicinfo binfo = c->getbasicinfo();
495 const char *p = gg_pubdir50_get(sp, i, GG_PUBDIR50_NICKNAME);
496 if(p) {
497 c->setnick(p);
498 c->setdispnick(c->getnick());
501 p = gg_pubdir50_get(sp, i, GG_PUBDIR50_FIRSTNAME);
502 if(p) binfo.fname = p;
504 p = gg_pubdir50_get(sp, i, GG_PUBDIR50_LASTNAME);
505 if(p) binfo.lname = p;
507 string line;
509 p = gg_pubdir50_get(sp, i, GG_PUBDIR50_STATUS);
510 if(p && atoi(p) != GG_STATUS_NOT_AVAIL) line = "o ";
511 else line = " ";
513 line += c->getnick();
514 if(line.size() > 12) line.resize(12);
515 else line += string(12-line.size(), ' ');
517 line += " " + binfo.fname + " " + binfo.lname;
519 c->setbasicinfo(binfo);
521 foundguys.push_back(c);
522 searchdest->additem(conf.getcolor(cp_clist_gadu), c, line);
525 searchdest->redraw();
526 face.findready();
527 log(logSearchFinished, foundguys.size());
528 searchdest = 0;
530 } else if(ireq) {
531 icqcontact *c = clist.get(imcontact(strtoul(gg_pubdir50_get(sp, 0, GG_PUBDIR50_UIN), 0, 0), gadu));
532 string nick, gender;
533 const char *p = 0;
535 if(!c) c = clist.get(contactroot);
537 icqcontact::basicinfo bi = c->getbasicinfo();
538 icqcontact::moreinfo mi = c->getmoreinfo();
540 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_FIRSTNAME);
541 if(p) bi.fname = p;
543 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_LASTNAME);
544 if(p) bi.lname = p;
546 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_NICKNAME);
548 if(p) {
549 nick = p;
551 if((c->getnick() == c->getdispnick())
552 || (c->getdispnick() == i2str(c->getdesc().uin)))
553 c->setdispnick(nick);
555 c->setnick(nick);
557 } else {
558 c->setdispnick(bi.fname);
562 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_CITY);
563 if(p) bi.city = p;
565 p = gg_pubdir50_get(sp, 0, GG_PUBDIR50_GENDER);
566 if(p) {
567 gender = p;
569 if(gender == GG_PUBDIR50_GENDER_MALE) mi.gender = genderMale; else
570 if(gender == GG_PUBDIR50_GENDER_FEMALE) mi.gender = genderFemale; else
571 mi.gender = genderUnspec;
574 c->setbasicinfo(bi);
575 c->setmoreinfo(mi);
579 void gaduhook::userstatuschange(unsigned int uin, int status, const char *desc) {
580 icqcontact *c = clist.get(imcontact(uin, gadu));
581 if(c) {
582 imstatus ust = gg2imstatus(status);
583 logger.putonline(c, c->getstatus(), ust);
584 c->setstatus(ust);
586 if(desc)
587 awaymsgs[uin] = rusconv("wk", desc);
588 else
589 awaymsgs[uin] = "";
593 void gaduhook::userlistsend() {
594 int i;
595 vector<uin_t> uins;
596 icqcontact *c;
598 for(i = 0; i < clist.count; i++) {
599 c = (icqcontact *) clist.at(i);
601 if(c->getdesc().pname == proto && c->getdesc().uin)
602 uins.push_back(c->getdesc().uin);
605 auto_ptr<uin_t> cuins(new uin_t[uins.size()]);
606 /* TODO: allocated with new[], but will be freed by delete */
607 auto_ptr<char> ctypes(new char[uins.size()]);
609 for(vector<uin_t>::const_iterator iu = uins.begin(); iu != uins.end(); ++iu) {
610 cuins.get()[iu-uins.begin()] = *iu;
611 ctypes.get()[iu-uins.begin()] = GG_USER_NORMAL;
614 gg_notify_ex(sess, cuins.get(), ctypes.get(), uins.size());
617 void gaduhook::usernotify(unsigned int uin, int status, const char *desc,
618 unsigned int ip, int port, int version) {
619 imcontact ic(uin, gadu);
620 icqcontact *c = clist.get(ic);
622 if(c) {
623 struct in_addr addr;
624 addr.s_addr = ntohl(ip);
625 char *p = inet_ntoa(addr);
626 if(p) c->setlastip(p);
628 imstatus ust = gg2imstatus(status);
629 logger.putonline(c, c->getstatus(), ust);
630 c->setstatus(ust);
632 if(desc)
633 awaymsgs[ic.uin] = rusconv("wk", desc);
634 else
635 awaymsgs[ic.uin] = "";
640 // ----------------------------------------------------------------------------
642 const int token_char_height = 12;
643 const char token_id_char[] = {"0123456789abcdef"};
644 const char token_id[][15] = {
645 "..####..",
646 ".##..##.",
647 "##....##",
648 "##....##",
649 "##....##",
650 "##....##",
651 "##....##",
652 "##....##",
653 "##....##",
654 "##....##",
655 ".##..##.",
656 "..####..",
658 "...##",
659 "..###",
660 "#####",
661 "...##",
662 "...##",
663 "...##",
664 "...##",
665 "...##",
666 "...##",
667 "...##",
668 "...##",
669 "...##",
671 "..####..",
672 ".##..##.",
673 "##....##",
674 "##....##",
675 "......##",
676 ".....##.",
677 "....##..",
678 "...##...",
679 "..##....",
680 ".##.....",
681 "##......",
682 "########",
684 "..####..",
685 ".##..##.",
686 "##....##",
687 "##....##",
688 ".....##.",
689 "...###..",
690 ".....##.",
691 "......##",
692 "##....##",
693 "##....##",
694 ".##..##.",
695 "..####..",
697 ".....##.",
698 "....###.",
699 "...####.",
700 "...#.##.",
701 "..##.##.",
702 "..#..##.",
703 ".##..##.",
704 "##...##.",
705 "########",
706 ".....##.",
707 ".....##.",
708 ".....##.",
710 ".#######",
711 ".##.....",
712 ".##.....",
713 "##......",
714 "######..",
715 "##...##.",
716 "......##",
717 "......##",
718 "......##",
719 "##....##",
720 "##...##.",
721 ".#####..",
723 "..#####.",
724 ".##...##",
725 ".#.....#",
726 "##......",
727 "##.###..",
728 "###..##.",
729 "##....##",
730 "##....##",
731 "##....##",
732 "##....##",
733 ".##..##.",
734 "..####..",
736 "########",
737 "......##",
738 ".....##.",
739 ".....##.",
740 "....##..",
741 "....##..",
742 "...##...",
743 "...##...",
744 "...##...",
745 "..##....",
746 "..##....",
747 "..##....",
749 "..####..",
750 ".##..##.",
751 "##....##",
752 "##....##",
753 ".##..##.",
754 "..####..",
755 ".##..##.",
756 "##....##",
757 "##....##",
758 "##....##",
759 ".##..##.",
760 "..####..",
762 "..####..",
763 ".##..##.",
764 "##....##",
765 "##....##",
766 "##....##",
767 "##....##",
768 ".##..###",
769 "..###.##",
770 "......##",
771 "#.....#.",
772 "##...##.",
773 ".#####..",
775 "........",
776 "........",
777 "........",
778 "........",
779 ".#####..",
780 "##...##.",
781 ".....##.",
782 ".######.",
783 "##...##.",
784 "##...##.",
785 "##...##.",
786 ".####.##",
788 "##.....",
789 "##.....",
790 "##.....",
791 "##.....",
792 "######.",
793 "##...##",
794 "##...##",
795 "##...##",
796 "##...##",
797 "##...##",
798 "##...##",
799 "######.",
801 ".......",
802 ".......",
803 ".......",
804 ".......",
805 ".#####.",
806 "##...##",
807 "##...##",
808 "##.....",
809 "##.....",
810 "##...##",
811 "##...##",
812 ".#####.",
814 ".....##",
815 ".....##",
816 ".....##",
817 ".....##",
818 ".######",
819 "##...##",
820 "##...##",
821 "##...##",
822 "##...##",
823 "##...##",
824 "##...##",
825 ".######",
827 ".......",
828 ".......",
829 ".......",
830 ".......",
831 ".#####.",
832 "##...##",
833 "##...##",
834 "#######",
835 "##.....",
836 "##....#",
837 "##...##",
838 ".#####.",
840 "..###",
841 ".##..",
842 ".##..",
843 ".##..",
844 "#####",
845 ".##..",
846 ".##..",
847 ".##..",
848 ".##..",
849 ".##..",
850 ".##..",
851 ".##.."};
853 static int token_check(int nr, int x, int y, const char *ocr, int maxx, int maxy) {
854 int i;
856 for(i = nr*token_char_height; i < (nr+1)*token_char_height; i++, y++) {
857 int j, xx = x;
859 for(j = 0; token_id[i][j] && j + xx < maxx; j++, xx++) {
860 if(token_id[i][j] != ocr[y * (maxx + 1) + xx])
861 return 0;
865 return 1;
868 static char *token_ocr(const char *ocr, int width, int height, int length) {
869 int x, y, count = 0;
870 char *token;
872 token = (char *) malloc(length + 1);
873 memset(token, 0, length + 1);
875 for(x = 0; x < width; x++) {
876 for(y = 0; y < height - token_char_height; y++) {
877 int result = 0, token_part = 0;
879 do {
880 result = token_check(token_part++, x, y, ocr, width, height);
881 } while(!result && token_part < 16);
883 if(result && count < length)
884 token[count++] = token_id_char[token_part - 1];
888 if(count == length)
889 return token;
891 free(token);
893 return NULL;
896 string gaduhook::handletoken(struct gg_http *h) {
897 struct gg_token *t;
898 string fname, r;
900 if(!h)
901 return "";
903 if(gg_token_watch_fd(h) || h->state == GG_STATE_ERROR)
904 return "";
906 if(h->state != GG_STATE_DONE)
907 return "";
909 if(!(t = (struct gg_token *) h->data) || !h->body)
910 return "";
912 do {
913 fname = (getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
914 fname += "/gg.token." + i2str(getpid()) + i2str(time(0));
915 } while(!access(fname.c_str(), F_OK));
917 ofstream bf(fname.c_str());
919 if(bf.is_open()) {
920 bf.write(h->body, h->body_size);
921 bf.close();
922 } else {
923 return "";
926 #ifdef HAVE_LIBJPEG
928 struct jpeg_decompress_struct j;
929 struct jpeg_error_mgr e;
930 JSAMPROW buf[1];
931 int size, i;
932 char *token, *tmp;
933 FILE *f;
934 int ih = 0;
936 if(!(f = fopen(fname.c_str(), "rb")))
937 return "";
939 j.err = jpeg_std_error(&e);
940 jpeg_create_decompress(&j);
941 jpeg_stdio_src(&j, f);
942 jpeg_read_header(&j, TRUE);
943 jpeg_start_decompress(&j);
945 size = j.output_width * j.output_components;
946 buf[0] = (JSAMPLE *) malloc(size);
948 token = (char *) malloc((j.output_width + 1) * j.output_height);
950 while(j.output_scanline < j.output_height) {
951 jpeg_read_scanlines(&j, buf, 1);
953 for(i = 0; i < j.output_width; i++, ih++)
954 token[ih] = (buf[0][i*3] + buf[0][i*3+1] + buf[0][i*3+2] < 384) ? '#' : '.';
956 token[ih++] = 0;
959 if((tmp = token_ocr(token, j.output_width, j.output_height, t->length))) {
960 r = tmp;
961 free(tmp);
964 free(token);
966 jpeg_finish_decompress(&j);
967 jpeg_destroy_decompress(&j);
969 free(buf[0]);
970 fclose(f);
972 unlink(fname.c_str());
974 #endif
976 return r;
979 #endif