Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
eab-gui-util.c:738:0 | cppcheck | uninitvar | Uninitialized variable: ret | |
eab-gui-util.c:738:0 | cppcheck | uninitvar | Uninitialized variable: ret | |
eab-gui-util.c:864:11 | clang-analyzer | Dereference of null pointer (loaded from variable 'format') | ||
eab-gui-util.c:864:11 | clang-analyzer | Dereference of null pointer (loaded from variable 'format') | ||
eab-gui-util.c:865:21 | clang-analyzer | Dereference of null pointer (loaded from variable 'country_position') | ||
eab-gui-util.c:865:21 | clang-analyzer | Dereference of null pointer (loaded from variable 'country_position') |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Chris Toshok <toshok@ximian.com>
18 * Dan Vratil <dvratil@redhat.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <locale.h>
33 #include <string.h>
34
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37
38 #include <libedataserverui/libedataserverui.h>
39
40 #include <e-util/e-util.h>
41 #include "eab-gui-util.h"
42 #include "util/eab-book-util.h"
43 #include "libevolution-utils/e-alert-dialog.h"
44 #include "e-util/e-html-utils.h"
45 #include "shell/e-shell.h"
46 #include "misc/e-image-chooser.h"
47 #include <e-util/e-icon-factory.h>
48 #include "eab-contact-merging.h"
49
50 /* we link to camel for decoding quoted printable email addresses */
51 #include <camel/camel.h>
52
53 /* Template tags for address format localization */
54 #define ADDRESS_REALNAME "%n" /* this is not used intentionally */
55 #define ADDRESS_REALNAME_UPPER "%N" /* this is not used intentionally */
56 #define ADDRESS_COMPANY "%m"
57 #define ADDRESS_COMPANY_UPPER "%M"
58 #define ADDRESS_POBOX "%p"
59 #define ADDRESS_STREET "%s"
60 #define ADDRESS_STREET_UPPER "%S"
61 #define ADDRESS_ZIPCODE "%z"
62 #define ADDRESS_LOCATION "%l"
63 #define ADDRESS_LOCATION_UPPER "%L"
64 #define ADDRESS_REGION "%r"
65 #define ADDRESS_REGION_UPPER "%R"
66 #define ADDRESS_CONDCOMMA "%," /* Conditional comma is removed when a surrounding tag is evaluated to zero */
67 #define ADDRESS_CONDWHITE "%w" /* Conditional whitespace is removed when a surrounding tag is evaluated to zero */
68 #define ADDRESS_COND_PURGEEMPTY "%0" /* Purge empty has following syntax: %0(...) and is removed when no tag within () is evaluated non-zero */
69
70 /* Fallback formats */
71 #define ADDRESS_DEFAULT_FORMAT "%0(%n\n)%0(%m\n)%0(%s\n)%0(PO BOX %p\n)%0(%l%w%r)%,%z"
72 #define ADDRESS_DEFAULT_COUNTRY_POSITION "below"
73
74 enum {
75 LOCALES_LANGUAGE = 0,
76 LOCALES_COUNTRY = 1
77 };
78
79 typedef enum {
80 ADDRESS_FORMAT_HOME = 0,
81 ADDRESS_FORMAT_BUSINESS = 1
82 } AddressFormat;
83
84 void
85 eab_error_dialog (EAlertSink *alert_sink,
86 const gchar *msg,
87 const GError *error)
88 {
89 if (error && error->message) {
90 if (alert_sink)
91 e_alert_submit (
92 alert_sink,
93 "addressbook:generic-error",
94 msg, error->message, NULL);
95 else
96 e_alert_run_dialog_for_args (
97 e_shell_get_active_window (NULL),
98 "addressbook:generic-error",
99 msg, error->message, NULL);
100 }
101 }
102
103 void
104 eab_load_error_dialog (GtkWidget *parent,
105 EAlertSink *alert_sink,
106 ESource *source,
107 const GError *error)
108 {
109 ESourceBackend *extension;
110 gchar *label_string, *label = NULL;
111 gboolean can_detail_error = TRUE;
112 const gchar *backend_name;
113 const gchar *extension_name;
114
115 g_return_if_fail (source != NULL);
116
117 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
118 extension = e_source_get_extension (source, extension_name);
119 backend_name = e_source_backend_get_backend_name (extension);
120
121 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_OFFLINE_UNAVAILABLE)) {
122 can_detail_error = FALSE;
123 label_string =
124 _("This address book cannot be opened. This either "
125 "means this book is not marked for offline usage "
126 "or not yet downloaded for offline usage. Please "
127 "load the address book once in online mode to "
128 "download its contents.");
129 }
130
131 else if (g_strcmp0 (backend_name, "local") == 0) {
132 const gchar *user_data_dir;
133 const gchar *uid;
134 gchar *path;
135
136 uid = e_source_get_uid (source);
137 user_data_dir = e_get_user_data_dir ();
138
139 path = g_build_filename (
140 user_data_dir, "addressbook", uid, NULL);
141
142 label = g_strdup_printf (
143 _("This address book cannot be opened. Please check that the "
144 "path %s exists and that permissions are set to access it."), path);
145
146 g_free (path);
147 label_string = label;
148 }
149
150 #ifndef HAVE_LDAP
151 else if (g_strcmp0 (backend_name, "ldap") == 0) {
152 /* special case for ldap: contact folders so we can tell the user about openldap */
153
154 can_detail_error = FALSE;
155 label_string =
156 _("This version of Evolution does not have LDAP support "
157 "compiled in to it. To use LDAP in Evolution "
158 "an LDAP-enabled Evolution package must be installed.");
159
160 }
161 #endif
162 else {
163 /* other network folders (or if ldap is enabled and server is unreachable) */
164 label_string =
165 _("This address book cannot be opened. This either "
166 "means that an incorrect URI was entered, or the server "
167 "is unreachable.");
168 }
169
170 if (can_detail_error) {
171 /* do not show repository offline message, it's kind of generic error */
172 if (error && !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE)) {
173 label = g_strconcat (label_string, "\n\n", _("Detailed error message:"), " ", error->message, NULL);
174 label_string = label;
175 }
176 }
177
178 if (alert_sink) {
179 e_alert_submit (
180 alert_sink, "addressbook:load-error",
181 label_string, NULL);
182 } else {
183 GtkWidget *dialog;
184
185 dialog = e_alert_dialog_new_for_args (
186 (GtkWindow *) parent,
187 "addressbook:load-error",
188 label_string, NULL);
189 g_signal_connect (
190 dialog, "response",
191 G_CALLBACK (gtk_widget_destroy), NULL);
192 gtk_widget_show (dialog);
193 }
194
195 g_free (label);
196 }
197
198 void
199 eab_search_result_dialog (EAlertSink *alert_sink,
200 const GError *error)
201 {
202 gchar *str = NULL;
203
204 if (!error)
205 return;
206
207 if (error->domain == E_CLIENT_ERROR) {
208 switch (error->code) {
209 case E_CLIENT_ERROR_SEARCH_SIZE_LIMIT_EXCEEDED:
210 str = _("More cards matched this query than either the server is \n"
211 "configured to return or Evolution is configured to display.\n"
212 "Please make your search more specific or raise the result limit in\n"
213 "the directory server preferences for this address book.");
214 str = g_strdup (str);
215 break;
216 case E_CLIENT_ERROR_SEARCH_TIME_LIMIT_EXCEEDED:
217 str = _("The time to execute this query exceeded the server limit or the limit\n"
218 "configured for this address book. Please make your search\n"
219 "more specific or raise the time limit in the directory server\n"
220 "preferences for this address book.");
221 str = g_strdup (str);
222 break;
223 case E_CLIENT_ERROR_INVALID_QUERY:
224 /* Translators: %s is replaced with a detailed error message, or an empty string, if not provided */
225 str = _("The backend for this address book was unable to parse this query. %s");
226 str = g_strdup_printf (str, error->message);
227 break;
228 case E_CLIENT_ERROR_QUERY_REFUSED:
229 /* Translators: %s is replaced with a detailed error message, or an empty string, if not provided */
230 str = _("The backend for this address book refused to perform this query. %s");
231 str = g_strdup_printf (str, error->message);
232 break;
233 case E_CLIENT_ERROR_OTHER_ERROR:
234 default:
235 /* Translators: %s is replaced with a detailed error message, or an empty string, if not provided */
236 str = _("This query did not complete successfully. %s");
237 str = g_strdup_printf (str, error->message);
238 break;
239 }
240 } else {
241 /* Translators: %s is replaced with a detailed error message, or an empty string, if not provided */
242 str = _("This query did not complete successfully. %s");
243 str = g_strdup_printf (str, error->message);
244 }
245
246 e_alert_submit (alert_sink, "addressbook:search-error", str, NULL);
247
248 g_free (str);
249 }
250
251 gint
252 eab_prompt_save_dialog (GtkWindow *parent)
253 {
254 return e_alert_run_dialog_for_args (parent, "addressbook:prompt-save", NULL);
255 }
256
257 static gchar *
258 make_safe_filename (gchar *name)
259 {
260 gchar *safe;
261
262 if (!name) {
263 /* This is a filename. Translators take note. */
264 name = _("card.vcf");
265 }
266
267 if (!g_strrstr (name, ".vcf"))
268 safe = g_strdup_printf ("%s%s", name, ".vcf");
269 else
270 safe = g_strdup (name);
271
272 e_filename_make_safe (safe);
273
274 return safe;
275 }
276
277 static void
278 source_selection_changed_cb (ESourceSelector *selector,
279 GtkWidget *ok_button)
280 {
281 ESource *except_source = NULL, *selected;
282 gboolean sensitive;
283
284 except_source = g_object_get_data (G_OBJECT (ok_button), "except-source");
285 selected = e_source_selector_ref_primary_selection (selector);
286
287 sensitive = (selected != NULL && selected != except_source);
288 gtk_widget_set_sensitive (ok_button, sensitive);
289
290 if (selected != NULL)
291 g_object_unref (selected);
292 }
293
294 ESource *
295 eab_select_source (ESourceRegistry *registry,
296 ESource *except_source,
297 const gchar *title,
298 const gchar *message,
299 const gchar *select_uid,
300 GtkWindow *parent)
301 {
302 ESource *source;
303 GtkWidget *content_area;
304 GtkWidget *dialog;
305 GtkWidget *ok_button;
306 /* GtkWidget *label; */
307 GtkWidget *selector;
308 GtkWidget *scrolled_window;
309 const gchar *extension_name;
310 gint response;
311
312 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
313
314 dialog = gtk_dialog_new_with_buttons (
315 _("Select Address Book"), parent,
316 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
317 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
318 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
319 NULL);
320 gtk_window_set_default_size (GTK_WINDOW (dialog), 350, 300);
321
322 gtk_dialog_set_response_sensitive (
323 GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE);
324
325 /* label = gtk_label_new (message); */
326
327 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
328 selector = e_source_selector_new (registry, extension_name);
329 e_source_selector_set_show_toggles (
330 E_SOURCE_SELECTOR (selector), FALSE);
331
332 ok_button = gtk_dialog_get_widget_for_response (
333 GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
334
335 if (except_source)
336 g_object_set_data (
337 G_OBJECT (ok_button), "except-source", except_source);
338
339 g_signal_connect (
340 selector, "primary_selection_changed",
341 G_CALLBACK (source_selection_changed_cb), ok_button);
342
343 if (select_uid) {
344 source = e_source_registry_ref_source (registry, select_uid);
345 if (source != NULL) {
346 e_source_selector_set_primary_selection (
347 E_SOURCE_SELECTOR (selector), source);
348 g_object_unref (source);
349 }
350 }
351
352 scrolled_window = gtk_scrolled_window_new (NULL, NULL);
353 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
354 gtk_container_add (GTK_CONTAINER (scrolled_window), selector);
355
356 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
357 gtk_box_pack_start (GTK_BOX (content_area), scrolled_window, TRUE, TRUE, 4);
358
359 gtk_widget_show_all (dialog);
360 response = gtk_dialog_run (GTK_DIALOG (dialog));
361
362 if (response == GTK_RESPONSE_ACCEPT)
363 source = e_source_selector_ref_primary_selection (
364 E_SOURCE_SELECTOR (selector));
365 else
366 source = NULL;
367
368 gtk_widget_destroy (dialog);
369
370 /* XXX Return a borrowed reference for backward-compatibility. */
371 if (source != NULL)
372 g_object_unref (source);
373
374 return source;
375 }
376
377 gchar *
378 eab_suggest_filename (const GSList *contact_list)
379 {
380 gchar *res = NULL;
381
382 g_return_val_if_fail (contact_list != NULL, NULL);
383
384 if (!contact_list->next) {
385 EContact *contact = E_CONTACT (contact_list->data);
386 gchar *string;
387
388 string = e_contact_get (contact, E_CONTACT_FILE_AS);
389 if (string == NULL)
390 string = e_contact_get (contact, E_CONTACT_FULL_NAME);
391 if (string != NULL)
392 res = make_safe_filename (string);
393 g_free (string);
394 }
395
396 if (res == NULL)
397 res = make_safe_filename (_("list"));
398
399 return res;
400 }
401
402 typedef struct ContactCopyProcess_ ContactCopyProcess;
403
404 struct ContactCopyProcess_ {
405 gint count;
406 gboolean book_status;
407 GSList *contacts;
408 EBookClient *source;
409 EBookClient *destination;
410 ESourceRegistry *registry;
411 gboolean delete_from_source;
412 EAlertSink *alert_sink;
413 };
414
415 static void process_unref (ContactCopyProcess *process);
416
417 static void
418 remove_contact_ready_cb (GObject *source_object,
419 GAsyncResult *result,
420 gpointer user_data)
421 {
422 EBookClient *book_client = E_BOOK_CLIENT (source_object);
423 ContactCopyProcess *process = user_data;
424 GError *error = NULL;
425
426 e_book_client_remove_contact_by_uid_finish (book_client, result, &error);
427
428 if (error != NULL) {
429 g_warning (
430 "%s: Remove contact by uid failed: %s",
431 G_STRFUNC, error->message);
432 g_error_free (error);
433 }
434
435 process_unref (process);
436 }
437
438 static void
439 do_delete_from_source (gpointer data,
440 gpointer user_data)
441 {
442 ContactCopyProcess *process = user_data;
443 EContact *contact = data;
444 const gchar *id;
445 EBookClient *book_client = process->source;
446
447 id = e_contact_get_const (contact, E_CONTACT_UID);
448 g_return_if_fail (id != NULL);
449 g_return_if_fail (book_client != NULL);
450
451 process->count++;
452 e_book_client_remove_contact_by_uid (book_client, id, NULL, remove_contact_ready_cb, process);
453 }
454
455 static void
456 delete_contacts (ContactCopyProcess *process)
457 {
458 if (process->book_status == TRUE) {
459 g_slist_foreach (process->contacts,
460 do_delete_from_source,
461 process);
462 }
463 }
464
465 static void
466 process_unref (ContactCopyProcess *process)
467 {
468 process->count--;
469 if (process->count == 0) {
470 if (process->delete_from_source) {
471 delete_contacts (process);
472 /* to not repeate this again */
473 process->delete_from_source = FALSE;
474
475 if (process->count > 0)
476 return;
477 }
478 e_client_util_free_object_slist (process->contacts);
479 g_object_unref (process->source);
480 g_object_unref (process->destination);
481 g_object_unref (process->registry);
482 g_free (process);
483 }
484 }
485
486 static void
487 contact_added_cb (EBookClient *book_client,
488 const GError *error,
489 const gchar *id,
490 gpointer user_data)
491 {
492 ContactCopyProcess *process = user_data;
493
494 if (error && !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) &&
495 !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
496 process->book_status = FALSE;
497 eab_error_dialog (process->alert_sink, _("Error adding contact"), error);
498 } else if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
499 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
500 process->book_status = FALSE;
501 }
502 else {
503 /* success */
504 process->book_status = TRUE;
505 }
506
507 process_unref (process);
508 }
509
510 static void
511 do_copy (gpointer data,
512 gpointer user_data)
513 {
514 EBookClient *book_client;
515 EContact *contact;
516 ContactCopyProcess *process;
517
518 process = user_data;
519 contact = data;
520
521 book_client = process->destination;
522 e_contact_inline_local_photos (contact, NULL);
523
524 process->count++;
525 eab_merging_book_add_contact (
526 process->registry, book_client,
527 contact, contact_added_cb, process);
528 }
529
530 static void
531 book_loaded_cb (GObject *source_object,
532 GAsyncResult *result,
533 gpointer user_data)
534 {
535 ESource *destination = E_SOURCE (source_object);
536 ContactCopyProcess *process = user_data;
537 EClient *client = NULL;
538 GError *error = NULL;
539
540 e_client_utils_open_new_finish (destination, result, &client, &error);
541
542 if (error != NULL) {
543 g_warn_if_fail (client == NULL);
544 g_warning (
545 "%s: Failed to open destination client: %s",
546 G_STRFUNC, error->message);
547 g_error_free (error);
548 goto exit;
549 }
550
551 g_return_if_fail (E_IS_CLIENT (client));
552
553 process->destination = E_BOOK_CLIENT (client);
554 process->book_status = TRUE;
555 g_slist_foreach (process->contacts, do_copy, process);
556
557 exit:
558 process_unref (process);
559 }
560
561 void
562 eab_transfer_contacts (ESourceRegistry *registry,
563 EBookClient *source_client,
564 GSList *contacts /* adopted */,
565 gboolean delete_from_source,
566 EAlertSink *alert_sink)
567 {
568 ESource *source;
569 ESource *destination;
570 static gchar *last_uid = NULL;
571 ContactCopyProcess *process;
572 gchar *desc;
573 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (alert_sink)));
574
575 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
576 g_return_if_fail (E_IS_BOOK_CLIENT (source_client));
577
578 if (contacts == NULL)
579 return;
580
581 if (last_uid == NULL)
582 last_uid = g_strdup ("");
583
584 if (contacts->next == NULL) {
585 if (delete_from_source)
586 desc = _("Move contact to");
587 else
588 desc = _("Copy contact to");
589 } else {
590 if (delete_from_source)
591 desc = _("Move contacts to");
592 else
593 desc = _("Copy contacts to");
594 }
595
596 source = e_client_get_source (E_CLIENT (source_client));
597
598 destination = eab_select_source (
599 registry, source, desc, NULL, last_uid, window);
600
601 if (!destination)
602 return;
603
604 if (strcmp (last_uid, e_source_get_uid (destination)) != 0) {
605 g_free (last_uid);
606 last_uid = g_strdup (e_source_get_uid (destination));
607 }
608
609 process = g_new (ContactCopyProcess, 1);
610 process->count = 1;
611 process->book_status = FALSE;
612 process->source = g_object_ref (source_client);
613 process->contacts = contacts;
614 process->destination = NULL;
615 process->registry = g_object_ref (registry);
616 process->alert_sink = alert_sink;
617 process->delete_from_source = delete_from_source;
618
619 e_client_utils_open_new (
620 destination, E_CLIENT_SOURCE_TYPE_CONTACTS, FALSE, NULL,
621 book_loaded_cb, process);
622 }
623
624 /* To parse something like...
625 * =?UTF-8?Q?=E0=A4=95=E0=A4=95=E0=A4=AC=E0=A5=82=E0=A5=8B=E0=A5=87?=\t\n=?UTF-8?Q?=E0=A4=B0?=\t\n<aa@aa.ccom>
626 * and return the decoded representation of name & email parts. */
627 gboolean
628 eab_parse_qp_email (const gchar *string,
629 gchar **name,
630 gchar **email)
631 {
632 struct _camel_header_address *address;
633 gboolean res = FALSE;
634
635 address = camel_header_address_decode (string, "UTF-8");
636
637 if (!address)
638 return FALSE;
639
640 /* report success only when we have filled both name and email address */
641 if (address->type == CAMEL_HEADER_ADDRESS_NAME && address->name && *address->name && address->v.addr && *address->v.addr) {
642 *name = g_strdup (address->name);
643 *email = g_strdup (address->v.addr);
644 res = TRUE;
645 }
646
647 camel_header_address_unref (address);
648
649 return res;
650 }
651
652 /* This is only wrapper to parse_qp_mail, it decodes string and if returned TRUE,
653 * then makes one string and returns it, otherwise returns NULL.
654 * Returned string is usable to place directly into GtkHtml stream.
655 * Returned value should be freed with g_free. */
656 gchar *
657 eab_parse_qp_email_to_html (const gchar *string)
658 {
659 gchar *name = NULL, *mail = NULL;
660 gchar *html_name, *html_mail;
661 gchar *value;
662
663 if (!eab_parse_qp_email (string, &name, &mail))
664 return NULL;
665
666 html_name = e_text_to_html (name, 0);
667 html_mail = e_text_to_html (mail, E_TEXT_TO_HTML_CONVERT_ADDRESSES);
668
669 value = g_strdup_printf ("%s <%s>", html_name, html_mail);
670
671 g_free (html_name);
672 g_free (html_mail);
673 g_free (name);
674 g_free (mail);
675
676 return value;
677 }
678
679 /*
680 * eab_format_address helper function
681 *
682 * Splits locales from en_US to array "en","us",NULL. When
683 * locales don't have the second part (for example "C"),
684 * the output array is "c",NULL
685 */
686 static gchar **
687 get_locales (void)
688 {
689 gchar *locale, *l_locale;
690 gchar *dot;
691 gchar **split;
692
693 #ifdef LC_ADDRESS
694 locale = g_strdup (setlocale (LC_ADDRESS, NULL));
695 #else
696 locale = NULL;
697 #endif
698 if (!locale)
699 return NULL;
700
701 l_locale = g_utf8_strdown (locale, -1);
702 g_free (locale);
703
704 dot = strchr (l_locale, '.');
705 if (dot != NULL) {
706 gchar *p = l_locale;
707 l_locale = g_strndup (l_locale, dot - l_locale);
708 g_free (p);
709 }
710
711 split = g_strsplit (l_locale, "_", 2);
712
713 g_free (l_locale);
714 return split;
715
716 }
717
718 static gchar *
719 get_locales_str (void)
720 {
721 gchar *ret;
722 gchar **loc = get_locales ();
723
724 if (!loc)
725 return g_strdup ("C");
726
727 if (!loc[0] ||
728 (loc[0] && !loc[1])) /* We don't care about language now, we need a country at first! */
729 ret = g_strdup ("C");
730 else if (loc[0] && loc[1]) {
731 if (*loc[0])
732 ret = g_strconcat (loc[LOCALES_COUNTRY], "_", loc[LOCALES_LANGUAGE], NULL);
733 else
734 ret = g_strdup (loc[LOCALES_COUNTRY]);
735 }
736
737 g_strfreev (loc);
738 return ret;
(emitted by cppcheck) (emitted by cppcheck) 739 }
740
741 /*
742 * Reads countrytransl.map file, which contains map of localized
743 * country names and their ISO codes and tries to find matching record
744 * for given country. The search is case insensitive.
745 * When no record is found (country is probably in untranslated language), returns
746 * code of local computer country (from locales)
747 */
748 static gchar *
749 country_to_ISO (const gchar *country)
750 {
751 FILE *file = fopen (EVOLUTION_RULEDIR "/countrytransl.map", "r");
752 gchar buffer[100];
753 gint length = 100;
754 gchar **pair;
755 gchar *res;
756 gchar *l_country = g_utf8_strdown (country, -1);
757
758 if (!file) {
759 gchar **loc;
760 g_warning ("%s: Failed to open countrytransl.map. Check your installation.", G_STRFUNC);
761 loc = get_locales ();
762 res = g_strdup (loc ? loc[LOCALES_COUNTRY] : NULL);
763 g_free (l_country);
764 g_strfreev (loc);
765 return res;
766 }
767
768 while (fgets (buffer, length, file) != NULL) {
769 gchar *low = NULL;
770 pair = g_strsplit (buffer, "\t", 2);
771
772 if (pair[0]) {
773 low = g_utf8_strdown (pair[0], -1);
774 if (g_utf8_collate (low, l_country) == 0) {
775 gchar *ret = g_strdup (pair[1]);
776 gchar *pos;
777 /* Remove trailing newline character */
778 if ((pos = g_strrstr (ret, "\n")) != NULL)
779 pos[0] = '\0';
780 fclose (file);
781 g_strfreev (pair);
782 g_free (low);
783 g_free (l_country);
784 return ret;
785 }
786 }
787
788 g_strfreev (pair);
789 g_free (low);
790 }
791
792 /* If we get here, then no match was found in the map file and we
793 * fallback to local system locales */
794 fclose (file);
795
796 pair = get_locales ();
797 res = g_strdup (pair ? pair[LOCALES_COUNTRY] : NULL);
798 g_strfreev (pair);
799 g_free (l_country);
800 return res;
801 }
802
803 /*
804 * Tries to find given key in "country_LANGUAGE" group. When fails to find
805 * such group, then fallbacks to "country" group. When such group does not
806 * exist either, NULL is returned
807 */
808 static gchar *
809 get_key_file_locale_string (GKeyFile *key_file,
810 const gchar *key,
811 const gchar *locale)
812 {
813 gchar *result;
814 gchar *group;
815
816 g_return_val_if_fail (locale, NULL);
817
818 /* Default locale is in "country_lang", but such group may not exist. In such case use group "country" */
819 if (g_key_file_has_group (key_file, locale))
820 group = g_strdup (locale);
821 else {
822 gchar **locales = g_strsplit (locale, "_", 0);
823 group = g_strdup (locales[LOCALES_COUNTRY]);
824 g_strfreev (locales);
825 }
826
827 /* When group or key does not exist, returns NULL and fallback string will be used */
828 result = g_key_file_get_string (key_file, group, key, NULL);
829 g_free (group);
830 return result;
831 }
832
833 static void
834 get_address_format (AddressFormat address_format,
835 const gchar *locale,
836 gchar **format,
837 gchar **country_position)
838 {
839 GKeyFile *key_file;
840 GError *error;
841 gchar *loc;
842 const gchar *addr_key, *country_key;
843
844 if (address_format == ADDRESS_FORMAT_HOME) {
845 addr_key = "AddressFormat";
846 country_key = "CountryPosition";
847 } else if (address_format == ADDRESS_FORMAT_BUSINESS) {
848 addr_key = "BusinessAddressFormat";
849 country_key = "BusinessCountryPosition";
850 } else {
851 return;
852 }
853
854 if (locale == NULL)
855 loc = get_locales_str ();
856 else
857 loc = g_strdup (locale);
858
859 error = NULL;
860 key_file = g_key_file_new ();
861 g_key_file_load_from_file (key_file, EVOLUTION_RULEDIR "/address_formats.dat", 0, &error);
862 if (error) {
863 g_warning ("%s: Failed to load address_formats.dat file: %s", G_STRFUNC, error->message);
864 *format = g_strdup (ADDRESS_DEFAULT_FORMAT);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
865 *country_position = g_strdup (ADDRESS_DEFAULT_COUNTRY_POSITION);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
866 g_key_file_free (key_file);
867 g_free (loc);
868 g_error_free (error);
869 return;
870 }
871
872 if (format) {
873 if (*format)
874 g_free (*format);
875 *format = get_key_file_locale_string (key_file, addr_key, loc);
876 if (!*format && address_format == ADDRESS_FORMAT_HOME) {
877 *format = g_strdup (ADDRESS_DEFAULT_FORMAT);
878 } else if (!*format && address_format == ADDRESS_FORMAT_BUSINESS)
879 get_address_format (ADDRESS_FORMAT_HOME, loc, format, NULL);
880 }
881
882 if (country_position) {
883 if (*country_position)
884 g_free (*country_position);
885 *country_position = get_key_file_locale_string (key_file, country_key, loc);
886 if (!*country_position && address_format == ADDRESS_FORMAT_HOME)
887 *country_position = g_strdup (ADDRESS_DEFAULT_COUNTRY_POSITION);
888 else if (!*country_position && address_format == ADDRESS_FORMAT_BUSINESS)
889 get_address_format (ADDRESS_FORMAT_HOME, loc, NULL, country_position);
890 }
891
892 g_free (loc);
893 g_key_file_free (key_file);
894 }
895
896 static const gchar *
897 find_balanced_bracket (const gchar *str)
898 {
899 gint balance_counter = 0;
900 gint i = 0;
901
902 do {
903 if (str[i] == '(')
904 balance_counter++;
905
906 if (str[i] == ')')
907 balance_counter--;
908
909 i++;
910
911 } while ((balance_counter > 0) && (str[i]));
912
913 if (balance_counter > 0)
914 return str;
915
916 return str + i;
917 }
918
919 static GString *
920 string_append_upper (GString *str,
921 const gchar *c)
922 {
923 gchar *up_c;
924
925 g_return_val_if_fail (str, NULL);
926
927 if (!c || !*c)
928 return str;
929
930 up_c = g_utf8_strup (c, -1);
931 str = g_string_append (str, up_c);
932 g_free (up_c);
933
934 return str;
935 }
936
937 static gboolean
938 parse_address_template_section (const gchar *format,
939 const gchar *realname,
940 const gchar *org_name,
941 EContactAddress *address,
942 gchar **result)
943
944 {
945 const gchar *pos, *old_pos;
946 gboolean ret = FALSE; /* Indicates, wheter at least something was replaced */
947
948 GString *res = g_string_new ("");
949
950 pos = format;
951 old_pos = pos;
952 while ((pos = strchr (pos, '%')) != NULL) {
953
954 if (old_pos != pos)
955 g_string_append_len (res, old_pos, pos - old_pos);
956
957 switch (pos[1]) {
958 case 'n':
959 if (realname && *realname) {
960 g_string_append (res, realname);
961 ret = TRUE;
962 }
963 pos += 2; /* Jump behind the modifier, see what's next */
964 break;
965 case 'N':
966 if (realname && *realname) {
967 string_append_upper (res, realname);
968 ret = TRUE;
969 }
970 pos += 2;
971 break;
972 case 'm':
973 if (org_name && *org_name) {
974 g_string_append (res, org_name);
975 ret = TRUE;
976 }
977 pos += 2;
978 break;
979 case 'M':
980 if (org_name && *org_name) {
981 string_append_upper (res, org_name);
982 ret = TRUE;
983 }
984 pos += 2;
985 break;
986 case 'p':
987 if (address->po && *(address->po)) {
988 g_string_append (res, address->po);
989 ret = TRUE;
990 }
991 pos += 2;
992 break;
993 case 's':
994 if (address->street && *(address->street)) {
995 g_string_append (res, address->street);
996 if (address->ext && *(address->ext))
997 g_string_append_printf (
998 res, "\n%s",
999 address->ext);
1000 ret = TRUE;
1001 }
1002 pos += 2;
1003 break;
1004 case 'S':
1005 if (address->street && *(address->street)) {
1006 string_append_upper (res, address->street);
1007 if (address->ext && *(address->ext)) {
1008 g_string_append (res, "\n");
1009 string_append_upper (res, address->ext);
1010 }
1011 ret = TRUE;
1012 }
1013 pos += 2;
1014 break;
1015 case 'z':
1016 if (address->code && *(address->code)) {
1017 g_string_append (res, address->code);
1018 ret = TRUE;
1019 }
1020 pos += 2;
1021 break;
1022 case 'l':
1023 if (address->locality && *(address->locality)) {
1024 g_string_append (res, address->locality);
1025 ret = TRUE;
1026 }
1027 pos += 2;
1028 break;
1029 case 'L':
1030 if (address->locality && *(address->locality)) {
1031 string_append_upper (res, address->locality);
1032 ret = TRUE;
1033 }
1034 pos += 2;
1035 break;
1036 case 'r':
1037 if (address->region && *(address->region)) {
1038 g_string_append (res, address->region);
1039 ret = TRUE;
1040 }
1041 pos += 2;
1042 break;
1043 case 'R':
1044 if (address->region && *(address->region)) {
1045 string_append_upper (res, address->region);
1046 ret = TRUE;
1047 }
1048 pos += 2;
1049 break;
1050 case ',':
1051 if (ret && (pos >= format + 2) && /* If there's something before %, */
1052 (g_ascii_strcasecmp (pos - 2, "\n") != 0) && /* And if it is not a newline */
1053 (g_ascii_strcasecmp (pos - 2, "%w") != 0)) /* Nor whitespace */
1054 g_string_append (res, ", ");
1055 pos += 2;
1056 break;
1057 case 'w':
1058 if (ret && (pos >= format + 2) &&
1059 (g_ascii_strcasecmp (pos - 2, "\n") != 0) &&
1060 (g_ascii_strcasecmp (pos - 1, " ") != 0))
1061 g_string_append (res, " ");
1062 pos += 2;
1063 break;
1064 case '0': {
1065 const gchar *bpos1, *bpos2;
1066 gchar *inner;
1067 gchar *ires;
1068 gboolean replaced;
1069
1070 bpos1 = pos + 2;
1071 bpos2 = find_balanced_bracket (bpos1);
1072
1073 inner = g_strndup (bpos1 + 1, bpos2 - bpos1 - 2); /* Get inner content of the %0 (...) */
1074 replaced = parse_address_template_section (inner, realname, org_name, address, &ires);
1075 if (replaced)
1076 g_string_append (res, ires);
1077
1078 g_free (ires);
1079 g_free (inner);
1080
1081 ret = replaced;
1082 pos += (bpos2 - bpos1 + 2);
1083 } break;
1084 }
1085
1086 old_pos = pos;
1087 }
1088 g_string_append (res, old_pos);
1089
1090 *result = g_strdup (res->str);
1091
1092 g_string_free (res, TRUE);
1093
1094 return ret;
1095 }
1096
1097 gchar *
1098 eab_format_address (EContact *contact,
1099 EContactField address_type)
1100 {
1101 gchar *result;
1102 gchar *format = NULL;
1103 gchar *country_position = NULL;
1104 gchar *locale;
1105 EContactAddress *addr = e_contact_get (contact, address_type);
1106
1107 if (!addr)
1108 return NULL;
1109
1110 if (!addr->po && !addr->ext && !addr->street && !addr->locality && !addr->region &&
1111 !addr->code && !addr->country) {
1112 e_contact_address_free (addr);
1113 return NULL;
1114 }
1115
1116 if (addr->country) {
1117 gchar *cntry = country_to_ISO (addr->country);
1118 gchar **loc = get_locales ();
1119 locale = g_strconcat (loc ? loc[LOCALES_LANGUAGE] : "C", "_", cntry, NULL);
1120 g_strfreev (loc);
1121 g_free (cntry);
1122 } else
1123 locale = get_locales_str ();
1124
1125 if (address_type == E_CONTACT_ADDRESS_HOME)
1126 get_address_format (ADDRESS_FORMAT_HOME, locale, &format, &country_position);
1127 else if (address_type == E_CONTACT_ADDRESS_WORK)
1128 get_address_format (ADDRESS_FORMAT_BUSINESS, locale, &format, &country_position);
1129 else {
1130 e_contact_address_free (addr);
1131 g_free (locale);
1132 return NULL;
1133 }
1134
1135 /* Expand all the variables in format.
1136 * Don't display organization in home address;
1137 * and skip full names, as it's part of the EContact itself,
1138 * check this bug for reason: https://bugzilla.gnome.org/show_bug.cgi?id=667912
1139 */
1140 parse_address_template_section (
1141 format,
1142 NULL,
1143 (address_type == E_CONTACT_ADDRESS_WORK) ?
1144 e_contact_get_const (contact, E_CONTACT_ORG) : NULL,
1145 addr,
1146 &result);
1147
1148 /* Add the country line. In some countries, the address can be located above the
1149 * rest of the address */
1150 if (addr->country && country_position) {
1151 gchar *country_upper = g_utf8_strup (addr->country, -1);
1152 gchar *p = result;
1153 if (g_strcmp0 (country_position, "BELOW") == 0) {
1154 result = g_strconcat (p, "\n\n", country_upper, NULL);
1155 g_free (p);
1156 } else if (g_strcmp0 (country_position, "below") == 0) {
1157 result = g_strconcat (p, "\n\n", addr->country, NULL);
1158 g_free (p);
1159 } else if (g_strcmp0 (country_position, "ABOVE") == 0) {
1160 result = g_strconcat (country_upper, "\n\n", p, NULL);
1161 g_free (p);
1162 } else if (g_strcmp0 (country_position, "above") == 0) {
1163 result = g_strconcat (addr->country, "\n\n", p, NULL);
1164 g_free (p);
1165 }
1166 g_free (country_upper);
1167 }
1168
1169 e_contact_address_free (addr);
1170 g_free (locale);
1171 g_free (format);
1172 g_free (country_position);
1173
1174 return result;
1175 }