evolution-3.6.4/addressbook/gui/widgets/eab-gui-util.c

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 &lt;%s&gt;", 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;
Uninitialized variable: ret
(emitted by cppcheck)
Uninitialized variable: ret
(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);
Dereference of null pointer (loaded from variable 'format')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Dereference of null pointer (loaded from variable 'format')
(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);
Dereference of null pointer (loaded from variable 'country_position')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Dereference of null pointer (loaded from variable 'country_position')
(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 }