1 /* gdict-source.c - Source configuration for Gdict
2  *
3  * Copyright (C) 2005  Emmanuele Bassi <ebassi@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * SECTION:gdict-source
21  * @short_description: A dictionary source definition
22  *
23  * #GdictSource is the representation of a #GdictContext. Each dictionary
24  * source provides a list of available dictionaries (databases) and a list
25  * of available matching strategies. Using a #GdictContext you can query
26  * the dictionary source for matching words and for definitions.
27  *
28  * By using a #GdictSource object you can retrieve the appropriate
29  * #GdictContext, already set up with the right parameters.
30  */
31 
32 #include "config.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 
39 #include <glib/gi18n-lib.h>
40 
41 #include "gdict-source.h"
42 #include "gdict-client-context.h"
43 #include "gdict-utils.h"
44 #include "gdict-enum-types.h"
45 #include "gdict-private.h"
46 
47 /* Main group */
48 #define SOURCE_GROUP		"Dictionary Source"
49 
50 /* Common keys */
51 #define SOURCE_KEY_NAME		"Name"
52 #define SOURCE_KEY_DESCRIPTION	"Description" /* Deprecated */
53 #define SOURCE_KEY_COMMENT	"Comment"
54 #define SOURCE_KEY_TRANSPORT	"Transport"
55 #define SOURCE_KEY_DATABASE 	"Database"
56 #define SOURCE_KEY_STRATEGY 	"Strategy"
57 
58 /* dictd transport keys */
59 #define SOURCE_KEY_HOSTNAME	"Hostname"
60 #define SOURCE_KEY_PORT		"Port"
61 
62 struct _GdictSourcePrivate
63 {
64   gchar *filename;
65   GKeyFile *keyfile;
66 
67   gchar *name;
68   gchar *description;
69   gboolean editable;
70 
71   gchar *database;
72   gchar *strategy;
73 
74   GdictSourceTransport transport;
75 
76   GdictContext *context;
77 };
78 
79 enum
80 {
81   PROP_0,
82 
83   PROP_FILENAME,
84   PROP_NAME,
85   PROP_DESCRIPTION,
86   PROP_EDITABLE,
87   PROP_DATABASE,
88   PROP_STRATEGY,
89   PROP_TRANSPORT,
90   PROP_CONTEXT
91 };
92 
93 /* keep in sync with GdictSourceTransport */
94 static const gchar *valid_transports[] =
95 {
96   "dictd",	/* GDICT_SOURCE_TRANSPORT_DICTD */
97 
98   NULL		/* GDICT_SOURCE_TRANSPORT_INVALID */
99 };
100 
101 #define IS_VALID_TRANSPORT(t)	(((t) >= GDICT_SOURCE_TRANSPORT_DICTD) && \
102 				 ((t) < GDICT_SOURCE_TRANSPORT_INVALID))
103 
104 GQuark
gdict_source_error_quark(void)105 gdict_source_error_quark (void)
106 {
107   static GQuark quark = 0;
108 
109   if (G_UNLIKELY (quark == 0))
110     quark = g_quark_from_static_string ("gdict-source-error-quark");
111 
112   return quark;
113 }
114 
115 
G_DEFINE_TYPE_WITH_PRIVATE(GdictSource,gdict_source,G_TYPE_OBJECT)116 G_DEFINE_TYPE_WITH_PRIVATE (GdictSource, gdict_source, G_TYPE_OBJECT)
117 
118 
119 static void
120 gdict_source_set_property (GObject      *object,
121 			   guint         prop_id,
122 			   const GValue *value,
123 			   GParamSpec   *pspec)
124 {
125   GdictSource *source = GDICT_SOURCE (object);
126 
127   switch (prop_id)
128     {
129     case PROP_NAME:
130       gdict_source_set_name (source, g_value_get_string (value));
131       break;
132     case PROP_DESCRIPTION:
133       gdict_source_set_description (source, g_value_get_string (value));
134       break;
135     case PROP_TRANSPORT:
136       gdict_source_set_transport (source, g_value_get_enum (value), NULL);
137       break;
138     case PROP_DATABASE:
139       gdict_source_set_database (source, g_value_get_string (value));
140       break;
141     case PROP_STRATEGY:
142       gdict_source_set_strategy (source, g_value_get_string (value));
143       break;
144     default:
145       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
146       break;
147     }
148 }
149 
150 static void
gdict_source_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)151 gdict_source_get_property (GObject    *object,
152 			   guint       prop_id,
153 			   GValue     *value,
154 			   GParamSpec *pspec)
155 {
156   GdictSource *source = GDICT_SOURCE (object);
157   GdictSourcePrivate *priv = source->priv;
158 
159   switch (prop_id)
160     {
161     case PROP_FILENAME:
162       g_value_set_string (value, priv->filename);
163       break;
164     case PROP_NAME:
165       g_value_set_string (value, priv->name);
166       break;
167     case PROP_DESCRIPTION:
168       g_value_set_string (value, priv->description);
169       break;
170     case PROP_EDITABLE:
171       g_value_set_boolean (value, priv->editable);
172       break;
173     case PROP_DATABASE:
174       g_value_set_string (value, priv->database);
175       break;
176     case PROP_STRATEGY:
177       g_value_set_string (value, priv->strategy);
178       break;
179     case PROP_TRANSPORT:
180       g_value_set_enum (value, priv->transport);
181       break;
182     case PROP_CONTEXT:
183       g_value_set_object (value, gdict_source_peek_context (source));
184       break;
185     default:
186       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
187       break;
188     }
189 }
190 
191 static void
gdict_source_finalize(GObject * object)192 gdict_source_finalize (GObject *object)
193 {
194   GdictSource *self = GDICT_SOURCE (object);
195   GdictSourcePrivate *priv = gdict_source_get_instance_private (self);
196 
197   g_free (priv->filename);
198   g_free (priv->name);
199   g_free (priv->description);
200   g_free (priv->database);
201   g_free (priv->strategy);
202 
203   g_clear_pointer (&priv->keyfile, g_key_file_unref);
204   g_clear_object (&priv->context);
205 
206   G_OBJECT_CLASS (gdict_source_parent_class)->finalize (object);
207 }
208 
209 static void
gdict_source_class_init(GdictSourceClass * klass)210 gdict_source_class_init (GdictSourceClass *klass)
211 {
212   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
213 
214   gobject_class->set_property = gdict_source_set_property;
215   gobject_class->get_property = gdict_source_get_property;
216   gobject_class->finalize = gdict_source_finalize;
217 
218   /**
219    * GdictSource:filename
220    *
221    * The filename used by this dictionary source.
222    *
223    * Since: 1.0
224    */
225   g_object_class_install_property (gobject_class,
226   				   PROP_FILENAME,
227   				   g_param_spec_string ("filename",
228                                                         "Filename",
229                                                         "The filename used by this dictionary source",
230   				   			NULL,
231   				   			G_PARAM_READABLE));
232   /**
233    * GdictSource:name
234    *
235    * The display name of this dictionary source.
236    *
237    * Since: 1.0
238    */
239   g_object_class_install_property (gobject_class,
240   				   PROP_NAME,
241   				   g_param_spec_string ("name",
242                                                         "Name",
243                                                         "The display name of this dictionary source",
244   				   			NULL,
245   				   			(G_PARAM_READABLE | G_PARAM_WRITABLE)));
246   /**
247    * GdictSource:description
248    *
249    * The description of this dictionary source.
250    *
251    * Since: 1.0
252    */
253   g_object_class_install_property (gobject_class,
254   				   PROP_DESCRIPTION,
255   				   g_param_spec_string ("description",
256                                                         "Description",
257                                                         "The description of this dictionary source",
258   				   			NULL,
259   				   			(G_PARAM_READABLE | G_PARAM_WRITABLE)));
260   /**
261    * GdictSource:editable
262    *
263    * Whether the dictionary source is editable or not.
264    *
265    * Since: 1.0
266    */
267   g_object_class_install_property (gobject_class,
268   				   PROP_EDITABLE,
269   				   g_param_spec_boolean ("editable",
270                                                          "Editable",
271                                                          "Whether the dictionary source is editable or not",
272   				   			 TRUE,
273   				   			 (G_PARAM_STATIC_STRINGS | G_PARAM_READABLE)));
274   /**
275    * GdictSource:database
276    *
277    * The default database of this dictionary source.
278    *
279    * Since: 1.0
280    */
281   g_object_class_install_property (gobject_class,
282   				   PROP_DATABASE,
283   				   g_param_spec_string ("database",
284                                                         "Database",
285                                                         "The default database of this dictionary source",
286   				   			NULL,
287   				   			(G_PARAM_READABLE | G_PARAM_WRITABLE)));
288   /**
289    * GdictSource:strategy
290    *
291    * The default strategy of this dictionary source.
292    *
293    * Since: 1.0
294    */
295   g_object_class_install_property (gobject_class,
296   				   PROP_STRATEGY,
297   				   g_param_spec_string ("strategy",
298                                                         "Strategy",
299                                                         "The default strategy of this dictionary source",
300   				   			NULL,
301   				   			(G_PARAM_READABLE | G_PARAM_WRITABLE)));
302   /**
303    * GdictSource:transport
304    *
305    * The transport mechanism used by this source.
306    *
307    * Since: 1.0
308    */
309   g_object_class_install_property (gobject_class,
310   				   PROP_TRANSPORT,
311   				   g_param_spec_enum ("transport",
312                                                       "Transport",
313                                                       "The transport mechanism used by this dictionary source",
314   				   		      GDICT_TYPE_SOURCE_TRANSPORT,
315   				   		      GDICT_SOURCE_TRANSPORT_INVALID,
316   				   		      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
317   /**
318    * GdictSource:context
319    *
320    * The #GdictContext bound to this source.
321    *
322    * Since: 1.0
323    */
324   g_object_class_install_property (gobject_class,
325   				   PROP_CONTEXT,
326   				   g_param_spec_object ("context",
327                                                         "Context",
328                                                         "The GdictContext bound to this source",
329   				   			GDICT_TYPE_CONTEXT,
330   				   			G_PARAM_READABLE));
331 }
332 
333 static void
gdict_source_init(GdictSource * source)334 gdict_source_init (GdictSource *source)
335 {
336   GdictSourcePrivate *priv;
337 
338   priv = gdict_source_get_instance_private (source);
339   source->priv = priv;
340 
341   priv->keyfile = g_key_file_new ();
342   priv->transport = GDICT_SOURCE_TRANSPORT_INVALID;
343   priv->editable = TRUE;
344 }
345 
346 /**
347  * gdict_source_new:
348  *
349  * Creates an empty #GdictSource object.  Use gdict_load_from_file() to
350  * read an existing dictionary source definition file.
351  *
352  * Return value: an empty #GdictSource
353  */
354 GdictSource *
gdict_source_new(void)355 gdict_source_new (void)
356 {
357   return g_object_new (GDICT_TYPE_SOURCE, NULL);
358 }
359 
360 static GdictSourceTransport
gdict_source_resolve_transport(const gchar * transport)361 gdict_source_resolve_transport (const gchar *transport)
362 {
363   if (!transport)
364     return GDICT_SOURCE_TRANSPORT_INVALID;
365 
366   if (strcmp (transport, "dictd") == 0)
367     return GDICT_SOURCE_TRANSPORT_DICTD;
368   else
369     return GDICT_SOURCE_TRANSPORT_INVALID;
370 
371   g_assert_not_reached ();
372 }
373 
374 static GdictContext *
gdict_source_create_context(GdictSource * source,GdictSourceTransport transport,GError ** error)375 gdict_source_create_context (GdictSource           *source,
376 			     GdictSourceTransport   transport,
377 			     GError               **error)
378 {
379   GdictSourcePrivate *priv;
380   GdictContext *context;
381 
382   g_assert (GDICT_IS_SOURCE (source));
383 
384   priv = source->priv;
385 
386   switch (transport)
387     {
388     case GDICT_SOURCE_TRANSPORT_DICTD:
389       {
390       gchar *hostname;
391       gint port;
392 
393       hostname = g_key_file_get_string (priv->keyfile,
394       					SOURCE_GROUP,
395       					SOURCE_KEY_HOSTNAME,
396       					NULL);
397 
398       port = g_key_file_get_integer (priv->keyfile,
399       				     SOURCE_GROUP,
400       				     SOURCE_KEY_PORT,
401       				     NULL);
402       if (!port)
403         port = -1;
404 
405       context = gdict_client_context_new (hostname, port);
406 
407       if (hostname)
408         g_free (hostname);
409       }
410       break;
411     default:
412       g_set_error (error, GDICT_SOURCE_ERROR,
413                    GDICT_SOURCE_ERROR_PARSE,
414                    _("Invalid transport type “%d”"),
415                    transport);
416       return NULL;
417     }
418 
419   g_assert (context != NULL);
420 
421   if (priv->transport != transport)
422     priv->transport = transport;
423 
424   return context;
425 }
426 
427 static gboolean
gdict_source_parse(GdictSource * source,GError ** error)428 gdict_source_parse (GdictSource  *source,
429 		    GError      **error)
430 {
431   GdictSourcePrivate *priv;
432   GError *parse_error;
433   gchar *transport;
434   GdictSourceTransport t;
435 
436   priv = source->priv;
437 
438   if (!g_key_file_has_group (priv->keyfile, SOURCE_GROUP))
439     {
440       g_set_error (error, GDICT_SOURCE_ERROR,
441                    GDICT_SOURCE_ERROR_PARSE,
442                    _("No “%s” group found inside the dictionary source definition"),
443                    SOURCE_GROUP);
444 
445       return FALSE;
446     }
447 
448   /* fetch the name for the dictionary source definition */
449   parse_error = NULL;
450   priv->name = g_key_file_get_string (priv->keyfile,
451 		  		      SOURCE_GROUP,
452 				      SOURCE_KEY_NAME,
453 				      &parse_error);
454   if (parse_error)
455     {
456       g_set_error (error, GDICT_SOURCE_ERROR,
457                    GDICT_SOURCE_ERROR_PARSE,
458                    _("Unable to get the “%s” key inside the dictionary "
459                      "source definition: %s"),
460                    SOURCE_KEY_NAME,
461                    parse_error->message);
462       g_error_free (parse_error);
463 
464       g_key_file_free (priv->keyfile);
465       priv->keyfile = NULL;
466 
467       return FALSE;
468     }
469 
470   /* if present, fetch the localized description */
471   const char *description_key;
472 
473   if (g_key_file_has_key (priv->keyfile, SOURCE_GROUP, SOURCE_KEY_COMMENT, NULL))
474     description_key = SOURCE_KEY_COMMENT;
475   else if (g_key_file_has_key (priv->keyfile, SOURCE_GROUP, SOURCE_KEY_DESCRIPTION, NULL))
476     description_key = SOURCE_KEY_DESCRIPTION;
477   else
478     description_key = NULL;
479 
480   if (description_key != NULL)
481     {
482       priv->description = g_key_file_get_locale_string (priv->keyfile,
483                                                         SOURCE_GROUP,
484                                                         description_key,
485                                                         NULL,
486                                                         &parse_error);
487       if (parse_error)
488         {
489           g_set_error (error, GDICT_SOURCE_ERROR,
490                        GDICT_SOURCE_ERROR_PARSE,
491                        _("Unable to get the “%s” key inside the dictionary "
492                          "source definition: %s"),
493                        description_key,
494                        parse_error->message);
495 
496           g_error_free (parse_error);
497           g_key_file_free (priv->keyfile);
498           priv->keyfile = NULL;
499           g_free (priv->name);
500 
501           return FALSE;
502         }
503     }
504 
505   if (g_key_file_has_key (priv->keyfile, SOURCE_GROUP, SOURCE_KEY_DATABASE, NULL))
506     {
507       priv->database = g_key_file_get_string (priv->keyfile,
508                                               SOURCE_GROUP,
509                                               SOURCE_KEY_DATABASE,
510                                               &parse_error);
511       if (parse_error)
512         {
513           g_set_error (error, GDICT_SOURCE_ERROR,
514                        GDICT_SOURCE_ERROR_PARSE,
515                        _("Unable to get the “%s” key inside the dictionary "
516                          "source definition: %s"),
517                        SOURCE_KEY_DATABASE,
518                        parse_error->message);
519 
520           g_error_free (parse_error);
521           g_key_file_free (priv->keyfile);
522           priv->keyfile = NULL;
523           g_free (priv->name);
524           g_free (priv->description);
525 
526           return FALSE;
527         }
528     }
529 
530   if (g_key_file_has_key (priv->keyfile, SOURCE_GROUP, SOURCE_KEY_STRATEGY, NULL))
531     {
532       priv->strategy = g_key_file_get_string (priv->keyfile,
533                                               SOURCE_GROUP,
534                                               SOURCE_KEY_STRATEGY,
535                                               &parse_error);
536       if (parse_error)
537         {
538           g_set_error (error, GDICT_SOURCE_ERROR,
539                        GDICT_SOURCE_ERROR_PARSE,
540                        _("Unable to get the “%s” key inside the dictionary "
541                          "source definition: %s"),
542                        SOURCE_KEY_STRATEGY,
543                        parse_error->message);
544 
545           g_error_free (parse_error);
546           g_key_file_free (priv->keyfile);
547           priv->keyfile = NULL;
548 
549           g_free (priv->name);
550           g_free (priv->description);
551           g_free (priv->database);
552 
553           return FALSE;
554         }
555     }
556 
557   transport = g_key_file_get_string (priv->keyfile,
558   				     SOURCE_GROUP,
559   				     SOURCE_KEY_TRANSPORT,
560   				     &parse_error);
561   if (parse_error)
562     {
563       g_set_error (error, GDICT_SOURCE_ERROR,
564       		   GDICT_SOURCE_ERROR_PARSE,
565       		   _("Unable to get the “%s” key inside the dictionary "
566       		     "source definition file: %s"),
567       		   SOURCE_KEY_TRANSPORT,
568       		   parse_error->message);
569 
570       g_error_free (parse_error);
571       g_key_file_free (priv->keyfile);
572       priv->keyfile = NULL;
573       g_free (priv->name);
574       g_free (priv->description);
575       g_free (priv->database);
576       g_free (priv->strategy);
577 
578       return FALSE;
579     }
580 
581   t = gdict_source_resolve_transport (transport);
582   g_free (transport);
583 
584   priv->context = gdict_source_create_context (source, t, &parse_error);
585   if (parse_error)
586     {
587       g_propagate_error (error, parse_error);
588 
589       g_key_file_free (priv->keyfile);
590       priv->keyfile = NULL;
591 
592       g_free (priv->name);
593       g_free (priv->description);
594       g_free (priv->database);
595       g_free (priv->strategy);
596 
597       return FALSE;
598     }
599 
600   return TRUE;
601 }
602 
603 /**
604  * gdict_source_load_from_file:
605  * @source: an empty #GdictSource
606  * @filename: path to a dictionary source file
607  * @error: return location for a #GError or %NULL
608  *
609  * Loads a dictionary source definition file into an empty #GdictSource
610  * object.
611  *
612  * Return value: %TRUE if @filename was loaded successfully.
613  *
614  * Since: 1.0
615  */
616 gboolean
gdict_source_load_from_file(GdictSource * source,const gchar * filename,GError ** error)617 gdict_source_load_from_file (GdictSource  *source,
618 			     const gchar  *filename,
619 			     GError      **error)
620 {
621   struct stat stat_buf;
622   GdictSourcePrivate *priv;
623   GError *read_error;
624   GError *parse_error;
625 
626   g_return_val_if_fail (GDICT_IS_SOURCE (source), FALSE);
627   g_return_val_if_fail (filename != NULL, FALSE);
628 
629   priv = source->priv;
630 
631   if (!priv->keyfile)
632     priv->keyfile = g_key_file_new ();
633 
634   read_error = NULL;
635   g_key_file_load_from_file (priv->keyfile,
636                              filename,
637                              G_KEY_FILE_KEEP_TRANSLATIONS,
638                              &read_error);
639   if (read_error)
640     {
641       g_propagate_error (error, read_error);
642 
643       return FALSE;
644     }
645 
646   parse_error = NULL;
647   gdict_source_parse (source, &parse_error);
648   if (parse_error)
649     {
650       g_propagate_error (error, parse_error);
651 
652       return FALSE;
653     }
654 
655   g_assert (priv->context != NULL);
656 
657   priv->filename = g_strdup (filename);
658 
659   if (lstat (filename, &stat_buf) < 0)
660     {
661       return FALSE;
662     }
663   priv->editable = (stat_buf.st_mode & S_IWUSR) ? TRUE : FALSE;
664 
665   return TRUE;
666 }
667 
668 /**
669  * gdict_source_load_from_data:
670  * @source: a #GdictSource
671  * @data: string containing a dictionary source
672  * @length: length of @data
673  * @error: return location for a #GError or %NULL
674  *
675  * Loads a dictionary source definition from @data inside an empty
676  * #GdictSource object.
677  *
678  * Return value: %TRUE if @filename was loaded successfully.
679  *
680  * Since: 1.0
681  */
682 gboolean
gdict_source_load_from_data(GdictSource * source,const gchar * data,gsize length,GError ** error)683 gdict_source_load_from_data (GdictSource  *source,
684 			     const gchar  *data,
685 			     gsize         length,
686 			     GError      **error)
687 {
688   GdictSourcePrivate *priv;
689   GError *read_error;
690   GError *parse_error;
691 
692   g_return_val_if_fail (GDICT_IS_SOURCE (source), FALSE);
693   g_return_val_if_fail (data != NULL, FALSE);
694 
695   priv = source->priv;
696 
697   if (!priv->keyfile)
698     priv->keyfile = g_key_file_new ();
699 
700   read_error = NULL;
701   g_key_file_load_from_data (priv->keyfile,
702                              data,
703                              length,
704                              G_KEY_FILE_KEEP_TRANSLATIONS,
705                              &read_error);
706   if (read_error)
707     {
708       g_propagate_error (error, read_error);
709 
710       return FALSE;
711     }
712 
713   parse_error = NULL;
714   gdict_source_parse (source, &parse_error);
715   if (parse_error)
716     {
717       g_propagate_error (error, parse_error);
718 
719       return FALSE;
720     }
721 
722   g_assert (priv->context != NULL);
723 
724   g_free (priv->filename);
725   priv->filename = NULL;
726 
727   return TRUE;
728 }
729 
730 /**
731  * gdict_source_to_data:
732  * @source: a #GdictSource
733  * @length: (out) (optional): return loaction for the length
734  *     of the string, or %NULL
735  * @error: return location for a #GError or %NULL
736  *
737  * Outputs a dictionary source as a string.
738  *
739  * Return value: a newly allocated string holding the contents of @source.
740  *
741  * Since: 1.0
742  */
743 gchar *
gdict_source_to_data(GdictSource * source,gsize * length,GError ** error)744 gdict_source_to_data (GdictSource  *source,
745 		      gsize        *length,
746 		      GError      **error)
747 {
748   GdictSourcePrivate *priv;
749   gchar *retval = NULL;
750 
751   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
752 
753   priv = source->priv;
754 
755   if (!priv->name)
756     {
757       g_set_error (error, GDICT_SOURCE_ERROR,
758                    GDICT_SOURCE_ERROR_INVALID_NAME,
759                    _("Dictionary source does not have name"));
760 
761       return NULL;
762     }
763 
764   if (!IS_VALID_TRANSPORT (priv->transport))
765     {
766       g_set_error (error, GDICT_SOURCE_ERROR,
767                    GDICT_SOURCE_ERROR_INVALID_TRANSPORT,
768                    _("Dictionary source “%s” has invalid transport “%s”"),
769                    priv->name,
770                    valid_transports[priv->transport]);
771 
772       return NULL;
773     }
774 
775   if (priv->keyfile)
776     {
777       GError *write_error = NULL;
778 
779       retval = g_key_file_to_data (priv->keyfile,
780       				   length,
781       				   &write_error);
782       if (write_error)
783         g_propagate_error (error, write_error);
784     }
785 
786   return retval;
787 }
788 
789 /**
790  * gdict_source_set_name:
791  * @source: a #GdictSource
792  * @name: the UTF8-encoded name of the dictionary source
793  *
794  * Sets @name as the displayable name of the dictionary source.
795  *
796  * Since: 1.0
797  */
798 void
gdict_source_set_name(GdictSource * source,const gchar * name)799 gdict_source_set_name (GdictSource *source,
800 		       const gchar *name)
801 {
802   g_return_if_fail (GDICT_IS_SOURCE (source));
803   g_return_if_fail (name != NULL);
804 
805   g_free (source->priv->name);
806   source->priv->name = g_strdup (name);
807 
808   if (!source->priv->keyfile)
809     source->priv->keyfile = g_key_file_new ();
810 
811   g_key_file_set_string (source->priv->keyfile,
812     			 SOURCE_GROUP,
813     			 SOURCE_KEY_NAME,
814     			 name);
815 }
816 
817 /**
818  * gdict_source_get_name:
819  * @source: a #GdictSource
820  *
821  * Retrieves the name of @source.
822  *
823  * Return value: the name of a #GdictSource.  The returned string is owned
824  *   by the #GdictSource object, and should not be modified or freed.
825  *
826  * Since: 1.0
827  */
828 const gchar *
gdict_source_get_name(GdictSource * source)829 gdict_source_get_name (GdictSource *source)
830 {
831   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
832 
833   return source->priv->name;
834 }
835 
836 /**
837  * gdict_source_set_description:
838  * @source: a #GdictSource
839  * @description: (nullable): a UTF-8 encoded description or %NULL
840  *
841  * Sets the description of @source.  If @description is %NULL, unsets the
842  * currently set description.
843  *
844  * Since: 1.0
845  */
846 void
gdict_source_set_description(GdictSource * source,const gchar * description)847 gdict_source_set_description (GdictSource *source,
848 			      const gchar *description)
849 {
850   g_return_if_fail (GDICT_IS_SOURCE (source));
851 
852   g_free (source->priv->description);
853 
854   if (!source->priv->keyfile)
855     source->priv->keyfile = g_key_file_new ();
856 
857   if (description && description[0] != '\0')
858     {
859       source->priv->description = g_strdup (description);
860 
861       g_key_file_set_string (source->priv->keyfile,
862   			     SOURCE_GROUP,
863   			     SOURCE_KEY_DESCRIPTION,
864   			     description);
865     }
866   else
867     {
868       if (g_key_file_has_key (source->priv->keyfile,
869       			      SOURCE_GROUP,
870       			      SOURCE_KEY_DESCRIPTION,
871       			      NULL))
872         g_key_file_remove_key (source->priv->keyfile,
873                                SOURCE_GROUP,
874                                SOURCE_KEY_DESCRIPTION,
875                                NULL);
876     }
877 }
878 
879 /**
880  * gdict_source_get_description:
881  * @source: a #GdictSource
882  *
883  * Retrieves the description of @source.
884  *
885  * Return value: the description of a #GdictSource.  The returned string is
886  *   owned by the #GdictSource object, and should not be modified or freed.
887  *
888  * Since: 1.0
889  */
890 const gchar *
gdict_source_get_description(GdictSource * source)891 gdict_source_get_description (GdictSource *source)
892 {
893   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
894 
895   return source->priv->description;
896 }
897 
898 /**
899  * gdict_source_is_editable:
900  * @source: a #GdictSource
901  *
902  * Retrieves the is-editable property of @source.
903  *
904  * Return value: %TRUE if @source is editable.
905  *
906  * Since: 1.0
907  */
908 gboolean
gdict_source_is_editable(GdictSource * source)909 gdict_source_is_editable (GdictSource *source)
910 {
911   g_return_val_if_fail (GDICT_IS_SOURCE (source), FALSE);
912 
913   return source->priv->editable;
914 }
915 
916 /**
917  * gdict_source_set_database:
918  * @source: a #GdictSource
919  * @database: (nullable): a UTF-8 encoded database name or %NULL
920  *
921  * Sets the default database of @source.  If @database is %NULL, unsets the
922  * currently set database.
923  *
924  * Since: 1.0
925  */
926 void
gdict_source_set_database(GdictSource * source,const gchar * database)927 gdict_source_set_database (GdictSource *source,
928 			   const gchar *database)
929 {
930   g_return_if_fail (GDICT_IS_SOURCE (source));
931 
932   g_free (source->priv->database);
933 
934   if (!source->priv->keyfile)
935     source->priv->keyfile = g_key_file_new ();
936 
937   if (database && database[0] != '\0')
938     {
939       source->priv->database = g_strdup (database);
940 
941       g_key_file_set_string (source->priv->keyfile,
942   			     SOURCE_GROUP,
943   			     SOURCE_KEY_DATABASE,
944   			     database);
945     }
946   else
947     {
948       if (g_key_file_has_key (source->priv->keyfile,
949       			      SOURCE_GROUP,
950       			      SOURCE_KEY_DATABASE,
951       			      NULL))
952         g_key_file_remove_key (source->priv->keyfile,
953                                SOURCE_GROUP,
954                                SOURCE_KEY_DATABASE,
955                                NULL);
956     }
957 }
958 
959 /**
960  * gdict_source_get_database:
961  * @source: a #GdictSource
962  *
963  * Retrieves the default database of @source.
964  *
965  * Return value: the default strategy of a #GdictSource.  The returned string
966  *   is owned by the #GdictSource object, and should not be modified or freed.
967  *
968  * Since: 1.0
969  */
970 const gchar *
gdict_source_get_database(GdictSource * source)971 gdict_source_get_database (GdictSource *source)
972 {
973   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
974 
975   return source->priv->database;
976 }
977 
978 /**
979  * gdict_source_set_strategy:
980  * @source: a #GdictSource
981  * @strategy: (nullable): a UTF-8 encoded strategy or %NULL
982  *
983  * Sets the description of @source.  If @strategy is %NULL, unsets the
984  * currently set strategy.
985  *
986  * Since: 1.0
987  */
988 void
gdict_source_set_strategy(GdictSource * source,const gchar * strategy)989 gdict_source_set_strategy (GdictSource *source,
990 			   const gchar *strategy)
991 {
992   g_return_if_fail (GDICT_IS_SOURCE (source));
993 
994   g_free (source->priv->strategy);
995 
996   if (!source->priv->keyfile)
997     source->priv->keyfile = g_key_file_new ();
998 
999   if (strategy && strategy[0] != '\0')
1000     {
1001       source->priv->strategy = g_strdup (strategy);
1002 
1003       g_key_file_set_string (source->priv->keyfile,
1004   			     SOURCE_GROUP,
1005   			     SOURCE_KEY_STRATEGY,
1006   			     strategy);
1007     }
1008   else
1009     {
1010       if (g_key_file_has_key (source->priv->keyfile,
1011       			      SOURCE_GROUP,
1012       			      SOURCE_KEY_STRATEGY,
1013       			      NULL))
1014         g_key_file_remove_key (source->priv->keyfile,
1015                                SOURCE_GROUP,
1016                                SOURCE_KEY_STRATEGY,
1017                                NULL);
1018     }
1019 }
1020 
1021 /**
1022  * gdict_source_get_strategy:
1023  * @source: a #GdictSource
1024  *
1025  * Retrieves the default strategy of @source.
1026  *
1027  * Return value: the default strategy of a #GdictSource.  The returned string
1028  *   is owned by the #GdictSource object, and should not be modified or freed.
1029  *
1030  * Since: 1.0
1031  */
1032 const gchar *
gdict_source_get_strategy(GdictSource * source)1033 gdict_source_get_strategy (GdictSource *source)
1034 {
1035   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
1036 
1037   return source->priv->strategy;
1038 }
1039 
1040 /**
1041  * gdict_source_set_transportv
1042  * @source: a #GdictSource
1043  * @transport: a #GdictSourceTransport
1044  * @first_transport_property: FIXME
1045  * @var_args: FIXME
1046  *
1047  * FIXME
1048  *
1049  * Since: 1.0
1050  */
1051 void
gdict_source_set_transportv(GdictSource * source,GdictSourceTransport transport,const gchar * first_transport_property,va_list var_args)1052 gdict_source_set_transportv (GdictSource          *source,
1053 			     GdictSourceTransport  transport,
1054 			     const gchar          *first_transport_property,
1055 			     va_list               var_args)
1056 {
1057   GdictSourcePrivate *priv;
1058 
1059   g_return_if_fail (GDICT_IS_SOURCE (source));
1060   g_return_if_fail (IS_VALID_TRANSPORT (transport));
1061 
1062   priv = source->priv;
1063 
1064   priv->transport = transport;
1065 
1066   if (priv->context)
1067     g_object_unref (priv->context);
1068 
1069   switch (priv->transport)
1070     {
1071     case GDICT_SOURCE_TRANSPORT_DICTD:
1072       priv->context = gdict_client_context_new (NULL, -1);
1073       g_assert (GDICT_IS_CLIENT_CONTEXT (priv->context));
1074 
1075       g_object_set_valist (G_OBJECT (priv->context),
1076                            first_transport_property,
1077                            var_args);
1078 
1079       break;
1080     case GDICT_SOURCE_TRANSPORT_INVALID:
1081     default:
1082       g_assert_not_reached ();
1083       break;
1084     }
1085 
1086   /* update the keyfile */
1087   if (!priv->keyfile)
1088     priv->keyfile = g_key_file_new ();
1089 
1090   g_key_file_set_string (priv->keyfile,
1091   			 SOURCE_GROUP,
1092   			 SOURCE_KEY_TRANSPORT,
1093   			 valid_transports[transport]);
1094 
1095   switch (priv->transport)
1096     {
1097     case GDICT_SOURCE_TRANSPORT_DICTD:
1098       g_key_file_set_string (priv->keyfile,
1099       			     SOURCE_GROUP,
1100       			     SOURCE_KEY_HOSTNAME,
1101       			     gdict_client_context_get_hostname (GDICT_CLIENT_CONTEXT (priv->context)));
1102       g_key_file_set_integer (priv->keyfile,
1103       			      SOURCE_GROUP,
1104       			      SOURCE_KEY_PORT,
1105       			      gdict_client_context_get_port (GDICT_CLIENT_CONTEXT (priv->context)));
1106       break;
1107     case GDICT_SOURCE_TRANSPORT_INVALID:
1108     default:
1109       g_assert_not_reached ();
1110       break;
1111     }
1112 }
1113 
1114 /**
1115  * gdict_source_set_transport:
1116  * @source: a #GdictSource
1117  * @transport: a valid transport
1118  * @first_transport_property: property for the context bound to
1119  *   the transport, or %NULL
1120  * @...: property value for first property name, then additionary
1121  *   properties, ending with %NULL
1122  *
1123  * Sets @transport as the choosen transport for @source.  The @transport
1124  * argument is a method of retrieving dictionary data from a source; it is
1125  * used to create the right #GdictContext for this #GdictSource.  After
1126  * @transport, property name/value pairs should be listed, with a %NULL
1127  * pointer ending the list.  Properties are the same passed to a #GdictContext
1128  * implementation instance using g_object_set().
1129  *
1130  * Here's a simple example:
1131  *
1132  * <informalexample><programlisting>
1133  * #include &lt;gdict/gdict.h&gt;
1134  *  GdictSource *source = gdict_source_new ();
1135  *
1136  *  gdict_source_set_name (source, "My Source");
1137  *  gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD,
1138  *                              "hostname", "dictionary-server.org",
1139  *                              "port", 2628,
1140  *                              NULL);
1141  * </programlisting></informalexample>
1142  *
1143  * Since: 1.0
1144  */
1145 void
gdict_source_set_transport(GdictSource * source,GdictSourceTransport transport,const gchar * first_transport_property,...)1146 gdict_source_set_transport (GdictSource          *source,
1147 			    GdictSourceTransport  transport,
1148 			    const gchar          *first_transport_property,
1149 			    ...)
1150 {
1151   va_list args;
1152 
1153   g_return_if_fail (GDICT_IS_SOURCE (source));
1154   g_return_if_fail (IS_VALID_TRANSPORT (transport));
1155 
1156   va_start (args, first_transport_property);
1157 
1158   gdict_source_set_transportv (source, transport,
1159                                first_transport_property,
1160                                args);
1161 
1162   va_end (args);
1163 }
1164 
1165 /**
1166  * gdict_source_get_transport:
1167  * @source: a #GdictSource
1168  *
1169  * FIXME
1170  *
1171  * Return value: FIXME
1172  *
1173  * Since: 1.0
1174  */
1175 GdictSourceTransport
gdict_source_get_transport(GdictSource * source)1176 gdict_source_get_transport (GdictSource *source)
1177 {
1178   g_return_val_if_fail (GDICT_IS_SOURCE (source), GDICT_SOURCE_TRANSPORT_INVALID);
1179 
1180   return source->priv->transport;
1181 }
1182 
1183 /**
1184  * gdict_source_get_context:
1185  * @source: a #GdictSource
1186  *
1187  * Gets the #GdictContext bound to @source.
1188  *
1189  * Return value: (transfer full): a #GdictContext for @source.
1190  *   Use g_object_unref() when you don't need it anymore.
1191  *
1192  * Since: 1.0
1193  */
1194 GdictContext *
gdict_source_get_context(GdictSource * source)1195 gdict_source_get_context (GdictSource *source)
1196 {
1197   GdictContext *retval;
1198 
1199   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
1200 
1201   retval = gdict_source_create_context (source,
1202 		  			source->priv->transport,
1203 					NULL);
1204 
1205   return retval;
1206 }
1207 
1208 /**
1209  * gdict_source_peek_context:
1210  * @source: a #GdictSource
1211  *
1212  * Gets the #GdictContext bound to @source.  The returned object is a
1213  * referenced copy of the context held by @source; if you want a different
1214  * instance, use gdict_source_get_context().
1215  *
1216  * Return value: (transfer full): a referenced #GdictContext.
1217  *
1218  * Since: 1.0
1219  */
1220 GdictContext *
gdict_source_peek_context(GdictSource * source)1221 gdict_source_peek_context (GdictSource *source)
1222 {
1223   g_return_val_if_fail (GDICT_IS_SOURCE (source), NULL);
1224 
1225   if (!source->priv->context)
1226     source->priv->context = gdict_source_create_context (source,
1227     							 source->priv->transport,
1228     							 NULL);
1229   return g_object_ref (source->priv->context);
1230 }
1231