1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2003-2005 Martin Maurer (martinmaurer@gmx.at)
4  * Copyright (C) 2007-2014 Licq developers <licq-dev@googlegroups.com>
5  *
6  * Licq is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Licq is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Licq; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "licq-osd.h"
22 
23 #include <cerrno>
24 #include <cstdio>
25 #include <cstring>
26 #include <ctime>
27 #include <iconv.h>
28 #include <iostream>
29 #include <langinfo.h>
30 #include <libintl.h>
31 #include <locale.h>
32 #include <unistd.h>
33 
34 #include <licq/logging/log.h>
35 #include <licq/contactlist/owner.h>
36 #include <licq/contactlist/user.h>
37 #include <licq/contactlist/usermanager.h>
38 #include <licq/daemon.h>
39 #include <licq/event.h>
40 #include <licq/inifile.h>
41 #include <licq/pluginsignal.h>
42 #include <licq/userevents.h>
43 
44 #include "my_xosd.h"
45 #include "licq_osd.conf.h"
46 
47 using Licq::User;
48 using Licq::UserId;
49 using Licq::gLog;
50 using Licq::gUserManager;
51 using std::string;
52 
53 #define _(String) gettext (String)
54 
55 // if you don't want to use codepage translation, comment out this
56 #define CP_TRANSLATE
57 
58 struct Config {
59     unsigned long Showmessages; //=SHOWLOGON;
60     unsigned long Showlogon; //=SHOWLOGON;
61     unsigned long ShowStatusChange; //=SHOWSTATUSCHANGE;
62     unsigned long ShowAutoResponseCheck; //=SHOWAUTORESPONSECHECK;
63     unsigned long quiettimeout; //=QUIETTIMEOUT;
64     string pluginfont;
65   unsigned showInModes;
66   unsigned showMsgsInModes;
67     string colour;
68     string controlcolour;
69     bool osd_wait;
70     unsigned long timeout;
71     unsigned long hoffset;
72     unsigned long voffset;
73     unsigned long linelen, lines;
74     unsigned long shadowoffset;
75     unsigned long outlineoffset;
76     unsigned long DelayPerCharacter;
77     string vpos;
78     string hpos;
79     string shadowcolour;
80     string outlinecolour;
81     string localencoding;
82     bool marksecuremessages;
83 } config;
84 
85 
86 // some forward declarations
87 void ProcessSignal(const Licq::PluginSignal* s);
88 #ifdef CP_TRANSLATE
89     const char *get_iconv_encoding_name(const char *licq_encoding_name);
90 string my_translate(const UserId& userId, const string& msg, const char* userenc);
91 #endif
92 
93 // some variables representing the internal state
94 time_t disabletimer=0;
95 bool Online;
96 bool Enabled;
97 bool Configured=false; // is the xosd display initialized?
98 
OsdPlugin()99 OsdPlugin::OsdPlugin()
100 {
101   // Empty
102 }
103 
104 // status of plugin - so they can be deactivated
105 // not implemented for this plugin
isEnabled() const106 bool OsdPlugin::isEnabled() const
107 {
108   return Enabled;
109 }
110 
111 // a wrapper so we can log from my_xosd.cpp to standard licq log
log(int mode,const char * message)112 void log(int mode, const char *message)
113 {
114     if (mode==0) // warn/info
115       gLog.warning("%s", message);
116     if (mode==1) // error
117       gLog.error("%s", message);
118 }
119 
120 // issue a few warnings for wrong config values.
verifyconfig(string pluginfont,unsigned long,unsigned long hoffset,unsigned long voffset,string vpos,string hpos,unsigned long lines,unsigned long linelen,unsigned long quiettimeout,string colour,bool,unsigned long showmessages,unsigned long showlogon,unsigned long shadowoffset,unsigned long outlineoffset,string shadowcolour,string outlinecolour,string localencoding)121 void verifyconfig(string pluginfont, unsigned long /* timeout */,
122     unsigned long hoffset, unsigned long voffset, string vpos, string hpos,
123     unsigned long lines, unsigned long linelen, unsigned long quiettimeout,
124     string colour, bool /* wait */, unsigned long showmessages,
125     unsigned long showlogon, unsigned long shadowoffset,
126     unsigned long outlineoffset, string shadowcolour, string outlinecolour,
127     string localencoding)
128 {
129     try {
130 	if ((pluginfont=="") || (pluginfont.at(0)=='"') || (pluginfont.at(0)=='\''))
131 	    gLog.error("CONFIG: Invalid pluginfont %s. This will fail", pluginfont.c_str());
132 	if (hoffset>10000)
133 	    gLog.warning("CONFIG: Very high horizontal offset %lu. This might fail", hoffset);
134 	if (voffset>10000)
135 	    gLog.warning("CONFIG: Very high vertical offset %lu. This might fail", voffset);
136 	if ((vpos!="top") && (vpos!="bottom") && (vpos!="middle"))
137             gLog.error("CONFIG: Invalid vertical position %s. Should be \"top\" or \"bottom\" or \"middle\". This will fail.", vpos.c_str());
138 	if ((hpos!="left") && (hpos!="right") && (hpos!="center"))
139 	    gLog.error("CONFIG: Invalid horizontal position %s. Should be \"left\" or \"right\" or \"center\". This will fail.", hpos.c_str());
140 	if (lines>50)
141 	    gLog.error("CONFIG: More than 50 lines not allowed. You used %lu", lines);
142 	if (linelen>500)
143 	    gLog.error("CONFIG: More than 500 characters per line not allowed. You used %lu", linelen);
144 	if (quiettimeout>500)
145 	    gLog.warning("CONFIG: Your quiettimeout %lu is higher than 500. Do you really want this?", quiettimeout);
146 	if (colour=="")
147 	    gLog.error("CONFIG: Invalid colour %s. For possible values look at rgb.txt from your Xfree86 distribution", colour.c_str());
148 	if (showmessages>4)
149             gLog.error("CONFIG: Invalid value for showmessages %lu", showmessages);
150 	if (showlogon>2)
151             gLog.error("CONFIG: Invalid value for showlogon %lu", showlogon);
152 	if (shadowoffset>200)
153             gLog.warning("CONFIG: Very high Shadowoffset value %lu", shadowoffset);
154 	if (outlineoffset>200)
155             gLog.warning("CONFIG: Very high Outlineoffset value %lu", outlineoffset);
156 	if (shadowcolour=="")
157 	    gLog.error("CONFIG: Invalid shadow colour %s. For possible values look at rgb.txt from your Xfree86 distribution", shadowcolour.c_str());
158 	if (outlinecolour=="")
159 	    gLog.error("CONFIG: Invalid outline colour %s. For possible values look at rgb.txt from your Xfree86 distribution", outlinecolour.c_str());
160 	if (localencoding=="")
161 	    gLog.warning("Localencoding could not be determined from your locale");
162     }
163     catch (...)
164     {
165 	gLog.error("CONFIG: Exception while verifying config values");
166     }
167 }
168 
parseShowInModesStr(const char * ShowInModesStr)169 unsigned parseShowInModesStr(const char* ShowInModesStr)
170 {
171   unsigned showInModes = 0;
172     if (strstr(ShowInModesStr, "Online"))
173     showInModes |= User::OnlineStatus;
174     if (strstr(ShowInModesStr, "FreeForChat"))
175     showInModes |= User::FreeForChatStatus;
176     if (strstr(ShowInModesStr, "Away"))
177     showInModes |= User::AwayStatus;
178     if (strstr(ShowInModesStr, "NA"))
179     showInModes |= User::NotAvailableStatus;
180     if (strstr(ShowInModesStr, "Occupied"))
181     showInModes |= User::OccupiedStatus;
182     if (strstr(ShowInModesStr, "DND"))
183     showInModes |= User::DoNotDisturbStatus;
184     if (strstr(ShowInModesStr, "Invisible"))
185     showInModes |= User::InvisibleStatus;
186   return showInModes;
187 }
188 
189 // called once on Load of the plugin
init(int,char **)190 bool OsdPlugin::init(int /* argc */, char** /* argv */)
191 {
192   string showInModes;
193   string showMsgsInModes;
194     try {
195 	Configured=false;
196 	gLog.info("OSD Plugin initializing\n");
197 
198     string filename = "licq_osd.conf";
199     Licq::IniFile conf(filename);
200     if (!conf.loadFile()) // no configfile found
201     {
202       filename = Licq::gDaemon.baseDir() + filename;
203 	    FILE *f = fopen(filename.c_str(), "w");
204 	    if (f) // create config file
205 	    {
206 		fprintf(f, "%s", OSD_CONF);
207 		fclose(f);
208 	    }
209 	    else // configfile cannot be created
210 	    {
211 		gLog.error("Configfile can not be created. Check the permissions on %s", filename.c_str());
212 		return 0;
213 	    }
214       if (!conf.loadFile()) // configfile cannot be read after creating it - this should not happen
215       {
216 		gLog.error("Configfile created but cannot be loaded. This should not happen");
217 		return 0;
218       }
219     }
220     conf.setSection("Main");
221     conf.get("Wait", config.osd_wait, WAIT);
222     conf.get("Font", config.pluginfont, FONT);
223     conf.get("Timeout", config.timeout, DISPLAYTIMEOUT);
224     conf.get("HOffset", config.hoffset, HORIZONTAL_OFFSET);
225     conf.get("VOffset", config.voffset, VERTICAL_OFFSET);
226     conf.get("VPos", config.vpos, VPOS);
227     conf.get("HPos", config.hpos, HPOS);
228     conf.get("Lines", config.lines, LINES);
229     conf.get("Linelen", config.linelen, LINELEN);
230     conf.get("Quiettimeout", config.quiettimeout, QUIETTIMEOUT);
231     conf.get("Colour", config.colour, COLOUR);
232     conf.get("ControlColour", config.controlcolour, CONTROLCOLOUR);
233     conf.get("Showmessages", config.Showmessages, SHOWMESSAGES);
234     conf.get("ShowAutoResponseCheck", config.ShowAutoResponseCheck, SHOWAUTORESPONSECHECK);
235     conf.get("Showlogon", config.Showlogon, SHOWLOGON);
236     conf.get("DelayPerCharacter", config.DelayPerCharacter, DELAYPERCHARACTER);
237     conf.get("ShowStatusChange", config.ShowStatusChange, SHOWSTATUSCHANGE);
238     conf.get("ShadowOffset", config.shadowoffset, SHADOW_OFFSET);
239     conf.get("OutlineOffset", config.outlineoffset, OUTLINE_OFFSET);
240     conf.get("MarkSecureMessages", config.marksecuremessages, MARKSECUREMESSAGES);
241     conf.get("ShadowColour", config.shadowcolour, SHADOW_COLOUR);
242     conf.get("OutlineColour", config.outlinecolour, OUTLINE_COLOUR);
243     conf.get("ShowInModes", showInModes, SHOWINMODESSTR);
244     conf.get("ShowMsgsInModes", showMsgsInModes, SHOWMSGSINMODESSTR);
245 
246     config.showInModes = parseShowInModesStr(showInModes.c_str());
247     config.showMsgsInModes = parseShowInModesStr(showMsgsInModes.c_str());
248 
249 	setlocale(LC_ALL, "");
250 
251 	config.localencoding=nl_langinfo(CODESET);
252 	bindtextdomain (PACKAGE, LOCALEDIR);
253 	bind_textdomain_codeset(PACKAGE, config.localencoding.c_str());
254 	textdomain (PACKAGE);
255 	// check config values for validity
256 	verifyconfig(config.pluginfont, config.timeout, config.hoffset, config.voffset, config.vpos, config.hpos, config.lines, config.linelen, config.quiettimeout,
257 		     config.colour, config.osd_wait, config.Showmessages, config.Showlogon, config.shadowoffset, config.outlineoffset, config.shadowcolour,
258 		     config.outlinecolour, config.localencoding);
259 
260 	return 1;
261     }
262     catch (...)
263     {
264 	return 0;
265     }
266 }
267 
268 
269 // run method of plugin
run()270 int OsdPlugin::run()
271 {
272     // register plugin at the licq daemon
273   int nPipe = getReadPipe();
274   setSignalMask(Licq::PluginSignal::SignalUser |
275       Licq::PluginSignal::SignalLogon | Licq::PluginSignal::SignalLogoff);
276     bool Exit=false; // exit plugin?
277     char buf[16];
278 
279     if (nPipe==-1)
280     {
281 	gLog.warning("Invalid Pipe received");
282 	return 1;
283     }
284 
285     disabletimer=time(0);
286     Enabled=true;
287     Online=false;
288     // as long as no shutdown command from licq daemon received
289     while (!Exit)
290     {
291 	read(nPipe, buf, 1); // Information about a new signal is sent through pipe
292 	if (!Configured)
293 	{
294 	    if (!my_xosd_init(config.pluginfont, config.colour, config.hoffset, config.voffset, config.vpos, config.hpos, config.timeout, config.DelayPerCharacter, config.lines, config.linelen, config.osd_wait, config.shadowoffset, config.outlineoffset, config.shadowcolour, config.outlinecolour))
295 		return 0;
296 	    Configured=true;
297 	}
298 
299 	switch (buf[0])
300 	{
301       case PipeSignal:
302       {
303         // read the actual signal from the daemon
304         ProcessSignal(popSignal().get());
305         break;
306       }
307 
308 	    // An event is pending - skip it - shouldnt happen
309 	    // events are responses to some requests to the licq daemon
310 	    // like send a message - we never do such a thing
311       case PipeEvent:
312       {
313         gLog.warning("Event received - should not happen in this plugin");
314         popEvent();
315         break;
316       }
317 	    // shutdown command from daemon
318 	    // every plugin has to implement this command
319       case PipeShutdown:
320       {
321 		Exit = true;
322 		gLog.info("OSD Plugin shutting down");
323 		break;
324 	    }
325 
326       case PipeDisable:
327 	    Enabled=false;
328 	    gLog.info("OSD Plugin disabled");
329 	    break;
330       case PipeEnable:
331 	    Enabled=true;
332 	    gLog.info("OSD Plugin enabled");
333 	    break;
334 	default:
335 	    gLog.warning("Unknown message type %d", buf[0]);
336 	}
337     }
338 
339     if (Configured)
340     {
341 	my_xosd_exit();
342         Configured=false;
343     }
344 
345     return 0;
346 }
347 
ProcessSignal(const Licq::PluginSignal * s)348 void ProcessSignal(const Licq::PluginSignal* s)
349 {
350     string username;
351     bool notify=false;
352     bool ignore=false;
353   bool want_osd = true; // if we are in DND,... we maybe don't want OSD
354     bool want_osd_msgs_only=false; // though we don't want OSD we want msgs
355     bool secure=false;
356   unsigned status = User::OnlineStatus;
357   const Licq::UserEvent* e = NULL;
358 
359   switch (s->signal()) // signaltype
360   {
361     case Licq::PluginSignal::SignalUser:
362     {
363 	    // FIX: we seem to get others logged on messages before
364 	    // our own one - so start quiettimeout in this case too
365 	    if (!Online)
366 	    {
367 		Online=true;
368 		disabletimer=time(0);
369 	    }
370 	    if (disabletimer) // no time() calls when timeout has been done
371 	    {
372 		if (time(0)-disabletimer>=(time_t)config.quiettimeout)
373 		    disabletimer=0;
374 		else
375 		    want_osd=false;
376 	    }
377 
378             if (want_osd)
379 	    {
380         Licq::OwnerReadGuard o(s->userId().ownerId());
381         if (o.isLocked())
382         {
383           status = o->status();
384 		    //want_osd=true;
385 
386           if ((status & User::DoNotDisturbStatus) && (!(config.showInModes & User::DoNotDisturbStatus)))
387 			want_osd=false;
388           else if ((status & User::OccupiedStatus) && (!(config.showInModes & User::OccupiedStatus)))
389 			want_osd=false;
390           else if ((status & User::NotAvailableStatus) && (!(config.showInModes & User::NotAvailableStatus)))
391 			want_osd=false;
392           else if ((status & User::AwayStatus) && (!(config.showInModes & User::AwayStatus)))
393 			want_osd=false;
394           else if ((status & User::FreeForChatStatus) && (!(config.showInModes & User::FreeForChatStatus)))
395 			want_osd=false;
396           else if ((status & User::InvisibleStatus) && (!(config.showInModes & User::InvisibleStatus)))
397 			want_osd=false;
398           else if (!(config.showInModes & User::OnlineStatus))
399             want_osd = false;
400 
401 		    if (!want_osd) {
402             if ((status & User::DoNotDisturbStatus) && (config.showMsgsInModes & User::DoNotDisturbStatus))
403 			    want_osd_msgs_only=true;
404             else if ((status & User::OccupiedStatus) && (config.showMsgsInModes & User::OccupiedStatus))
405 			    want_osd_msgs_only=true;
406             else if ((status & User::NotAvailableStatus) && (config.showMsgsInModes & User::NotAvailableStatus))
407 			    want_osd_msgs_only=true;
408             else if ((status & User::AwayStatus) && (config.showMsgsInModes & User::AwayStatus))
409 			    want_osd_msgs_only=true;
410             else if ((status & User::FreeForChatStatus) && (config.showMsgsInModes & User::FreeForChatStatus))
411 			    want_osd_msgs_only=true;
412             else if ((status & User::InvisibleStatus) && (config.showMsgsInModes & User::InvisibleStatus))
413 			    want_osd_msgs_only=true;
414             else if (config.showMsgsInModes & User::OnlineStatus)
415               want_osd_msgs_only = true;
416 
417 			if (want_osd_msgs_only) {
418 			    want_osd=true;
419 			}
420 		    }
421         }
422 	    }
423 
424 	    if (want_osd)
425 	    {
426         Licq::UserReadGuard u(s->userId());
427         if (u.isLocked())
428         {
429 		    // user alias as displayed in licq -
430 		    // (if no alias known, ICQ number as string returned)
431           username = u->getAlias();
432 		    notify=u->OnlineNotify();
433 		    ignore=u->InvisibleList() || u->IgnoreList();
434           status = u->status();
435             secure=u->Secure();
436 
437           username = my_translate(s->userId(), username, "UTF-8");
438 
439           if (s->subSignal() == Licq::PluginSignal::UserEvents && s->argument() > 0) // message
440           {
441 			// get the user event whose ID we got as the signal argument
442             e = u->EventPeekId(s->argument());
443 
444 			if (e == NULL) // event not found
445 			{
446               gLog.warning("Event for user %s not found", s->userId().toString().c_str());
447                             want_osd=false;
448 			}
449 		    }
450 		}
451 		else
452 		{
453           gLog.warning("User %s not found", s->userId().toString().c_str());
454 		    want_osd=false;
455 		}
456 	    }
457 
458 	    if (!Enabled)
459 		want_osd=false;
460 	    if (ignore)
461 		want_osd=false;
462       if (s->userId().isOwner()) // no messages for our own actions
463                 want_osd=false;
464 
465 	    // user checked our auto-response
466       // this is some evil functionality - most users don't know
467 	    // that you realize when they check your auto-response
468 	    // i implemented this just for fun :)
469 	    if ((want_osd) && (!want_osd_msgs_only) && // not ignored or disabled
470           (s->subSignal() == Licq::PluginSignal::UserEvents) &&
471           (s->argument() == 0) // auto response
472           )
473       {
474 		if (
475 		     (config.ShowAutoResponseCheck==1) ||  // display auto_reponse checks
476 		     ((config.ShowAutoResponseCheck==2)&&(notify))
477 		   )
478 		{
479 		    string msg(username);
480 		    msg+=_(" checked your auto-response");
481 		    my_xosd_display("autoresponse", msg, config.controlcolour);
482 		}
483 	    }
484 
485 	    // messages that are not sent by myself
486 	    if ((want_osd) && // not ignored or disabled
487           (s->subSignal() == Licq::PluginSignal::UserEvents) &&
488           (s->argument() > 0) // message
489 	       )
490 	    {
491 		if (
492 		    (config.Showmessages==1) ||  // display messages on screen if configured in config file
493 		    ((config.Showmessages==2)&&(notify))
494 		   )
495 		{
496 		    string msg="";
497 		    if (secure && config.marksecuremessages)
498 			msg="(S) ";
499           msg += my_translate(s->userId(), e->text(), "UTF-8");
500 		    my_xosd_display(username.c_str(), msg.c_str(), config.colour);
501 		}
502 		if (
503 		    (config.Showmessages==3) ||   // only display information about message on screen
504 		    ((config.Showmessages==4)&&(notify))
505 		   )
506 		{
507 		    string msg=_("Message from ");
508 		    msg += username;
509 		    my_xosd_display("", msg, config.colour);
510 		}
511 	    }
512       if (want_osd && (!want_osd_msgs_only) && (s->subSignal() == Licq::PluginSignal::UserStatus)) // logon/logoff or status change
513 	    {
514 		string msg = username;
515         if (s->argument()!=0) // logon/logoff
516         {
517                     if (
518 			(config.Showlogon==1) ||
519 			((notify)&&(config.Showlogon==2))
520 		       )
521           {
522             if (s->argument() > 0)
523 			    msg+=_(" logged on");
524 			else
525 			    msg+=_(" logged off");
526 //			my_xosd_settimeout(config.timeout);
527 			my_xosd_display("", msg, config.controlcolour);
528 		    }
529 		}
530 		else // status change
531 		{
532                     if (
533 			(config.ShowStatusChange==1) ||
534 			((notify)&&(config.ShowStatusChange==2))
535 		       )
536 		    {
537 			string msg = username;
538 			msg+=_(" changed status to: ");
539             if (status == User::OfflineStatus)
540               msg += _("offline");
541             else if (status & User::DoNotDisturbStatus)
542 			    msg+=_("do not disturb");
543             else if (status & User::OccupiedStatus)
544 			    msg+=_("occupied");
545             else if (status & User::NotAvailableStatus)
546 			    msg+=_("not available");
547             else if (status & User::AwayStatus)
548 			    msg+=_("away");
549             else if (status & User::FreeForChatStatus)
550 			    msg+=_("free for chat");
551             else if (status & User::InvisibleStatus)
552 			    msg+=_("invisible");
553             else if (status & User::IdleStatus)
554               msg += _("idle");
555             else
556               msg += _("online");
557 //			my_xosd_settimeout(config.timeout);
558 			my_xosd_display("", msg, config.controlcolour);
559 		    }
560 		}
561 
562 	    }
563 	}
564 	break;
565     case Licq::PluginSignal::SignalLogoff:
566 	gLog.info("OSD Plugin received logoff");
567 	disabletimer=time(0);
568 	Online=false;
569 	break;
570     case Licq::PluginSignal::SignalLogon:
571 	gLog.info("OSD Plugin received logon");
572 	disabletimer=time(0);
573 	Online=true;
574 	break;
575 	// we are not interested in those
576     case Licq::PluginSignal::SignalAddedToServer:
577     case Licq::PluginSignal::SignalPluginEvent:
578       break;
579     default: // shouldnt happen
580       gLog.warning("Unknown signal %d", s->signal());
581       break;
582     }
583 }
584 
585 #ifdef CP_TRANSLATE
586 // this function maps the licq character encoding names to the iconv ones.
587 // this is a mess and should be replaced
588 // FIXME !!!
get_iconv_encoding_name(const char * licq_encoding_name)589 const char *get_iconv_encoding_name(const char *licq_encoding_name)
590 {
591     static const char iso8859_1[] = "ISO-8859-1";
592     static const char iso8859_2[] = "ISO-8859-2";
593     static const char iso8859_3[] = "ISO-8859-3";
594     static const char iso8859_5[] = "ISO-8859-5";
595     static const char iso8859_6[] = "ISO-8859-6";
596     static const char iso8859_7[] = "ISO-8859-7";
597     static const char iso8859_8_i[] = "ISO-8859-8";
598     static const char iso8859_9[] = "ISO-8859-9";
599     static const char iso8859_15[] = "ISO-8859-15";
600 
601     static const char cp_1250[] = "CP1250";
602     static const char cp_1251[] = "CP1251";
603     static const char cp_1252[] = "CP1252";
604     static const char cp_1253[] = "CP1253";
605     static const char cp_1254[] = "CP1254";
606     static const char cp_1255[] = "CP1255";
607     static const char cp_1256[] = "CP1256";
608     static const char cp_1257[] = "CP1257";
609 
610     static const char gbk[] = "GBK";
611     static const char big5[] = "BIG-5";
612     static const char koi8_r[] = "KOI8R";
613     static const char shift_jis[] = "SHIFT-JIS";
614     static const char jis7[] = ""; // no idea
615     static const char eucjp[] = "EUCJP";
616     static const char euckr[] = "EUCKR";
617     static const char tscii[] = "TSCII";
618     static const char tis_620[] = "TIS620";
619     static const char koi8_u[] = "KOI8U";
620     static const char utf_8[] = "UTF-8";
621 
622     if (strcasecmp(licq_encoding_name, "ISO 8859-1")==0)
623 	return iso8859_1;
624     if (strcasecmp(licq_encoding_name, "ISO 8859-2")==0)
625 	return iso8859_2;
626     if (strcasecmp(licq_encoding_name, "ISO 8859-3")==0)
627 	return iso8859_3;
628     if (strcasecmp(licq_encoding_name, "ISO 8859-5")==0)
629 	return iso8859_5;
630     if (strcasecmp(licq_encoding_name, "ISO 8859-6")==0)
631 	return iso8859_6;
632     if (strcasecmp(licq_encoding_name, "ISO 8859-7")==0)
633 	return iso8859_7;
634     if (strcasecmp(licq_encoding_name, "ISO 8859-8-I")==0)
635 	return iso8859_8_i;
636     if (strcasecmp(licq_encoding_name, "ISO 8859-9")==0)
637 	return iso8859_9;
638     if (strcasecmp(licq_encoding_name, "ISO 8859-15")==0)
639 	return iso8859_15;
640 
641     if (strcasecmp(licq_encoding_name, "CP 1250")==0)
642 	return cp_1250;
643     if (strcasecmp(licq_encoding_name, "CP 1251")==0)
644 	return cp_1251;
645     if (strcasecmp(licq_encoding_name, "CP 1252")==0)
646 	return cp_1252;
647     if (strcasecmp(licq_encoding_name, "CP 1253")==0)
648 	return cp_1253;
649     if (strcasecmp(licq_encoding_name, "CP 1254")==0)
650 	return cp_1254;
651     if (strcasecmp(licq_encoding_name, "CP 1255")==0)
652 	return cp_1255;
653     if (strcasecmp(licq_encoding_name, "CP 1256")==0)
654 	return cp_1256;
655     if (strcasecmp(licq_encoding_name, "CP 1257")==0)
656 	return cp_1257;
657 
658     if (strcasecmp(licq_encoding_name, "GBK")==0)
659 	return gbk;
660     if (strcasecmp(licq_encoding_name, "BIG5")==0)
661 	return big5;
662     if (strcasecmp(licq_encoding_name, "KOI8-R")==0)
663 	return koi8_r;
664     if (strcasecmp(licq_encoding_name, "Shift-JIS")==0)
665 	return shift_jis;
666     if (strcasecmp(licq_encoding_name, "JIS7")==0)
667 	return jis7;
668     if (strcasecmp(licq_encoding_name, "eucJP")==0)
669 	return eucjp;
670     if (strcasecmp(licq_encoding_name, "eucKR")==0)
671 	return euckr;
672     if (strcasecmp(licq_encoding_name, "TSCII")==0)
673 	return tscii;
674     if (strcasecmp(licq_encoding_name, "TIS-620")==0)
675 	return tis_620;
676     if (strcasecmp(licq_encoding_name, "KOI8-U")==0)
677 	return koi8_u;
678     if (strcasecmp(licq_encoding_name, "UTF-8")==0)
679 	return utf_8;
680     return licq_encoding_name;
681 }
682 
683 
684 //translates incoming messages or user names to our codepage.
685 // this works only if it is convertable by iconv
686 // the codepage of the other user is determined by the UserEncoding property of
687 // the other user. (change it for example via the qt-gui message window)
688 // Licq:PluginSignal is needed to get the User for this message -
689 // some day i will do this more elegant
my_translate(const UserId &,const string & msg,const char * userenc)690 string my_translate(const UserId& /* userId */, const string& msg, const char* userenc)
691 {
692     iconv_t conv;
693     size_t fromsize, tosize, ressize;
694     const char *msgptr;
695     char *resptr;
696 
697     if (config.localencoding == "") {
698       gLog.warning("Didn't get our local encoding");
699     return msg;
700   }
701 
702     if ((userenc == 0) || (*userenc == 0))
703 	{
704           gLog.info("No translation needs to be done");
705     return msg;
706   }
707   conv = iconv_open((config.localencoding + "//IGNORE").c_str(), get_iconv_encoding_name(userenc));
708 
709     // no translation possible?
710     if (conv==(iconv_t)-1)
711     {
712       gLog.warning("Error initializing iconv");
713     return msg;
714   }
715 
716   fromsize = msg.size();
717     tosize=fromsize;
718     ressize=tosize;
719   msgptr = msg.c_str();
720 
721   char* result = (char*)malloc(msg.size() + 1);
722     resptr=result;
723 
724     while ((fromsize>0) && (tosize>0))
725     {
726 	if ((int)iconv(conv, (ICONV_CONST char **)&msgptr, &fromsize, &resptr, &tosize)==-1)
727 	{
728 	    // array is not enough
729 	    if (errno == E2BIG)
730 	    {
731 		// add fromsize + 4 more characters to array
732 		result = (char*) realloc(result,ressize + fromsize + 4);
733 		resptr = result + ressize;
734 		ressize += fromsize + 4;
735 		tosize += fromsize + 4;
736 		continue;
737 	    }
738 	    gLog.warning("Error in my_translate - stopping translation, error on %ld. char",
739                          (long int)(msgptr - msg.c_str() + 1));
740       free(result);
741       return msg;
742     }
743   }
744     *resptr = 0;
745     iconv_close(conv);
746 
747   string ret(result);
748   free(result);
749   return ret;
750 }
751 #else
752 // a dummy function which helps me to remove the #ifdef CP_TRANSLATEs in a lot of my code
my_translate(const UserId &,const string & msg,Licq::PluginSignal *)753 string my_translate(const UserId& /* userId */, const string& msg, Licq::PluginSignal* /* s */)
754 {
755     return strdup(msg.c_str());
756 }
757 
758 #endif
759