1 /*
2   LibRCC - external module interfacying libtranslate library
3 
4   Copyright (C) 2005-2008 Suren A. Chilingaryan <csa@dside.dyndns.org>
5 
6   This library is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License version 2.1 or later
8   as published by the Free Software Foundation.
9 
10   This library is distributed in the hope that it will be useful, but WITHOUT
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
13   for more details.
14 
15   You should have received a copy of the GNU Lesser General Public License
16   along with this program; if not, write to the Free Software Foundation, Inc.,
17   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 
25 #include "../config.h"
26 
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif /* HAVE_UNISTD_H */
30 #ifdef HAVE_SYS_TYPES_H
31 # include <sys/types.h>
32 #endif /* HAVE_SYS_TYPES_H */
33 #ifdef HAVE_SYS_STAT_H
34 # include <sys/stat.h>
35 #endif /* HAVE_SYS_STAT_H */
36 
37 #ifdef HAVE_LIBTRANSLATE
38 #include <translate.h>
39 #endif /* HAVE_LIBTRANSLATE */
40 
41 #include "../src/rccexternal.h"
42 #include "../src/rcctranslate.h"
43 #include "../src/rccdb4.h"
44 #include "rcclibtranslate.h"
45 #include "compat.h"
46 
47 #ifdef HAVE_LIBTRANSLATE
48 static TranslateSession *session = NULL;
49 static db4_context db4ctx = NULL;
50 
51 static int exitflag = 0;
52 static GMutex *mutex = NULL;
53 static GCond *cond = NULL;
54 static GQueue *queue = NULL;
55 static GThread *thread = NULL;
56 
57 extern char rcc_external_offline;
58 
rccCreateKey(const char * from,const char * to,const char * data,size_t * keysize)59 static char *rccCreateKey(const char *from, const char *to, const char *data, size_t *keysize) {
60     char *res;
61 
62     *keysize = strlen(data) + 4;
63     res = (char*)malloc((*keysize+1)*sizeof(char));
64     if (res) sprintf(res,"%s%s%s",from,to,data);
65     return res;
66 }
67 
rccTranslateFixEOL(char * result,const char * text)68 static char *rccTranslateFixEOL(char *result, const char *text) {
69     size_t i,j;
70     char *res;
71 
72     if (!result) return result;
73     if (strstr(text, "\r\n")) return result;
74 
75     res = (char*)malloc((strlen(result)+1)*sizeof(char));
76     if (!res) {
77 	free(result);
78 	return NULL;
79     }
80 
81     for (i=0, j=0;result[i];i++) {
82 	if ((result[i]=='\r')&&(result[i+1]=='\n')) i++;
83 	else res[j++] = result[i];
84     }
85     res[j] = 0;
86     free(result);
87     return res;
88 }
89 
rccLibPostponed(void * info)90 static void *rccLibPostponed(void *info) {
91     char *result;
92     char *data;
93     char from[3];
94     char to[3];
95     size_t datalen;
96 
97     from[2] = 0;
98     to[2] = 0;
99 
100     g_mutex_lock(mutex);
101     while ((!exitflag)||(rcc_external_offline)) {
102 	data = (char*)g_queue_pop_head(queue);
103 	if (data) {
104 	    g_mutex_unlock(mutex);
105 
106 	    datalen = strlen(data);
107 
108 	    memcpy(from, data, 2);
109 	    memcpy(to, data + 2, 2);
110 
111 	    result = rccDb4GetKey(db4ctx, data, datalen);
112 	    if (result) free(result);
113 	    else {
114 		result = translate_session_translate_text(session, data + 4, from, to, NULL, NULL, NULL);
115 
116 		if (result) {
117 		    result = rccTranslateFixEOL(result, data+4);
118 		    rccDb4SetKey(db4ctx, data, datalen, result);
119 		    free(result);
120 		}
121 	    }
122 
123 	    free(data);
124 	    g_mutex_lock(mutex);
125 	} else {
126 	    if (exitflag) break;
127 	    g_cond_wait(cond, mutex);
128 	}
129     }
130     g_mutex_unlock(mutex);
131     return NULL;
132 }
133 
134 #endif /* HAVE_LIBTRANSLATE */
135 
136 
rccLibTranslateInit(const char * rcc_home_dir)137 int rccLibTranslateInit(const char *rcc_home_dir) {
138 #ifdef HAVE_LIBTRANSLATE
139     size_t size;
140     char *dbname;
141     GSList *list = NULL;
142 
143     const char *http_proxy;
144 
145     if (!translate_init(NULL)) return -1;
146 
147     http_proxy = getenv("HTTP_PROXY");
148     if (!http_proxy) http_proxy = getenv("http_proxy");
149     translate_set_proxy(http_proxy);
150 
151     list = translate_get_services();
152 
153     session = translate_session_new(list);
154 
155     g_slist_foreach(list, (GFunc) g_object_unref, NULL);
156     g_slist_free(list);
157 
158 
159     size = strlen(rcc_home_dir) + 32;
160     dbname = (char*)malloc(size*sizeof(char));
161     if (dbname) {
162         sprintf(dbname,"%s/.rcc/",rcc_home_dir);
163 	mkdir(dbname, 00755);
164 
165 	sprintf(dbname,"%s/.rcc/libtranslate.db/",rcc_home_dir);
166 	mkdir(dbname, 00755);
167 
168 	db4ctx = rccDb4CreateContext(dbname, 0);
169 	free(dbname);
170     }
171     if (db4ctx) {
172 	mutex = g_mutex_new_compat();
173 	cond = g_cond_new_compat();
174 	if ((mutex)&&(cond))
175 	    queue = g_queue_new();
176 	if (queue)
177 	    thread = g_thread_create_compat(rccLibPostponed, NULL, TRUE);
178     }
179 #endif /* HAVE_LIBTRANSLATE */
180 
181     return 0;
182 }
183 
rccLibTranslateFree()184 void rccLibTranslateFree() {
185 #ifdef HAVE_LIBTRANSLATE
186     char *ptr;
187     if  (session) {
188 	if (thread) {
189 	    exitflag = 1;
190 	    g_mutex_lock(mutex);
191 	    g_cond_signal(cond);
192 	    g_mutex_unlock(mutex);
193 	    g_thread_join(thread);
194 	    thread = NULL;
195 	    exitflag = 0;
196 	}
197 	if (queue) {
198 	    do {
199 		ptr = g_queue_pop_head(queue);
200 		if (ptr) free(ptr);
201 	    } while (ptr);
202 	    g_queue_free(queue);
203 	    queue = NULL;
204 	}
205 	if (mutex) {
206 	    g_mutex_free_compat(mutex);
207 	    mutex = NULL;
208 	}
209 	if (cond) {
210 	    g_cond_free_compat(cond);
211 	    cond = NULL;
212 	}
213 	if (db4ctx) rccDb4FreeContext(db4ctx);
214 	g_object_unref(session);
215 	session = NULL;
216     }
217 #endif /* HAVE_LIBTRANSLATE */
218 }
219 
220 
rccLibTranslateQueue(const char * from,const char * to,const char * text)221 static void rccLibTranslateQueue(const char *from, const char *to, const char *text) {
222 #ifdef HAVE_LIBTRANSLATE
223     char *key = NULL;
224     size_t keysize;
225 
226     if ((!session)||(!from)||(!to)||(!text)) return;
227     if ((strlen(from)!=2)||(strlen(to)!=2)) return;
228 
229     if (db4ctx) {
230 	key = rccCreateKey(from,to,text,&keysize);
231 	if (key) {
232 	    g_mutex_lock(mutex);
233 	    g_queue_push_tail(queue, key);
234 	    g_mutex_unlock(mutex);
235 	    g_cond_signal(cond);
236 	}
237     }
238 #endif /* HAVE_LIBTRANSLATE */
239 }
240 
rccLibTranslateDo(const char * from,const char * to,const char * text,unsigned long timeout)241 static char *rccLibTranslateDo(const char *from, const char *to, const char *text, unsigned long timeout) {
242 #ifdef HAVE_LIBTRANSLATE
243     char *result;
244     char *key = NULL;
245     size_t keysize;
246 
247     if ((!session)||(!from)||(!to)||(!text)) return NULL;
248     if ((strlen(from)!=2)||(strlen(to)!=2)) return NULL;
249 
250     if (db4ctx) {
251 	key = rccCreateKey(from,to,text,&keysize);
252 	if (key) {
253 	    result = rccDb4GetKey(db4ctx, key, keysize);
254 	    if (result) {
255 		free(key);
256 		return result;
257 	    }
258 	}
259     }
260 # ifdef HAVE_LIBTRANSLATE_TIMED_TRANSLATE
261     result = translate_session_timed_translate_text(session, text, from, to, timeout, NULL, NULL, NULL);
262 # else
263     result = translate_session_translate_text(session, text, from, to, NULL, NULL, NULL);
264 # endif /* HAVE_LIBTRANSLATE_TIMED_TRANSLATE */
265 
266     result = rccTranslateFixEOL(result, text);
267 
268     if ((db4ctx)&&(key)) {
269 	if (result) {
270 	    rccDb4SetKey(db4ctx, key, keysize, result);
271 	    free(key);
272 	}
273 	else {
274 	    g_mutex_lock(mutex);
275 	    g_queue_push_tail(queue, key);
276 	    g_mutex_unlock(mutex);
277 	    g_cond_signal(cond);
278 	}
279     }
280 
281     return result;
282 #else
283     return NULL;
284 #endif /* HAVE_LIBTRANSLATE */
285 }
286 
287 
rccLibTranslate(void * info)288 void *rccLibTranslate(void *info) {
289     int s;
290 #ifdef HAVE_LIBTRANSLATE
291     unsigned char connected = 1;
292     rcc_translate_prefix_s prefix;
293     ssize_t readed, writed, res;
294     char *buffer;
295     size_t size;
296     char *translated = NULL;
297 #endif /* HAVE_LIBTRANSLATE */
298 
299     if (!info) return NULL;
300 
301     s = ((rcc_external_info)info)->s;
302     free(info);
303 
304 #ifdef HAVE_LIBTRANSLATE
305     while (connected) {
306 	readed = read(s, &prefix, sizeof(rcc_translate_prefix_s)-1);
307 	if ((readed<=0)||(readed != (sizeof(rcc_translate_prefix_s)-1))) break;
308 
309 	switch (prefix.cmd.cmd) {
310 	    case RCC_EXTERNAL_COMMAND_CLOSE:
311 		connected = 0;
312 	    break;
313 	    case RCC_EXTERNAL_COMMAND_TRANSLATE:
314 		size = 1 + prefix.cmd.size + sizeof(rcc_external_command_s) - sizeof(rcc_translate_prefix_s);
315 		buffer = (char*)malloc(size);
316 		if (buffer) {
317 		    for (readed = 0; (readed < size)&&(connected); readed += res) {
318 			res = read(s, buffer + readed, size - readed);
319 			if (res<=0) connected = 0;
320 		    }
321 		    if (!connected)  goto clear;
322 
323 		    prefix.cmd.cmd = 0;
324 		    prefix.cmd.size = 0;
325 
326 		    if ((prefix.from[2])||(prefix.to[2])||(buffer[readed-1])) goto respond;
327 
328 		    translated = rccLibTranslateDo(prefix.from, prefix.to, buffer, prefix.timeout);
329 		    if (translated) {
330 			prefix.cmd.cmd = RCC_EXTERNAL_COMMAND_TRANSLATE;
331 			prefix.cmd.size = strlen(translated) + 1;
332 		    }
333 
334 respond:
335 		    res = write(s, &prefix.cmd, sizeof(rcc_external_command_s));
336 		    if (res == sizeof(rcc_external_command_s)) {
337 			for (writed = 0; (writed < prefix.cmd.size)&&(connected); writed += res) {
338 			    res = write(s, translated + writed, prefix.cmd.size - writed);
339 			    if (res<=0) connected = 0;
340 			}
341 		    } else connected = 0;
342 
343 		    if (prefix.cmd.size) free(translated);
344 clear:
345 		    free(buffer);
346 		} else connected = 0;
347 	    break;
348 	    case RCC_EXTERNAL_COMMAND_TRANSLATE_QUEUE:
349 		size = 1 + prefix.cmd.size + sizeof(rcc_external_command_s) - sizeof(rcc_translate_prefix_s);
350 		buffer = (char*)malloc(size);
351 		if (buffer) {
352 		    for (readed = 0; (readed < size)&&(connected); readed += res) {
353 			res = read(s, buffer + readed, size - readed);
354 			if (res<=0) connected = 0;
355 		    }
356 		    if ((connected)&&(!prefix.from[2])&&(!prefix.to[2])&&(!buffer[readed-1])) {
357 			rccLibTranslateQueue(prefix.from, prefix.to, buffer);
358 		    }
359 		    free(buffer);
360 		} else connected = 0;
361 	    break;
362 	    default:
363 		size = 1 + prefix.cmd.size + sizeof(rcc_external_command_s) - sizeof(rcc_translate_prefix_s);
364 		buffer = (char*)malloc(size);
365 		if (buffer) {
366 		    for (readed = 0; (readed < size)&&(connected); readed += res) {
367 			res = read(s, buffer + readed, size - readed);
368 			if (res<=0) connected = 0;
369 		    }
370 		    free(buffer);
371 		} else connected = 0;
372 	}
373     }
374 #endif /* HAVE_LIBTRANSLATE */
375 
376     close(s);
377     return NULL;
378 }
379 
380