1 /*
2  * Copyright (C) 2004, 2005 Jean-Yves Lefort
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
18  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 #include <string.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <glib/gi18n-lib.h>
37 #include <libsoup/soup.h>
38 #ifdef HAVE_LIBSOUP22
39 #include <libsoup/soup-message-filter.h>
40 #endif
41 #include <libxml/HTMLparser.h>
42 #include "translate.h"
43 #include "translate-generic-service.h"
44 #include "translate-generic-main.h"
45 #include "translate-generic-parser.h"
46 #include "translate-generic-soup-cookie-jar.h"
47 
48 #ifdef HAVE_LIBSOUP22
49 #define soup_message_headers_get soup_message_get_header
50 #define soup_message_headers_append soup_message_add_header
51 #define SoupURI SoupUri
52 #define trans_SOUP_MESSAGE_RESPONSE_BODY(msg) ((msg)->response.body)
53 #define trans_SOUP_MESSAGE_RESPONSE_LENGTH(msg) ((msg)->response.length)
54 #else
55 #define trans_SOUP_MESSAGE_RESPONSE_BODY(msg) ((msg)->response_body->data)
56 #define trans_SOUP_MESSAGE_RESPONSE_LENGTH(msg) ((msg)->response_body->length)
57 #endif
58 
59 #define MAKE_WARNING_PREFIX(service, group_pos, attribute, element) \
60   g_strdup_printf(_("in %s, group %i, \"%s\" attribute of \"%s\" element"), \
61 		  translate_service_get_name((service)), \
62 		  (group_pos), (attribute), (element))
63 
64 enum {
65   PROP_0,
66   PROP_GROUPS
67 };
68 
69 struct _TranslateGenericServicePrivate
70 {
71   GSList *groups;
72 };
73 
74 typedef struct
75 {
76   GSList			**pairs;
77   TranslatePairFlags		flags;
78 } GetPairsInfo;
79 
80 typedef struct
81 {
82   gboolean			found;
83   const char			*from;
84   const char			*to;
85 } GetGroupInfo;
86 
87 typedef enum
88 {
89   TRANSFER_FOLLOW_REFRESH	= 1 << 0,
90   TRANSFER_CONVERT		= 1 << 1
91 } TransferFlags;
92 
93 typedef enum
94 {
95   HTML_CONTEXT_PRE_HEAD,
96   HTML_CONTEXT_HEAD,
97   HTML_CONTEXT_POST_HEAD
98 } HTMLContext;
99 
100 typedef struct
101 {
102   SoupSession			*session;
103   TranslateProgressFunc		progress_func;
104   gpointer			user_data;
105   unsigned int			length;
106   unsigned int			received;
107 
108   /* HTML parser */
109   gboolean			parse_html;
110   HTMLContext			html_context;
111   GHashTable			*html_http_equiv;
112 } TransferInfo;
113 
114 static GObjectClass *parent_class = NULL;
115 
116 static void translate_generic_service_register_type (GType *type);
117 static void translate_generic_service_class_init (TranslateGenericServiceClass *class);
118 static void translate_generic_service_init (TranslateGenericService *service);
119 static void translate_generic_service_finalize (GObject *object);
120 static void translate_generic_service_set_property (GObject *object,
121 						    unsigned int prop_id,
122 						    const GValue *value,
123 						    GParamSpec *pspec);
124 
125 static gboolean translate_generic_service_get_pairs (TranslateService *service,
126 						     GSList **pairs,
127 						     TranslateProgressFunc progress_func,
128 						     gpointer user_data,
129 						     GError **err);
130 static gboolean translate_generic_service_get_pairs_cb (const char *from,
131 							const char *to,
132 							gpointer user_data);
133 
134 static TranslateGenericGroup *translate_generic_service_get_group (TranslateGenericService *service,
135 								   const char *from,
136 								   const char *to,
137 								   int *pos);
138 static gboolean translate_generic_service_get_group_cb (const char *from,
139 							const char *to,
140 							gpointer user_data);
141 
142 static char *translate_generic_service_get (const char *uri,
143 					    const char *post,
144 					    const char *post_content_type,
145 					    const GSList *headers,
146 					    TransferFlags flags,
147 					    TranslateProgressFunc progress_func,
148 					    gpointer user_data,
149 					    GError **err);
150 
151 static const char *translate_generic_service_get_header (SoupMessage *message,
152 							 TransferInfo *info,
153 							 const char *name);
154 
155 static void translate_generic_service_log_connect (SoupMessage *message);
156 #ifdef HAVE_LIBSOUP22
157 static void translate_generic_service_log_wrote_headers_h (SoupMessage *message,
158 							   gpointer user_data);
159 static void translate_generic_service_log_wrote_body_h (SoupMessage *message,
160 							gpointer user_data);
161 static void translate_generic_service_log_got_headers_h (SoupMessage *message,
162 							 gpointer user_data);
163 static void translate_generic_service_log_got_body_h (SoupMessage *message,
164 						      gpointer user_data);
165 static void translate_generic_service_log_headers_cb (const char *key,
166 						      const char *value,
167 						      gpointer user_data);
168 #else
169 static void translate_generic_service_log_printer (SoupLogger *logger,
170 						   SoupLoggerLogLevel level,
171 						   char direction,
172 						   const char *data,
173 						   gpointer user_data);
174 #endif
175 
176 static void translate_generic_service_progress_got_headers_h (SoupMessage *message,
177 							      gpointer user_data);
178 static void translate_generic_service_progress_got_chunk_h (SoupMessage *message,
179 #ifdef HAVE_LIBSOUP24
180 							    SoupBuffer *chunk,
181 #endif
182 							    gpointer user_data);
183 
184 static void translate_generic_service_html_got_headers_h (SoupMessage *message,
185 							  gpointer user_data);
186 static void translate_generic_service_html_got_body_h (SoupMessage *message,
187 						       gpointer user_data);
188 static void translate_generic_service_html_start_element_cb (gpointer user_data,
189 							     const xmlChar *name,
190 							     const xmlChar **atts);
191 static void translate_generic_service_html_end_element_cb (gpointer user_data,
192 							   const xmlChar *name);
193 
194 static void translate_generic_service_refresh_got_body_h (SoupMessage *message,
195 							  gpointer user_data);
196 
197 #ifdef HAVE_LIBSOUP22
198 static void translate_generic_service_redirect_handler (SoupMessage *message,
199 							gpointer user_data);
200 #endif
201 
202 static char *translate_generic_service_translate_text (TranslateService *service,
203 						       const char *text,
204 						       const char *from,
205 						       const char *to,
206 						       TranslateProgressFunc progress_func,
207 						       gpointer user_data,
208 						       GError **err);
209 
210 char *translate_generic_service_expand (const char *warning_prefix,
211 					const char *str,
212 					...);
213 static char *translate_generic_service_expand_variable (const char *warning_prefix,
214 							const char *variable,
215 							GHashTable *subs);
216 static char *translate_generic_service_modify_value (const char *warning_prefix,
217 						     const char *value,
218 						     const char *modifier_name,
219 						     const char *modifier_value);
220 
221 static char *translate_generic_service_translate_web_page (TranslateService *service,
222 							   const char *url,
223 							   const char *from,
224 							   const char *to,
225 							   TranslateProgressFunc progress_func,
226 							   gpointer user_data,
227 							   GError **err);
228 
229 static SoupSession *translate_generic_service_soup_session_sync_new (void);
230 
231 GType
translate_generic_service_get_type(void)232 translate_generic_service_get_type (void)
233 {
234   static GType type;
235   static GOnce once = G_ONCE_INIT;
236 
237   g_once(&once, (GThreadFunc) translate_generic_service_register_type, &type);
238 
239   return type;
240 }
241 
242 static void
translate_generic_service_register_type(GType * type)243 translate_generic_service_register_type (GType *type)
244 {
245   static const GTypeInfo info = {
246     sizeof(TranslateGenericServiceClass),
247     NULL,
248     NULL,
249     (GClassInitFunc) translate_generic_service_class_init,
250     NULL,
251     NULL,
252     sizeof(TranslateGenericService),
253     0,
254     (GInstanceInitFunc) translate_generic_service_init
255   };
256 
257   *type = g_type_register_static(TRANSLATE_TYPE_SERVICE,
258 				 "TranslateGenericService",
259 				 &info,
260 				 0);
261 }
262 
263 static void
translate_generic_service_class_init(TranslateGenericServiceClass * class)264 translate_generic_service_class_init (TranslateGenericServiceClass *class)
265 {
266   GObjectClass *object_class = G_OBJECT_CLASS(class);
267   TranslateServiceClass *service_class = TRANSLATE_SERVICE_CLASS(class);
268 
269   g_type_class_add_private(class, sizeof(TranslateGenericServicePrivate));
270   parent_class = g_type_class_peek_parent(class);
271 
272   object_class->finalize = translate_generic_service_finalize;
273   object_class->set_property = translate_generic_service_set_property;
274 
275   service_class->get_pairs = translate_generic_service_get_pairs;
276   service_class->translate_text = translate_generic_service_translate_text;
277   service_class->translate_web_page = translate_generic_service_translate_web_page;
278 
279   g_object_class_install_property(object_class,
280 				  PROP_GROUPS,
281 				  g_param_spec_pointer("groups",
282 						       NULL,
283 						       NULL,
284 						       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
285 }
286 
287 static void
translate_generic_service_init(TranslateGenericService * service)288 translate_generic_service_init (TranslateGenericService *service)
289 {
290   service->priv = G_TYPE_INSTANCE_GET_PRIVATE(service,
291 					      TRANSLATE_GENERIC_TYPE_SERVICE,
292 					      TranslateGenericServicePrivate);
293 }
294 
295 static void
translate_generic_service_finalize(GObject * object)296 translate_generic_service_finalize (GObject *object)
297 {
298   TranslateGenericService *service = TRANSLATE_GENERIC_SERVICE(object);
299 
300   g_slist_foreach(service->priv->groups, (GFunc) translate_generic_group_unref, NULL);
301   g_slist_free(service->priv->groups);
302 
303   parent_class->finalize(object);
304 }
305 
306 static void
translate_generic_service_set_property(GObject * object,unsigned int prop_id,const GValue * value,GParamSpec * pspec)307 translate_generic_service_set_property (GObject *object,
308 					unsigned int prop_id,
309 					const GValue *value,
310 					GParamSpec *pspec)
311 {
312   TranslateGenericService *service = TRANSLATE_GENERIC_SERVICE(object);
313 
314   switch (prop_id)
315     {
316     case PROP_GROUPS:
317       service->priv->groups = g_slist_copy(g_value_get_pointer(value));
318       g_slist_foreach(service->priv->groups, (GFunc) translate_generic_group_ref, NULL);
319       break;
320 
321     default:
322       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
323       break;
324     }
325 }
326 
327 static gboolean
translate_generic_service_get_pairs(TranslateService * service,GSList ** pairs,TranslateProgressFunc progress_func,gpointer user_data,GError ** err)328 translate_generic_service_get_pairs (TranslateService *service,
329 				     GSList **pairs,
330 				     TranslateProgressFunc progress_func,
331 				     gpointer user_data,
332 				     GError **err)
333 {
334   TranslateGenericService *generic = TRANSLATE_GENERIC_SERVICE(service);
335   GetPairsInfo info;
336   GSList *l;
337 
338   info.pairs = pairs;
339   *info.pairs = NULL;
340 
341   for (l = generic->priv->groups; l != NULL; l = l ->next)
342     {
343       TranslateGenericGroup *group = l->data;
344 
345       info.flags = 0;
346       if (group->text_location)
347 	info.flags |= TRANSLATE_PAIR_TEXT;
348       if (group->web_page_location)
349 	info.flags |= TRANSLATE_PAIR_WEB_PAGE;
350 
351       translate_generic_group_foreach_pair(group, translate_generic_service_get_pairs_cb, &info);
352     }
353 
354   return TRUE;
355 }
356 
357 static gboolean
translate_generic_service_get_pairs_cb(const char * from,const char * to,gpointer user_data)358 translate_generic_service_get_pairs_cb (const char *from,
359 					const char *to,
360 					gpointer user_data)
361 {
362   GetPairsInfo *info = user_data;
363 
364   *info->pairs = g_slist_append(*info->pairs, translate_pair_new(info->flags, from, to));
365 
366   return TRUE;			/* continue */
367 }
368 
369 static TranslateGenericGroup *
translate_generic_service_get_group(TranslateGenericService * service,const char * from,const char * to,int * pos)370 translate_generic_service_get_group (TranslateGenericService *service,
371 				     const char *from,
372 				     const char *to,
373 				     int *pos)
374 {
375   GSList *l;
376   int _pos;
377   GetGroupInfo info = { FALSE, from, to };
378 
379   g_return_val_if_fail(TRANSLATE_GENERIC_IS_SERVICE(service), NULL);
380   g_return_val_if_fail(from != NULL, NULL);
381   g_return_val_if_fail(to != NULL, NULL);
382   g_return_val_if_fail(pos != NULL, NULL);
383 
384   for (l = service->priv->groups, _pos = 1; l != NULL; l = l ->next, _pos++)
385     {
386       TranslateGenericGroup *group = l->data;
387 
388       translate_generic_group_foreach_pair(group, translate_generic_service_get_group_cb, &info);
389       if (info.found)
390 	{
391 	  *pos = _pos;
392 	  return group;
393 	}
394     }
395 
396   *pos = -1;
397   return NULL;
398 }
399 
400 static gboolean
translate_generic_service_get_group_cb(const char * from,const char * to,gpointer user_data)401 translate_generic_service_get_group_cb (const char *from,
402 					const char *to,
403 					gpointer user_data)
404 {
405   GetGroupInfo *info = user_data;
406 
407   if (! g_ascii_strcasecmp(from, info->from) && ! g_ascii_strcasecmp(to, info->to))
408     {
409       info->found = TRUE;
410       return FALSE;		/* abort */
411     }
412   else
413     return TRUE;		/* continue */
414 }
415 
416 static char *
translate_generic_service_get(const char * uri,const char * post,const char * post_content_type,const GSList * headers,TransferFlags flags,TranslateProgressFunc progress_func,gpointer user_data,GError ** err)417 translate_generic_service_get (const char *uri,
418 			       const char *post,
419 			       const char *post_content_type,
420 			       const GSList *headers,
421 			       TransferFlags flags,
422 			       TranslateProgressFunc progress_func,
423 			       gpointer user_data,
424 			       GError **err)
425 {
426   TransferInfo info;
427   SoupMessage *message;
428   const GSList *l;
429   char *response = NULL;
430 
431   g_return_val_if_fail(uri != NULL, FALSE);
432 
433   message = soup_message_new(post ? SOUP_METHOD_POST : SOUP_METHOD_GET, uri);
434   if (! message)
435     {
436       g_set_error(err,
437 		  TRANSLATE_GENERIC_SERVICE_ERROR,
438 		  TRANSLATE_GENERIC_SERVICE_ERROR_TRANSFER,
439 		  _("unable to parse URI \"%s\""), uri);
440       return NULL;
441     }
442 
443   if (post)
444     {
445       g_return_val_if_fail(post_content_type != NULL, NULL);
446       soup_message_set_request(message,
447 			       post_content_type,
448 #ifdef HAVE_LIBSOUP22
449 			       SOUP_BUFFER_USER_OWNED,
450 #else
451 			       SOUP_MEMORY_TEMPORARY,
452 #endif
453 			       (char *) post,
454 			       strlen(post));
455     }
456 
457   for (l = headers; l != NULL; l = l->next)
458     {
459       TranslateGenericHttpHeader *header = l->data;
460       soup_message_headers_append(message->request_headers, header->name, header->value);
461     }
462 
463   info.session = translate_generic_service_soup_session_sync_new();
464   info.parse_html = FALSE;
465   info.html_http_equiv = NULL;
466 
467   if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
468     {
469 #ifdef HAVE_LIBSOUP22
470       g_object_connect(message,
471 		       "signal::wrote-headers", translate_generic_service_log_wrote_headers_h, &info,
472 		       "signal::wrote-body", translate_generic_service_log_wrote_body_h, &info,
473 		       "signal::got-headers", translate_generic_service_log_got_headers_h, &info,
474 		       "signal::got-body", translate_generic_service_log_got_body_h, &info,
475 		       NULL);
476 #else
477       SoupLogger *logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
478       soup_logger_set_printer (logger, translate_generic_service_log_printer, NULL, NULL);
479       soup_logger_attach (logger, info.session);
480       g_object_unref (logger);
481 #endif
482     }
483 
484   if (progress_func)
485     {
486       info.progress_func = progress_func;
487       info.user_data = user_data;
488       info.length = -1;
489       info.received = 0;
490 
491       g_object_connect(message,
492 		       "signal::got-headers", translate_generic_service_progress_got_headers_h, &info,
493 		       "signal::got-chunk", translate_generic_service_progress_got_chunk_h, &info,
494 		       NULL);
495     }
496 
497   /*
498    * We parse the HTML to retrieve the http-equiv meta tags, and we
499    * only need them if we convert or follow refresh.
500    */
501   if ((flags & TRANSFER_FOLLOW_REFRESH) || (flags & TRANSFER_CONVERT))
502     g_object_connect(message,
503 		     "signal::got-headers", translate_generic_service_html_got_headers_h, &info,
504 		     "signal::got-body", translate_generic_service_html_got_body_h, &info,
505 		     NULL);
506 
507   if (flags & TRANSFER_FOLLOW_REFRESH)
508     g_signal_connect(message, "got-body", G_CALLBACK(translate_generic_service_refresh_got_body_h), &info);
509 
510 #ifdef HAVE_LIBSOUP22
511   /* http://bugzilla.ximian.com/show_bug.cgi?id=70688 */
512   soup_message_set_flags(message, SOUP_MESSAGE_NO_REDIRECT);
513   soup_message_add_status_class_handler(message,
514 					SOUP_STATUS_CLASS_REDIRECT,
515 					SOUP_HANDLER_POST_BODY,
516 					translate_generic_service_redirect_handler,
517 					info.session);
518 #endif
519 
520   if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
521     translate_generic_service_log_connect(message);
522 
523   soup_session_send_message(info.session, message);
524   g_object_unref(info.session);
525 
526   if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
527     {
528       char *charset = NULL;
529 
530       if (flags & TRANSFER_CONVERT)
531 	{
532 	  const char *content_type;
533 
534 	  content_type = translate_generic_service_get_header(message, &info, "Content-Type");
535 	  if (content_type)
536 	    {
537 	      const char *tmp;
538 
539 	      tmp = translate_ascii_strcasestr(content_type, "charset=");
540 	      if (tmp)
541 		{
542 		  int len;
543 
544 		  tmp += 8;
545 		  if (*tmp == '\'' || *tmp == '"')
546 		    tmp++;
547 
548 		  len = strlen(tmp);
549 		  if (len > 0 && (tmp[len - 1] == '\'' || tmp[len - 1] == '"'))
550 		    len--;
551 
552 		  charset = g_strndup(tmp, len);
553 		}
554 	    }
555 	}
556 
557       if (charset)
558 	{
559 	  response = g_convert(trans_SOUP_MESSAGE_RESPONSE_BODY (message), trans_SOUP_MESSAGE_RESPONSE_LENGTH (message), "UTF-8", charset, NULL, NULL, err);
560 	  g_free(charset);
561 	}
562       else
563 	{
564 	  if ((flags & TRANSFER_CONVERT) && ! g_utf8_validate(trans_SOUP_MESSAGE_RESPONSE_BODY (message), trans_SOUP_MESSAGE_RESPONSE_LENGTH (message), NULL))
565 	    g_set_error(err,
566 			TRANSLATE_GENERIC_SERVICE_ERROR,
567 			TRANSLATE_GENERIC_SERVICE_ERROR_TRANSFER,
568 			_("invalid UTF-8"));
569 	  else
570 	    response = g_strndup(trans_SOUP_MESSAGE_RESPONSE_BODY (message), trans_SOUP_MESSAGE_RESPONSE_LENGTH (message));
571 	}
572     }
573   else
574     {
575       if (message->status_code == SOUP_STATUS_CANCELLED)
576 	g_set_error(err,
577 		    TRANSLATE_ERROR,
578 		    TRANSLATE_ERROR_CANCELLED,
579 		    "%s", message->reason_phrase);
580       else
581 	g_set_error(err,
582 		    TRANSLATE_GENERIC_SERVICE_ERROR,
583 		    TRANSLATE_GENERIC_SERVICE_ERROR_TRANSFER,
584 		    "%s", message->reason_phrase);
585     }
586 
587   if (info.html_http_equiv)
588     g_hash_table_destroy(info.html_http_equiv);
589 
590   g_object_unref(message);
591 
592   return response;
593 }
594 
595 static const char *
translate_generic_service_get_header(SoupMessage * message,TransferInfo * info,const char * name)596 translate_generic_service_get_header (SoupMessage *message,
597 				      TransferInfo *info,
598 				      const char *name)
599 {
600   const char *value;
601 
602   g_return_val_if_fail(SOUP_IS_MESSAGE(message), NULL);
603   g_return_val_if_fail(info != NULL, NULL);
604   g_return_val_if_fail(name != NULL, NULL);
605 
606   value = info->html_http_equiv
607     ? g_hash_table_lookup(info->html_http_equiv, name)
608     : NULL;
609 
610   if (! value)
611     value = soup_message_headers_get(message->response_headers, name);
612 
613   return value;
614 }
615 
616 static void
translate_generic_service_log_connect(SoupMessage * message)617 translate_generic_service_log_connect (SoupMessage *message)
618 {
619   const SoupURI *uri;
620 
621   uri = soup_message_get_uri(message);
622   g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, _("connecting to %s:%i"), uri->host, uri->port);
623 }
624 
625 #ifdef HAVE_LIBSOUP22
626 
627 static void
translate_generic_service_log_wrote_headers_h(SoupMessage * message,gpointer user_data)628 translate_generic_service_log_wrote_headers_h (SoupMessage *message,
629 					       gpointer user_data)
630 {
631   const SoupUri *suri;
632   char *uri;
633 
634   suri = soup_message_get_uri(message);
635   uri = soup_uri_to_string(suri, FALSE);
636 
637   g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "> %s %s", message->method, uri);
638   g_free(uri);
639 
640   soup_message_foreach_header(message->request_headers, (GHFunc) translate_generic_service_log_headers_cb, ">");
641 }
642 
643 static void
translate_generic_service_log_wrote_body_h(SoupMessage * message,gpointer user_data)644 translate_generic_service_log_wrote_body_h (SoupMessage *message,
645 					    gpointer user_data)
646 {
647   if (message->request.body)
648     g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "> %s", message->request.body);
649 }
650 
651 static void
translate_generic_service_log_got_headers_h(SoupMessage * message,gpointer user_data)652 translate_generic_service_log_got_headers_h (SoupMessage *message,
653 					     gpointer user_data)
654 {
655   g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "< %s", soup_status_get_phrase(message->status_code));
656 
657   soup_message_foreach_header(message->response_headers, (GHFunc) translate_generic_service_log_headers_cb, "<");
658 
659   if (message->response.body)
660     g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "< %s", message->response.body);
661 }
662 
663 static void
translate_generic_service_log_got_body_h(SoupMessage * message,gpointer user_data)664 translate_generic_service_log_got_body_h (SoupMessage *message,
665 					  gpointer user_data)
666 {
667   if (message->response.body)
668     g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "< %s", message->response.body);
669 }
670 
671 static void
translate_generic_service_log_headers_cb(const char * key,const char * value,gpointer user_data)672 translate_generic_service_log_headers_cb (const char *key,
673 					  const char *value,
674 					  gpointer user_data)
675 {
676   const char *prefix = user_data;
677 
678   g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%s %s: %s", prefix, key, value);
679 }
680 
681 #else /* !HAVE_LIBSOUP22 */
682 
683 static void
translate_generic_service_log_printer(SoupLogger * logger,SoupLoggerLogLevel level,char direction,const char * data,gpointer user_data)684 translate_generic_service_log_printer (SoupLogger *logger,
685 				       SoupLoggerLogLevel level,
686 				       char direction,
687 				       const char *data,
688 				       gpointer user_data)
689 {
690   g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "%c %s", direction, data);
691 }
692 
693 #endif /* HAVE_LIBSOUP22 */
694 
695 static void
translate_generic_service_progress_got_headers_h(SoupMessage * message,gpointer user_data)696 translate_generic_service_progress_got_headers_h (SoupMessage *message,
697 						  gpointer user_data)
698 {
699   TransferInfo *info = user_data;
700   const char *content_length;
701 
702   content_length = soup_message_headers_get(message->response_headers, "Content-Length");
703   info->length = (content_length
704 		  && *content_length
705 		  && strspn(content_length, "0123456789") == strlen(content_length))
706     ? atoi(content_length) : -1;
707   info->received = 0;
708 }
709 
710 static void
translate_generic_service_progress_got_chunk_h(SoupMessage * message,SoupBuffer * chunk,gpointer user_data)711 translate_generic_service_progress_got_chunk_h (SoupMessage *message,
712 #ifdef HAVE_LIBSOUP24
713 						SoupBuffer *chunk,
714 #endif
715 						gpointer user_data)
716 {
717   TransferInfo *info = user_data;
718   double progress;
719 
720   if (info->length == -1)
721     progress = -1;
722   else
723     {
724 #ifdef HAVE_LIBSOUP22
725       info->received += message->response.length;
726 #else
727       info->received += chunk->length;
728 #endif
729       progress = (double) info->received / info->length;
730       progress = CLAMP(progress, 0.0, 1.0);
731     }
732 
733   if (! info->progress_func(progress, info->user_data))
734     soup_session_abort(info->session);
735 }
736 
737 static void
translate_generic_service_html_got_headers_h(SoupMessage * message,gpointer user_data)738 translate_generic_service_html_got_headers_h (SoupMessage *message,
739 					      gpointer user_data)
740 {
741   TransferInfo *info = user_data;
742   const char *content_type;
743 
744   content_type = soup_message_headers_get(message->response_headers, "Content-Type");
745   info->parse_html = content_type
746     && (g_str_has_prefix(content_type, "text/html")
747 	|| g_str_has_prefix(content_type, "application/xhtml+xml")
748 	|| g_str_has_prefix(content_type, "application/xml")
749 	|| g_str_has_prefix(content_type, "text/xml"));
750 }
751 
752 static void
translate_generic_service_html_got_body_h(SoupMessage * message,gpointer user_data)753 translate_generic_service_html_got_body_h (SoupMessage *message,
754 					   gpointer user_data)
755 {
756   TransferInfo *info = user_data;
757 
758   if (info->html_http_equiv)
759     {
760       g_hash_table_destroy(info->html_http_equiv);
761       info->html_http_equiv = NULL;
762     }
763 
764   if (info->parse_html && trans_SOUP_MESSAGE_RESPONSE_LENGTH (message) > 0)
765     {
766       char *body;
767       xmlSAXHandler sax_handler = { NULL };
768 
769       info->html_context = HTML_CONTEXT_PRE_HEAD;
770       info->html_http_equiv = g_hash_table_new_full(translate_ascii_strcase_hash,
771 						    translate_ascii_strcase_equal,
772 						    g_free,
773 						    g_free);
774 
775       sax_handler.startElement = translate_generic_service_html_start_element_cb;
776       sax_handler.endElement = translate_generic_service_html_end_element_cb;
777 
778       body = g_strndup(trans_SOUP_MESSAGE_RESPONSE_BODY (message), trans_SOUP_MESSAGE_RESPONSE_LENGTH (message));
779       htmlSAXParseDoc(body, NULL, &sax_handler, user_data);
780       g_free(body);
781     }
782 }
783 
784 static void
translate_generic_service_html_start_element_cb(gpointer user_data,const xmlChar * name,const xmlChar ** atts)785 translate_generic_service_html_start_element_cb (gpointer user_data,
786 						 const xmlChar *name,
787 						 const xmlChar **atts)
788 {
789   TransferInfo *info = user_data;
790 
791   if (info->html_context == HTML_CONTEXT_PRE_HEAD)
792     {
793       if (! g_ascii_strcasecmp(name, "head"))
794 	info->html_context = HTML_CONTEXT_HEAD;
795     }
796   else if (info->html_context == HTML_CONTEXT_HEAD)
797     {
798       if (! g_ascii_strcasecmp(name, "meta"))
799 	{
800 	  int i;
801 	  const char *http_equiv = NULL;
802 
803 	  for (i = 0; atts[i] && atts[i + 1]; i += 2)
804 	    if (! g_ascii_strcasecmp(atts[i], "http-equiv"))
805 	      {
806 		http_equiv = atts[i + 1];
807 		break;
808 	      }
809 
810 	  if (http_equiv)
811 	    {
812 	      const char *content = NULL;
813 
814 	      for (i = 0; atts[i] && atts[i + 1]; i += 2)
815 		if (! g_ascii_strcasecmp(atts[i], "content"))
816 		  {
817 		    content = atts[i + 1];
818 		    break;
819 		  }
820 
821 	      if (content)
822 		g_hash_table_insert(info->html_http_equiv, g_strdup(http_equiv), g_strdup(content));
823 	    }
824 	}
825     }
826 }
827 
828 static void
translate_generic_service_html_end_element_cb(gpointer user_data,const xmlChar * name)829 translate_generic_service_html_end_element_cb (gpointer user_data,
830 					       const xmlChar *name)
831 {
832   TransferInfo *info = user_data;
833 
834   if (info->html_context == HTML_CONTEXT_HEAD
835       && ! g_ascii_strcasecmp(name, "head"))
836     info->html_context = HTML_CONTEXT_POST_HEAD;
837 }
838 
839 static void
translate_generic_service_refresh_got_body_h(SoupMessage * message,gpointer user_data)840 translate_generic_service_refresh_got_body_h (SoupMessage *message,
841 					      gpointer user_data)
842 {
843   TransferInfo *info = user_data;
844   const char *refresh_uri;
845   SoupURI *new_uri = NULL;
846 
847   refresh_uri = translate_generic_service_get_header(message, info, "Refresh");
848   if (refresh_uri)
849     {
850       refresh_uri = translate_ascii_strcasestr(refresh_uri, "url=");
851       if (refresh_uri)
852 	refresh_uri += 4;
853     }
854 
855   if (refresh_uri)
856     {
857       new_uri = soup_uri_new(refresh_uri);
858       if (! new_uri)
859 	{
860 	  SoupURI *base_uri;
861 
862 	  base_uri = (SoupURI *)soup_message_get_uri(message);
863 	  new_uri = soup_uri_new_with_base(base_uri, refresh_uri);
864 	}
865     }
866 
867   if (new_uri)
868     {
869       soup_message_set_uri(message, new_uri);
870       soup_uri_free(new_uri);
871 
872       if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
873 	translate_generic_service_log_connect(message);
874 
875       soup_session_requeue_message(info->session, message);
876     }
877 }
878 
879 #ifdef HAVE_LIBSOUP22
880 static void
translate_generic_service_redirect_handler(SoupMessage * message,gpointer user_data)881 translate_generic_service_redirect_handler (SoupMessage *message,
882 					    gpointer user_data)
883 {
884   const char *new_location;
885 
886   new_location = soup_message_get_header(message->response_headers, "Location");
887   if (new_location)
888     {
889       SoupSession *session = user_data;
890       SoupUri *new_uri;
891 
892       new_uri = soup_uri_new(new_location);
893       if (! new_uri)
894 	{
895 	  const SoupUri *base_uri;
896 
897 	  base_uri = soup_message_get_uri(message);
898 	  new_uri = soup_uri_new_with_base(base_uri, new_location);
899 
900 	  if (! new_uri)
901 	    {
902 	      soup_message_set_status_full(message, SOUP_STATUS_MALFORMED, _("invalid redirect URL"));
903 	      return;
904 	    }
905 	}
906 
907       soup_message_set_uri(message, new_uri);
908       soup_uri_free(new_uri);
909 
910       if (translate_generic_debug_flags & TRANSLATE_GENERIC_DEBUG_LOG_TRANSFERS)
911 	translate_generic_service_log_connect(message);
912 
913       soup_session_requeue_message(session, message);
914     }
915 }
916 #endif
917 
918 static char *
translate_generic_service_translate_text(TranslateService * service,const char * text,const char * from,const char * to,TranslateProgressFunc progress_func,gpointer user_data,GError ** err)919 translate_generic_service_translate_text (TranslateService *service,
920 					  const char *text,
921 					  const char *from,
922 					  const char *to,
923 					  TranslateProgressFunc progress_func,
924 					  gpointer user_data,
925 					  GError **err)
926 {
927   TranslateGenericService *generic = TRANSLATE_GENERIC_SERVICE(service);
928   TranslateGenericGroup *group;
929   int group_pos;
930   const char *service_from;
931   const char *service_to;
932   char *warning_prefix;
933   char *url;
934   char *post = NULL;
935   GSList *headers;
936   char *response;
937   GString *answer = NULL;
938 
939   group = translate_generic_service_get_group(generic, from, to, &group_pos);
940   g_return_val_if_fail(group != NULL, NULL);
941 
942   service_from = translate_generic_group_get_service_tag(group, from);
943   service_to = translate_generic_group_get_service_tag(group, to);
944 
945   warning_prefix = MAKE_WARNING_PREFIX(service, group_pos, "url", "text-translation");
946   url = translate_generic_service_expand(warning_prefix,
947 					 group->text_location->url,
948 					 "text", text,
949 					 "from", service_from,
950 					 "to", service_to,
951 					 NULL);
952   g_free(warning_prefix);
953 
954   if (group->text_location->post)
955     {
956       warning_prefix = MAKE_WARNING_PREFIX(service, group_pos, "post", "text-translation");
957       post = translate_generic_service_expand(warning_prefix,
958 					      group->text_location->post,
959 					      "text", text,
960 					      "from", service_from,
961 					      "to", service_to,
962 					      NULL);
963       g_free(warning_prefix);
964     }
965 
966   headers = g_slist_copy(group->http_headers);
967   headers = g_slist_concat(headers, g_slist_copy(group->text_location->http_headers));
968 
969   response = translate_generic_service_get(url,
970 					   post,
971 					   group->text_location->content_type,
972 					   headers,
973 					   TRANSFER_FOLLOW_REFRESH | TRANSFER_CONVERT,
974 					   progress_func,
975 					   user_data,
976 					   err);
977 
978   g_free(url);
979   g_free(post);
980   g_slist_free(headers);
981 
982   if (response)
983     {
984       const char *work = response;
985       char *error = NULL;
986       GSList *l;
987 
988       for (l = group->text_error_markers; l != NULL && ! error; l = l->next)
989 	{
990 	  const char *marker = l->data;
991 
992 	  error = strstr(work, marker);
993 	}
994 
995       if (error)
996 	g_set_error(err,
997 		    TRANSLATE_GENERIC_SERVICE_ERROR,
998 		    TRANSLATE_GENERIC_SERVICE_ERROR_FAILED,
999 		    _("server failure"));
1000       else
1001 	{
1002 	  char *raw = NULL;
1003 
1004 	  for (l = group->text_pre_markers; l != NULL && work; l = l->next)
1005 	    {
1006 	      const char *marker = l->data;
1007 
1008 	      work = strstr(work, marker);
1009 	      if (work)
1010 		work += strlen(marker);
1011 	    }
1012 
1013 	  if (work)
1014 	    {
1015 	      if (group->text_post_marker)
1016 		{
1017 		  char *s;
1018 
1019 		  s = strstr(work, group->text_post_marker);
1020 		  if (s)
1021 		    raw = g_strndup(work, s - work);
1022 		}
1023 	      else
1024 		raw = g_strdup(work);
1025 	    }
1026 
1027 	  if (raw)
1028 	    {
1029 	      char *expanded;
1030 	      const char *s;
1031 	      int len;
1032 
1033 	      expanded = translate_sgml_ref_expand(raw);
1034 	      g_free(raw);
1035 
1036 	      /*
1037 	       * If the service has removed the leading and/or
1038 	       * trailing whitespace, restore it.
1039 	       */
1040 
1041 	      answer = g_string_new(NULL);
1042 
1043 	      for (s = text;
1044 		   *s && g_unichar_isspace(g_utf8_get_char(s));
1045 		   s = g_utf8_next_char(s));
1046 
1047 	      len = s - text;
1048 	      if (len > 0 && strncmp(expanded, text, len))
1049 		g_string_append_len(answer, text, len);
1050 
1051 	      g_string_append(answer, expanded);
1052 
1053 	      if (*s)
1054 		{ /* there was a middle block, handle trailing spaces */
1055 		  for (s = g_utf8_find_prev_char(text, strchr(text, 0));
1056 		       s && g_unichar_isspace(g_utf8_get_char(s));
1057 		       s = g_utf8_find_prev_char(text, s));
1058 		  s = s ? g_utf8_next_char(s) : text;
1059 
1060 		  if (! g_str_has_suffix(expanded, s))
1061 		    g_string_append(answer, s);
1062 		}
1063 	    }
1064 	  else
1065 	    g_set_error(err,
1066 			TRANSLATE_GENERIC_SERVICE_ERROR,
1067 			TRANSLATE_GENERIC_SERVICE_ERROR_PARSE,
1068 			_("unable to parse server data"));
1069 	}
1070 
1071       g_free(response);
1072     }
1073 
1074   return answer ? g_string_free(answer, FALSE) : NULL;
1075 }
1076 
1077 char *
translate_generic_service_expand(const char * warning_prefix,const char * str,...)1078 translate_generic_service_expand (const char *warning_prefix,
1079 				  const char *str,
1080 				  ...)
1081 {
1082   GHashTable *subs;
1083   va_list args;
1084   const char *name;
1085   GString *result;
1086   int i;
1087   int dollar = -1;
1088 
1089   g_return_val_if_fail(warning_prefix != NULL, NULL);
1090   g_return_val_if_fail(str != NULL, NULL);
1091 
1092   va_start(args, str);
1093   subs = g_hash_table_new(g_str_hash, g_str_equal);
1094 
1095   while ((name = va_arg(args, const char *)))
1096     {
1097       const char *value;
1098 
1099       value = va_arg(args, const char *);
1100       g_return_val_if_fail(value != NULL, NULL);
1101 
1102       g_hash_table_insert(subs, (gpointer) name, (gpointer) value);
1103     }
1104 
1105   result = g_string_new(NULL);
1106 
1107   for (i = 0; str[i]; i++)
1108     if (dollar >= 0)
1109       {
1110 	if (dollar == i - 1)
1111 	  {
1112 	    if (str[i] == '$')
1113 	      {
1114 		g_string_append_c(result, '$');
1115 		dollar = -1;
1116 	      }
1117 	    else if (str[i] != '{')
1118 	      {
1119 		g_string_append_len(result, str + (i - 1), 2);
1120 		dollar = -1;
1121 	      }
1122 	  }
1123 	else
1124 	  {
1125 	    if (str[i] == '}')
1126 	      {
1127 		int start;
1128 		char *variable;
1129 		char *expanded;
1130 
1131 		start = dollar + 2;
1132 
1133 		variable = g_strndup(str + start, i - start);
1134 		expanded = translate_generic_service_expand_variable(warning_prefix,
1135 								     variable,
1136 								     subs);
1137 		g_free(variable);
1138 
1139 		if (expanded)
1140 		  {
1141 		    g_string_append(result, expanded);
1142 		    g_free(expanded);
1143 		  }
1144 
1145 		dollar = -1;
1146 	      }
1147 	  }
1148       }
1149     else
1150       {
1151 	if (str[i] == '$')
1152 	  dollar = i;
1153 	else
1154 	  g_string_append_c(result, str[i]);
1155       }
1156 
1157   g_hash_table_destroy(subs);
1158   va_end(args);
1159 
1160   return g_string_free(result, FALSE);
1161 }
1162 
1163 static char *
translate_generic_service_expand_variable(const char * warning_prefix,const char * variable,GHashTable * subs)1164 translate_generic_service_expand_variable (const char *warning_prefix,
1165 					   const char *variable,
1166 					   GHashTable *subs)
1167 {
1168   char *s;
1169   char *varname;
1170   char **modifiers = NULL;
1171   char *expanded = NULL;
1172 
1173   g_return_val_if_fail(warning_prefix != NULL, NULL);
1174   g_return_val_if_fail(variable != NULL, NULL);
1175   g_return_val_if_fail(subs != NULL, NULL);
1176 
1177   s = strchr(variable, ':');
1178   if (s)
1179     {
1180       varname = g_strndup(variable, s - variable);
1181       modifiers = g_strsplit(s + 1, ",", 0);
1182     }
1183   else
1184     varname = g_strdup(variable);
1185 
1186   if (! strcmp(varname, "time"))
1187     {
1188       time_t now;
1189 
1190       now = translate_time();
1191       expanded = g_strdup_printf("%ul", now);
1192     }
1193   else
1194     expanded = g_strdup(g_hash_table_lookup(subs, varname));
1195 
1196   if (expanded)
1197     {
1198       if (modifiers)
1199 	{
1200 	  int i;
1201 
1202 	  for (i = 0; modifiers[i]; i++)
1203 	    {
1204 	      char *mod_name;
1205 	      char *mod_value;
1206 	      char *modified;
1207 
1208 	      s = strchr(modifiers[i], '=');
1209 	      if (s)
1210 		{
1211 		  mod_name = g_strndup(modifiers[i], s - modifiers[i]);
1212 		  mod_value = g_strdup(s + 1);
1213 		}
1214 	      else
1215 		{
1216 		  mod_name = g_strdup(modifiers[i]);
1217 		  mod_value = NULL;
1218 		}
1219 
1220 	      modified = translate_generic_service_modify_value(warning_prefix,
1221 								expanded,
1222 								mod_name,
1223 								mod_value);
1224 	      g_free(mod_name);
1225 	      g_free(mod_value);
1226 
1227 	      g_free(expanded);
1228 	      expanded = modified;
1229 	    }
1230 	}
1231     }
1232   else
1233     g_warning(_("%s: unknown variable \"%s\""), warning_prefix, varname);
1234 
1235   g_free(varname);
1236   g_strfreev(modifiers);
1237 
1238   return expanded;
1239 }
1240 
1241 static char *
translate_generic_service_modify_value(const char * warning_prefix,const char * value,const char * modifier_name,const char * modifier_value)1242 translate_generic_service_modify_value (const char *warning_prefix,
1243 					const char *value,
1244 					const char *modifier_name,
1245 					const char *modifier_value)
1246 {
1247   char *modified = NULL;
1248 
1249   g_return_val_if_fail(warning_prefix != NULL, NULL);
1250   g_return_val_if_fail(value != NULL, NULL);
1251   g_return_val_if_fail(modifier_name != NULL, NULL);
1252 
1253   if (! strcmp(modifier_name, "escape"))
1254     {
1255       if (modifier_value)
1256 	g_warning(_("%s: value specified for \"escape\" modifier"), warning_prefix);
1257       modified = soup_uri_encode(value, NULL);
1258     }
1259   else if (! strcmp(modifier_name, "charset"))
1260     {
1261       if (modifier_value)
1262 	{
1263 	  GError *err = NULL;
1264 
1265 	  modified = g_convert(value, -1, modifier_value, "UTF-8", NULL, NULL, &err);
1266 	  if (! modified)
1267 	    {
1268 	      g_warning(_("%s: unable to convert to \"%s\": %s"), warning_prefix, modifier_value, err->message);
1269 	      g_error_free(err);
1270 	    }
1271 	}
1272       else
1273 	g_warning(_("%s: value of \"charset\" modifier missing"), warning_prefix);
1274     }
1275   else
1276     g_warning(_("%s: unknown modifier \"%s\""), warning_prefix, modifier_name);
1277 
1278   return modified ? modified : g_strdup(value);
1279 }
1280 
1281 static char *
translate_generic_service_translate_web_page(TranslateService * service,const char * url,const char * from,const char * to,TranslateProgressFunc progress_func,gpointer user_data,GError ** err)1282 translate_generic_service_translate_web_page (TranslateService *service,
1283 					      const char *url,
1284 					      const char *from,
1285 					      const char *to,
1286 					      TranslateProgressFunc progress_func,
1287 					      gpointer user_data,
1288 					      GError **err)
1289 {
1290   TranslateGenericService *generic = TRANSLATE_GENERIC_SERVICE(service);
1291   TranslateGenericGroup *group;
1292   int group_pos;
1293   const char *service_from;
1294   const char *service_to;
1295   char *warning_prefix;
1296   char *translation_url;
1297   char *post = NULL;
1298   GSList *headers;
1299   char *response;
1300 
1301   group = translate_generic_service_get_group(generic, from, to, &group_pos);
1302   g_return_val_if_fail(group != NULL, NULL);
1303 
1304   service_from = translate_generic_group_get_service_tag(group, from);
1305   service_to = translate_generic_group_get_service_tag(group, to);
1306 
1307   warning_prefix = MAKE_WARNING_PREFIX(service, group_pos, "url", "web-page-translation");
1308   translation_url = translate_generic_service_expand(warning_prefix,
1309 						     group->web_page_location->url,
1310 						     "url", url,
1311 						     "from", service_from,
1312 						     "to", service_to,
1313 						     NULL);
1314   g_free(warning_prefix);
1315 
1316   headers = g_slist_copy(group->http_headers);
1317   headers = g_slist_concat(headers, g_slist_copy(group->web_page_location->http_headers));
1318 
1319   if (group->web_page_location->post)
1320     {
1321       warning_prefix = MAKE_WARNING_PREFIX(service, group_pos, "post", "web-page-translation");
1322       post = translate_generic_service_expand(warning_prefix,
1323 					      group->web_page_location->post,
1324 					      "url", url,
1325 					      "from", service_from,
1326 					      "to", service_to,
1327 					      NULL);
1328       g_free(warning_prefix);
1329     }
1330   else if (! headers)
1331     return translation_url;
1332 
1333   response = translate_generic_service_get(translation_url,
1334 					   post,
1335 					   group->web_page_location->content_type,
1336 					   headers,
1337 					   0,
1338 					   progress_func,
1339 					   user_data,
1340 					   err);
1341 
1342   g_free(translation_url);
1343   translation_url = NULL;
1344 
1345   g_free(post);
1346   g_slist_free(headers);
1347 
1348   if (response)
1349     {
1350       int fd;
1351       char *filename;
1352 
1353       fd = g_file_open_tmp("libtranslate.XXXXXX", &filename, err);
1354       if (fd >= 0)
1355 	{
1356 	  GIOChannel *channel;
1357 
1358 	  channel = g_io_channel_unix_new(fd);
1359 
1360 	  if (g_io_channel_set_encoding(channel, NULL, err)
1361 	      && g_io_channel_write_chars(channel, response, -1, NULL, err))
1362 	    {
1363 	      if (g_io_channel_shutdown(channel, TRUE, err))
1364 		translation_url = g_strconcat("file://", filename, NULL);
1365 	    }
1366 	  else
1367 	    g_io_channel_shutdown(channel, FALSE, NULL);
1368 
1369 	  g_io_channel_unref(channel);
1370 	  g_free(filename);
1371 	}
1372 
1373       g_free(response);
1374     }
1375 
1376   return translation_url;
1377 }
1378 
1379 static SoupSession *
translate_generic_service_soup_session_sync_new(void)1380 translate_generic_service_soup_session_sync_new (void)
1381 {
1382   char *proxy_text_uri;
1383   SoupURI *proxy_uri = NULL;
1384   SoupSession *session;
1385   TranslateGenericSoupCookieJar *cookie_jar;
1386 
1387   proxy_text_uri = translate_get_proxy();
1388   if (proxy_text_uri)
1389     {
1390       proxy_uri = soup_uri_new(proxy_text_uri);
1391       if (! proxy_uri)
1392 	g_warning(_("unable to parse proxy URI \"%s\""), proxy_text_uri);
1393 
1394       g_free(proxy_text_uri);
1395     }
1396 
1397   session = soup_session_sync_new_with_options(SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
1398 
1399   if (proxy_uri)
1400     soup_uri_free(proxy_uri);
1401 
1402   cookie_jar = translate_generic_soup_cookie_jar_new();
1403   translate_generic_soup_cookie_jar_attach(cookie_jar, session);
1404   g_object_unref(cookie_jar);
1405 
1406   return session;
1407 }
1408 
1409 TranslateService *
translate_generic_service_new(const char * name,const char * nick,unsigned int max_chunk_len,const GSList * groups)1410 translate_generic_service_new (const char *name,
1411 			       const char *nick,
1412 			       unsigned int max_chunk_len,
1413 			       const GSList *groups)
1414 {
1415   g_return_val_if_fail(name != NULL, NULL);
1416   g_return_val_if_fail(nick != NULL, NULL);
1417 
1418   return g_object_new(TRANSLATE_GENERIC_TYPE_SERVICE,
1419 		      "name", name,
1420 		      "nick", nick,
1421 		      "max-chunk-len", max_chunk_len,
1422 		      "groups", groups,
1423 		      NULL);
1424 }
1425 
1426 GQuark
translate_generic_service_error_quark(void)1427 translate_generic_service_error_quark (void)
1428 {
1429   return g_quark_from_static_string("translate-service-generic-error");
1430 }
1431