evolution-3.6.4/plugins/publish-calendar/publish-calendar.c

No issues found

   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  *		David Trowbridge <trowbrds@cs.colorado.edu>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 #include <gtk/gtk.h>
  29 #include <glib/gi18n.h>
  30 #include <gio/gio.h>
  31 
  32 #include <calendar/gui/e-cal-config.h>
  33 #include <shell/es-event.h>
  34 #include <e-util/e-util.h>
  35 #include <e-util/e-util-private.h>
  36 #include <e-util/e-dialog-utils.h>
  37 
  38 #include <shell/e-shell.h>
  39 #include <shell/e-shell-view.h>
  40 
  41 #include "url-editor-dialog.h"
  42 #include "publish-format-fb.h"
  43 #include "publish-format-ical.h"
  44 
  45 #ifdef HAVE_LIBNOTIFY
  46 #include <libnotify/notify.h>
  47 #endif
  48 
  49 static GtkListStore *store = NULL;
  50 static GHashTable *uri_timeouts = NULL;
  51 static GSList *publish_uris = NULL;
  52 static GSList *queued_publishes = NULL;
  53 static gint online = 0;
  54 
  55 static GSList *error_queue = NULL;
  56 static GStaticMutex error_queue_lock = G_STATIC_MUTEX_INIT;
  57 static guint error_queue_show_idle_id = 0;
  58 static void  error_queue_add (gchar *descriptions, GError *error);
  59 
  60 gint          e_plugin_lib_enable (EPlugin *ep, gint enable);
  61 GtkWidget   *publish_calendar_locations (EPlugin *epl, EConfigHookItemFactoryData *data);
  62 static void  update_timestamp (EPublishUri *uri);
  63 static void publish (EPublishUri *uri, gboolean can_report_success);
  64 
  65 static GtkStatusIcon *status_icon = NULL;
  66 static guint status_icon_timeout_id = 0;
  67 #ifdef HAVE_LIBNOTIFY
  68 static NotifyNotification *notify = NULL;
  69 
  70 static gboolean
  71 show_notify_cb (gpointer data)
  72 {
  73 	return notify && !notify_notification_show (notify, NULL);
  74 }
  75 #endif
  76 
  77 static gboolean
  78 remove_notification (gpointer data)
  79 {
  80 	if (status_icon_timeout_id)
  81 		g_source_remove (status_icon_timeout_id);
  82 	status_icon_timeout_id = 0;
  83 
  84 #ifdef HAVE_LIBNOTIFY
  85 	if (notify)
  86 		notify_notification_close (notify, NULL);
  87 	notify = NULL;
  88 #endif
  89 
  90 	gtk_status_icon_set_visible (status_icon, FALSE);
  91 	g_object_unref (status_icon);
  92 	status_icon = NULL;
  93 
  94 	return FALSE;
  95 }
  96 
  97 static void
  98 update_publish_notification (GtkMessageType msg_type,
  99                              const gchar *msg_text)
 100 {
 101 	static GString *actual_msg = NULL;
 102 	#ifdef HAVE_LIBNOTIFY
 103 	static gboolean can_notify = TRUE;
 104 	#endif
 105 	gboolean new_icon = !status_icon;
 106 	const gchar *stock_name;
 107 
 108 	g_return_if_fail (msg_text != NULL);
 109 
 110 	if (new_icon) {
 111 		status_icon = gtk_status_icon_new ();
 112 		if (actual_msg) {
 113 			g_string_free (actual_msg, TRUE);
 114 			actual_msg = NULL;
 115 		}
 116 	} else if (status_icon_timeout_id) {
 117 		g_source_remove (status_icon_timeout_id);
 118 	}
 119 
 120 	switch (msg_type) {
 121 	case GTK_MESSAGE_WARNING:
 122 		stock_name = GTK_STOCK_DIALOG_WARNING;
 123 		break;
 124 	case GTK_MESSAGE_ERROR:
 125 		stock_name = GTK_STOCK_DIALOG_ERROR;
 126 		break;
 127 	default:
 128 		stock_name = GTK_STOCK_DIALOG_INFO;
 129 		break;
 130 	}
 131 
 132 	if (!actual_msg) {
 133 		actual_msg = g_string_new (msg_text);
 134 	} else {
 135 		g_string_append (actual_msg, "\n");
 136 		g_string_append (actual_msg, msg_text);
 137 	}
 138 
 139 	gtk_status_icon_set_from_stock (status_icon, stock_name);
 140 	gtk_status_icon_set_tooltip_text (status_icon, actual_msg->str);
 141 
 142 #ifdef HAVE_LIBNOTIFY
 143 	if (can_notify) {
 144 		if (notify) {
 145 			notify_notification_update (notify, _("Calendar Publishing"), actual_msg->str, stock_name);
 146 		} else {
 147 			if (!notify_init ("evolution-publish-calendar")) {
 148 				can_notify = FALSE;
 149 				return;
 150 			}
 151 
 152 			notify  = notify_notification_new (_("Calendar Publishing"), actual_msg->str, stock_name);
 153 			notify_notification_set_urgency (notify, NOTIFY_URGENCY_NORMAL);
 154 			notify_notification_set_timeout (notify, NOTIFY_EXPIRES_DEFAULT);
 155 			g_timeout_add (500, show_notify_cb, NULL);
 156 
 157 			g_signal_connect (
 158 				notify, "closed",
 159 				G_CALLBACK (remove_notification), NULL);
 160 		}
 161 	}
 162 #endif
 163 
 164 	status_icon_timeout_id = g_timeout_add_seconds (15, remove_notification, NULL);
 165 
 166 	if (new_icon) {
 167 		g_signal_connect (
 168 			status_icon, "activate",
 169 			G_CALLBACK (remove_notification), NULL);
 170 	}
 171 }
 172 
 173 static void
 174 publish_no_succ_info (EPublishUri *uri)
 175 {
 176 	publish (uri, FALSE);
 177 }
 178 
 179 static void
 180 publish_uri_async (EPublishUri *uri)
 181 {
 182 	GError *error = NULL;
 183 
 184 	g_thread_create (
 185 		(GThreadFunc) publish_no_succ_info, uri, FALSE, &error);
 186 	if (error != NULL) {
 187 		g_warning (G_STRLOC ": %s", error->message);
 188 		g_error_free (error);
 189 	}
 190 }
 191 
 192 static void
 193 publish_online (EPublishUri *uri,
 194                 GFile *file,
 195                 GError **perror,
 196                 gboolean can_report_success)
 197 {
 198 	GOutputStream *stream;
 199 	GError *error = NULL;
 200 
 201 	stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
 202 
 203 	if (!stream || error) {
 204 		if (stream)
 205 			g_object_unref (stream);
 206 
 207 		if (perror) {
 208 			*perror = error;
 209 		} else if (error) {
 210 
 211 			error_queue_add (g_strdup_printf (_("Could not open %s:"), uri->location), error);
 212 		} else {
 213 			error_queue_add (g_strdup_printf (_("Could not open %s: Unknown error"), uri->location), NULL);
 214 		}
 215 		return;
 216 	}
 217 
 218 	switch (uri->publish_format) {
 219 	case URI_PUBLISH_AS_ICAL:
 220 		publish_calendar_as_ical (stream, uri, &error);
 221 		break;
 222 	case URI_PUBLISH_AS_FB:
 223 		publish_calendar_as_fb (stream, uri, &error);
 224 		break;
 225 	/*
 226 	case URI_PUBLISH_AS_HTML:
 227 		publish_calendar_as_html (handle, uri);
 228 		break;
 229 	*/
 230 	}
 231 
 232 	if (error)
 233 		error_queue_add (g_strdup_printf (_("There was an error while publishing to %s:"), uri->location), error);
 234 	else if (can_report_success)
 235 		error_queue_add (g_strdup_printf (_("Publishing to %s finished successfully"), uri->location), NULL);
 236 
 237 	update_timestamp (uri);
 238 
 239 	g_output_stream_close (stream, NULL, NULL);
 240 	g_object_unref (stream);
 241 }
 242 
 243 static void
 244 unmount_done_cb (GObject *source_object,
 245                  GAsyncResult *result,
 246                  gpointer user_data)
 247 {
 248 	GError *error = NULL;
 249 
 250 	g_mount_unmount_with_operation_finish (G_MOUNT (source_object), result, &error);
 251 
 252 	if (error) {
 253 		g_warning ("Unmount failed: %s", error->message);
 254 		g_error_free (error);
 255 	}
 256 
 257 	g_object_unref (source_object);
 258 }
 259 
 260 struct mnt_struct {
 261 	EPublishUri *uri;
 262 	GFile *file;
 263 	GMountOperation *mount_op;
 264 	gboolean can_report_success;
 265 };
 266 
 267 static void
 268 mount_ready_cb (GObject *source_object,
 269                 GAsyncResult *result,
 270                 gpointer user_data)
 271 {
 272 	struct mnt_struct *ms = (struct mnt_struct *) user_data;
 273 	GError *error = NULL;
 274 	GMount *mount;
 275 
 276 	g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
 277 
 278 	if (error) {
 279 		error_queue_add (g_strdup_printf (_("Mount of %s failed:"), ms->uri->location), error);
 280 
 281 		if (ms)
 282 			g_object_unref (ms->mount_op);
 283 		g_free (ms);
 284 
 285 		g_object_unref (source_object);
 286 
 287 		return;
 288 	}
 289 
 290 	g_return_if_fail (ms != NULL);
 291 
 292 	publish_online (ms->uri, ms->file, NULL, ms->can_report_success);
 293 
 294 	g_object_unref (ms->mount_op);
 295 	g_free (ms);
 296 
 297 	mount = g_file_find_enclosing_mount (G_FILE (source_object), NULL, NULL);
 298 	if (mount)
 299 		g_mount_unmount_with_operation (mount, G_MOUNT_UNMOUNT_NONE, NULL, NULL, unmount_done_cb, NULL);
 300 
 301 	g_object_unref (source_object);
 302 }
 303 
 304 static void
 305 ask_password (GMountOperation *op,
 306               const gchar *message,
 307               const gchar *default_user,
 308               const gchar *default_domain,
 309               GAskPasswordFlags flags,
 310               gpointer user_data)
 311 {
 312 	struct mnt_struct *ms = (struct mnt_struct *) user_data;
 313 	const gchar *username;
 314 	gchar *password;
 315 	gboolean req_pass = FALSE;
 316 	SoupURI *soup_uri;
 317 
 318 	g_return_if_fail (ms != NULL);
 319 
 320 	/* we can ask only for a password */
 321 	if ((flags & G_ASK_PASSWORD_NEED_PASSWORD) == 0)
 322 		return;
 323 
 324 	soup_uri = soup_uri_new (ms->uri->location);
 325 	g_return_if_fail (soup_uri != NULL);
 326 
 327 	username = soup_uri_get_user (soup_uri);
 328 	password = e_passwords_get_password (NULL, ms->uri->location);
 329 	req_pass =
 330 		((username && *username) &&
 331 		!(ms->uri->service_type == TYPE_ANON_FTP &&
 332 		!strcmp (username, "anonymous")));
 333 
 334 	if (!password && req_pass) {
 335 		gboolean remember = FALSE;
 336 
 337 		password = e_passwords_ask_password (
 338 			_("Enter password"), NULL,
 339 			ms->uri->location, message,
 340 			E_PASSWORDS_REMEMBER_FOREVER |
 341 			E_PASSWORDS_SECRET |
 342 			E_PASSWORDS_ONLINE,
 343 			&remember, NULL);
 344 
 345 		if (!password) {
 346 			/* user canceled password dialog */
 347 			g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
 348 			soup_uri_free (soup_uri);
 349 
 350 			return;
 351 		}
 352 	}
 353 
 354 	if (!req_pass)
 355 		g_mount_operation_set_anonymous (op, TRUE);
 356 	else {
 357 		g_mount_operation_set_anonymous (op, FALSE);
 358 		g_mount_operation_set_username  (op, username);
 359 		g_mount_operation_set_password  (op, password);
 360 	}
 361 
 362 	g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
 363 
 364 	soup_uri_free (soup_uri);
 365 }
 366 
 367 static void
 368 ask_question (GMountOperation *op,
 369               const gchar *message,
 370               const gchar *choices[])
 371 {
 372 	/* this has been stolen from file-chooser */
 373 	GtkWidget *dialog;
 374 	gint cnt, len;
 375 	gchar *primary;
 376 	const gchar *secondary = NULL;
 377 	gint res;
 378 
 379 	primary = strstr (message, "\n");
 380 	if (primary) {
 381 		secondary = primary + 1;
 382 		primary = g_strndup (message, strlen (message) - strlen (primary));
 383 	}
 384 
 385 	dialog = gtk_message_dialog_new (
 386 		NULL,
 387 		0, GTK_MESSAGE_QUESTION,
 388 		GTK_BUTTONS_NONE, "%s", primary);
 389 	g_free (primary);
 390 
 391 	if (secondary) {
 392 		gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
 393 							  "%s", secondary);
 394 	}
 395 
 396 	if (choices) {
 397 		/* First count the items in the list then
 398 		 * add the buttons in reverse order */
 399 		for (len = 0; choices[len] != NULL; len++) {
 400 			;
 401 		}
 402 
 403 		for (cnt = len - 1; cnt >= 0; cnt--) {
 404 			gtk_dialog_add_button (GTK_DIALOG (dialog), choices[cnt], cnt);
 405 		}
 406 	}
 407 
 408 	res = gtk_dialog_run (GTK_DIALOG (dialog));
 409 	if (res >= 0) {
 410 		g_mount_operation_set_choice (op, res);
 411 		g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
 412 	} else {
 413 		g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
 414 	}
 415 
 416 	gtk_widget_destroy (GTK_WIDGET (dialog));
 417 }
 418 
 419 static void
 420 mount_first (EPublishUri *uri,
 421              GFile *file,
 422              gboolean can_report_success)
 423 {
 424 	struct mnt_struct *ms = g_malloc (sizeof (struct mnt_struct));
 425 
 426 	ms->uri = uri;
 427 	ms->file = g_object_ref (file);
 428 	ms->mount_op = g_mount_operation_new ();
 429 	ms->can_report_success = can_report_success;
 430 
 431 	g_signal_connect (
 432 		ms->mount_op, "ask-password",
 433 		G_CALLBACK (ask_password), ms);
 434 	g_signal_connect (
 435 		ms->mount_op, "ask-question",
 436 		G_CALLBACK (ask_question), ms);
 437 
 438 	g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE, ms->mount_op, NULL, mount_ready_cb, ms);
 439 }
 440 
 441 static void
 442 publish (EPublishUri *uri,
 443          gboolean can_report_success)
 444 {
 445 	if (online) {
 446 		GError *error = NULL;
 447 		GFile *file;
 448 
 449 		if (g_slist_find (queued_publishes, uri))
 450 			queued_publishes = g_slist_remove (queued_publishes, uri);
 451 
 452 		if (!uri->enabled)
 453 			return;
 454 
 455 		file = g_file_new_for_uri (uri->location);
 456 
 457 		g_return_if_fail (file != NULL);
 458 
 459 		publish_online (uri, file, &error, can_report_success);
 460 
 461 		if (error && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) {
 462 			g_error_free (error);
 463 			error = NULL;
 464 
 465 			mount_first (uri, file, can_report_success);
 466 		}
 467 
 468 		if (error)
 469 			error_queue_add (g_strdup_printf (_("Could not open %s:"), uri->location), error);
 470 
 471 		g_object_unref (file);
 472 	} else {
 473 		if (g_slist_find (queued_publishes, uri) == NULL)
 474 			queued_publishes = g_slist_prepend (queued_publishes, uri);
 475 	}
 476 }
 477 
 478 typedef struct {
 479 	GSettings *settings;
 480 	GtkWidget *treeview;
 481 	GtkWidget *url_add;
 482 	GtkWidget *url_edit;
 483 	GtkWidget *url_remove;
 484 	GtkWidget *url_enable;
 485 } PublishUIData;
 486 
 487 static void
 488 add_timeout (EPublishUri *uri)
 489 {
 490 	guint id;
 491 
 492 	/* Set the timeout for now+frequency */
 493 	switch (uri->publish_frequency) {
 494 	case URI_PUBLISH_DAILY:
 495 		id = g_timeout_add_seconds (24 * 60 * 60, (GSourceFunc) publish, uri);
 496 		g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
 497 		break;
 498 	case URI_PUBLISH_WEEKLY:
 499 		id = g_timeout_add_seconds (7 * 24 * 60 * 60, (GSourceFunc) publish, uri);
 500 		g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
 501 		break;
 502 	}
 503 }
 504 
 505 static void
 506 update_timestamp (EPublishUri *uri)
 507 {
 508 	GSettings *settings;
 509 	gchar **set_uris;
 510 	GPtrArray *uris_array;
 511 	gboolean found = FALSE;
 512 	gchar *xml;
 513 	gint ii;
 514 	guint id;
 515 
 516 	/* Remove timeout if we have one */
 517 	id = GPOINTER_TO_UINT (g_hash_table_lookup (uri_timeouts, uri));
 518 	if (id) {
 519 		g_source_remove (id);
 520 		add_timeout (uri);
 521 	}
 522 
 523 	/* Update timestamp in settings */
 524 	xml = e_publish_uri_to_xml (uri);
 525 
 526 	if (uri->last_pub_time)
 527 		g_free (uri->last_pub_time);
 528 	uri->last_pub_time = g_strdup_printf ("%d", (gint) time (NULL));
 529 
 530 	uris_array = g_ptr_array_new_full (3, g_free);
 531 	settings = g_settings_new (PC_SETTINGS_ID);
 532 	set_uris = g_settings_get_strv (settings, PC_SETTINGS_URIS);
 533 
 534 	for (ii = 0; set_uris && set_uris[ii]; ii++) {
 535 		const gchar *d = set_uris[ii];
 536 
 537 		if (!found && g_str_equal (d, xml)) {
 538 			found = TRUE;
 539 			g_ptr_array_add (uris_array, e_publish_uri_to_xml (uri));
 540 		} else {
 541 			g_ptr_array_add (uris_array, g_strdup (d));
 542 		}
 543 	}
 544 
 545 	g_strfreev (set_uris);
 546 	g_free (xml);
 547 
 548 	/* this should not happen, right? */
 549 	if (!found)
 550 		g_ptr_array_add (uris_array, e_publish_uri_to_xml (uri));
 551 	g_ptr_array_add (uris_array, NULL);
 552 
 553 	g_settings_set_strv (settings, PC_SETTINGS_URIS, (const gchar * const *) uris_array->pdata);
 554 
 555 	g_object_unref (settings);
 556 	g_ptr_array_free (uris_array, TRUE);
 557 }
 558 
 559 static void
 560 add_offset_timeout (EPublishUri *uri)
 561 {
 562 	guint id;
 563 	time_t offset = atoi (uri->last_pub_time);
 564 	time_t current = time (NULL);
 565 	gint elapsed = current - offset;
 566 
 567 	switch (uri->publish_frequency) {
 568 	case URI_PUBLISH_DAILY:
 569 		if (elapsed > 24 * 60 * 60) {
 570 			publish (uri, FALSE);
 571 			add_timeout (uri);
 572 		} else {
 573 			id = g_timeout_add_seconds (24 * 60 * 60 - elapsed, (GSourceFunc) publish, uri);
 574 			g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
 575 			break;
 576 		}
 577 		break;
 578 	case URI_PUBLISH_WEEKLY:
 579 		if (elapsed > 7 * 24 * 60 * 60) {
 580 			publish (uri, FALSE);
 581 			add_timeout (uri);
 582 		} else {
 583 			id = g_timeout_add_seconds (7 * 24 * 60 * 60 - elapsed, (GSourceFunc) publish, uri);
 584 			g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
 585 			break;
 586 		}
 587 		break;
 588 	}
 589 }
 590 
 591 static void
 592 url_list_changed (PublishUIData *ui)
 593 {
 594 	GtkTreeModel *model = NULL;
 595 	GPtrArray *uris;
 596 	GtkTreeIter iter;
 597 	gboolean valid;
 598 	GSettings *settings;
 599 
 600 	uris = g_ptr_array_new_full (3, g_free);
 601 
 602 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 603 	valid = gtk_tree_model_get_iter_first (model, &iter);
 604 	while (valid) {
 605 		EPublishUri *url;
 606 		gchar *xml;
 607 
 608 		gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
 609 
 610 		if ((xml = e_publish_uri_to_xml (url)) != NULL)
 611 			g_ptr_array_add (uris, xml);
 612 
 613 		valid = gtk_tree_model_iter_next (model, &iter);
 614 	}
 615 
 616 	g_ptr_array_add (uris, NULL);
 617 
 618 	settings = g_settings_new (PC_SETTINGS_ID);
 619 	g_settings_set_strv (settings, PC_SETTINGS_URIS, (const gchar * const *) uris->pdata);
 620 	g_object_unref (settings);
 621 
 622 	g_ptr_array_free (uris, TRUE);
 623 }
 624 
 625 static void
 626 update_url_enable_button (EPublishUri *url,
 627                           GtkWidget *url_enable)
 628 {
 629 	g_return_if_fail (url_enable != NULL);
 630 	g_return_if_fail (GTK_IS_BUTTON (url_enable));
 631 
 632 	gtk_button_set_label (GTK_BUTTON (url_enable), url && url->enabled ? _("_Disable") : _("E_nable"));
 633 }
 634 
 635 static void
 636 url_list_enable_toggled (GtkCellRendererToggle *renderer,
 637                          const gchar *path_string,
 638                          PublishUIData *ui)
 639 {
 640 	EPublishUri *url = NULL;
 641 	GtkTreeModel *model;
 642 	GtkTreePath *path;
 643 	GtkTreeIter iter;
 644 
 645 	path = gtk_tree_path_new_from_string (path_string);
 646 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 647 
 648 	if (gtk_tree_model_get_iter (model, &iter, path)) {
 649 		gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
 650 
 651 		url->enabled = !url->enabled;
 652 
 653 		update_url_enable_button (url, ui->url_enable);
 654 
 655 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, URL_LIST_ENABLED_COLUMN, url->enabled, -1);
 656 
 657 		url_list_changed (ui);
 658 	}
 659 
 660 	gtk_tree_path_free (path);
 661 }
 662 
 663 static void
 664 selection_changed (GtkTreeSelection *selection,
 665                    PublishUIData *ui)
 666 {
 667 	GtkTreeModel *model;
 668 	GtkTreeIter iter;
 669 	EPublishUri *url = NULL;
 670 
 671 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 672 		gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
 673 		gtk_widget_set_sensitive (ui->url_edit, TRUE);
 674 		gtk_widget_set_sensitive (ui->url_remove, TRUE);
 675 		gtk_widget_set_sensitive (ui->url_enable, TRUE);
 676 	} else {
 677 		gtk_widget_set_sensitive (ui->url_edit, FALSE);
 678 		gtk_widget_set_sensitive (ui->url_remove, FALSE);
 679 		gtk_widget_set_sensitive (ui->url_enable, FALSE);
 680 	}
 681 
 682 	update_url_enable_button (url, ui->url_enable);
 683 }
 684 
 685 static void
 686 url_add_clicked (GtkButton *button,
 687                  PublishUIData *ui)
 688 {
 689 	GtkTreeModel *model;
 690 	GtkTreeIter iter;
 691 	GtkWidget *url_editor;
 692 	EPublishUri *uri;
 693 
 694 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
 695 	url_editor = url_editor_dialog_new (model, NULL);
 696 
 697 	if (url_editor_dialog_run ((UrlEditorDialog *) url_editor)) {
 698 		uri = URL_EDITOR_DIALOG (url_editor)->uri;
 699 		if (uri->location) {
 700 			gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 701 			gtk_list_store_set (
 702 				GTK_LIST_STORE (model), &iter,
 703 				URL_LIST_ENABLED_COLUMN, uri->enabled,
 704 				URL_LIST_LOCATION_COLUMN, uri->location,
 705 				URL_LIST_URL_COLUMN, uri, -1);
 706 			url_list_changed (ui);
 707 			publish_uris = g_slist_prepend (publish_uris, uri);
 708 			add_timeout (uri);
 709 			publish_uri_async (uri);
 710 		} else {
 711 			g_free (uri);
 712 		}
 713 	}
 714 	gtk_widget_destroy (url_editor);
 715 }
 716 
 717 static void
 718 url_edit_clicked (GtkButton *button,
 719                   PublishUIData *ui)
 720 {
 721 	GtkTreeSelection *selection;
 722 	GtkTreeModel *model;
 723 	GtkTreeIter iter;
 724 
 725 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 726 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 727 		EPublishUri *uri;
 728 		GtkWidget *url_editor;
 729 		guint id;
 730 
 731 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 2, &uri, -1);
 732 		url_editor = url_editor_dialog_new (model, uri);
 733 
 734 		if (url_editor_dialog_run ((UrlEditorDialog *) url_editor)) {
 735 			gtk_list_store_set (
 736 				GTK_LIST_STORE (model), &iter,
 737 				URL_LIST_ENABLED_COLUMN, uri->enabled,
 738 				URL_LIST_LOCATION_COLUMN, uri->location,
 739 				URL_LIST_URL_COLUMN, uri, -1);
 740 
 741 			id = GPOINTER_TO_UINT (g_hash_table_lookup (uri_timeouts, uri));
 742 			if (id)
 743 				g_source_remove (id);
 744 			add_timeout (uri);
 745 			url_list_changed (ui);
 746 			publish_uri_async (uri);
 747 		}
 748 
 749 		gtk_widget_destroy (url_editor);
 750 	}
 751 }
 752 
 753 static void
 754 url_list_double_click (GtkTreeView *treeview,
 755                        GtkTreePath *path,
 756                        GtkTreeViewColumn *column,
 757                        PublishUIData *ui)
 758 {
 759 	url_edit_clicked (NULL, ui);
 760 }
 761 
 762 static void
 763 url_remove_clicked (GtkButton *button,
 764                     PublishUIData *ui)
 765 {
 766 	EPublishUri *url = NULL;
 767 	GtkTreeSelection *selection;
 768 	GtkTreeModel *model;
 769 	GtkTreeIter iter;
 770 	GtkWidget *confirm;
 771 	gint response;
 772 
 773 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 774 	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
 775 		return;
 776 
 777 	gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
 778 
 779 	confirm = gtk_message_dialog_new (
 780 		NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 781 		GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
 782 		_("Are you sure you want to remove this location?"));
 783 	gtk_dialog_add_button (GTK_DIALOG (confirm), GTK_STOCK_CANCEL, GTK_RESPONSE_NO);
 784 	gtk_dialog_add_button (GTK_DIALOG (confirm), GTK_STOCK_REMOVE, GTK_RESPONSE_YES);
 785 	gtk_dialog_set_default_response (GTK_DIALOG (confirm), GTK_RESPONSE_CANCEL);
 786 
 787 	response = gtk_dialog_run (GTK_DIALOG (confirm));
 788 	gtk_widget_destroy (confirm);
 789 
 790 	if (response == GTK_RESPONSE_YES) {
 791 		gint len;
 792 		guint id;
 793 		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 794 
 795 		len = gtk_tree_model_iter_n_children (model, NULL);
 796 		if (len > 0) {
 797 			gtk_tree_selection_select_iter (selection, &iter);
 798 		} else {
 799 			gtk_widget_set_sensitive (ui->url_edit, FALSE);
 800 			gtk_widget_set_sensitive (ui->url_remove, FALSE);
 801 			gtk_widget_set_sensitive (ui->url_enable, FALSE);
 802 
 803 			update_url_enable_button (NULL, ui->url_enable);
 804 		}
 805 
 806 		publish_uris = g_slist_remove (publish_uris, url);
 807 		id = GPOINTER_TO_UINT (g_hash_table_lookup (uri_timeouts, url));
 808 		if (id)
 809 			g_source_remove (id);
 810 
 811 		g_free (url);
 812 		url_list_changed (ui);
 813 	}
 814 }
 815 
 816 static void
 817 url_enable_clicked (GtkButton *button,
 818                     PublishUIData *ui)
 819 {
 820 	EPublishUri *url = NULL;
 821 	GtkTreeSelection *selection;
 822 	GtkTreeModel *model;
 823 	GtkTreeIter iter;
 824 
 825 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 826 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
 827 		gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
 828 		url->enabled = !url->enabled;
 829 
 830 		update_url_enable_button (url, ui->url_enable);
 831 
 832 		gtk_list_store_set (GTK_LIST_STORE (model), &iter, URL_LIST_ENABLED_COLUMN, url->enabled, -1);
 833 		gtk_tree_selection_select_iter (selection, &iter);
 834 		url_list_changed (ui);
 835 	}
 836 }
 837 
 838 static void
 839 online_state_changed (EShell *shell)
 840 {
 841 	online = e_shell_get_online (shell);
 842 	if (online)
 843 		while (queued_publishes)
 844 			publish (queued_publishes->data, FALSE);
 845 }
 846 
 847 GtkWidget *
 848 publish_calendar_locations (EPlugin *epl,
 849                             EConfigHookItemFactoryData *data)
 850 {
 851 	GtkBuilder *builder;
 852 	GtkCellRenderer *renderer;
 853 	GtkTreeSelection *selection;
 854 	GtkWidget *toplevel;
 855 	PublishUIData *ui = g_new0 (PublishUIData, 1);
 856 	GSList *l;
 857 	GtkTreeIter iter;
 858 
 859 	builder = gtk_builder_new ();
 860 	e_load_ui_builder_definition (builder, "publish-calendar.ui");
 861 
 862 	ui->treeview = e_builder_get_widget (builder, "url list");
 863 	if (store == NULL)
 864 		store = gtk_list_store_new (URL_LIST_N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
 865 	else
 866 		gtk_list_store_clear (store);
 867 
 868 	gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (store));
 869 
 870 	renderer = gtk_cell_renderer_toggle_new ();
 871 	g_object_set (renderer, "activatable", TRUE, NULL);
 872 	gtk_tree_view_insert_column_with_attributes (
 873 		GTK_TREE_VIEW (ui->treeview), -1, _("Enabled"),
 874 		renderer, "active", URL_LIST_ENABLED_COLUMN, NULL);
 875 	g_signal_connect (
 876 		renderer, "toggled",
 877 		G_CALLBACK (url_list_enable_toggled), ui);
 878 	renderer = gtk_cell_renderer_text_new ();
 879 	gtk_tree_view_insert_column_with_attributes (
 880 		GTK_TREE_VIEW (ui->treeview), -1, _("Location"),
 881 		renderer, "text", URL_LIST_LOCATION_COLUMN, NULL);
 882 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
 883 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 884 	g_signal_connect (
 885 		selection, "changed",
 886 		G_CALLBACK (selection_changed), ui);
 887 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE);
 888 	g_signal_connect (
 889 		ui->treeview, "row-activated",
 890 		G_CALLBACK (url_list_double_click), ui);
 891 
 892 	ui->url_add = e_builder_get_widget (builder, "url add");
 893 	ui->url_edit = e_builder_get_widget (builder, "url edit");
 894 	ui->url_remove = e_builder_get_widget (builder, "url remove");
 895 	ui->url_enable = e_builder_get_widget (builder, "url enable");
 896 	update_url_enable_button (NULL, ui->url_enable);
 897 	g_signal_connect (
 898 		ui->url_add, "clicked",
 899 		G_CALLBACK (url_add_clicked), ui);
 900 	g_signal_connect (
 901 		ui->url_edit, "clicked",
 902 		G_CALLBACK (url_edit_clicked), ui);
 903 	g_signal_connect (
 904 		ui->url_remove, "clicked",
 905 		G_CALLBACK (url_remove_clicked), ui);
 906 	g_signal_connect (
 907 		ui->url_enable, "clicked",
 908 		G_CALLBACK (url_enable_clicked), ui);
 909 	gtk_widget_set_sensitive (GTK_WIDGET (ui->url_edit), FALSE);
 910 	gtk_widget_set_sensitive (GTK_WIDGET (ui->url_remove), FALSE);
 911 	gtk_widget_set_sensitive (GTK_WIDGET (ui->url_enable), FALSE);
 912 
 913 	gtk_button_set_image (GTK_BUTTON (ui->url_enable), gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
 914 	gtk_button_set_use_underline (GTK_BUTTON (ui->url_enable), TRUE);
 915 
 916 	l = publish_uris;
 917 	while (l) {
 918 		EPublishUri *url = (EPublishUri *) l->data;
 919 
 920 		gtk_list_store_append (store, &iter);
 921 		gtk_list_store_set (
 922 			store, &iter,
 923 			URL_LIST_ENABLED_COLUMN, url->enabled,
 924 			URL_LIST_LOCATION_COLUMN, url->location,
 925 			URL_LIST_URL_COLUMN, url, -1);
 926 
 927 		l = g_slist_next (l);
 928 	}
 929 	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
 930 		gtk_tree_selection_select_iter (selection, &iter);
 931 
 932 	toplevel = e_builder_get_widget (builder, "toplevel");
 933 	gtk_widget_show_all (toplevel);
 934 	gtk_box_pack_start (GTK_BOX (data->parent), toplevel, FALSE, TRUE, 0);
 935 
 936 	g_object_unref (builder);
 937 
 938 	return toplevel;
 939 }
 940 
 941 static gpointer
 942 publish_urls (gpointer data)
 943 {
 944 	GSList *l;
 945 
 946 	for (l = publish_uris; l; l = g_slist_next (l)) {
 947 		EPublishUri *uri = l->data;
 948 		publish (uri, TRUE);
 949 	}
 950 
 951 	return GINT_TO_POINTER (0);
 952 }
 953 
 954 static gpointer
 955 publish_uris_set_timeout (gchar **uris)
 956 {
 957 	gint ii;
 958 
 959 	uri_timeouts = g_hash_table_new (g_direct_hash, g_direct_equal);
 960 
 961 	for (ii = 0; uris && uris[ii]; ii++) {
 962 		const gchar *xml = uris[ii];
 963 		EPublishUri *uri = e_publish_uri_from_xml (xml);
 964 
 965 		if (!uri->location) {
 966 			g_free (uri);
 967 			continue;
 968 		}
 969 
 970 		publish_uris = g_slist_prepend (publish_uris, uri);
 971 
 972 		/* Add a timeout based on the last publish time */
 973 		add_offset_timeout (uri);
 974 	}
 975 
 976 	g_strfreev (uris);
 977 
 978 	return NULL;
 979 }
 980 
 981 gint
 982 e_plugin_lib_enable (EPlugin *ep,
 983                      gint enable)
 984 {
 985 	EShell *shell = e_shell_get_default ();
 986 
 987 	if (shell) {
 988 		g_signal_handlers_disconnect_by_func (shell, G_CALLBACK (online_state_changed), NULL);
 989 		if (enable) {
 990 			online = e_shell_get_online (shell);
 991 			g_signal_connect (
 992 				shell, "notify::online",
 993 				G_CALLBACK (online_state_changed), NULL);
 994 		}
 995 	}
 996 
 997 	if (enable) {
 998 		GSettings *settings;
 999 		gchar **uris;
1000 		GError *error = NULL;
1001 
1002 		settings = g_settings_new (PC_SETTINGS_ID);
1003 		uris = g_settings_get_strv (settings, PC_SETTINGS_URIS);
1004 		g_object_unref (settings);
1005 
1006 		g_thread_create (
1007 			(GThreadFunc) publish_uris_set_timeout,
1008 			uris, FALSE, &error);
1009 		if (error != NULL) {
1010 			g_warning ("Could create thread to set timeout for publishing uris : %s", error->message);
1011 			g_error_free (error);
1012 		}
1013 	}
1014 
1015 	return 0;
1016 }
1017 
1018 struct eq_data {
1019 	gchar *description;
1020 	GError *error;
1021 };
1022 
1023 static gboolean
1024 error_queue_show_idle (gpointer user_data)
1025 {
1026 	GString *info = NULL;
1027 	GSList *l;
1028 	gboolean has_error = FALSE, has_info = FALSE;
1029 
1030 	g_static_mutex_lock (&error_queue_lock);
1031 
1032 	for (l = error_queue; l; l = l->next) {
1033 		struct eq_data *data = l->data;
1034 
1035 		if (data) {
1036 			if (data->description) {
1037 				if (!info) {
1038 					info = g_string_new (data->description);
1039 				} else {
1040 					g_string_append (info, "\n\n");
1041 					g_string_append (info, data->description);
1042 				}
1043 
1044 				g_free (data->description);
1045 			}
1046 
1047 			if (data->error) {
1048 				has_error = TRUE;
1049 				if (!info) {
1050 					info = g_string_new (data->error->message);
1051 				} else if (data->description) {
1052 					g_string_append (info, " ");
1053 					g_string_append (info, data->error->message);
1054 				} else {
1055 					g_string_append (info, "\n\n");
1056 					g_string_append (info, data->error->message);
1057 				}
1058 
1059 				g_error_free (data->error);
1060 			} else if (data->description) {
1061 				has_info = TRUE;
1062 			}
1063 
1064 			g_free (data);
1065 		}
1066 	}
1067 
1068 	g_slist_free (error_queue);
1069 
1070 	error_queue = NULL;
1071 	error_queue_show_idle_id = 0;
1072 
1073 	g_static_mutex_unlock (&error_queue_lock);
1074 
1075 	if (info) {
1076 		update_publish_notification (has_error && has_info ? GTK_MESSAGE_WARNING : has_error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, info->str);
1077 
1078 		g_string_free (info, TRUE);
1079 	}
1080 
1081 	return FALSE;
1082 }
1083 
1084 void
1085 error_queue_add (gchar *description,
1086                  GError *error)
1087 {
1088 	struct eq_data *data;
1089 
1090 	if (!error && !description)
1091 		return;
1092 
1093 	data = g_new0 (struct eq_data, 1);
1094 	data->description = description;
1095 	data->error = error;
1096 
1097 	g_static_mutex_lock (&error_queue_lock);
1098 	error_queue = g_slist_append (error_queue, data);
1099 	if (error_queue_show_idle_id == 0)
1100 		error_queue_show_idle_id = g_idle_add (error_queue_show_idle, NULL);
1101 	g_static_mutex_unlock (&error_queue_lock);
1102 }
1103 
1104 static void
1105 action_calendar_publish_cb (GtkAction *action,
1106                             EShellView *shell_view)
1107 {
1108 	GError *error = NULL;
1109 
1110 	g_thread_create ((GThreadFunc) publish_urls, NULL, FALSE, &error);
1111 	if (error != NULL) {
1112 		/* To Translators: This is shown to a user when creation of a new thread,
1113 		 * where the publishing should be done, fails. Basically, this shouldn't
1114 		 * ever happen, and if so, then something is really wrong. */
1115 		error_queue_add (g_strdup (_("Could not create publish thread.")), error);
1116 	}
1117 }
1118 
1119 static GtkActionEntry entries[] = {
1120 
1121 	{ "calendar-publish",
1122 	  NULL,
1123 	  N_("_Publish Calendar Information"),
1124 	  NULL,
1125 	  NULL,  /* XXX Add a tooltip! */
1126 	  G_CALLBACK (action_calendar_publish_cb) }
1127 };
1128 
1129 gboolean e_plugin_ui_init (GtkUIManager *ui_manager, EShellView *shell_view);
1130 
1131 gboolean
1132 e_plugin_ui_init (GtkUIManager *ui_manager,
1133                   EShellView *shell_view)
1134 {
1135 	EShellWindow *shell_window;
1136 	GtkActionGroup *action_group;
1137 
1138 	shell_window = e_shell_view_get_shell_window (shell_view);
1139 	action_group = e_shell_window_get_action_group (shell_window, "calendar");
1140 
1141 	gtk_action_group_add_actions (
1142 		action_group, entries,
1143 		G_N_ELEMENTS (entries), shell_view);
1144 
1145 	return TRUE;
1146 }