evolution-3.6.4/modules/itip-formatter/itip-view.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  *		JP Rosevear <jpr@novell.com>
  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 <glib/gi18n.h>
  29 #include <libedataserverui/libedataserverui.h>
  30 #include <libedataserver/libedataserver.h>
  31 
  32 #include <e-util/e-util.h>
  33 #include <e-util/e-unicode.h>
  34 #include <calendar/gui/itip-utils.h>
  35 #include <webkit/webkitdom.h>
  36 
  37 #include <libevolution-utils/e-alert-dialog.h>
  38 #include <e-util/e-mktemp.h>
  39 
  40 #include <shell/e-shell.h>
  41 #include <shell/e-shell-utils.h>
  42 
  43 #include <libemail-utils/mail-mt.h>
  44 
  45 #include <libemail-engine/mail-folder-cache.h>
  46 #include <libemail-engine/mail-tools.h>
  47 
  48 #include <mail/em-config.h>
  49 #include <mail/em-utils.h>
  50 #include <em-format/e-mail-formatter-utils.h>
  51 
  52 #include <calendar/gui/itip-utils.h>
  53 
  54 #include "e-conflict-search-selector.h"
  55 #include "e-source-conflict-search.h"
  56 #include "itip-view.h"
  57 #include "e-mail-part-itip.h"
  58 
  59 #define d(x)
  60 
  61 #define MEETING_ICON "stock_new-meeting"
  62 
  63 #define ITIP_VIEW_GET_PRIVATE(obj) \
  64 	(G_TYPE_INSTANCE_GET_PRIVATE \
  65 	((obj), ITIP_TYPE_VIEW, ItipViewPrivate))
  66 
  67 G_DEFINE_TYPE (ItipView, itip_view, G_TYPE_OBJECT)
  68 
  69 typedef struct  {
  70 	ItipViewInfoItemType type;
  71 	gchar *message;
  72 
  73 	guint id;
  74 } ItipViewInfoItem;
  75 
  76 struct _ItipViewPrivate {
  77 	ESourceRegistry *registry;
  78 	gulong source_added_id;
  79 	gulong source_removed_id;
  80 	gchar *extension_name;
  81 
  82 	ItipViewMode mode;
  83 	ECalClientSourceType type;
  84 
  85         gchar *sender;
  86 	gchar *organizer;
  87 	gchar *organizer_sentby;
  88 	gchar *delegator;
  89 	gchar *attendee;
  90 	gchar *attendee_sentby;
  91 	gchar *proxy;
  92 
  93 	gchar *summary;
  94 
  95 	gchar *location;
  96         gchar *status;
  97 	gchar *comment;
  98 
  99 	struct tm *start_tm;
 100 	gint start_tm_is_date : 1;
 101         gchar *start_label;
 102         const gchar *start_header;
 103 
 104 	struct tm *end_tm;
 105 	gint end_tm_is_date : 1;
 106         gchar *end_label;
 107         const gchar *end_header;
 108 
 109 	GSList *upper_info_items;
 110 	GSList *lower_info_items;
 111 
 112 	guint next_info_item_id;
 113 
 114 	gchar *description;
 115 
 116 	gint buttons_sensitive : 1;
 117 
 118         gboolean is_recur_set;
 119 
 120 	gint needs_decline : 1;
 121 
 122         WebKitDOMDocument *dom_document;
 123         EMailPartItip *itip_part;
 124 
 125         gchar *error;
 126 };
 127 
 128 #define TEXT_ROW_SENDER "text_row_sender"
 129 #define TABLE_ROW_SUMMARY "table_row_summary"
 130 #define TABLE_ROW_LOCATION "table_row_location"
 131 #define TABLE_ROW_START_DATE "table_row_start_time"
 132 #define TABLE_ROW_END_DATE "table_row_end_time"
 133 #define TABLE_ROW_STATUS "table_row_status"
 134 #define TABLE_ROW_COMMENT "table_row_comment"
 135 #define TABLE_ROW_DESCRIPTION "table_row_description"
 136 #define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
 137 #define TABLE_ROW_ESCB "table_row_escb"
 138 #define TABLE_ROW_BUTTONS "table_row_buttons"
 139 #define TABLE_ROW_ESCB_LABEL "table_row_escb_label"
 140 
 141 #define TABLE_BUTTONS "table_buttons"
 142 
 143 #define SELECT_ESOURCE "select_esource"
 144 #define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"
 145 
 146 #define CHECKBOX_RSVP "checkbox_rsvp"
 147 #define CHECKBOX_RECUR "checkbox_recur"
 148 #define CHECKBOX_UPDATE "checkbox_update"
 149 #define CHECKBOX_FREE_TIME "checkbox_free_time"
 150 #define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
 151 #define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"
 152 
 153 #define BUTTON_OPEN_CALENDAR "button_open_calendar"
 154 #define BUTTON_DECLINE "button_decline"
 155 #define BUTTON_DECLINE_ALL "button_decline_all"
 156 #define BUTTON_ACCEPT "button_accept"
 157 #define BUTTON_ACCEPT_ALL "button_accept_all"
 158 #define BUTTON_TENTATIVE "button_tentative"
 159 #define BUTTON_TENTATIVE_ALL "button_tentative_all"
 160 #define BUTTON_SEND_INFORMATION "button_send_information"
 161 #define BUTTON_UPDATE "button_update"
 162 #define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
 163 #define BUTTON_SAVE "button_save"
 164 
 165 #define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
 166 #define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"
 167 
 168 #define DIV_ITIP_CONTENT "div_itip_content"
 169 #define DIV_ITIP_ERROR "div_itip_error"
 170 
 171 enum {
 172 	PROP_0,
 173 	PROP_EXTENSION_NAME,
 174 	PROP_REGISTRY
 175 };
 176 
 177 enum {
 178 	SOURCE_SELECTED,
 179 	RESPONSE,
 180 	LAST_SIGNAL
 181 };
 182 
 183 static guint signals[LAST_SIGNAL] = { 0 };
 184 
 185 static void
 186 format_date_and_time_x (struct tm *date_tm,
 187                         struct tm *current_tm,
 188                         gboolean use_24_hour_format,
 189                         gboolean show_midnight,
 190                         gboolean show_zero_seconds,
 191                         gboolean is_date,
 192                         gchar *buffer,
 193                         gint buffer_size)
 194 {
 195 	gchar *format;
 196 	struct tm tomorrow_tm, week_tm;
 197 
 198 	/* Calculate a normalized "tomorrow" */
 199 	tomorrow_tm = *current_tm;
 200 	/* Don't need this if date is in the past. Also, year assumption won't fail. */
 201 	if (date_tm->tm_year >= current_tm->tm_year && tomorrow_tm.tm_mday == time_days_in_month (current_tm->tm_year + 1900, current_tm->tm_mon)) {
 202 		tomorrow_tm.tm_mday = 1;
 203 		if (tomorrow_tm.tm_mon == 11) {
 204 			tomorrow_tm.tm_mon = 1;
 205 			tomorrow_tm.tm_year++;
 206 		} else {
 207 			tomorrow_tm.tm_mon++;
 208 		}
 209 	} else {
 210 		tomorrow_tm.tm_mday++;
 211 	}
 212 
 213 	/* Calculate a normalized "next seven days" */
 214 	week_tm = *current_tm;
 215 	/* Don't need this if date is in the past. Also, year assumption won't fail. */
 216 	if (date_tm->tm_year >= current_tm->tm_year && week_tm.tm_mday + 6 > time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon)) {
 217 		week_tm.tm_mday = (week_tm.tm_mday + 6) % time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon);
 218 		if (week_tm.tm_mon == 11) {
 219 			week_tm.tm_mon = 1;
 220 			week_tm.tm_year++;
 221 		} else {
 222 			week_tm.tm_mon++;
 223 		}
 224 	} else {
 225 		week_tm.tm_mday += 6;
 226 	}
 227 
 228 	/* Today */
 229 	if (date_tm->tm_mday == current_tm->tm_mday &&
 230 	    date_tm->tm_mon == current_tm->tm_mon &&
 231 	    date_tm->tm_year == current_tm->tm_year) {
 232 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 233 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 234 			/* strftime format of a weekday and a date. */
 235 			format = _("Today");
 236 		} else if (use_24_hour_format) {
 237 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 238 				/* strftime format of a time,
 239 				 * in 24-hour format, without seconds. */
 240 				format = _("Today %H:%M");
 241 			else
 242 				/* strftime format of a time,
 243 				 * in 24-hour format. */
 244 				format = _("Today %H:%M:%S");
 245 		} else {
 246 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 247 				/* strftime format of a time,
 248 				 * in 12-hour format, without seconds. */
 249 				format = _("Today %l:%M %p");
 250 			else
 251 				/* strftime format of a time,
 252 				 * in 12-hour format. */
 253 				format = _("Today %l:%M:%S %p");
 254 		}
 255 
 256 	/* Tomorrow */
 257 	} else if (date_tm->tm_mday == tomorrow_tm.tm_mday &&
 258 		   date_tm->tm_mon == tomorrow_tm.tm_mon &&
 259 		   date_tm->tm_year == tomorrow_tm.tm_year) {
 260 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 261 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 262 			/* strftime format of a weekday and a date. */
 263 			format = _("Tomorrow");
 264 		} else if (use_24_hour_format) {
 265 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 266 				/* strftime format of a time,
 267 				 * in 24-hour format, without seconds. */
 268 				format = _("Tomorrow %H:%M");
 269 			else
 270 				/* strftime format of a time,
 271 				 * in 24-hour format. */
 272 				format = _("Tomorrow %H:%M:%S");
 273 		} else {
 274 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 275 				/* strftime format of a time,
 276 				 * in 12-hour format, without seconds. */
 277 				format = _("Tomorrow %l:%M %p");
 278 			else
 279 				/* strftime format of a time,
 280 				 * in 12-hour format. */
 281 				format = _("Tomorrow %l:%M:%S %p");
 282 		}
 283 
 284 	/* Within 6 days */
 285 	} else if ((date_tm->tm_year >= current_tm->tm_year &&
 286 		    date_tm->tm_mon >= current_tm->tm_mon &&
 287 		    date_tm->tm_mday >= current_tm->tm_mday) &&
 288 
 289 		   (date_tm->tm_year < week_tm.tm_year ||
 290 
 291 		   (date_tm->tm_year == week_tm.tm_year &&
 292 		    date_tm->tm_mon < week_tm.tm_mon) ||
 293 
 294 		   (date_tm->tm_year == week_tm.tm_year &&
 295 		    date_tm->tm_mon == week_tm.tm_mon &&
 296 		    date_tm->tm_mday < week_tm.tm_mday))) {
 297 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 298 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 299 			/* strftime format of a weekday. */
 300 			format = _("%A");
 301 		} else if (use_24_hour_format) {
 302 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 303 				/* strftime format of a weekday and a
 304 				 * time, in 24-hour format, without seconds. */
 305 				format = _("%A %H:%M");
 306 			else
 307 				/* strftime format of a weekday and a
 308 				 * time, in 24-hour format. */
 309 				format = _("%A %H:%M:%S");
 310 		} else {
 311 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 312 				/* strftime format of a weekday and a
 313 				 * time, in 12-hour format, without seconds. */
 314 				format = _("%A %l:%M %p");
 315 			else
 316 				/* strftime format of a weekday and a
 317 				 * time, in 12-hour format. */
 318 				format = _("%A %l:%M:%S %p");
 319 		}
 320 
 321 	/* This Year */
 322 	} else if (date_tm->tm_year == current_tm->tm_year) {
 323 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 324 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 325 			/* strftime format of a weekday and a date
 326 			 * without a year. */
 327 			format = _("%A, %B %e");
 328 		} else if (use_24_hour_format) {
 329 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 330 				/* strftime format of a weekday, a date
 331 				 * without a year and a time,
 332 				 * in 24-hour format, without seconds. */
 333 				format = _("%A, %B %e %H:%M");
 334 			else
 335 				/* strftime format of a weekday, a date without a year
 336 				 * and a time, in 24-hour format. */
 337 				format = _("%A, %B %e %H:%M:%S");
 338 		} else {
 339 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 340 				/* strftime format of a weekday, a date without a year
 341 				 * and a time, in 12-hour format, without seconds. */
 342 				format = _("%A, %B %e %l:%M %p");
 343 			else
 344 				/* strftime format of a weekday, a date without a year
 345 				 * and a time, in 12-hour format. */
 346 				format = _("%A, %B %e %l:%M:%S %p");
 347 		}
 348 	} else {
 349 		if (is_date || (!show_midnight && date_tm->tm_hour == 0
 350 		    && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
 351 			/* strftime format of a weekday and a date. */
 352 			format = _("%A, %B %e, %Y");
 353 		} else if (use_24_hour_format) {
 354 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 355 				/* strftime format of a weekday, a date and a
 356 				 * time, in 24-hour format, without seconds. */
 357 				format = _("%A, %B %e, %Y %H:%M");
 358 			else
 359 				/* strftime format of a weekday, a date and a
 360 				 * time, in 24-hour format. */
 361 				format = _("%A, %B %e, %Y %H:%M:%S");
 362 		} else {
 363 			if (!show_zero_seconds && date_tm->tm_sec == 0)
 364 				/* strftime format of a weekday, a date and a
 365 				 * time, in 12-hour format, without seconds. */
 366 				format = _("%A, %B %e, %Y %l:%M %p");
 367 			else
 368 				/* strftime format of a weekday, a date and a
 369 				 * time, in 12-hour format. */
 370 				format = _("%A, %B %e, %Y %l:%M:%S %p");
 371 		}
 372 	}
 373 
 374 	/* strftime returns 0 if the string doesn't fit, and leaves the buffer
 375 	 * undefined, so we set it to the empty string in that case. */
 376 	if (e_utf8_strftime_fix_am_pm (buffer, buffer_size, format, date_tm) == 0)
 377 		buffer[0] = '\0';
 378 }
 379 
 380 static gchar *
 381 dupe_first_bold (const gchar *format,
 382                  const gchar *first,
 383                  const gchar *second)
 384 {
 385 	gchar *f, *s, *res;
 386 
 387 	f = g_markup_printf_escaped ("<b>%s</b>", first ? first : "");
 388 	s = g_markup_escape_text (second ? second : "", -1);
 389 
 390 	res = g_strdup_printf (format, f, s);
 391 
 392 	g_free (f);
 393 	g_free (s);
 394 
 395 	return res;
 396 }
 397 
 398 static gchar *
 399 set_calendar_sender_text (ItipView *view)
 400 {
 401 	ItipViewPrivate *priv;
 402 	const gchar *organizer, *attendee;
 403 	gchar *sender = NULL;
 404 	gchar *on_behalf_of = NULL;
 405 
 406 	priv = view->priv;
 407 
 408 	organizer = priv->organizer ? priv->organizer : _("An unknown person");
 409 	attendee = priv->attendee ? priv->attendee : _("An unknown person");
 410 
 411 	/* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
 412 	if (priv->organizer && priv->proxy)
 413 		on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
 414 	else if (priv->attendee && priv->proxy)
 415 		on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
 416 
 417 	switch (priv->mode) {
 418 	case ITIP_VIEW_MODE_PUBLISH:
 419 		if (priv->organizer_sentby)
 420 			sender = dupe_first_bold (_("%s through %s has published the following meeting information:"), organizer, priv->organizer_sentby);
 421 		else
 422 			sender = dupe_first_bold (_("%s has published the following meeting information:"), organizer, NULL);
 423 		break;
 424 	case ITIP_VIEW_MODE_REQUEST:
 425 		/* FIXME is the delegator stuff handled correctly here? */
 426 		if (priv->delegator) {
 427 			sender = dupe_first_bold (_("%s has delegated the following meeting to you:"), priv->delegator, NULL);
 428 		} else {
 429 			if (priv->organizer_sentby)
 430 				sender = dupe_first_bold (_("%s through %s requests your presence at the following meeting:"), organizer, priv->organizer_sentby);
 431 			else
 432 				sender = dupe_first_bold (_("%s requests your presence at the following meeting:"), organizer, NULL);
 433 		}
 434 		break;
 435 	case ITIP_VIEW_MODE_ADD:
 436 		/* FIXME What text for this? */
 437 		if (priv->organizer_sentby)
 438 			sender = dupe_first_bold (_("%s through %s wishes to add to an existing meeting:"), organizer, priv->organizer_sentby);
 439 		else
 440 			sender = dupe_first_bold (_("%s wishes to add to an existing meeting:"), organizer, NULL);
 441 		break;
 442 	case ITIP_VIEW_MODE_REFRESH:
 443 		if (priv->attendee_sentby)
 444 			sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following meeting:"), attendee, priv->attendee_sentby);
 445 		else
 446 			sender = dupe_first_bold (_("%s wishes to receive the latest information for the following meeting:"), attendee, NULL);
 447 		break;
 448 	case ITIP_VIEW_MODE_REPLY:
 449 		if (priv->attendee_sentby)
 450 			sender = dupe_first_bold (_("%s through %s has sent back the following meeting response:"), attendee, priv->attendee_sentby);
 451 		else
 452 			sender = dupe_first_bold (_("%s has sent back the following meeting response:"), attendee, NULL);
 453 		break;
 454 	case ITIP_VIEW_MODE_CANCEL:
 455 		if (priv->organizer_sentby)
 456 			sender = dupe_first_bold (_("%s through %s has canceled the following meeting:"), organizer, priv->organizer_sentby);
 457 		else
 458 			sender = dupe_first_bold (_("%s has canceled the following meeting:"), organizer, NULL);
 459 		break;
 460 	case ITIP_VIEW_MODE_COUNTER:
 461 		if (priv->attendee_sentby)
 462 			sender = dupe_first_bold (_("%s through %s has proposed the following meeting changes."), attendee, priv->attendee_sentby);
 463 		else
 464 			sender = dupe_first_bold (_("%s has proposed the following meeting changes:"), attendee, NULL);
 465 		break;
 466 	case ITIP_VIEW_MODE_DECLINECOUNTER:
 467 		if (priv->organizer_sentby)
 468 			sender = dupe_first_bold (_("%s through %s has declined the following meeting changes:"), organizer, priv->organizer_sentby);
 469 		else
 470 			sender = dupe_first_bold (_("%s has declined the following meeting changes:"), organizer, NULL);
 471 		break;
 472 	default:
 473 		break;
 474 	}
 475 
 476 	if (sender && on_behalf_of) {
 477 		gchar *tmp;
 478 		tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
 479 		g_free (sender);
 480 		sender = tmp;
 481 	}
 482 
 483 	g_free (on_behalf_of);
 484 
 485 	return sender;
 486 }
 487 
 488 static gchar *
 489 set_tasklist_sender_text (ItipView *view)
 490 {
 491 	ItipViewPrivate *priv;
 492 	const gchar *organizer, *attendee;
 493 	gchar *sender = NULL;
 494 	gchar *on_behalf_of = NULL;
 495 
 496 	priv = view->priv;
 497 
 498 	organizer = priv->organizer ? priv->organizer : _("An unknown person");
 499 	attendee = priv->attendee ? priv->attendee : _("An unknown person");
 500 
 501 	/* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
 502 	if (priv->organizer && priv->proxy)
 503 		on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
 504 	else if (priv->attendee && priv->proxy)
 505 		on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
 506 
 507 	switch (priv->mode) {
 508 	case ITIP_VIEW_MODE_PUBLISH:
 509 		if (priv->organizer_sentby)
 510 			sender = dupe_first_bold (_("%s through %s has published the following task:"), organizer, priv->organizer_sentby);
 511 		else
 512 			sender = dupe_first_bold (_("%s has published the following task:"), organizer, NULL);
 513 		break;
 514 	case ITIP_VIEW_MODE_REQUEST:
 515 		/* FIXME is the delegator stuff handled correctly here? */
 516 		if (priv->delegator) {
 517 			sender = dupe_first_bold (_("%s requests the assignment of %s to the following task:"), organizer, priv->delegator);
 518 		} else {
 519 			if (priv->organizer_sentby)
 520 				sender = dupe_first_bold (_("%s through %s has assigned you a task:"), organizer, priv->organizer_sentby);
 521 			else
 522 				sender = dupe_first_bold (_("%s has assigned you a task:"), organizer, NULL);
 523 		}
 524 		break;
 525 	case ITIP_VIEW_MODE_ADD:
 526 		/* FIXME What text for this? */
 527 		if (priv->organizer_sentby)
 528 			sender = dupe_first_bold (_("%s through %s wishes to add to an existing task:"), organizer, priv->organizer_sentby);
 529 		else
 530 			sender = dupe_first_bold (_("%s wishes to add to an existing task:"), organizer, NULL);
 531 		break;
 532 	case ITIP_VIEW_MODE_REFRESH:
 533 		if (priv->attendee_sentby)
 534 			sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following assigned task:"), attendee, priv->attendee_sentby);
 535 		else
 536 			sender = dupe_first_bold (_("%s wishes to receive the latest information for the following assigned task:"), attendee, NULL);
 537 		break;
 538 	case ITIP_VIEW_MODE_REPLY:
 539 		if (priv->attendee_sentby)
 540 			sender = dupe_first_bold (_("%s through %s has sent back the following assigned task response:"), attendee, priv->attendee_sentby);
 541 		else
 542 			sender = dupe_first_bold (_("%s has sent back the following assigned task response:"), attendee, NULL);
 543 		break;
 544 	case ITIP_VIEW_MODE_CANCEL:
 545 		if (priv->organizer_sentby)
 546 			sender = dupe_first_bold (_("%s through %s has canceled the following assigned task:"), organizer, priv->organizer_sentby);
 547 		else
 548 			sender = dupe_first_bold (_("%s has canceled the following assigned task:"), organizer, NULL);
 549 		break;
 550 	case ITIP_VIEW_MODE_COUNTER:
 551 		if (priv->attendee_sentby)
 552 			sender = dupe_first_bold (_("%s through %s has proposed the following task assignment changes:"), attendee, priv->attendee_sentby);
 553 		else
 554 			sender = dupe_first_bold (_("%s has proposed the following task assignment changes:"), attendee, NULL);
 555 		break;
 556 	case ITIP_VIEW_MODE_DECLINECOUNTER:
 557 		if (priv->organizer_sentby)
 558 			sender = dupe_first_bold (_("%s through %s has declined the following assigned task:"), organizer, priv->organizer_sentby);
 559 		else
 560 			sender = dupe_first_bold (_("%s has declined the following assigned task:"), organizer, NULL);
 561 		break;
 562 	default:
 563 		break;
 564 	}
 565 
 566 	if (sender && on_behalf_of) {
 567 		gchar *tmp;
 568 		tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
 569 		g_free (sender);
 570 		sender = tmp;
 571 	}
 572 
 573 	g_free (on_behalf_of);
 574 
 575 	return sender;
 576 }
 577 
 578 static gchar *
 579 set_journal_sender_text (ItipView *view)
 580 {
 581 	ItipViewPrivate *priv;
 582 	const gchar *organizer;
 583 	gchar *sender = NULL;
 584 	gchar *on_behalf_of = NULL;
 585 
 586 	priv = view->priv;
 587 
 588 	organizer = priv->organizer ? priv->organizer : _("An unknown person");
 589 
 590 	/* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
 591 	if (priv->organizer && priv->proxy)
 592 		on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
 593 	else if (priv->attendee && priv->proxy)
 594 		on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
 595 
 596 	switch (priv->mode) {
 597 	case ITIP_VIEW_MODE_PUBLISH:
 598 		if (priv->organizer_sentby)
 599 			sender = dupe_first_bold (_("%s through %s has published the following memo:"), organizer, priv->organizer_sentby);
 600 		else
 601 			sender = dupe_first_bold (_("%s has published the following memo:"), organizer, NULL);
 602 		break;
 603 	case ITIP_VIEW_MODE_ADD:
 604 		/* FIXME What text for this? */
 605 		if (priv->organizer_sentby)
 606 			sender = dupe_first_bold (_("%s through %s wishes to add to an existing memo:"), organizer, priv->organizer_sentby);
 607 		else
 608 			sender = dupe_first_bold (_("%s wishes to add to an existing memo:"), organizer, NULL);
 609 		break;
 610 	case ITIP_VIEW_MODE_CANCEL:
 611 		if (priv->organizer_sentby)
 612 			sender = dupe_first_bold (_("%s through %s has canceled the following shared memo:"), organizer, priv->organizer_sentby);
 613 		else
 614 			sender = dupe_first_bold (_("%s has canceled the following shared memo:"), organizer, NULL);
 615 		break;
 616 	default:
 617 		break;
 618 	}
 619 
 620 	if (sender && on_behalf_of)
 621 		sender = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
 622 
 623 	g_free (on_behalf_of);
 624 
 625 	return sender;
 626 }
 627 
 628 static void
 629 set_sender_text (ItipView *view)
 630 {
 631 	ItipViewPrivate *priv;
 632 	priv = view->priv;
 633 
 634 	if (priv->sender)
 635 		g_free (priv->sender);
 636 
 637 	switch (priv->type) {
 638 	case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
 639 		priv->sender = set_calendar_sender_text (view);
 640 		break;
 641 	case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
 642 		priv->sender = set_tasklist_sender_text (view);
 643 		break;
 644 	case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
 645 		priv->sender = set_journal_sender_text (view);
 646 		break;
 647 	default:
 648 		priv->sender = NULL;
 649 		break;
 650 	}
 651 
 652 	if (priv->sender && priv->dom_document) {
 653 		WebKitDOMElement *div;
 654 
 655 		div = webkit_dom_document_get_element_by_id (
 656 			priv->dom_document, TEXT_ROW_SENDER);
 657 		webkit_dom_html_element_set_inner_html (
 658 			WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL);
 659 	}
 660 }
 661 
 662 static void
 663 update_start_end_times (ItipView *view)
 664 {
 665 	ItipViewPrivate *priv;
 666 	WebKitDOMElement *row, *col;
 667 	gchar buffer[256];
 668 	time_t now;
 669 	struct tm *now_tm;
 670 
 671 	priv = view->priv;
 672 
 673 	now = time (NULL);
 674 	now_tm = localtime (&now);
 675 
 676 	if (priv->start_label)
 677 		g_free (priv->start_label);
 678 	if (priv->end_label)
 679 		g_free (priv->end_label);
 680 
 681 	#define is_same(_member) (priv->start_tm->_member == priv->end_tm->_member)
 682 	if (priv->start_tm && priv->end_tm && priv->start_tm_is_date && priv->end_tm_is_date
 683 	    && is_same (tm_mday) && is_same (tm_mon) && is_same (tm_year)) {
 684 		/* it's an all day event in one particular day */
 685 		format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
 686 		priv->start_label = g_strdup (buffer);
 687 		priv->start_header = _("All day:");
 688 		priv->end_header = NULL;
 689 		priv->end_label = NULL;
 690 	} else {
 691 		if (priv->start_tm) {
 692 			format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
 693 			priv->start_header = priv->start_tm_is_date ? _("Start day:") : _("Start time:");
 694 			priv->start_label = g_strdup (buffer);
 695 		} else {
 696 			priv->start_header = NULL;
 697 			priv->start_label = NULL;
 698 		}
 699 
 700 		if (priv->end_tm) {
 701 			format_date_and_time_x (priv->end_tm, now_tm, FALSE, TRUE, FALSE, priv->end_tm_is_date, buffer, 256);
 702 			priv->end_header = priv->end_tm_is_date ? _("End day:") : _("End time:");
 703 			priv->end_label = g_strdup (buffer);
 704 		} else {
 705 			priv->end_header = NULL;
 706 			priv->end_label = NULL;
 707 		}
 708 	}
 709 	#undef is_same
 710 
 711 	if (priv->dom_document) {
 712 		row = webkit_dom_document_get_element_by_id (
 713 			priv->dom_document, TABLE_ROW_START_DATE);
 714 		if (priv->start_header && priv->start_label) {
 715 			webkit_dom_html_element_set_hidden (
 716 				WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
 717 
 718 			col = webkit_dom_element_get_first_element_child (row);
 719 			webkit_dom_html_element_set_inner_html (
 720 				WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL);
 721 
 722 			col = webkit_dom_element_get_last_element_child (row);
 723 			webkit_dom_html_element_set_inner_html (
 724 				WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL);
 725 		} else {
 726 			webkit_dom_html_element_set_hidden (
 727 				WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
 728 		}
 729 
 730 		row = webkit_dom_document_get_element_by_id (
 731 			priv->dom_document, TABLE_ROW_END_DATE);
 732 		if (priv->end_header && priv->end_label) {
 733 			webkit_dom_html_element_set_hidden (
 734 				WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
 735 
 736 			col = webkit_dom_element_get_first_element_child (row);
 737 			webkit_dom_html_element_set_inner_html (
 738 				WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL);
 739 
 740 			col = webkit_dom_element_get_last_element_child (row);
 741 			webkit_dom_html_element_set_inner_html (
 742 				WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL);
 743 		} else {
 744 			webkit_dom_html_element_set_hidden (
 745 				WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
 746 		}
 747 	}
 748 }
 749 
 750 static void
 751 button_clicked_cb (WebKitDOMElement *element,
 752                    WebKitDOMEvent *event,
 753                    gpointer data)
 754 {
 755 	ItipViewResponse response;
 756 	gchar *responseStr;
 757 
 758 	responseStr = webkit_dom_html_button_element_get_value (
 759 		WEBKIT_DOM_HTML_BUTTON_ELEMENT (element));
 760 
 761 	response = atoi (responseStr);
 762 
 763 	g_signal_emit (data, signals[RESPONSE], 0, response);
 764 }
 765 
 766 static void
 767 rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
 768                  WebKitDOMEvent *event,
 769                  gpointer data)
 770 {
 771 	WebKitDOMElement *el;
 772 
 773 	ItipView *view = data;
 774 	gboolean rsvp;
 775 
 776 	rsvp = webkit_dom_html_input_element_get_checked (input);
 777 
 778 	el = webkit_dom_document_get_element_by_id (
 779 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
 780 	webkit_dom_html_text_area_element_set_disabled (
 781 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
 782 }
 783 
 784 static void
 785 recur_toggled_cb (WebKitDOMHTMLInputElement *input,
 786                   WebKitDOMEvent *event,
 787                   gpointer data)
 788 {
 789 	ItipView *view = data;
 790 
 791 	itip_view_set_mode (view, view->priv->mode);
 792 }
 793 
 794 /*
 795   alarm_check_toggled_cb
 796   check1 was changed, so make the second available based on state of the first check.
 797 */
 798 static void
 799 alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
 800                         WebKitDOMEvent *event,
 801                         ItipView *view)
 802 {
 803 	WebKitDOMElement *check2;
 804 	gchar *id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1));
 805 
 806 	if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
 807 		check2 = webkit_dom_document_get_element_by_id (
 808 			view->priv->dom_document, CHECKBOX_KEEP_ALARM);
 809 	} else {
 810 		check2 = webkit_dom_document_get_element_by_id (
 811 			view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
 812 	}
 813 
 814 	g_free (id);
 815 
 816 	webkit_dom_html_input_element_set_disabled (
 817 		WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
 818 		(webkit_dom_html_element_get_hidden (
 819 				WEBKIT_DOM_HTML_ELEMENT (check1)) &&
 820 			webkit_dom_html_input_element_get_checked (check1)));
 821 }
 822 
 823 static void
 824 source_changed_cb (WebKitDOMElement *select,
 825                    WebKitDOMEvent *event,
 826                    ItipView *view)
 827 {
 828 	ESource *source;
 829 
 830 	source = itip_view_ref_source (view);
 831 
 832 	d (printf ("Source changed to '%s'\n", e_source_get_display_name (source)));
 833 	g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);
 834 
 835 	g_object_unref (source);
 836 }
 837 
 838 static void
 839 append_checkbox_table_row (GString *buffer,
 840                            const gchar *name,
 841                            const gchar *label)
 842 {
 843 	gchar *access_key, *html_label;
 844 
 845 	html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);
 846 
 847 	g_string_append_printf (
 848 		buffer,
 849 		"<tr id=\"table_row_%s\" hidden=\"\"><td colspan=\"2\">"
 850 		"<input type=\"checkbox\" name=\"%s\" id=\"%s\" value=\"%s\" >"
 851 		"<label for=\"%s\" accesskey=\"%s\">%s</label>"
 852 		"</td></tr>\n",
 853 		name, name, name, name, name,
 854 		access_key ? access_key : "", html_label);
 855 
 856 	g_free (html_label);
 857 
 858 	if (access_key)
 859 		g_free (access_key);
 860 }
 861 
 862 static void
 863 append_text_table_row (GString *buffer,
 864                        const gchar *id,
 865                        const gchar *label,
 866                        const gchar *value)
 867 {
 868 	if (label && *label) {
 869 
 870 		g_string_append_printf (
 871 			buffer,
 872 			"<tr id=\"%s\" %s><th>%s</th><td>%s</td></tr>\n",
 873 			id, (value && *value) ? "" : "hidden=\"\"", label, value ? value : "");
 874 
 875 	} else {
 876 
 877 		g_string_append_printf (
 878 			buffer,
 879 			"<tr id=\"%s\"%s><td colspan=\"2\">%s</td></tr>\n",
 880 			id, g_strcmp0 (id, TABLE_ROW_SUMMARY) == 0 ? "" : " hidden=\"\"", value ? value : "");
 881 
 882 	}
 883 }
 884 
 885 static void
 886 append_text_table_row_nonempty (GString *buffer,
 887                                 const gchar *id,
 888                                 const gchar *label,
 889                                 const gchar *value)
 890 {
 891 	if (!value || !*value)
 892 		return;
 893 
 894 	append_text_table_row (buffer, id, label, value);
 895 }
 896 
 897 static void
 898 append_info_item_row (ItipView *view,
 899                       const gchar *table_id,
 900                       ItipViewInfoItem *item)
 901 {
 902 	WebKitDOMElement *table;
 903 	WebKitDOMHTMLElement *row, *cell;
 904 	const gchar *icon_name;
 905 	gchar *id;
 906 
 907 	table = webkit_dom_document_get_element_by_id (
 908 		view->priv->dom_document, table_id);
 909 	row = webkit_dom_html_table_element_insert_row (
 910 		WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
 911 
 912 	id = g_strdup_printf ("%s_row_%d", table_id, item->id);
 913 	webkit_dom_html_element_set_id (row, id);
 914 	g_free (id);
 915 
 916 	switch (item->type) {
 917 		case ITIP_VIEW_INFO_ITEM_TYPE_INFO:
 918 			icon_name = GTK_STOCK_DIALOG_INFO;
 919 			break;
 920 		case ITIP_VIEW_INFO_ITEM_TYPE_WARNING:
 921 			icon_name = GTK_STOCK_DIALOG_WARNING;
 922 			break;
 923 		case ITIP_VIEW_INFO_ITEM_TYPE_ERROR:
 924 			icon_name = GTK_STOCK_DIALOG_ERROR;
 925 			break;
 926 		case ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS:
 927 			icon_name = GTK_STOCK_FIND;
 928 			break;
 929 		case ITIP_VIEW_INFO_ITEM_TYPE_NONE:
 930 			default:
 931 			icon_name = NULL;
 932 	}
 933 
 934 	cell = webkit_dom_html_table_row_element_insert_cell (
 935 		(WebKitDOMHTMLTableRowElement *) row, -1, NULL);
 936 
 937 	if (icon_name) {
 938 		WebKitDOMElement *image;
 939 		gchar *icon_uri;
 940 
 941 		image = webkit_dom_document_create_element (
 942 			view->priv->dom_document, "IMG", NULL);
 943 
 944 		icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
 945 		webkit_dom_html_image_element_set_src (
 946 			WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
 947 		g_free (icon_uri);
 948 
 949 		webkit_dom_node_append_child (
 950 			WEBKIT_DOM_NODE (cell),
 951 			WEBKIT_DOM_NODE (image),
 952 			NULL);
 953 	}
 954 
 955 	cell = webkit_dom_html_table_row_element_insert_cell (
 956 		(WebKitDOMHTMLTableRowElement *) row, -1, NULL);
 957 
 958 	webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
 959 
 960 	d (printf ("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message));
 961 }
 962 
 963 static void
 964 remove_info_item_row (ItipView *view,
 965                       const gchar *table_id,
 966                       guint id)
 967 {
 968 	WebKitDOMElement *row;
 969 	gchar *row_id;
 970 
 971 	row_id = g_strdup_printf ("%s_row_%d", table_id, id);
 972 	row = webkit_dom_document_get_element_by_id (
 973 		view->priv->dom_document, row_id);
 974 	g_free (row_id);
 975 
 976 	webkit_dom_node_remove_child (
 977 		webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
 978 		WEBKIT_DOM_NODE (row),
 979 		NULL);
 980 
 981 	d (printf ("Removed row %s_row_%d\n", table_id, id));
 982 }
 983 
 984 static void
 985 buttons_table_write_button (GString *buffer,
 986                             const gchar *name,
 987                             const gchar *label,
 988                             const gchar *icon,
 989                             ItipViewResponse response)
 990 {
 991 	gchar *access_key, *html_label;
 992 
 993 	html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);
 994 
 995 	g_string_append_printf (
 996 		buffer,
 997 		"<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>"
 998 		"<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>"
 999 		"</button></td>\n",
1000 		name, response, name, access_key ? access_key : "" , icon,
1001 		GTK_ICON_SIZE_BUTTON, html_label);
1002 
1003 	g_free (html_label);
1004 
1005 	if (access_key)
1006 		g_free (access_key);
1007 }
1008 
1009 static void
1010 append_buttons_table (GString *buffer)
1011 {
1012 	g_string_append (
1013 		buffer,
1014 		"<table class=\"itip buttons\" border=\"0\" "
1015 		"id=\"" TABLE_BUTTONS "\" cellspacing=\"6\" "
1016 		"cellpadding=\"0\" >"
1017 		"<tr id=\"" TABLE_ROW_BUTTONS "\">");
1018 
1019         /* Everything gets the open button */
1020 	buttons_table_write_button (
1021 		buffer, BUTTON_OPEN_CALENDAR, _("_Open Calendar"),
1022 		GTK_STOCK_JUMP_TO, ITIP_VIEW_RESPONSE_OPEN);
1023 	buttons_table_write_button (
1024 		buffer, BUTTON_DECLINE_ALL, _("_Decline all"),
1025 		GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
1026 	buttons_table_write_button (
1027 		buffer, BUTTON_DECLINE, _("_Decline"),
1028 		GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
1029 	buttons_table_write_button (
1030 		buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
1031 		GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
1032 	buttons_table_write_button (
1033 		buffer, BUTTON_TENTATIVE, _("_Tentative"),
1034 		GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
1035 	buttons_table_write_button (
1036 		buffer, BUTTON_ACCEPT_ALL, _("A_ccept all"),
1037 		GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
1038 	buttons_table_write_button (
1039 		buffer, BUTTON_ACCEPT, _("A_ccept"),
1040 		GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
1041 	buttons_table_write_button (
1042 		buffer, BUTTON_SEND_INFORMATION, _("_Send Information"),
1043 		GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_REFRESH);
1044 	buttons_table_write_button (
1045 		buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
1046 		GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_UPDATE);
1047 	buttons_table_write_button (
1048 		buffer, BUTTON_UPDATE,  _("_Update"),
1049 		GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_CANCEL);
1050 
1051 	g_string_append (buffer, "</tr></table>");
1052 }
1053 
1054 static void
1055 itip_view_rebuild_source_list (ItipView *view)
1056 {
1057 	ESourceRegistry *registry;
1058 	WebKitDOMElement *select;
1059 	GList *list, *link;
1060 	const gchar *extension_name;
1061 	GHashTable *groups;
1062 
1063 	d (printf ("Assigning a new source list!\n"));
1064 
1065 	if (!view->priv->dom_document)
1066 		return;
1067 
1068 	registry = itip_view_get_registry (view);
1069 	extension_name = itip_view_get_extension_name (view);
1070 
1071 	select = webkit_dom_document_get_element_by_id (
1072 		view->priv->dom_document, SELECT_ESOURCE);
1073 
1074 	while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) {
1075 		webkit_dom_node_remove_child (
1076 			WEBKIT_DOM_NODE (select),
1077 			webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select)),
1078 			NULL);
1079 	}
1080 
1081 	if (extension_name == NULL)
1082 		return;
1083 
1084 	list = e_source_registry_list_sources (registry, extension_name);
1085 	groups = g_hash_table_new_full (
1086 			g_str_hash, g_str_equal,
1087 			(GDestroyNotify) g_free, NULL);
1088 	for (link = list; link != NULL; link = g_list_next (link)) {
1089 		ESource *source = E_SOURCE (link->data);
1090 		ESource *parent;
1091 		WebKitDOMElement *option;
1092 		WebKitDOMHTMLOptGroupElement *optgroup;
1093 
1094 		parent = e_source_registry_ref_source (
1095 				registry, e_source_get_parent (source));
1096 
1097 		optgroup = g_hash_table_lookup (groups, e_source_get_uid (parent));
1098 		if (!optgroup) {
1099 			optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
1100 					webkit_dom_document_create_element (
1101 						view->priv->dom_document,
1102 						"OPTGROUP", NULL));
1103 			webkit_dom_html_opt_group_element_set_label (
1104 				optgroup, e_source_get_display_name (parent));
1105 			g_hash_table_insert (
1106 				groups, g_strdup (e_source_get_uid (parent)), optgroup);
1107 		}
1108 		g_object_unref (parent);
1109 
1110 		option = webkit_dom_document_create_element (
1111 			view->priv->dom_document, "OPTION", NULL);
1112 		webkit_dom_html_option_element_set_value (
1113 			WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
1114 			e_source_get_uid (source));
1115 		webkit_dom_html_option_element_set_label (
1116 			WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
1117 			e_source_get_display_name (source));
1118 		webkit_dom_html_element_set_inner_html (
1119 			WEBKIT_DOM_HTML_ELEMENT (option),
1120 			e_source_get_display_name (source), NULL);
1121 
1122 		/* See https://bugzilla.gnome.org/show_bug.cgi?id=681400
1123 		 * FIXME: This can be removed once we require WebKitGtk 1.10+ */
1124 		#if WEBKIT_CHECK_VERSION (1, 9, 6)
1125 			webkit_dom_element_set_class_name (
1126 				WEBKIT_DOM_ELEMENT (option), "calendar");
1127 		#else
1128 			webkit_dom_html_element_set_class_name (
1129 				WEBKIT_DOM_HTML_ELEMENT (option), "calendar");
1130 		#endif
1131 
1132 		if (!e_source_get_writable (source)) {
1133 			webkit_dom_html_option_element_set_disabled (
1134 				WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE);
1135 		}
1136 
1137 		webkit_dom_node_append_child (
1138 			WEBKIT_DOM_NODE (optgroup),
1139 			WEBKIT_DOM_NODE (option),
1140 			NULL);
1141 	}
1142 
1143 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1144 
1145 	list = g_hash_table_get_values (groups);
1146 	for (link = list; link != NULL; link = g_list_next (link)) {
1147 		WebKitDOMNode *optgroup = link->data;
1148 
1149 		webkit_dom_node_append_child (
1150 			WEBKIT_DOM_NODE (select), optgroup, NULL);
1151 	}
1152 	g_list_free (list);
1153 	g_hash_table_destroy (groups);
1154 
1155 	source_changed_cb (select, NULL, view);
1156 }
1157 
1158 static void
1159 itip_view_source_added_cb (ESourceRegistry *registry,
1160                            ESource *source,
1161                            ItipView *view)
1162 {
1163 	const gchar *extension_name;
1164 
1165 	extension_name = itip_view_get_extension_name (view);
1166 
1167 	/* If we don't have an extension name set
1168 	 * yet then disregard the signal emission. */
1169 	if (extension_name == NULL)
1170 		return;
1171 
1172 	if (e_source_has_extension (source, extension_name))
1173 		itip_view_rebuild_source_list (view);
1174 }
1175 
1176 static void
1177 itip_view_source_removed_cb (ESourceRegistry *registry,
1178                              ESource *source,
1179                              ItipView *view)
1180 {
1181 	const gchar *extension_name;
1182 
1183 	extension_name = itip_view_get_extension_name (view);
1184 
1185 	/* If we don't have an extension name set
1186 	 * yet then disregard the signal emission. */
1187 	if (extension_name == NULL)
1188 		return;
1189 
1190 	if (e_source_has_extension (source, extension_name))
1191 		itip_view_rebuild_source_list (view);
1192 }
1193 
1194 static void
1195 itip_view_set_registry (ItipView *view,
1196                         ESourceRegistry *registry)
1197 {
1198 	g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
1199 	g_return_if_fail (view->priv->registry == NULL);
1200 
1201 	view->priv->registry = g_object_ref (registry);
1202 }
1203 
1204 static void
1205 itip_view_set_property (GObject *object,
1206                         guint property_id,
1207                         const GValue *value,
1208                         GParamSpec *pspec)
1209 {
1210 	switch (property_id) {
1211 		case PROP_EXTENSION_NAME:
1212 			itip_view_set_extension_name (
1213 				ITIP_VIEW (object),
1214 				g_value_get_string (value));
1215 			return;
1216 
1217 		case PROP_REGISTRY:
1218 			itip_view_set_registry (
1219 				ITIP_VIEW (object),
1220 				g_value_get_object (value));
1221 			return;
1222 	}
1223 
1224 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1225 }
1226 
1227 static void
1228 itip_view_get_property (GObject *object,
1229                         guint property_id,
1230                         GValue *value,
1231                         GParamSpec *pspec)
1232 {
1233 	switch (property_id) {
1234 		case PROP_EXTENSION_NAME:
1235 			g_value_set_string (
1236 				value, itip_view_get_extension_name (
1237 				ITIP_VIEW (object)));
1238 			return;
1239 
1240 		case PROP_REGISTRY:
1241 			g_value_set_object (
1242 				value, itip_view_get_registry (
1243 				ITIP_VIEW (object)));
1244 			return;
1245 	}
1246 
1247 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1248 }
1249 
1250 static void
1251 itip_view_dispose (GObject *object)
1252 {
1253 	ItipViewPrivate *priv;
1254 
1255 	priv = ITIP_VIEW_GET_PRIVATE (object);
1256 
1257 	if (priv->registry != NULL) {
1258 		g_signal_handler_disconnect (
1259 			priv->registry, priv->source_added_id);
1260 		g_signal_handler_disconnect (
1261 			priv->registry, priv->source_removed_id);
1262 		g_object_unref (priv->registry);
1263 		priv->registry = NULL;
1264 	}
1265 
1266 	/* Chain up to parent's dispose() method. */
1267 	G_OBJECT_CLASS (itip_view_parent_class)->dispose (object);
1268 }
1269 
1270 static void
1271 itip_view_finalize (GObject *object)
1272 {
1273 	ItipViewPrivate *priv;
1274 	GSList *iter;
1275 
1276 	priv = ITIP_VIEW_GET_PRIVATE (object);
1277 
1278 	d (printf ("Itip view finalized!\n"));
1279 
1280 	g_clear_object (&priv->dom_document);
1281 	g_free (priv->extension_name);
1282 	g_free (priv->sender);
1283 	g_free (priv->organizer);
1284 	g_free (priv->organizer_sentby);
1285 	g_free (priv->delegator);
1286 	g_free (priv->attendee);
1287 	g_free (priv->attendee_sentby);
1288 	g_free (priv->proxy);
1289 	g_free (priv->summary);
1290 	g_free (priv->location);
1291 	g_free (priv->status);
1292 	g_free (priv->comment);
1293 	g_free (priv->start_tm);
1294 	g_free (priv->start_label);
1295 	g_free (priv->end_tm);
1296 	g_free (priv->end_label);
1297 	g_free (priv->description);
1298 	g_free (priv->error);
1299 
1300 	for (iter = priv->lower_info_items; iter; iter = iter->next) {
1301 		ItipViewInfoItem *item = iter->data;
1302 		g_free (item->message);
1303 		g_free (item);
1304 	}
1305 
1306 	g_slist_free (priv->lower_info_items);
1307 
1308 	for (iter = priv->upper_info_items; iter; iter = iter->next) {
1309 		ItipViewInfoItem *item = iter->data;
1310 		g_free (item->message);
1311 		g_free (item);
1312 	}
1313 
1314 	g_slist_free (priv->upper_info_items);
1315 
1316 	/* Chain up to parent's finalize() method. */
1317 	G_OBJECT_CLASS (itip_view_parent_class)->finalize (object);
1318 }
1319 
1320 static void
1321 itip_view_constructed (GObject *object)
1322 {
1323 	ItipView *view;
1324 	ESourceRegistry *registry;
1325 
1326 	view = ITIP_VIEW (object);
1327 	registry = itip_view_get_registry (view);
1328 
1329 	view->priv->source_added_id = g_signal_connect (
1330 		registry, "source-added",
1331 		G_CALLBACK (itip_view_source_added_cb), view);
1332 
1333 	view->priv->source_removed_id = g_signal_connect (
1334 		registry, "source-removed",
1335 		G_CALLBACK (itip_view_source_removed_cb), view);
1336 
1337 	/* Chain up to parent's constructed() method. */
1338 	G_OBJECT_CLASS (itip_view_parent_class)->constructed (object);
1339 }
1340 
1341 static void
1342 itip_view_class_init (ItipViewClass *class)
1343 {
1344 	GObjectClass *object_class;
1345 
1346 	g_type_class_add_private (class, sizeof (ItipViewPrivate));
1347 
1348 	object_class = G_OBJECT_CLASS (class);
1349 	object_class->set_property = itip_view_set_property;
1350 	object_class->get_property = itip_view_get_property;
1351 	object_class->dispose = itip_view_dispose;
1352 	object_class->finalize = itip_view_finalize;
1353 	object_class->constructed = itip_view_constructed;
1354 
1355 	g_object_class_install_property (
1356 		object_class,
1357 		PROP_REGISTRY,
1358 		g_param_spec_string (
1359 			"extension-name",
1360 			"Extension Name",
1361 			"Show only data sources with this extension",
1362 			NULL,
1363 			G_PARAM_READWRITE));
1364 
1365 	g_object_class_install_property (
1366 		object_class,
1367 		PROP_REGISTRY,
1368 		g_param_spec_object (
1369 			"registry",
1370 			"Registry",
1371 			"Data source registry",
1372 			E_TYPE_SOURCE_REGISTRY,
1373 			G_PARAM_READWRITE |
1374 			G_PARAM_CONSTRUCT_ONLY));
1375 
1376 	signals[SOURCE_SELECTED] = g_signal_new (
1377 		"source_selected",
1378 		G_TYPE_FROM_CLASS (class),
1379 		G_SIGNAL_RUN_LAST,
1380 		G_STRUCT_OFFSET (ItipViewClass, source_selected),
1381 		NULL, NULL,
1382 		g_cclosure_marshal_VOID__OBJECT,
1383 		G_TYPE_NONE, 1,
1384 		E_TYPE_SOURCE);
1385 
1386 	signals[RESPONSE] = g_signal_new (
1387 		"response",
1388 		G_TYPE_FROM_CLASS (class),
1389 		G_SIGNAL_RUN_LAST,
1390 		G_STRUCT_OFFSET (ItipViewClass, response),
1391 		NULL, NULL,
1392 		g_cclosure_marshal_VOID__INT,
1393 		G_TYPE_NONE, 1,
1394 		G_TYPE_INT);
1395 }
1396 
1397 EMailPartItip *
1398 itip_view_get_mail_part (ItipView *view)
1399 {
1400 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1401 
1402 	return view->priv->itip_part;
1403 }
1404 
1405 ESourceRegistry *
1406 itip_view_get_registry (ItipView *view)
1407 {
1408 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1409 
1410 	return view->priv->registry;
1411 }
1412 
1413 const gchar *
1414 itip_view_get_extension_name (ItipView *view)
1415 {
1416 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1417 
1418 	return view->priv->extension_name;
1419 }
1420 
1421 void
1422 itip_view_set_extension_name (ItipView *view,
1423                               const gchar *extension_name)
1424 {
1425 	g_return_if_fail (ITIP_IS_VIEW (view));
1426 
1427 	/* Avoid unnecessary rebuilds. */
1428 	if (g_strcmp0 (extension_name, view->priv->extension_name) == 0)
1429 		return;
1430 
1431 	g_free (view->priv->extension_name);
1432 	view->priv->extension_name = g_strdup (extension_name);
1433 
1434 	g_object_notify (G_OBJECT (view), "extension-name");
1435 
1436 	itip_view_rebuild_source_list (view);
1437 }
1438 
1439 void
1440 itip_view_write (EMailFormatter *formatter,
1441                  GString *buffer)
1442 {
1443 	gchar *header = e_mail_formatter_get_html_header (formatter);
1444 	g_string_append (buffer, header);
1445 	g_free (header);
1446 
1447 	g_string_append_printf (
1448 		buffer,
1449 		"<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" />\n",
1450 			MEETING_ICON, GTK_ICON_SIZE_BUTTON);
1451 
1452 	g_string_append (
1453 		buffer,
1454 		"<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");
1455 
1456         /* The first section listing the sender */
1457         /* FIXME What to do if the send and organizer do not match */
1458 	g_string_append (
1459 		buffer,
1460 		"<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");
1461 
1462 	g_string_append (buffer, "<hr>\n");
1463 
1464         /* Elementary event information */
1465 	g_string_append (
1466 		buffer,
1467 		"<table class=\"itip table\" border=\"0\" "
1468 		"cellspacing=\"5\" cellpadding=\"0\">\n");
1469 
1470 	append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
1471 	append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
1472 	append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
1473 	append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
1474 	append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
1475 	append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);
1476 
1477 	g_string_append (buffer, "</table>\n");
1478 
1479 	/* Upper Info items */
1480 	g_string_append (
1481 		buffer,
1482 		"<table class=\"itip info\" id=\"" TABLE_UPPER_ITIP_INFO "\" border=\"0\" "
1483 		"cellspacing=\"5\" cellpadding=\"0\">");
1484 
1485         /* Description */
1486 	g_string_append (
1487 		buffer,
1488 		"<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\" hidden=\"\"></div>\n");
1489 
1490 	g_string_append (buffer, "<hr>\n");
1491 
1492 	/* Lower Info items */
1493 	g_string_append (
1494 		buffer,
1495 		"<table class=\"itip info\" id=\"" TABLE_LOWER_ITIP_INFO "\" border=\"0\" "
1496 		"cellspacing=\"5\" cellpadding=\"0\">");
1497 
1498 	g_string_append (
1499 		buffer,
1500 		"<table class=\"itip table\" border=\"0\" "
1501 		"cellspacing=\"5\" cellpadding=\"0\">\n");
1502 
1503 	g_string_append (
1504 		buffer,
1505 		"<tr id=\"" TABLE_ROW_ESCB "\" hidden=\"\""">"
1506 		"<th><label id=\"" TABLE_ROW_ESCB_LABEL "\" for=\"" SELECT_ESOURCE "\"></label></th>"
1507 		"<td><select name=\"" SELECT_ESOURCE "\" id=\"" SELECT_ESOURCE "\"></select></td>"
1508 		"</tr>\n");
1509 
1510 	/* RSVP area */
1511 	append_checkbox_table_row (buffer, CHECKBOX_RSVP, _("Send reply to sender"));
1512 
1513         /* Comments */
1514 	g_string_append_printf (
1515 		buffer,
1516 		"<tr id=\"" TABLE_ROW_RSVP_COMMENT "\" hidden=\"\">"
1517 		"<th>%s</th>"
1518 		"<td><textarea name=\"" TEXTAREA_RSVP_COMMENT "\" "
1519 		"id=\"" TEXTAREA_RSVP_COMMENT "\" "
1520 		"rows=\"3\" cols=\"40\" disabled=\"\">"
1521 		"</textarea></td>\n"
1522 		"</tr>\n",
1523 		_("Comment:"));
1524 
1525         /* Updates */
1526 	append_checkbox_table_row (buffer, CHECKBOX_UPDATE, _("Send _updates to attendees"));
1527 
1528         /* The recurrence check button */
1529 	append_checkbox_table_row (buffer, CHECKBOX_RECUR, _("_Apply to all instances"));
1530 	append_checkbox_table_row (buffer, CHECKBOX_FREE_TIME, _("Show time as _free"));
1531 	append_checkbox_table_row (buffer, CHECKBOX_KEEP_ALARM, _("_Preserve my reminder"));
1532 	append_checkbox_table_row (buffer, CHECKBOX_INHERIT_ALARM, _("_Inherit reminder"));
1533 
1534 	g_string_append (buffer, "</table>\n");
1535 
1536         /* Buttons table */
1537 	append_buttons_table (buffer);
1538 
1539         /* <div class="itip content" > */
1540 	g_string_append (buffer, "</div>\n");
1541 
1542 	g_string_append (buffer, "<div class=\"itip error\" id=\"" DIV_ITIP_ERROR "\"></div>");
1543 
1544 	g_string_append (buffer, "</body></html>");
1545 }
1546 
1547 void
1548 itip_view_write_for_printing (ItipView *view,
1549                               GString *buffer)
1550 {
1551 	if (view->priv->error && *view->priv->error) {
1552 		g_string_append (buffer, view->priv->error);
1553 		return;
1554 	}
1555 
1556 	g_string_append (
1557 		buffer,
1558 		"<div class=\"itip print_content\" id=\"" DIV_ITIP_CONTENT "\">\n");
1559 
1560         /* The first section listing the sender */
1561 	if (view->priv->sender && *view->priv->sender) {
1562 		/* FIXME What to do if the send and organizer do not match */
1563 		g_string_append_printf (
1564 			buffer,
1565 			"<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\">%s</div>\n",
1566 			view->priv->sender);
1567 
1568 		g_string_append (buffer, "<hr>\n");
1569 	}
1570 
1571         /* Elementary event information */
1572 	g_string_append (
1573 		buffer,
1574 		"<table class=\"itip table\" border=\"0\" "
1575 		"cellspacing=\"5\" cellpadding=\"0\">\n");
1576 
1577 	append_text_table_row_nonempty (
1578 		buffer, TABLE_ROW_SUMMARY,
1579 		NULL, view->priv->summary);
1580 	append_text_table_row_nonempty (
1581 		buffer, TABLE_ROW_LOCATION,
1582 		_("Location:"), view->priv->location);
1583 	append_text_table_row_nonempty (
1584 		buffer, TABLE_ROW_START_DATE,
1585 		view->priv->start_header, view->priv->start_label);
1586 	append_text_table_row_nonempty (
1587 		buffer, TABLE_ROW_END_DATE,
1588 		view->priv->end_header, view->priv->end_label);
1589 	append_text_table_row_nonempty (
1590 		buffer, TABLE_ROW_STATUS,
1591 		_("Status:"), view->priv->status);
1592 	append_text_table_row_nonempty (
1593 		buffer, TABLE_ROW_COMMENT,
1594 		_("Comment:"), view->priv->comment);
1595 
1596 	g_string_append (buffer, "</table><br>\n");
1597 
1598         /* Description */
1599 	if (view->priv->description && *view->priv->description) {
1600 		g_string_append_printf (
1601 			buffer,
1602 			"<div id=\"" TABLE_ROW_DESCRIPTION "\" "
1603 			"class=\"itip description\" %s>%s</div>\n",
1604 			view->priv->description ? "" : "hidden=\"\"", view->priv->description);
1605 
1606 		g_string_append (buffer, "</div>");
1607 	}
1608 }
1609 
1610 void
1611 itip_view_create_dom_bindings (ItipView *view,
1612                                WebKitDOMElement *element)
1613 {
1614 	WebKitDOMElement *el;
1615 	WebKitDOMDocument *doc;
1616 
1617 	doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
1618 	view->priv->dom_document = g_object_ref (doc);
1619 
1620 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR);
1621 	if (el) {
1622 		webkit_dom_event_target_add_event_listener (
1623 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1624 			G_CALLBACK (recur_toggled_cb), FALSE, view);
1625 	}
1626 
1627 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP);
1628 	if (el) {
1629 		webkit_dom_event_target_add_event_listener (
1630 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1631 			G_CALLBACK (rsvp_toggled_cb), FALSE, view);
1632 	}
1633 
1634 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM);
1635 	if (el) {
1636 		webkit_dom_event_target_add_event_listener (
1637 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1638 			G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
1639 	}
1640 
1641 	el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM);
1642 	if (el) {
1643 		webkit_dom_event_target_add_event_listener (
1644 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1645 			G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
1646 	}
1647 
1648 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR);
1649 	if (el) {
1650 		webkit_dom_event_target_add_event_listener (
1651 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1652 			G_CALLBACK (button_clicked_cb), FALSE, view);
1653 	}
1654 
1655 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT);
1656 	if (el) {
1657 		webkit_dom_event_target_add_event_listener (
1658 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1659 			G_CALLBACK (button_clicked_cb), FALSE, view);
1660 	}
1661 
1662 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL);
1663 	if (el) {
1664 		webkit_dom_event_target_add_event_listener (
1665 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1666 			G_CALLBACK (button_clicked_cb), FALSE, view);
1667 	}
1668 
1669 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE);
1670 	if (el) {
1671 		webkit_dom_event_target_add_event_listener (
1672 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1673 			G_CALLBACK (button_clicked_cb), FALSE, view);
1674 	}
1675 
1676 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL);
1677 	if (el) {
1678 		webkit_dom_event_target_add_event_listener (
1679 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1680 			G_CALLBACK (button_clicked_cb), FALSE, view);
1681 	}
1682 
1683 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE);
1684 	if (el) {
1685 		webkit_dom_event_target_add_event_listener (
1686 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1687 			G_CALLBACK (button_clicked_cb), FALSE, view);
1688 	}
1689 
1690 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL);
1691 	if (el) {
1692 		webkit_dom_event_target_add_event_listener (
1693 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1694 			G_CALLBACK (button_clicked_cb), FALSE, view);
1695 	}
1696 
1697 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE);
1698 	if (el) {
1699 		webkit_dom_event_target_add_event_listener (
1700 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1701 			G_CALLBACK (button_clicked_cb), FALSE, view);
1702 	}
1703 
1704 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS);
1705 	if (el) {
1706 		webkit_dom_event_target_add_event_listener (
1707 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1708 			G_CALLBACK (button_clicked_cb), FALSE, view);
1709 	}
1710 
1711 	el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION);
1712 	if (el) {
1713 		webkit_dom_event_target_add_event_listener (
1714 			WEBKIT_DOM_EVENT_TARGET (el), "click",
1715 			G_CALLBACK (button_clicked_cb), FALSE, view);
1716 	}
1717 
1718 	el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE);
1719 	if (el) {
1720 		webkit_dom_event_target_add_event_listener (
1721 			WEBKIT_DOM_EVENT_TARGET (el), "change",
1722 			G_CALLBACK (source_changed_cb), FALSE, view);
1723 	}
1724 }
1725 
1726 static void
1727 itip_view_init (ItipView *view)
1728 {
1729 	view->priv = ITIP_VIEW_GET_PRIVATE (view);
1730 
1731 }
1732 
1733 ItipView *
1734 itip_view_new (EMailPartItip *puri,
1735                ESourceRegistry *registry)
1736 {
1737 	ItipView *view;
1738 
1739 	view = ITIP_VIEW (g_object_new (
1740 		ITIP_TYPE_VIEW,
1741 		"registry", registry,
1742 		NULL));
1743 	view->priv->itip_part = puri;
1744 
1745 	return view;
1746 }
1747 
1748 static void
1749 show_button (ItipView *view,
1750              const gchar *id)
1751 {
1752 	WebKitDOMElement *button;
1753 
1754 	button = webkit_dom_document_get_element_by_id (
1755 		view->priv->dom_document, id);
1756 	webkit_dom_html_element_set_hidden (
1757 		WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
1758 }
1759 
1760 void
1761 itip_view_set_mode (ItipView *view,
1762                     ItipViewMode mode)
1763 {
1764 	WebKitDOMElement *row, *cell;
1765 	WebKitDOMElement *button;
1766 
1767 	g_return_if_fail (ITIP_IS_VIEW (view));
1768 
1769 	view->priv->mode = mode;
1770 
1771 	set_sender_text (view);
1772 
1773 	if (!view->priv->dom_document)
1774 		return;
1775 
1776 	row = webkit_dom_document_get_element_by_id (
1777 		view->priv->dom_document, TABLE_ROW_BUTTONS);
1778 	cell = webkit_dom_element_get_first_element_child (row);
1779 	do {
1780 		button = webkit_dom_element_get_first_element_child (cell);
1781 		webkit_dom_html_element_set_hidden (
1782 			WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
1783 	} while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
1784 
1785 	view->priv->is_recur_set = itip_view_get_recur_check_state (view);
1786 
1787         /* Always visible */
1788 	show_button (view, BUTTON_OPEN_CALENDAR);
1789 
1790 	switch (mode) {
1791 	case ITIP_VIEW_MODE_PUBLISH:
1792 		if (view->priv->needs_decline) {
1793 			show_button (view, BUTTON_DECLINE);
1794 		}
1795 		show_button (view, BUTTON_ACCEPT);
1796 		break;
1797 	case ITIP_VIEW_MODE_REQUEST:
1798 		show_button (view, view->priv->is_recur_set ? BUTTON_DECLINE_ALL : BUTTON_DECLINE);
1799 		show_button (view, view->priv->is_recur_set ? BUTTON_TENTATIVE_ALL : BUTTON_TENTATIVE);
1800 		show_button (view, view->priv->is_recur_set ? BUTTON_ACCEPT_ALL : BUTTON_ACCEPT);
1801 		break;
1802 	case ITIP_VIEW_MODE_ADD:
1803 		if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
1804 			show_button (view, BUTTON_DECLINE);
1805 			show_button (view, BUTTON_TENTATIVE);
1806 		}
1807 		show_button (view, BUTTON_ACCEPT);
1808 		break;
1809 	case ITIP_VIEW_MODE_REFRESH:
1810 		show_button (view, BUTTON_SEND_INFORMATION);
1811 		break;
1812 	case ITIP_VIEW_MODE_REPLY:
1813 		show_button (view, BUTTON_UPDATE_ATTENDEE_STATUS);
1814 		break;
1815 	case ITIP_VIEW_MODE_CANCEL:
1816 		show_button (view, BUTTON_UPDATE);
1817 		break;
1818 	case ITIP_VIEW_MODE_COUNTER:
1819 	case ITIP_VIEW_MODE_DECLINECOUNTER:
1820 		show_button (view, BUTTON_DECLINE);
1821 		show_button (view, BUTTON_TENTATIVE);
1822 		show_button (view, BUTTON_ACCEPT);
1823 		break;
1824 	default:
1825 		break;
1826 	}
1827 }
1828 
1829 ItipViewMode
1830 itip_view_get_mode (ItipView *view)
1831 {
1832 	g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
1833 
1834 	return view->priv->mode;
1835 }
1836 
1837 void
1838 itip_view_set_item_type (ItipView *view,
1839                          ECalClientSourceType type)
1840 {
1841 	WebKitDOMElement *label;
1842 	const gchar *header;
1843 	gchar *access_key, *html_label;
1844 
1845 	g_return_if_fail (ITIP_IS_VIEW (view));
1846 
1847 	view->priv->type = type;
1848 
1849 	if (!view->priv->dom_document)
1850 		return;
1851 
1852 	label = webkit_dom_document_get_element_by_id (
1853 		view->priv->dom_document, TABLE_ROW_ESCB_LABEL);
1854 
1855 	switch (view->priv->type) {
1856 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1857 			header = _("_Calendar:");
1858 			break;
1859 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1860 			header = _("_Tasks:");
1861 			break;
1862 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1863 			header = _("_Memos:");
1864 			break;
1865 		default:
1866 			header = NULL;
1867 			break;
1868 	}
1869 
1870 	if (!header) {
1871 		set_sender_text (view);
1872 		return;
1873 	}
1874 
1875 	html_label = e_mail_formatter_parse_html_mnemonics (header, &access_key);
1876 
1877 	webkit_dom_html_element_set_access_key (
1878 		WEBKIT_DOM_HTML_ELEMENT (label), access_key);
1879 	webkit_dom_html_element_set_inner_html (
1880 		WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL);
1881 
1882 	g_free (html_label);
1883 
1884 	if (access_key)
1885 		g_free (access_key);
1886 
1887 	set_sender_text (view);
1888 }
1889 
1890 ECalClientSourceType
1891 itip_view_get_item_type (ItipView *view)
1892 {
1893 	g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
1894 
1895 	return view->priv->type;
1896 }
1897 
1898 void
1899 itip_view_set_organizer (ItipView *view,
1900                          const gchar *organizer)
1901 {
1902 	g_return_if_fail (ITIP_IS_VIEW (view));
1903 
1904 	if (view->priv->organizer)
1905 		g_free (view->priv->organizer);
1906 
1907 	view->priv->organizer = e_utf8_ensure_valid (organizer);
1908 
1909 	set_sender_text (view);
1910 }
1911 
1912 const gchar *
1913 itip_view_get_organizer (ItipView *view)
1914 {
1915 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1916 
1917 	return view->priv->organizer;
1918 }
1919 
1920 void
1921 itip_view_set_organizer_sentby (ItipView *view,
1922                                 const gchar *sentby)
1923 {
1924 	g_return_if_fail (ITIP_IS_VIEW (view));
1925 
1926 	if (view->priv->organizer_sentby)
1927 		g_free (view->priv->organizer_sentby);
1928 
1929 	view->priv->organizer_sentby = e_utf8_ensure_valid (sentby);
1930 
1931 	set_sender_text (view);
1932 }
1933 
1934 const gchar *
1935 itip_view_get_organizer_sentby (ItipView *view)
1936 {
1937 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1938 
1939 	return view->priv->organizer_sentby;
1940 }
1941 
1942 void
1943 itip_view_set_attendee (ItipView *view,
1944                         const gchar *attendee)
1945 {
1946 	g_return_if_fail (ITIP_IS_VIEW (view));
1947 
1948 	if (view->priv->attendee)
1949 		g_free (view->priv->attendee);
1950 
1951 	view->priv->attendee = e_utf8_ensure_valid (attendee);
1952 
1953 	set_sender_text (view);
1954 }
1955 
1956 const gchar *
1957 itip_view_get_attendee (ItipView *view)
1958 {
1959 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1960 
1961 	return view->priv->attendee;
1962 }
1963 
1964 void
1965 itip_view_set_attendee_sentby (ItipView *view,
1966                                const gchar *sentby)
1967 {
1968 	g_return_if_fail (ITIP_IS_VIEW (view));
1969 
1970 	if (view->priv->attendee_sentby)
1971 		g_free (view->priv->attendee_sentby);
1972 
1973 	view->priv->attendee_sentby = e_utf8_ensure_valid (sentby);
1974 
1975 	set_sender_text (view);
1976 }
1977 
1978 const gchar *
1979 itip_view_get_attendee_sentby (ItipView *view)
1980 {
1981 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1982 
1983 	return view->priv->attendee_sentby;
1984 }
1985 
1986 void
1987 itip_view_set_proxy (ItipView *view,
1988                      const gchar *proxy)
1989 {
1990 	g_return_if_fail (ITIP_IS_VIEW (view));
1991 
1992 	if (view->priv->proxy)
1993 		g_free (view->priv->proxy);
1994 
1995 	view->priv->proxy = e_utf8_ensure_valid (proxy);
1996 
1997 	set_sender_text (view);
1998 }
1999 
2000 const gchar *
2001 itip_view_get_proxy (ItipView *view)
2002 {
2003 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2004 
2005 	return view->priv->proxy;
2006 }
2007 
2008 void
2009 itip_view_set_delegator (ItipView *view,
2010                          const gchar *delegator)
2011 {
2012 	g_return_if_fail (ITIP_IS_VIEW (view));
2013 
2014 	if (view->priv->delegator)
2015 		g_free (view->priv->delegator);
2016 
2017 	view->priv->delegator = e_utf8_ensure_valid (delegator);
2018 
2019 	set_sender_text (view);
2020 }
2021 
2022 const gchar *
2023 itip_view_get_delegator (ItipView *view)
2024 {
2025 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2026 
2027 	return view->priv->delegator;
2028 }
2029 
2030 void
2031 itip_view_set_summary (ItipView *view,
2032                        const gchar *summary)
2033 {
2034 	WebKitDOMElement *row, *col;
2035 
2036 	g_return_if_fail (ITIP_IS_VIEW (view));
2037 
2038 	if (view->priv->summary)
2039 		g_free (view->priv->summary);
2040 
2041 	view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL;
2042 
2043 	if (!view->priv->dom_document)
2044 		return;
2045 
2046 	row = webkit_dom_document_get_element_by_id (
2047 		view->priv->dom_document, TABLE_ROW_SUMMARY);
2048 	webkit_dom_html_element_set_hidden (
2049 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL));
2050 
2051 	col = webkit_dom_element_get_last_element_child (row);
2052 	webkit_dom_html_element_set_inner_html (
2053 		WEBKIT_DOM_HTML_ELEMENT (col),
2054 		view->priv->summary ? view->priv->summary : "",
2055 		NULL);
2056 }
2057 
2058 const gchar *
2059 itip_view_get_summary (ItipView *view)
2060 {
2061 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2062 
2063 	return view->priv->summary;
2064 }
2065 
2066 void
2067 itip_view_set_location (ItipView *view,
2068                         const gchar *location)
2069 {
2070 	WebKitDOMElement *row, *col;
2071 
2072 	g_return_if_fail (ITIP_IS_VIEW (view));
2073 
2074 	if (view->priv->location)
2075 		g_free (view->priv->location);
2076 
2077 	view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL;
2078 
2079 	if (!view->priv->dom_document)
2080 		return;
2081 
2082 	row = webkit_dom_document_get_element_by_id (
2083 		view->priv->dom_document, TABLE_ROW_LOCATION);
2084 	webkit_dom_html_element_set_hidden (
2085 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL));
2086 
2087 	col = webkit_dom_element_get_last_element_child (row);
2088 	webkit_dom_html_element_set_inner_html (
2089 		WEBKIT_DOM_HTML_ELEMENT (col),
2090 		view->priv->location ? view->priv->location : "",
2091 		NULL);
2092 }
2093 
2094 const gchar *
2095 itip_view_get_location (ItipView *view)
2096 {
2097 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2098 
2099 	return view->priv->location;
2100 }
2101 
2102 void
2103 itip_view_set_status (ItipView *view,
2104                       const gchar *status)
2105 {
2106 	WebKitDOMElement *row, *col;
2107 
2108 	g_return_if_fail (ITIP_IS_VIEW (view));
2109 
2110 	if (view->priv->status)
2111 		g_free (view->priv->status);
2112 
2113 	view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL;
2114 
2115 	if (!view->priv->dom_document)
2116 		return;
2117 
2118 	row = webkit_dom_document_get_element_by_id (
2119 		view->priv->dom_document, TABLE_ROW_STATUS);
2120 	webkit_dom_html_element_set_hidden (
2121 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL));
2122 
2123 	col = webkit_dom_element_get_last_element_child (row);
2124 	webkit_dom_html_element_set_inner_html (
2125 		WEBKIT_DOM_HTML_ELEMENT (col),
2126 		view->priv->status ? view->priv->status : "",
2127 		NULL);
2128 }
2129 
2130 const gchar *
2131 itip_view_get_status (ItipView *view)
2132 {
2133 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2134 
2135 	return view->priv->status;
2136 }
2137 
2138 void
2139 itip_view_set_comment (ItipView *view,
2140                        const gchar *comment)
2141 {
2142 	WebKitDOMElement *row, *col;
2143 
2144 	g_return_if_fail (ITIP_IS_VIEW (view));
2145 
2146 	if (view->priv->comment)
2147 		g_free (view->priv->comment);
2148 
2149 	view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL;
2150 
2151 	if (!view->priv->dom_document)
2152 		return;
2153 
2154 	row = webkit_dom_document_get_element_by_id (
2155 		view->priv->dom_document, TABLE_ROW_COMMENT);
2156 	webkit_dom_html_element_set_hidden (
2157 		WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL));
2158 
2159 	col = webkit_dom_element_get_last_element_child (row);
2160 	webkit_dom_html_element_set_inner_html (
2161 		WEBKIT_DOM_HTML_ELEMENT (col),
2162 		view->priv->comment ? view->priv->comment : "",
2163 		NULL);
2164 }
2165 
2166 const gchar *
2167 itip_view_get_comment (ItipView *view)
2168 {
2169 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2170 
2171 	return view->priv->comment;
2172 }
2173 
2174 void
2175 itip_view_set_description (ItipView *view,
2176                            const gchar *description)
2177 {
2178 	WebKitDOMElement *div;
2179 
2180 	g_return_if_fail (ITIP_IS_VIEW (view));
2181 
2182 	if (view->priv->description)
2183 		g_free (view->priv->description);
2184 
2185 	view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL;
2186 
2187 	if (!view->priv->dom_document)
2188 		return;
2189 
2190 	div = webkit_dom_document_get_element_by_id (
2191 		view->priv->dom_document, TABLE_ROW_DESCRIPTION);
2192 	webkit_dom_html_element_set_hidden (
2193 		WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL));
2194 
2195 	webkit_dom_html_element_set_inner_html (
2196 		WEBKIT_DOM_HTML_ELEMENT (div),
2197 		view->priv->description ? view->priv->description : "",
2198 		NULL);
2199 }
2200 
2201 const gchar *
2202 itip_view_get_description (ItipView *view)
2203 {
2204 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2205 
2206 	return view->priv->description;
2207 }
2208 
2209 void
2210 itip_view_set_start (ItipView *view,
2211                      struct tm *start,
2212                      gboolean is_date)
2213 {
2214 	ItipViewPrivate *priv;
2215 
2216 	g_return_if_fail (ITIP_IS_VIEW (view));
2217 
2218 	priv = view->priv;
2219 
2220 	if (priv->start_tm && !start) {
2221 		g_free (priv->start_tm);
2222 		priv->start_tm = NULL;
2223 	} else if (start) {
2224 		if (!priv->start_tm)
2225 			priv->start_tm = g_new0 (struct tm, 1);
2226 
2227 		*priv->start_tm = *start;
2228 	}
2229 
2230 	priv->start_tm_is_date = is_date && start;
2231 
2232 	update_start_end_times (view);
2233 }
2234 
2235 const struct tm *
2236 itip_view_get_start (ItipView *view,
2237                      gboolean *is_date)
2238 {
2239 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2240 
2241 	if (is_date)
2242 		*is_date = view->priv->start_tm_is_date;
2243 
2244 	return view->priv->start_tm;
2245 }
2246 
2247 void
2248 itip_view_set_end (ItipView *view,
2249                    struct tm *end,
2250                    gboolean is_date)
2251 {
2252 	ItipViewPrivate *priv;
2253 
2254 	g_return_if_fail (ITIP_IS_VIEW (view));
2255 
2256 	priv = view->priv;
2257 
2258 	if (priv->end_tm && !end) {
2259 		g_free (priv->end_tm);
2260 		priv->end_tm = NULL;
2261 	} else if (end) {
2262 		if (!priv->end_tm)
2263 			priv->end_tm = g_new0 (struct tm, 1);
2264 
2265 		*priv->end_tm = *end;
2266 	}
2267 
2268 	priv->end_tm_is_date = is_date && end;
2269 
2270 	update_start_end_times (view);
2271 }
2272 
2273 const struct tm *
2274 itip_view_get_end (ItipView *view,
2275                    gboolean *is_date)
2276 {
2277 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2278 
2279 	if (is_date)
2280 		*is_date = view->priv->end_tm_is_date;
2281 
2282 	return view->priv->end_tm;
2283 }
2284 
2285 guint
2286 itip_view_add_upper_info_item (ItipView *view,
2287                                ItipViewInfoItemType type,
2288                                const gchar *message)
2289 {
2290 	ItipViewPrivate *priv;
2291 	ItipViewInfoItem *item;
2292 
2293 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2294 
2295 	priv = view->priv;
2296 
2297 	item = g_new0 (ItipViewInfoItem, 1);
2298 
2299 	item->type = type;
2300 	item->message = e_utf8_ensure_valid (message);
2301 	item->id = priv->next_info_item_id++;
2302 
2303 	priv->upper_info_items = g_slist_append (priv->upper_info_items, item);
2304 
2305 	if (!view->priv->dom_document)
2306 		return item->id;
2307 
2308 	append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item);
2309 
2310 	return item->id;
2311 }
2312 
2313 guint
2314 itip_view_add_upper_info_item_printf (ItipView *view,
2315                                       ItipViewInfoItemType type,
2316                                       const gchar *format,
2317                                       ...)
2318 {
2319 	va_list args;
2320 	gchar *message;
2321 	guint id;
2322 
2323 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2324 
2325 	va_start (args, format);
2326 	message = g_strdup_vprintf (format, args);
2327 	va_end (args);
2328 
2329 	id = itip_view_add_upper_info_item (view, type, message);
2330 	g_free (message);
2331 
2332 	return id;
2333 }
2334 
2335 void
2336 itip_view_remove_upper_info_item (ItipView *view,
2337                                   guint id)
2338 {
2339 	ItipViewPrivate *priv;
2340 	GSList *l;
2341 
2342 	g_return_if_fail (ITIP_IS_VIEW (view));
2343 
2344 	priv = view->priv;
2345 
2346 	for (l = priv->upper_info_items; l; l = l->next) {
2347 		ItipViewInfoItem *item = l->data;
2348 
2349 		if (item->id == id) {
2350 			priv->upper_info_items = g_slist_remove (priv->upper_info_items, item);
2351 
2352 			g_free (item->message);
2353 			g_free (item);
2354 
2355 			if (!view->priv->dom_document)
2356 				remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);
2357 
2358 			return;
2359 		}
2360 	}
2361 }
2362 
2363 void
2364 itip_view_clear_upper_info_items (ItipView *view)
2365 {
2366 	ItipViewPrivate *priv;
2367 	GSList *l;
2368 
2369 	g_return_if_fail (ITIP_IS_VIEW (view));
2370 
2371 	priv = view->priv;
2372 
2373 	for (l = priv->upper_info_items; l; l = l->next) {
2374 		ItipViewInfoItem *item = l->data;
2375 
2376 		if (view->priv->dom_document)
2377 			remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);
2378 
2379 		g_free (item->message);
2380 		g_free (item);
2381 	}
2382 
2383 	g_slist_free (priv->upper_info_items);
2384 	priv->upper_info_items = NULL;
2385 }
2386 
2387 guint
2388 itip_view_add_lower_info_item (ItipView *view,
2389                                ItipViewInfoItemType type,
2390                                const gchar *message)
2391 {
2392 	ItipViewPrivate *priv;
2393 	ItipViewInfoItem *item;
2394 
2395 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2396 
2397 	priv = view->priv;
2398 
2399 	item = g_new0 (ItipViewInfoItem, 1);
2400 
2401 	item->type = type;
2402 	item->message = e_utf8_ensure_valid (message);
2403 	item->id = priv->next_info_item_id++;
2404 
2405 	priv->lower_info_items = g_slist_append (priv->lower_info_items, item);
2406 
2407 	if (!view->priv->dom_document)
2408 		return item->id;
2409 
2410 	append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item);
2411 
2412 	return item->id;
2413 }
2414 
2415 guint
2416 itip_view_add_lower_info_item_printf (ItipView *view,
2417                                       ItipViewInfoItemType type,
2418                                       const gchar *format,
2419                                       ...)
2420 {
2421 	va_list args;
2422 	gchar *message;
2423 	guint id;
2424 
2425 	g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2426 
2427 	va_start (args, format);
2428 	message = g_strdup_vprintf (format, args);
2429 	va_end (args);
2430 
2431 	id = itip_view_add_lower_info_item (view, type, message);
2432 	g_free (message);
2433 
2434 	return id;
2435 }
2436 
2437 void
2438 itip_view_remove_lower_info_item (ItipView *view,
2439                                   guint id)
2440 {
2441 	ItipViewPrivate *priv;
2442 	GSList *l;
2443 
2444 	g_return_if_fail (ITIP_IS_VIEW (view));
2445 
2446 	priv = view->priv;
2447 
2448 	for (l = priv->lower_info_items; l; l = l->next) {
2449 		ItipViewInfoItem *item = l->data;
2450 
2451 		if (item->id == id) {
2452 			priv->lower_info_items = g_slist_remove (priv->lower_info_items, item);
2453 
2454 			g_free (item->message);
2455 			g_free (item);
2456 
2457 			if (view->priv->dom_document)
2458 				remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);
2459 
2460 			return;
2461 		}
2462 	}
2463 }
2464 
2465 void
2466 itip_view_clear_lower_info_items (ItipView *view)
2467 {
2468 	ItipViewPrivate *priv;
2469 	GSList *l;
2470 
2471 	g_return_if_fail (ITIP_IS_VIEW (view));
2472 
2473 	priv = view->priv;
2474 
2475 	for (l = priv->lower_info_items; l; l = l->next) {
2476 		ItipViewInfoItem *item = l->data;
2477 
2478 		if (view->priv->dom_document)
2479 			remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);
2480 
2481 		g_free (item->message);
2482 		g_free (item);
2483 	}
2484 
2485 	g_slist_free (priv->lower_info_items);
2486 	priv->lower_info_items = NULL;
2487 }
2488 
2489 void
2490 itip_view_set_source (ItipView *view,
2491                       ESource *source)
2492 {
2493 	WebKitDOMElement *select;
2494 	WebKitDOMElement *row;
2495 	ESource *selected_source;
2496 	gulong i, len;
2497 
2498 	g_return_if_fail (ITIP_IS_VIEW (view));
2499 
2500 	d (printf ("Settings default source '%s'\n", e_source_get_display_name (source)));
2501 
2502 	if (!view->priv->dom_document)
2503 		return;
2504 
2505 	row = webkit_dom_document_get_element_by_id (
2506 		view->priv->dom_document, TABLE_ROW_ESCB);
2507 	webkit_dom_html_element_set_hidden (
2508 		WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL));
2509 	if (source == NULL)
2510 		return;
2511 
2512 	select = webkit_dom_document_get_element_by_id (
2513 		view->priv->dom_document, SELECT_ESOURCE);
2514 
2515         /* <select> does not emit 'change' event when already selected
2516 	 * <option> is re-selected, but we need to notify itip formatter,
2517 	 * so that it would make all the buttons sensitive */
2518 	selected_source = itip_view_ref_source (view);
2519 	if (source == selected_source) {
2520 		source_changed_cb (select, NULL, view);
2521 		return;
2522 	}
2523 
2524 	if (selected_source != NULL)
2525 		g_object_unref (selected_source);
2526 
2527 	if (webkit_dom_html_select_element_get_disabled (
2528 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
2529 		webkit_dom_html_select_element_set_disabled (
2530 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
2531 	}
2532 
2533 	len = webkit_dom_html_select_element_get_length (
2534 		WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
2535 	for (i = 0; i < len; i++) {
2536 
2537 		WebKitDOMNode *node;
2538 		WebKitDOMHTMLOptionElement *option;
2539 		gchar *value;
2540 
2541 		node = webkit_dom_html_select_element_item (
2542 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i);
2543 		option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);
2544 
2545 		value = webkit_dom_html_option_element_get_value (option);
2546 		if (g_strcmp0 (value, e_source_get_uid (source)) == 0) {
2547 			webkit_dom_html_option_element_set_selected (
2548 				option, TRUE);
2549 
2550 			g_free (value);
2551 			break;
2552 		}
2553 
2554 		g_free (value);
2555 	}
2556 
2557 	source_changed_cb (select, NULL, view);
2558 }
2559 
2560 ESource *
2561 itip_view_ref_source (ItipView *view)
2562 {
2563 	ESourceRegistry *registry;
2564 	WebKitDOMElement *select;
2565 	gchar *uid;
2566 	ESource *source;
2567 	gboolean disable = FALSE;
2568 
2569 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2570 
2571 	if (!view->priv->dom_document)
2572 		return NULL;
2573 
2574 	select = webkit_dom_document_get_element_by_id (
2575 		view->priv->dom_document, SELECT_ESOURCE);
2576 	if (webkit_dom_html_select_element_get_disabled (
2577 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
2578 		webkit_dom_html_select_element_set_disabled (
2579 		WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
2580 		disable = TRUE;
2581 	}
2582 
2583 	uid = webkit_dom_html_select_element_get_value (
2584 		WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
2585 
2586 	registry = itip_view_get_registry (view);
2587 	source = e_source_registry_ref_source (registry, uid);
2588 
2589 	g_free (uid);
2590 
2591 	if (disable) {
2592 		webkit_dom_html_select_element_set_disabled (
2593 			WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE);
2594 	}
2595 
2596 	return source;
2597 }
2598 
2599 void
2600 itip_view_set_rsvp (ItipView *view,
2601                     gboolean rsvp)
2602 {
2603 	WebKitDOMElement *el;
2604 
2605 	g_return_if_fail (ITIP_IS_VIEW (view));
2606 
2607 	if (!view->priv->dom_document)
2608 		return;
2609 
2610 	el = webkit_dom_document_get_element_by_id (
2611 		view->priv->dom_document, CHECKBOX_RSVP);
2612 	webkit_dom_html_input_element_set_checked (
2613 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp);
2614 
2615 	el = webkit_dom_document_get_element_by_id (
2616 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2617 	webkit_dom_html_text_area_element_set_disabled (
2618 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
2619 }
2620 
2621 gboolean
2622 itip_view_get_rsvp (ItipView *view)
2623 {
2624 	WebKitDOMElement *el;
2625 
2626 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2627 
2628 	if (!view->priv->dom_document)
2629 		return FALSE;
2630 
2631 	el = webkit_dom_document_get_element_by_id (
2632 		view->priv->dom_document, CHECKBOX_RSVP);
2633 	return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2634 }
2635 
2636 void
2637 itip_view_set_show_rsvp_check (ItipView *view,
2638                                gboolean show)
2639 {
2640 	WebKitDOMElement *label;
2641 	WebKitDOMElement *el;
2642 
2643 	g_return_if_fail (ITIP_IS_VIEW (view));
2644 
2645 	if (!view->priv->dom_document)
2646 		return;
2647 
2648 	el = webkit_dom_document_get_element_by_id (
2649 		view->priv->dom_document, "table_row_" CHECKBOX_RSVP);
2650 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2651 
2652 	el = webkit_dom_document_get_element_by_id (
2653 		view->priv->dom_document, CHECKBOX_RSVP);
2654 	label = webkit_dom_element_get_next_element_sibling (el);
2655 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2656 
2657 	if (!show) {
2658 		webkit_dom_html_input_element_set_checked (
2659 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2660 	}
2661 
2662 	el = webkit_dom_document_get_element_by_id (
2663 		view->priv->dom_document, TABLE_ROW_RSVP_COMMENT);
2664 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2665 }
2666 
2667 gboolean
2668 itip_view_get_show_rsvp_check (ItipView *view)
2669 {
2670 	WebKitDOMElement *el;
2671 
2672 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2673 
2674 	if (!view->priv->dom_document)
2675 		return FALSE;
2676 
2677 	el = webkit_dom_document_get_element_by_id (
2678 		view->priv->dom_document, CHECKBOX_RSVP);
2679 	return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
2680 }
2681 
2682 void
2683 itip_view_set_update (ItipView *view,
2684                       gboolean update)
2685 {
2686 	WebKitDOMElement *el;
2687 
2688 	g_return_if_fail (ITIP_IS_VIEW (view));
2689 
2690 	if (!view->priv->dom_document)
2691 		return;
2692 
2693 	el = webkit_dom_document_get_element_by_id (
2694 		view->priv->dom_document, CHECKBOX_UPDATE);
2695 
2696 	webkit_dom_html_input_element_set_checked (
2697 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update);
2698 }
2699 
2700 gboolean
2701 itip_view_get_update (ItipView *view)
2702 {
2703 	WebKitDOMElement *el;
2704 
2705 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2706 
2707 	if (!view->priv->dom_document)
2708 		return FALSE;
2709 
2710 	el = webkit_dom_document_get_element_by_id (
2711 		view->priv->dom_document, CHECKBOX_UPDATE);
2712 	return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2713 }
2714 
2715 void
2716 itip_view_set_show_update_check (ItipView *view,
2717                                  gboolean show)
2718 {
2719 	WebKitDOMElement *label;
2720 	WebKitDOMElement *el;
2721 
2722 	g_return_if_fail (ITIP_IS_VIEW (view));
2723 
2724 	if (!view->priv->dom_document)
2725 		return;
2726 
2727 	el = webkit_dom_document_get_element_by_id (
2728 		view->priv->dom_document, "table_row_" CHECKBOX_UPDATE);
2729 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2730 
2731 	el = webkit_dom_document_get_element_by_id (
2732 		view->priv->dom_document, CHECKBOX_UPDATE);
2733 	label = webkit_dom_element_get_next_element_sibling (el);
2734 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2735 
2736 	if (!show) {
2737 		webkit_dom_html_input_element_set_checked (
2738 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2739 	}
2740 }
2741 
2742 gboolean
2743 itip_view_get_show_update_check (ItipView *view)
2744 {
2745 	WebKitDOMElement *el;
2746 
2747 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2748 
2749 	if (!view->priv->dom_document)
2750 		return FALSE;
2751 
2752 	el = webkit_dom_document_get_element_by_id (
2753 		view->priv->dom_document, CHECKBOX_UPDATE);
2754 	return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
2755 }
2756 
2757 void
2758 itip_view_set_rsvp_comment (ItipView *view,
2759                             const gchar *comment)
2760 {
2761 	WebKitDOMElement *el;
2762 
2763 	g_return_if_fail (ITIP_IS_VIEW (view));
2764 
2765 	if (!view->priv->dom_document)
2766 		return;
2767 
2768 	el = webkit_dom_document_get_element_by_id (
2769 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2770 	webkit_dom_html_element_set_hidden (
2771 		WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL));
2772 
2773 	if (comment) {
2774 		webkit_dom_html_text_area_element_set_value (
2775 			WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment);
2776 	}
2777 }
2778 
2779 gchar *
2780 itip_view_get_rsvp_comment (ItipView *view)
2781 {
2782 	WebKitDOMElement *el;
2783 
2784 	g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2785 
2786 	if (!view->priv->dom_document)
2787 		return NULL;
2788 
2789 	el = webkit_dom_document_get_element_by_id (
2790 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2791 
2792 	if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) {
2793 		return NULL;
2794 	}
2795 
2796 	return webkit_dom_html_text_area_element_get_value (
2797 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
2798 }
2799 
2800 void
2801 itip_view_set_needs_decline (ItipView *view,
2802                              gboolean needs_decline)
2803 {
2804 	g_return_if_fail (ITIP_IS_VIEW (view));
2805 
2806 	view->priv->needs_decline = needs_decline;
2807 }
2808 
2809 void
2810 itip_view_set_buttons_sensitive (ItipView *view,
2811                                  gboolean sensitive)
2812 {
2813 	WebKitDOMElement *el, *cell;
2814 
2815 	g_return_if_fail (ITIP_IS_VIEW (view));
2816 
2817 	d (printf ("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive"));
2818 
2819 	view->priv->buttons_sensitive = sensitive;
2820 
2821 	if (!view->priv->dom_document)
2822 		return;
2823 
2824 	el = webkit_dom_document_get_element_by_id (
2825 		view->priv->dom_document, CHECKBOX_UPDATE);
2826 	webkit_dom_html_input_element_set_disabled (
2827 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2828 
2829 	el = webkit_dom_document_get_element_by_id (
2830 		view->priv->dom_document, CHECKBOX_RECUR);
2831 	webkit_dom_html_input_element_set_disabled (
2832 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2833 
2834 	el = webkit_dom_document_get_element_by_id (
2835 		view->priv->dom_document, CHECKBOX_FREE_TIME);
2836 	webkit_dom_html_input_element_set_disabled (
2837 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2838 
2839 	el = webkit_dom_document_get_element_by_id (
2840 		view->priv->dom_document, CHECKBOX_KEEP_ALARM);
2841 	webkit_dom_html_input_element_set_disabled (
2842 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2843 
2844 	el = webkit_dom_document_get_element_by_id (
2845 		view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
2846 	webkit_dom_html_input_element_set_disabled (
2847 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2848 
2849 	el = webkit_dom_document_get_element_by_id (
2850 		view->priv->dom_document, CHECKBOX_RSVP);
2851 	webkit_dom_html_input_element_set_disabled (
2852 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2853 
2854 	el = webkit_dom_document_get_element_by_id (
2855 		view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2856 	webkit_dom_html_text_area_element_set_disabled (
2857 		WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);
2858 
2859 	el = webkit_dom_document_get_element_by_id (
2860 		view->priv->dom_document, TABLE_ROW_BUTTONS);
2861 	cell = webkit_dom_element_get_first_element_child (el);
2862 	do {
2863 		WebKitDOMElement *btn;
2864 		btn = webkit_dom_element_get_first_element_child (cell);
2865 		if (!webkit_dom_html_element_get_hidden (
2866 			WEBKIT_DOM_HTML_ELEMENT (btn))) {
2867 			webkit_dom_html_button_element_set_disabled (
2868 				WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
2869 		}
2870 	} while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
2871 }
2872 
2873 gboolean
2874 itip_view_get_buttons_sensitive (ItipView *view)
2875 {
2876 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2877 
2878 	return view->priv->buttons_sensitive;
2879 }
2880 
2881 gboolean
2882 itip_view_get_recur_check_state (ItipView *view)
2883 {
2884 	WebKitDOMElement *el;
2885 
2886 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2887 
2888 	if (!view->priv->dom_document)
2889 		return FALSE;
2890 
2891 	el = webkit_dom_document_get_element_by_id (
2892 		view->priv->dom_document, CHECKBOX_RECUR);
2893 	return webkit_dom_html_input_element_get_checked (
2894 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2895 }
2896 
2897 void
2898 itip_view_set_show_recur_check (ItipView *view,
2899                                 gboolean show)
2900 {
2901 	WebKitDOMElement *label;
2902 	WebKitDOMElement *el;
2903 
2904 	g_return_if_fail (ITIP_IS_VIEW (view));
2905 
2906 	if (!view->priv->dom_document)
2907 		return;
2908 
2909 	el = webkit_dom_document_get_element_by_id (
2910 		view->priv->dom_document, "table_row_" CHECKBOX_RECUR);
2911 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2912 
2913 	el = webkit_dom_document_get_element_by_id (
2914 		view->priv->dom_document, CHECKBOX_RECUR);
2915 	label = webkit_dom_element_get_next_element_sibling (el);
2916 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2917 
2918 	if (!show) {
2919 		webkit_dom_html_input_element_set_checked (
2920 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2921 	}
2922 
2923         /* and update state of the second check */
2924 	alarm_check_toggled_cb (
2925 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
2926 		NULL, view);
2927 }
2928 
2929 void
2930 itip_view_set_show_free_time_check (ItipView *view,
2931                                     gboolean show)
2932 {
2933 	WebKitDOMElement *label;
2934 	WebKitDOMElement *el;
2935 
2936 	g_return_if_fail (ITIP_IS_VIEW (view));
2937 
2938 	if (!view->priv->dom_document)
2939 		return;
2940 
2941 	el = webkit_dom_document_get_element_by_id (
2942 		view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME);
2943 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2944 
2945 	el = webkit_dom_document_get_element_by_id (
2946 		view->priv->dom_document, CHECKBOX_FREE_TIME);
2947 	label = webkit_dom_element_get_next_element_sibling (el);
2948 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2949 
2950 	if (!show) {
2951 		webkit_dom_html_input_element_set_checked (
2952 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2953 	}
2954 
2955         /* and update state of the second check */
2956 	alarm_check_toggled_cb (
2957 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
2958 		NULL, view);
2959 }
2960 
2961 gboolean
2962 itip_view_get_free_time_check_state (ItipView *view)
2963 {
2964 	WebKitDOMElement *el;
2965 
2966 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2967 
2968 	if (!view->priv->dom_document)
2969 		return FALSE;
2970 
2971 	el = webkit_dom_document_get_element_by_id (
2972 		view->priv->dom_document, CHECKBOX_FREE_TIME);
2973 	return webkit_dom_html_input_element_get_checked (
2974 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2975 }
2976 
2977 void
2978 itip_view_set_show_keep_alarm_check (ItipView *view,
2979                                      gboolean show)
2980 {
2981 	WebKitDOMElement *label;
2982 	WebKitDOMElement *el;
2983 
2984 	g_return_if_fail (ITIP_IS_VIEW (view));
2985 
2986 	if (!view->priv->dom_document)
2987 		return;
2988 
2989 	el = webkit_dom_document_get_element_by_id (
2990 		view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM);
2991 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2992 
2993 	el = webkit_dom_document_get_element_by_id (
2994 		view->priv->dom_document, CHECKBOX_KEEP_ALARM);
2995 	label = webkit_dom_element_get_next_element_sibling (el);
2996 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2997 
2998 	if (!show) {
2999 		webkit_dom_html_input_element_set_checked (
3000 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
3001 	}
3002 
3003         /* and update state of the second check */
3004 	alarm_check_toggled_cb (
3005 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
3006 		NULL, view);
3007 }
3008 
3009 gboolean
3010 itip_view_get_keep_alarm_check_state (ItipView *view)
3011 {
3012 	WebKitDOMElement *el;
3013 
3014 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
3015 
3016 	if (!view->priv->dom_document)
3017 		return FALSE;
3018 
3019 	el = webkit_dom_document_get_element_by_id (
3020 		view->priv->dom_document, CHECKBOX_KEEP_ALARM);
3021 	return webkit_dom_html_input_element_get_checked (
3022 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
3023 }
3024 
3025 void
3026 itip_view_set_show_inherit_alarm_check (ItipView *view,
3027                                         gboolean show)
3028 {
3029 	WebKitDOMElement *label;
3030 	WebKitDOMElement *el;
3031 
3032 	g_return_if_fail (ITIP_IS_VIEW (view));
3033 
3034 	if (!view->priv->dom_document)
3035 		return;
3036 
3037 	el = webkit_dom_document_get_element_by_id (
3038 		view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM);
3039 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
3040 
3041 	el = webkit_dom_document_get_element_by_id (
3042 		view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
3043 	label = webkit_dom_element_get_next_element_sibling (el);
3044 	webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
3045 
3046 	if (!show) {
3047 		webkit_dom_html_input_element_set_checked (
3048 			WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
3049 	}
3050 
3051 	/* and update state of the second check */
3052 	alarm_check_toggled_cb (
3053 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
3054 		NULL, view);
3055 }
3056 
3057 gboolean
3058 itip_view_get_inherit_alarm_check_state (ItipView *view)
3059 {
3060 	WebKitDOMElement *el;
3061 
3062 	g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
3063 
3064 	if (!view->priv->dom_document)
3065 		return FALSE;
3066 
3067 	el = webkit_dom_document_get_element_by_id (
3068 		view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
3069 	return webkit_dom_html_input_element_get_checked (
3070 		WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
3071 }
3072 
3073 void
3074 itip_view_set_error (ItipView *view,
3075                      const gchar *error_html,
3076                      gboolean show_save_btn)
3077 {
3078 	WebKitDOMElement *content, *error;
3079 	GString *str;
3080 
3081 	g_return_if_fail (ITIP_IS_VIEW (view));
3082 	g_return_if_fail (error_html);
3083 
3084 	str = g_string_new (error_html);
3085 
3086 	if (show_save_btn) {
3087 		g_string_append (
3088 			str,
3089 			"<table border=\"0\" width=\"100%\">"
3090 			"<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">");
3091 
3092 		buttons_table_write_button (
3093 			str, BUTTON_SAVE, _("_Save"),
3094 			GTK_STOCK_SAVE, ITIP_VIEW_RESPONSE_SAVE);
3095 
3096 		g_string_append (str, "</tr></table>");
3097 	}
3098 
3099 	view->priv->error = str->str;
3100 	g_string_free (str, FALSE);
3101 
3102 	if (!view->priv->dom_document)
3103 		return;
3104 
3105 	content = webkit_dom_document_get_element_by_id (
3106 		view->priv->dom_document, DIV_ITIP_CONTENT);
3107 	webkit_dom_html_element_set_hidden (
3108 		WEBKIT_DOM_HTML_ELEMENT (content), TRUE);
3109 
3110 	error = webkit_dom_document_get_element_by_id (
3111 		view->priv->dom_document, DIV_ITIP_ERROR);
3112 	webkit_dom_html_element_set_hidden (
3113 		WEBKIT_DOM_HTML_ELEMENT (error), FALSE);
3114 
3115 	webkit_dom_html_element_set_inner_html (
3116 		WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL);
3117 
3118 	if (show_save_btn) {
3119 		WebKitDOMElement *el;
3120 
3121 		show_button (view, BUTTON_SAVE);
3122 
3123 		el = webkit_dom_document_get_element_by_id (
3124 			view->priv->dom_document, BUTTON_SAVE);
3125 		webkit_dom_html_button_element_set_disabled (
3126 			WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
3127 		webkit_dom_event_target_add_event_listener (
3128 			WEBKIT_DOM_EVENT_TARGET (el), "click",
3129 			G_CALLBACK (button_clicked_cb), FALSE, view);
3130 	}
3131 }
3132 
3133 /******************************************************************************/
3134 
3135 typedef struct {
3136 	EMailPartItip *puri;
3137         ItipView *view;
3138 	GCancellable *itip_cancellable;
3139 	GCancellable *cancellable;
3140 	gulong cancelled_id;
3141 	gboolean keep_alarm_check;
3142 	GHashTable *conflicts;
3143 
3144 	gchar *uid;
3145 	gchar *rid;
3146 
3147 	gchar *sexp;
3148 
3149 	gint count;
3150 } FormatItipFindData;
3151 
3152 static gboolean check_is_instance (icalcomponent *icalcomp);
3153 
3154 static icalproperty *
3155 find_attendee (icalcomponent *ical_comp,
3156                const gchar *address)
3157 {
3158 	icalproperty *prop;
3159 
3160 	if (address == NULL)
3161 		return NULL;
3162 
3163 	for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
3164 	     prop != NULL;
3165 	     prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
3166 		gchar *attendee;
3167 		gchar *text;
3168 
3169 		attendee = icalproperty_get_value_as_string_r (prop);
3170 
3171 		 if (!attendee)
3172 			continue;
3173 
3174 		text = g_strdup (itip_strip_mailto (attendee));
3175 		text = g_strstrip (text);
3176 		if (text && !g_ascii_strcasecmp (address, text)) {
3177 			g_free (text);
3178 			g_free (attendee);
3179 			break;
3180 		}
3181 		g_free (text);
3182 		g_free (attendee);
3183 	}
3184 
3185 	return prop;
3186 }
3187 
3188 static icalproperty *
3189 find_attendee_if_sentby (icalcomponent *ical_comp,
3190                          const gchar *address)
3191 {
3192 	icalproperty *prop;
3193 
3194 	if (address == NULL)
3195 		return NULL;
3196 
3197 	for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
3198 	     prop != NULL;
3199 	     prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
3200 		icalparameter *param;
3201 		const gchar *attendee_sentby;
3202 		gchar *text;
3203 
3204 		param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
3205 		if (!param)
3206 			continue;
3207 
3208 		attendee_sentby = icalparameter_get_sentby (param);
3209 
3210 		if (!attendee_sentby)
3211 			continue;
3212 
3213 		text = g_strdup (itip_strip_mailto (attendee_sentby));
3214 		text = g_strstrip (text);
3215 		if (text && !g_ascii_strcasecmp (address, text)) {
3216 			g_free (text);
3217 			break;
3218 		}
3219 		g_free (text);
3220 	}
3221 
3222 	return prop;
3223 }
3224 
3225 static void
3226 find_to_address (EMailPartItip *itip_part,
3227                  icalcomponent *ical_comp,
3228                  icalparameter_partstat *status)
3229 {
3230 	ESourceRegistry *registry;
3231 	ESourceMailIdentity *extension;
3232 	GList *list, *link;
3233 	const gchar *extension_name;
3234 
3235 	registry = itip_part->registry;
3236 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3237 
3238 	if (itip_part->to_address != NULL)
3239 		return;
3240 
3241 	if (itip_part->msg != NULL && itip_part->folder != NULL) {
3242 		ESource *source;
3243 
3244 		source = em_utils_guess_mail_identity (
3245 			registry, itip_part->msg, itip_part->folder, itip_part->uid);
3246 
3247 		if (source != NULL) {
3248 			extension = e_source_get_extension (source, extension_name);
3249 
3250 			itip_part->to_address = e_source_mail_identity_dup_address (extension);
3251 
3252 			g_object_unref (source);
3253 		}
3254 	}
3255 
3256 	if (itip_part->to_address != NULL)
3257 		return;
3258 
3259 	/* Look through the list of attendees to find the user's address */
3260 	list = e_source_registry_list_sources (registry, extension_name);
3261 
3262 	for (link = list; link != NULL; link = g_list_next (link)) {
3263 		ESource *source = E_SOURCE (link->data);
3264 		icalproperty *prop = NULL;
3265 		icalparameter *param;
3266 		const gchar *address;
3267 		gchar *text;
3268 
3269 		if (!e_source_get_enabled (source))
3270 			continue;
3271 
3272 		extension = e_source_get_extension (source, extension_name);
3273 		address = e_source_mail_identity_get_address (extension);
3274 
3275 		prop = find_attendee (ical_comp, address);
3276 		if (prop == NULL)
3277 			continue;
3278 
3279 		param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3280 		if (param != NULL)
3281 			itip_part->to_name = g_strdup (icalparameter_get_cn (param));
3282 
3283 		text = icalproperty_get_value_as_string_r (prop);
3284 
3285 		itip_part->to_address = g_strdup (itip_strip_mailto (text));
3286 		g_free (text);
3287 		g_strstrip (itip_part->to_address);
3288 
3289 		itip_part->my_address = g_strdup (address);
3290 
3291 		param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
3292 		if (param != NULL &&
3293 		    icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE)
3294 			itip_part->no_reply_wanted = TRUE;
3295 
3296 		if (status) {
3297 			param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
3298 			*status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
3299 		}
3300 
3301 		break;
3302 	}
3303 
3304 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3305 
3306 	if (itip_part->to_address != NULL)
3307 		return;
3308 
3309 	/* If the user's address was not found in the attendee's list,
3310 	 * then the user might be responding on behalf of his/her delegator.
3311 	 * In this case, we would want to go through the SENT-BY fields of
3312 	 * the attendees to find the user's address.
3313  	 *
3314  *
3315 	 * Note: This functionality could have been (easily) implemented
3316 	 * in the previous loop, but it would hurt the performance for all
3317 	 * providers in general. Hence, we choose to iterate through the
3318 	 * accounts list again.
3319  	 */
3320 
3321 	list = e_source_registry_list_sources (registry, extension_name);
3322 
3323 	for (link = list; link != NULL; link = g_list_next (link)) {
3324 		ESource *source = E_SOURCE (link->data);
3325 		icalproperty *prop = NULL;
3326 		icalparameter *param;
3327 		const gchar *address;
3328 		gchar *text;
3329 
3330 		if (!e_source_get_enabled (source))
3331 			continue;
3332 
3333 		extension = e_source_get_extension (source, extension_name);
3334 		address = e_source_mail_identity_get_address (extension);
3335 
3336 		prop = find_attendee_if_sentby (ical_comp, address);
3337 		if (prop == NULL)
3338 			continue;
3339 
3340 		param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3341 		if (param != NULL)
3342 			itip_part->to_name = g_strdup (icalparameter_get_cn (param));
3343 
3344 		text = icalproperty_get_value_as_string_r (prop);
3345 
3346 		itip_part->to_address = g_strdup (itip_strip_mailto (text));
3347 		g_free (text);
3348 		g_strstrip (itip_part->to_address);
3349 
3350 		itip_part->my_address = g_strdup (address);
3351 
3352 		param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
3353 		if (param != NULL &&
3354 		    ICAL_RSVP_FALSE == icalparameter_get_rsvp (param))
3355 			itip_part->no_reply_wanted = TRUE;
3356 
3357 		if (status) {
3358 			param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
3359 			*status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
3360 		}
3361 
3362 		break;
3363 	}
3364 
3365 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3366 }
3367 
3368 static void
3369 find_from_address (EMailPartItip *pitip,
3370                    icalcomponent *ical_comp)
3371 {
3372 	GList *list, *link;
3373 	icalproperty *prop;
3374 	gchar *organizer;
3375 	icalparameter *param;
3376 	const gchar *extension_name;
3377 	const gchar *organizer_sentby;
3378 	gchar *organizer_clean = NULL;
3379 	gchar *organizer_sentby_clean = NULL;
3380 
3381 	prop = icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY);
3382 
3383 	if (!prop)
3384 		return;
3385 
3386 	organizer = icalproperty_get_value_as_string_r (prop);
3387 	if (organizer) {
3388 		organizer_clean = g_strdup (itip_strip_mailto (organizer));
3389 		organizer_clean = g_strstrip (organizer_clean);
3390 		g_free (organizer);
3391 	}
3392 
3393 	param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
3394 	if (param) {
3395 		organizer_sentby = icalparameter_get_sentby (param);
3396 		if (organizer_sentby) {
3397 			organizer_sentby_clean = g_strdup (itip_strip_mailto (organizer_sentby));
3398 			organizer_sentby_clean = g_strstrip (organizer_sentby_clean);
3399 		}
3400 	}
3401 
3402 	if (!(organizer_sentby_clean || organizer_clean))
3403 		return;
3404 
3405 	pitip->from_address = g_strdup (organizer_clean);
3406 
3407 	param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3408 	if (param)
3409 		pitip->from_name = g_strdup (icalparameter_get_cn (param));
3410 
3411 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3412 	list = e_source_registry_list_sources (pitip->registry, extension_name);
3413 
3414 	for (link = list; link != NULL; link = g_list_next (link)) {
3415 		ESource *source = E_SOURCE (link->data);
3416 		ESourceMailIdentity *extension;
3417 		const gchar *address;
3418 
3419 		if (!e_source_get_enabled (source))
3420 			continue;
3421 
3422 		extension = e_source_get_extension (source, extension_name);
3423 		address = e_source_mail_identity_get_address (extension);
3424 
3425 		if (address == NULL)
3426 			continue;
3427 
3428 		if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address))
3429 		    || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) {
3430 			pitip->my_address = g_strdup (address);
3431 
3432 			break;
3433 		}
3434 	}
3435 
3436 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
3437 
3438 	g_free (organizer_sentby_clean);
3439 	g_free (organizer_clean);
3440 }
3441 
3442 static ECalComponent *
3443 get_real_item (EMailPartItip *pitip)
3444 {
3445 	ECalComponent *comp = NULL;
3446 	ESource *source;
3447 
3448 	source = e_client_get_source (E_CLIENT (pitip->current_client));
3449 	if (source)
3450 		comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source));
3451 
3452 	if (!comp) {
3453 		return NULL;
3454 	}
3455 
3456 	return e_cal_component_clone (comp);
3457 }
3458 
3459 static void
3460 adjust_item (EMailPartItip *pitip,
3461              ECalComponent *comp)
3462 {
3463 	ECalComponent *real_comp;
3464 
3465 	real_comp = get_real_item (pitip);
3466 	if (real_comp != NULL) {
3467 		ECalComponentText text;
3468 		const gchar *string;
3469 		GSList *l;
3470 
3471 		e_cal_component_get_summary (real_comp, &text);
3472 		e_cal_component_set_summary (comp, &text);
3473 		e_cal_component_get_location (real_comp, &string);
3474 		e_cal_component_set_location (comp, string);
3475 		e_cal_component_get_description_list (real_comp, &l);
3476 		e_cal_component_set_description_list (comp, l);
3477 		e_cal_component_free_text_list (l);
3478 
3479 		g_object_unref (real_comp);
3480 	} else {
3481 		ECalComponentText text = {_("Unknown"), NULL};
3482 
3483 		e_cal_component_set_summary (comp, &text);
3484 	}
3485 }
3486 
3487 static void
3488 set_buttons_sensitive (EMailPartItip *pitip,
3489                        ItipView *view)
3490 {
3491 	gboolean read_only = TRUE;
3492 
3493 	if (pitip->current_client)
3494 		read_only = e_client_is_readonly (E_CLIENT (pitip->current_client));
3495 
3496 	itip_view_set_buttons_sensitive (view, pitip->current_client != NULL && !read_only);
3497 }
3498 
3499 static void
3500 add_failed_to_load_msg (ItipView *view,
3501                         ESource *source,
3502                         const GError *error)
3503 {
3504 	gchar *msg;
3505 
3506 	g_return_if_fail (view != NULL);
3507 	g_return_if_fail (source != NULL);
3508 	g_return_if_fail (error != NULL);
3509 
3510 	/* Translators: The first '%s' is replaced with a calendar name,
3511 	 * the second '%s' with an error message */
3512 	msg = g_strdup_printf (_("Failed to load the calendar '%s' (%s)"), e_source_get_display_name (source), error->message);
3513 
3514 	itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, msg);
3515 
3516 	g_free (msg);
3517 }
3518 
3519 static void
3520 cal_opened_cb (GObject *source_object,
3521                GAsyncResult *result,
3522                gpointer user_data)
3523 {
3524 	ESource *source = E_SOURCE (source_object);
3525 	ItipView *view = user_data;
3526 	EMailPartItip *pitip = itip_view_get_mail_part (view);
3527 	ECalClientSourceType source_type;
3528 	EClient *client = NULL;
3529 	ECalClient *cal_client;
3530 	const gchar *uid;
3531 	GError *error = NULL;
3532 
3533 	e_client_utils_open_new_finish (source, result, &client, &error);
3534 
3535 	/* Ignore cancellations. */
3536 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3537 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3538 		g_warn_if_fail (client == NULL);
3539 		g_error_free (error);
3540 		return;
3541 
3542 	} else if (error != NULL) {
3543 		g_warn_if_fail (client == NULL);
3544 		add_failed_to_load_msg (view, source, error);
3545 		g_error_free (error);
3546 		return;
3547 	}
3548 
3549 	g_return_if_fail (E_IS_CAL_CLIENT (client));
3550 
3551 	cal_client = E_CAL_CLIENT (client);
3552 	g_return_if_fail (cal_client != NULL);
3553 
3554 	uid = e_source_get_uid (source);
3555 	source_type = e_cal_client_get_source_type (cal_client);
3556 	g_hash_table_insert (
3557 		pitip->clients[source_type], g_strdup (uid), cal_client);
3558 
3559 	if (e_cal_client_check_recurrences_no_master (cal_client)) {
3560 		icalcomponent *icalcomp;
3561 		gboolean show_recur_check;
3562 
3563 		icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
3564 
3565 		show_recur_check = check_is_instance (icalcomp);
3566 		itip_view_set_show_recur_check (view, show_recur_check);
3567 	}
3568 
3569 	if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
3570 		gboolean needs_decline;
3571 
3572 		needs_decline = e_client_check_capability (
3573 			E_CLIENT (client),
3574 			CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING);
3575 		itip_view_set_needs_decline (view, needs_decline);
3576 		itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
3577 	}
3578 
3579 	pitip->current_client = cal_client;
3580 
3581 	set_buttons_sensitive (pitip, view);
3582 }
3583 
3584 static void
3585 start_calendar_server (EMailPartItip *pitip,
3586                        ItipView *view,
3587                        ESource *source,
3588                        ECalClientSourceType type,
3589                        GAsyncReadyCallback func,
3590                        gpointer data)
3591 {
3592 	ECalClient *client;
3593 
3594 	g_return_if_fail (source != NULL);
3595 
3596 	client = g_hash_table_lookup (pitip->clients[type], e_source_get_uid (source));
3597 	if (client) {
3598 		pitip->current_client = client;
3599 
3600 		itip_view_remove_lower_info_item (view, pitip->progress_info_id);
3601 		pitip->progress_info_id = 0;
3602 
3603 		set_buttons_sensitive (pitip, view);
3604 
3605 		return;
3606 	}
3607 
3608 	e_client_utils_open_new (
3609 		source,
3610 		type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? E_CLIENT_SOURCE_TYPE_EVENTS :
3611 		type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS ? E_CLIENT_SOURCE_TYPE_MEMOS :
3612 		type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ? E_CLIENT_SOURCE_TYPE_TASKS : E_CLIENT_SOURCE_TYPE_LAST,
3613 		TRUE, pitip->cancellable,
3614 		func, data);
3615 }
3616 
3617 static void
3618 start_calendar_server_by_uid (EMailPartItip *pitip,
3619                               ItipView *view,
3620                               const gchar *uid,
3621                               ECalClientSourceType type)
3622 {
3623 	ESource *source;
3624 
3625 	itip_view_set_buttons_sensitive (view, FALSE);
3626 
3627 	source = e_source_registry_ref_source (pitip->registry, uid);
3628 	if (source != NULL) {
3629 		start_calendar_server (
3630 			pitip, view, source, type, cal_opened_cb, view);
3631 		g_object_unref (source);
3632 	}
3633 }
3634 
3635 static void
3636 source_selected_cb (ItipView *view,
3637                     ESource *source,
3638                     gpointer data)
3639 {
3640 	EMailPartItip *pitip = data;
3641 
3642 	itip_view_set_buttons_sensitive (view, FALSE);
3643 
3644 	g_return_if_fail (source != NULL);
3645 
3646 	start_calendar_server (pitip, view, source, pitip->type, cal_opened_cb, view);
3647 }
3648 
3649 static void
3650 find_cal_update_ui (FormatItipFindData *fd,
3651                     ECalClient *cal_client)
3652 {
3653 	EMailPartItip *pitip;
3654 	ItipView *view;
3655 	ESource *source;
3656 
3657 	g_return_if_fail (fd != NULL);
3658 
3659 	pitip = fd->puri;
3660 	view = fd->view;
3661 
3662 	/* UI part gone */
3663 	if (g_cancellable_is_cancelled (fd->cancellable))
3664 		return;
3665 
3666 	source = cal_client ? e_client_get_source (E_CLIENT (cal_client)) : NULL;
3667 
3668 	if (cal_client && g_hash_table_lookup (fd->conflicts, cal_client)) {
3669 		itip_view_add_upper_info_item_printf (
3670 			view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3671 			_("An appointment in the calendar "
3672 			"'%s' conflicts with this meeting"),
3673 			e_source_get_display_name (source));
3674 	}
3675 
3676 	/* search for a master object if the detached object doesn't exist in the calendar */
3677 	if (pitip->current_client && pitip->current_client == cal_client) {
3678 		gboolean rsvp_enabled = FALSE;
3679 
3680 		itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check);
3681 
3682 		pitip->current_client = cal_client;
3683 
3684 		/* Provide extra info, since its not in the component */
3685 		/* FIXME Check sequence number of meeting? */
3686 		/* FIXME Do we need to adjust elsewhere for the delegated calendar item? */
3687 		/* FIXME Need to update the fields in the view now */
3688 		if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH)
3689 			adjust_item (pitip, pitip->comp);
3690 
3691 		/* We clear everything because we don't really care
3692 		 * about any other info/warnings now we found an
3693 		 * existing versions */
3694 		itip_view_clear_lower_info_items (view);
3695 		pitip->progress_info_id = 0;
3696 
3697 		/* FIXME Check read only state of calendar? */
3698 		itip_view_add_lower_info_item_printf (
3699 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
3700 			_("Found the appointment in the calendar '%s'"), e_source_get_display_name (source));
3701 
3702 		/*
3703 		 * Only allow replies if backend doesn't do that automatically.
3704 		 * Only enable it for forwarded invitiations (PUBLISH) or direct
3705 		 * invitiations (REQUEST), but not replies (REPLY).
3706 		 * Replies only make sense for events with an organizer.
3707 		 */
3708 		if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
3709 		    (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) &&
3710 		    pitip->has_organizer) {
3711 			rsvp_enabled = TRUE;
3712 		}
3713 		itip_view_set_show_rsvp_check (view, rsvp_enabled);
3714 
3715 		/* default is chosen in extract_itip_data() based on content of the VEVENT */
3716 		itip_view_set_rsvp (view, !pitip->no_reply_wanted);
3717 
3718 		set_buttons_sensitive (pitip, view);
3719 
3720 		g_cancellable_cancel (fd->cancellable);
3721 	} else if (!pitip->current_client)
3722 		itip_view_set_show_keep_alarm_check (view, FALSE);
3723 
3724 	if (pitip->current_client && pitip->current_client == cal_client) {
3725 		if (e_cal_client_check_recurrences_no_master (pitip->current_client)) {
3726 			icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
3727 
3728 			if (check_is_instance (icalcomp))
3729 				itip_view_set_show_recur_check (view, TRUE);
3730 			else
3731 				itip_view_set_show_recur_check (view, FALSE);
3732 		}
3733 
3734 		if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
3735 			/* TODO The static capability should be made generic to convey that the calendar contains unaccepted items */
3736 			if (e_client_check_capability (E_CLIENT (pitip->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
3737 				itip_view_set_needs_decline (view, TRUE);
3738 			else
3739 				itip_view_set_needs_decline (view, FALSE);
3740 
3741 			itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
3742 		}
3743 	}
3744 }
3745 
3746 static void
3747 decrease_find_data (FormatItipFindData *fd)
3748 {
3749 	g_return_if_fail (fd != NULL);
3750 
3751 	fd->count--;
3752 	d (printf ("Decreasing itip formatter search count to %d\n", fd->count));
3753 
3754 	if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) {
3755 		gboolean rsvp_enabled = FALSE;
3756 		EMailPartItip *pitip = fd->puri;
3757 		ItipView *view = fd->view;
3758 
3759 		itip_view_remove_lower_info_item (view, pitip->progress_info_id);
3760 		pitip->progress_info_id = 0;
3761 
3762 		/*
3763 		 * Only allow replies if backend doesn't do that automatically.
3764 		 * Only enable it for forwarded invitiations (PUBLISH) or direct
3765 		 * invitiations (REQUEST), but not replies (REPLY).
3766 		 * Replies only make sense for events with an organizer.
3767 		 */
3768 		if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
3769 		    (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) &&
3770 		    pitip->has_organizer) {
3771 			rsvp_enabled = TRUE;
3772 		}
3773 		itip_view_set_show_rsvp_check (view, rsvp_enabled);
3774 
3775 		/* default is chosen in extract_itip_data() based on content of the VEVENT */
3776 		itip_view_set_rsvp (view, !pitip->no_reply_wanted);
3777 
3778 		if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST)
3779 		    && !pitip->current_client) {
3780 			/* Reuse already declared one or rename? */
3781 			ESource *source = NULL;
3782 			const gchar *extension_name;
3783 
3784 			switch (pitip->type) {
3785 				case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3786 					extension_name = E_SOURCE_EXTENSION_CALENDAR;
3787 					break;
3788 				case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3789 					extension_name = E_SOURCE_EXTENSION_TASK_LIST;
3790 					break;
3791 				case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3792 					extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
3793 					break;
3794 				default:
3795 					g_return_if_reached ();
3796 			}
3797 
3798 			source = e_source_registry_ref_default_for_extension_name (
3799 				pitip->registry, extension_name);
3800 
3801 			itip_view_set_extension_name (view, extension_name);
3802 
3803 			g_signal_connect (
3804 				view, "source_selected",
3805 				G_CALLBACK (source_selected_cb), pitip);
3806 
3807 			if (source != NULL) {
3808 				itip_view_set_source (view, source);
3809 				g_object_unref (source);
3810 
3811  				/* FIXME Shouldn't the buttons be sensitized here? */
3812 			} else {
3813 				itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to find any calendars"));
3814 				itip_view_set_buttons_sensitive (view, FALSE);
3815 			}
3816 		} else if (!pitip->current_client) {
3817 			switch (pitip->type) {
3818 			case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3819 				itip_view_add_lower_info_item_printf (
3820 					view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3821 					_("Unable to find this meeting in any calendar"));
3822 				break;
3823 			case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3824 				itip_view_add_lower_info_item_printf (
3825 					view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3826 					_("Unable to find this task in any task list"));
3827 				break;
3828 			case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3829 				itip_view_add_lower_info_item_printf (
3830 					view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3831 					_("Unable to find this memo in any memo list"));
3832 				break;
3833 			default:
3834 				g_assert_not_reached ();
3835 				break;
3836 			}
3837 		}
3838 	}
3839 
3840 	if (fd->count == 0) {
3841 		g_hash_table_destroy (fd->conflicts);
3842 		g_cancellable_disconnect (fd->itip_cancellable, fd->cancelled_id);
3843 		g_object_unref (fd->cancellable);
3844 		g_object_unref (fd->itip_cancellable);
3845 		g_object_unref (fd->view);
3846 		g_free (fd->uid);
3847 		g_free (fd->rid);
3848 		if (fd->sexp)
3849 			g_free (fd->sexp);
3850 		g_free (fd);
3851 	}
3852 }
3853 
3854 static void
3855 get_object_without_rid_ready_cb (GObject *source_object,
3856                                  GAsyncResult *result,
3857                                  gpointer user_data)
3858 {
3859 	ECalClient *cal_client = E_CAL_CLIENT (source_object);
3860 	FormatItipFindData *fd = user_data;
3861 	icalcomponent *icalcomp = NULL;
3862 	GError *error = NULL;
3863 
3864 	if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
3865 		icalcomp = NULL;
3866 
3867 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3868 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
3869 	    g_cancellable_is_cancelled (fd->cancellable)) {
3870 		g_clear_error (&error);
3871 		find_cal_update_ui (fd, cal_client);
3872 		decrease_find_data (fd);
3873 		return;
3874 	}
3875 
3876 	g_clear_error (&error);
3877 
3878 	if (icalcomp) {
3879 		ECalComponent *comp;
3880 
3881 		fd->puri->current_client = cal_client;
3882 		fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method ==  ICAL_METHOD_REQUEST) &&
3883 			(icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
3884 			icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
3885 			icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
3886 			icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
3887 			icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
3888 
3889 		comp = e_cal_component_new_from_icalcomponent (icalcomp);
3890 		if (comp) {
3891 			ESource *source = e_client_get_source (E_CLIENT (cal_client));
3892 
3893 			g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
3894 		}
3895 
3896 		find_cal_update_ui (fd, cal_client);
3897 		decrease_find_data (fd);
3898 		return;
3899 	}
3900 
3901 	find_cal_update_ui (fd, cal_client);
3902 	decrease_find_data (fd);
3903 }
3904 
3905 static void
3906 get_object_with_rid_ready_cb (GObject *source_object,
3907                               GAsyncResult *result,
3908                               gpointer user_data)
3909 {
3910 	ECalClient *cal_client = E_CAL_CLIENT (source_object);
3911 	FormatItipFindData *fd = user_data;
3912 	icalcomponent *icalcomp = NULL;
3913 	GError *error = NULL;
3914 
3915 	if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
3916 		icalcomp = NULL;
3917 
3918 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3919 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
3920 	    g_cancellable_is_cancelled (fd->cancellable)) {
3921 		g_clear_error (&error);
3922 		find_cal_update_ui (fd, cal_client);
3923 		decrease_find_data (fd);
3924 		return;
3925 	}
3926 
3927 	g_clear_error (&error);
3928 
3929 	if (icalcomp) {
3930 		ECalComponent *comp;
3931 
3932 		fd->puri->current_client = cal_client;
3933 		fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method ==  ICAL_METHOD_REQUEST) &&
3934 			(icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
3935 			icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
3936 			icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
3937 			icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
3938 			icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
3939 
3940 		comp = e_cal_component_new_from_icalcomponent (icalcomp);
3941 		if (comp) {
3942 			ESource *source = e_client_get_source (E_CLIENT (cal_client));
3943 
3944 			g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
3945 		}
3946 
3947 		find_cal_update_ui (fd, cal_client);
3948 		decrease_find_data (fd);
3949 		return;
3950 	}
3951 
3952 	if (fd->rid && *fd->rid) {
3953 		e_cal_client_get_object (cal_client, fd->uid, NULL, fd->cancellable, get_object_without_rid_ready_cb, fd);
3954 		return;
3955 	}
3956 
3957 	find_cal_update_ui (fd, cal_client);
3958 	decrease_find_data (fd);
3959 }
3960 
3961 static void
3962 get_object_list_ready_cb (GObject *source_object,
3963                           GAsyncResult *result,
3964                           gpointer user_data)
3965 {
3966 	ECalClient *cal_client = E_CAL_CLIENT (source_object);
3967 	FormatItipFindData *fd = user_data;
3968 	GSList *objects = NULL;
3969 	GError *error = NULL;
3970 
3971 	if (!e_cal_client_get_object_list_finish (cal_client, result, &objects, &error))
3972 		objects = NULL;
3973 
3974 	if (g_cancellable_is_cancelled (fd->cancellable)) {
3975 		g_clear_error (&error);
3976 		decrease_find_data (fd);
3977 		return;
3978 	}
3979 
3980 	if (error) {
3981 		if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3982 		    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3983 			g_error_free (error);
3984 			decrease_find_data (fd);
3985 			return;
3986 		}
3987 
3988 		g_error_free (error);
3989 	} else {
3990 		g_hash_table_insert (fd->conflicts, cal_client, GINT_TO_POINTER (g_slist_length (objects)));
3991 		e_cal_client_free_icalcomp_slist (objects);
3992 	}
3993 
3994 	e_cal_client_get_object (cal_client, fd->uid, fd->rid, fd->cancellable, get_object_with_rid_ready_cb, fd);
3995 }
3996 
3997 static void
3998 find_cal_opened_cb (GObject *source_object,
3999                     GAsyncResult *result,
4000                     gpointer user_data)
4001 {
4002 	ESource *source = E_SOURCE (source_object);
4003 	FormatItipFindData *fd = user_data;
4004 	EMailPartItip *pitip = fd->puri;
4005 	ItipView *view = fd->view;
4006 	ECalClientSourceType source_type;
4007 	EClient *client = NULL;
4008 	ECalClient *cal_client;
4009 	gboolean search_for_conflicts = FALSE;
4010 	const gchar *extension_name;
4011 	const gchar *uid;
4012 	GError *error = NULL;
4013 
4014 	e_client_utils_open_new_finish (source, result, &client, &error);
4015 
4016 	/* Ignore cancellations. */
4017 	if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
4018 	    g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
4019 		g_warn_if_fail (client == NULL);
4020 		decrease_find_data (fd);
4021 		g_error_free (error);
4022 		return;
4023 	}
4024 
4025 	if (g_cancellable_is_cancelled (fd->cancellable)) {
4026 		g_clear_error (&error);
4027 		decrease_find_data (fd);
4028 		return;
4029 	}
4030 
4031 	if (error) {
4032 		/* FIXME Do we really want to warn here?  If we fail
4033 		 * to find the item, this won't be cleared but the
4034 		 * selector might be shown */
4035 		g_warn_if_fail (client == NULL);
4036 		add_failed_to_load_msg (view, source, error);
4037 		decrease_find_data (fd);
4038 		g_error_free (error);
4039 		return;
4040 	}
4041 
4042 	g_return_if_fail (E_IS_CAL_CLIENT (client));
4043 
4044 	/* Do not process read-only calendars */
4045 	if (e_client_is_readonly (client)) {
4046 		g_object_unref (client);
4047 		decrease_find_data (fd);
4048 		return;
4049 	}
4050 
4051 	cal_client = E_CAL_CLIENT (client);
4052 	source_type = e_cal_client_get_source_type (cal_client);
4053 
4054 	uid = e_source_get_uid (source);
4055 	g_hash_table_insert (
4056 		pitip->clients[source_type], g_strdup (uid), cal_client);
4057 
4058 	extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
4059 	if (e_source_has_extension (source, extension_name)) {
4060 		ESourceConflictSearch *extension;
4061 
4062 		extension = e_source_get_extension (source, extension_name);
4063 		search_for_conflicts =
4064 			(pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
4065 			e_source_conflict_search_get_include_me (extension);
4066 	}
4067 
4068  	/* Check for conflicts */
4069  	/* If the query fails, we'll just ignore it */
4070  	/* FIXME What happens for recurring conflicts? */
4071 	if (search_for_conflicts) {
4072 		e_cal_client_get_object_list (
4073 			cal_client, fd->sexp,
4074 			fd->cancellable,
4075 			get_object_list_ready_cb, fd);
4076 		return;
4077 	}
4078 
4079 	if (!pitip->current_client) {
4080 		e_cal_client_get_object (
4081 			cal_client, fd->uid, fd->rid,
4082 			fd->cancellable,
4083 			get_object_with_rid_ready_cb, fd);
4084 		return;
4085 	}
4086 
4087 	decrease_find_data (fd);
4088 }
4089 
4090 static void
4091 itip_cancellable_cancelled (GCancellable *itip_cancellable,
4092                             GCancellable *fd_cancellable)
4093 {
4094 	g_cancellable_cancel (fd_cancellable);
4095 }
4096 
4097 static void
4098 find_server (EMailPartItip *pitip,
4099              ItipView *view,
4100              ECalComponent *comp)
4101 {
4102 	FormatItipFindData *fd = NULL;
4103 	const gchar *uid;
4104 	gchar *rid = NULL;
4105 	CamelStore *parent_store;
4106 	ESourceRegistry *registry;
4107 	ESource *current_source = NULL;
4108 	GList *list, *link;
4109 	GList *conflict_list = NULL;
4110 	const gchar *extension_name;
4111 	const gchar *store_uid;
4112 
4113 	g_return_if_fail (pitip->folder != NULL);
4114 
4115 	switch (pitip->type) {
4116 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
4117 			extension_name = E_SOURCE_EXTENSION_CALENDAR;
4118 			break;
4119 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
4120 			extension_name = E_SOURCE_EXTENSION_TASK_LIST;
4121 			break;
4122 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
4123 			extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
4124 			break;
4125 		default:
4126 			g_return_if_reached ();
4127 	}
4128 
4129 	registry = pitip->registry;
4130 	list = e_source_registry_list_sources (registry, extension_name);
4131 
4132 	e_cal_component_get_uid (comp, &uid);
4133 	rid = e_cal_component_get_recurid_as_string (comp);
4134 
4135 	/* XXX Not sure what this was trying to do,
4136 	 *     but it propbably doesn't work anymore.
4137 	 *     Some comments would have been helpful. */
4138 	parent_store = camel_folder_get_parent_store (pitip->folder);
4139 
4140 	store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
4141 
4142 	itip_view_set_buttons_sensitive (view, FALSE);
4143 
4144 	for (link = list; link != NULL; link = g_list_next (link)) {
4145 		ESource *source = E_SOURCE (link->data);
4146 		gboolean search_for_conflicts = FALSE;
4147 		const gchar *source_uid;
4148 
4149 		extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
4150 		if (e_source_has_extension (source, extension_name)) {
4151 			ESourceConflictSearch *extension;
4152 
4153 			extension =
4154 				e_source_get_extension (source, extension_name);
4155 			search_for_conflicts =
4156 				e_source_conflict_search_get_include_me (extension);
4157 		}
4158 
4159 		if (search_for_conflicts)
4160 			conflict_list = g_list_prepend (
4161 				conflict_list, g_object_ref (source));
4162 
4163 		if (current_source != NULL)
4164 			continue;
4165 
4166 		source_uid = e_source_get_uid (source);
4167 		if (g_strcmp0 (source_uid, store_uid) == 0) {
4168 			current_source = source;
4169 			conflict_list = g_list_prepend (
4170 				conflict_list, g_object_ref (source));
4171 
4172 			continue;
4173 		}
4174 	}
4175 
4176 	if (current_source) {
4177 		link = conflict_list;
4178 
4179 		pitip->progress_info_id = itip_view_add_lower_info_item (
4180 			view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4181 			_("Opening the calendar. Please wait..."));
4182 	} else {
4183 		link = list;
4184 		pitip->progress_info_id = itip_view_add_lower_info_item (
4185 			view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4186 			_("Searching for an existing version of this appointment"));
4187 	}
4188 
4189 	for (; link != NULL; link = g_list_next (link)) {
4190 		ESource *source = E_SOURCE (link->data);
4191 
4192 		if (!fd) {
4193 			gchar *start = NULL, *end = NULL;
4194 
4195 			fd = g_new0 (FormatItipFindData, 1);
4196 			fd->puri = pitip;
4197 			fd->view = g_object_ref (view);
4198 			fd->itip_cancellable = g_object_ref (pitip->cancellable);
4199 			fd->cancellable = g_cancellable_new ();
4200 			fd->cancelled_id = g_cancellable_connect (
4201 				fd->itip_cancellable,
4202 				G_CALLBACK (itip_cancellable_cancelled), fd->cancellable, NULL);
4203 			fd->conflicts = g_hash_table_new (g_direct_hash, g_direct_equal);
4204 			fd->uid = g_strdup (uid);
4205 			fd->rid = rid;
4206 			/* avoid free this at the end */
4207 			rid = NULL;
4208 
4209 			if (pitip->start_time && pitip->end_time) {
4210 				start = isodate_from_time_t (pitip->start_time);
4211 				end = isodate_from_time_t (pitip->end_time);
4212 
4213 				fd->sexp = g_strdup_printf (
4214 					"(and (occur-in-time-range? "
4215 					"(make-time \"%s\") "
4216 					"(make-time \"%s\")) "
4217 					"(not (uid? \"%s\")))",
4218 					start, end,
4219 					icalcomponent_get_uid (pitip->ical_comp));
4220 			}
4221 
4222 			g_free (start);
4223 			g_free (end);
4224 		}
4225 		fd->count++;
4226 		d (printf ("Increasing itip formatter search count to %d\n", fd->count));
4227 
4228 		if (current_source == source)
4229 			start_calendar_server (
4230 				pitip, view, source, pitip->type,
4231 				find_cal_opened_cb, fd);
4232 		else
4233 			start_calendar_server (
4234 				pitip, view, source, pitip->type,
4235 				find_cal_opened_cb, fd);
4236 	}
4237 
4238 	g_list_free_full (conflict_list, (GDestroyNotify) g_object_unref);
4239 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
4240 
4241 	g_free (rid);
4242 }
4243 
4244 static gboolean
4245 change_status (ESourceRegistry *registry,
4246                icalcomponent *ical_comp,
4247                const gchar *address,
4248                icalparameter_partstat status)
4249 {
4250 	icalproperty *prop;
4251 
4252 	prop = find_attendee (ical_comp, address);
4253 	if (prop) {
4254 		icalparameter *param;
4255 
4256 		icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER);
4257 		param = icalparameter_new_partstat (status);
4258 		icalproperty_add_parameter (prop, param);
4259 	} else {
4260 		icalparameter *param;
4261 
4262 		if (address != NULL) {
4263 			prop = icalproperty_new_attendee (address);
4264 			icalcomponent_add_property (ical_comp, prop);
4265 
4266 			param = icalparameter_new_role (ICAL_ROLE_OPTPARTICIPANT);
4267 			icalproperty_add_parameter (prop, param);
4268 
4269 			param = icalparameter_new_partstat (status);
4270 			icalproperty_add_parameter (prop, param);
4271 		} else {
4272 			gchar *default_name = NULL;
4273 			gchar *default_address = NULL;
4274 
4275 			itip_get_default_name_and_address (
4276 				registry, &default_name, &default_address);
4277 
4278 			prop = icalproperty_new_attendee (default_address);
4279 			icalcomponent_add_property (ical_comp, prop);
4280 
4281 			param = icalparameter_new_cn (default_name);
4282 			icalproperty_add_parameter (prop, param);
4283 
4284 			param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
4285 			icalproperty_add_parameter (prop, param);
4286 
4287 			param = icalparameter_new_partstat (status);
4288 			icalproperty_add_parameter (prop, param);
4289 
4290 			g_free (default_name);
4291 			g_free (default_address);
4292 		}
4293 	}
4294 
4295 	return TRUE;
4296 }
4297 
4298 static void
4299 message_foreach_part (CamelMimePart *part,
4300                       GSList **part_list)
4301 {
4302 	CamelDataWrapper *containee;
4303 	gint parts, i;
4304 	gint go = TRUE;
4305 
4306 	if (!part)
4307 		return;
4308 
4309 	*part_list = g_slist_append (*part_list, part);
4310 
4311 	containee = camel_medium_get_content (CAMEL_MEDIUM (part));
4312 
4313 	if (containee == NULL)
4314 		return;
4315 
4316 	/* using the object types is more accurate than using the mime/types */
4317 	if (CAMEL_IS_MULTIPART (containee)) {
4318 		parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
4319 		for (i = 0; go && i < parts; i++) {
4320 			/* Reuse already declared *parts? */
4321 			CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
4322 
4323 			message_foreach_part (part, part_list);
4324 		}
4325 	} else if (CAMEL_IS_MIME_MESSAGE (containee)) {
4326 		message_foreach_part ((CamelMimePart *) containee, part_list);
4327 	}
4328 }
4329 
4330 static void
4331 attachment_load_finished (EAttachment *attachment,
4332                           GAsyncResult *result,
4333                           gpointer user_data)
4334 {
4335 	struct {
4336 		GFile *file;
4337 		gboolean done;
4338 	} *status = user_data;
4339 
4340 	/* Should be no need to check for error here. */
4341 	e_attachment_load_finish (attachment, result, NULL);
4342 
4343 	status->done = TRUE;
4344 }
4345 
4346 static void
4347 attachment_save_finished (EAttachment *attachment,
4348                           GAsyncResult *result,
4349                           gpointer user_data)
4350 {
4351 	GError *error = NULL;
4352 
4353 	struct {
4354 		GFile *file;
4355 		gboolean done;
4356 	} *status = user_data;
4357 
4358 	status->file = e_attachment_save_finish (attachment, result, &error);
4359 	status->done = TRUE;
4360 
4361 	/* XXX Error handling needs improvement. */
4362 	if (error != NULL) {
4363 		g_warning ("%s", error->message);
4364 		g_error_free (error);
4365 	}
4366 }
4367 
4368 static gchar *
4369 get_uri_for_part (CamelMimePart *mime_part)
4370 {
4371 	EAttachment *attachment;
4372 	GFile *temp_directory;
4373 	gchar *template;
4374 	gchar *path;
4375 
4376 	struct {
4377 		GFile *file;
4378 		gboolean done;
4379 	} status;
4380 
4381 	/* XXX Error handling leaves much to be desired. */
4382 
4383 	template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
4384 	path = e_mkdtemp (template);
4385 	g_free (template);
4386 
4387 	if (path == NULL)
4388 		return NULL;
4389 
4390 	temp_directory = g_file_new_for_path (path);
4391 	g_free (path);
4392 
4393 	attachment = e_attachment_new ();
4394 	e_attachment_set_mime_part (attachment, mime_part);
4395 
4396 	status.done = FALSE;
4397 
4398 	e_attachment_load_async (
4399 		attachment, (GAsyncReadyCallback)
4400 		attachment_load_finished, &status);
4401 
4402 	/* Loading should be instantaneous since we already have
4403 	 * the full content, but we still have to crank the main
4404 	 * loop until the callback gets triggered. */
4405 	while (!status.done)
4406 		gtk_main_iteration ();
4407 
4408 	status.file = NULL;
4409 	status.done = FALSE;
4410 
4411 	e_attachment_save_async (
4412 		attachment, temp_directory, (GAsyncReadyCallback)
4413 		attachment_save_finished, &status);
4414 
4415 	/* We can't return until we have results, so crank
4416 	 * the main loop until the callback gets triggered. */
4417 	while (!status.done)
4418 		gtk_main_iteration ();
4419 
4420 	if (status.file != NULL) {
4421 		path = g_file_get_path (status.file);
4422 		g_object_unref (status.file);
4423 	} else
4424 		path = NULL;
4425 
4426 	g_object_unref (attachment);
4427 	g_object_unref (temp_directory);
4428 
4429 	return path;
4430 }
4431 
4432 static void
4433 update_item_progress_info (EMailPartItip *pitip,
4434                            ItipView *view,
4435                            const gchar *message)
4436 {
4437 	if (pitip->update_item_progress_info_id) {
4438 		itip_view_remove_lower_info_item (view, pitip->update_item_progress_info_id);
4439 		pitip->update_item_progress_info_id = 0;
4440 
4441 		if (!message)
4442 			itip_view_set_buttons_sensitive (view, TRUE);
4443 	}
4444 
4445 	if (pitip->update_item_error_info_id) {
4446 		itip_view_remove_lower_info_item (view, pitip->update_item_error_info_id);
4447 		pitip->update_item_error_info_id = 0;
4448 	}
4449 
4450 	if (message) {
4451 		itip_view_set_buttons_sensitive (view, FALSE);
4452 		pitip->update_item_progress_info_id =
4453 			itip_view_add_lower_info_item (
4454 				view,
4455 				ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4456 				message);
4457 	}
4458 }
4459 
4460 static void
4461 finish_message_delete_with_rsvp (EMailPartItip *pitip,
4462                                  ItipView *view,
4463                                  ECalClient *client)
4464 {
4465 	if (pitip->delete_message && pitip->folder)
4466 		camel_folder_delete_message (pitip->folder, pitip->uid);
4467 
4468 	if (itip_view_get_rsvp (view)) {
4469 		ECalComponent *comp = NULL;
4470 		icalcomponent *ical_comp;
4471 		icalproperty *prop;
4472 		icalvalue *value;
4473 		const gchar *attendee;
4474 		gchar *comment;
4475 		GSList *l, *list = NULL;
4476 		gboolean found;
4477 
4478 		comp = e_cal_component_clone (pitip->comp);
4479 		if (comp == NULL)
4480 			return;
4481 
4482 		if (pitip->to_address == NULL)
4483 			find_to_address (pitip, pitip->ical_comp, NULL);
4484 		g_assert (pitip->to_address != NULL);
4485 
4486 		ical_comp = e_cal_component_get_icalcomponent (comp);
4487 
4488 		/* Remove all attendees except the one we are responding as */
4489 		found = FALSE;
4490 		for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
4491 		     prop != NULL;
4492 		     prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
4493 			gchar *text;
4494 
4495 			value = icalproperty_get_value (prop);
4496 			if (!value)
4497 				continue;
4498 
4499 			attendee = icalvalue_get_string (value);
4500 
4501 			text = g_strdup (itip_strip_mailto (attendee));
4502 			text = g_strstrip (text);
4503 
4504 			/* We do this to ensure there is at most one
4505 			 * attendee in the response */
4506 			if (found || g_ascii_strcasecmp (pitip->to_address, text))
4507 				list = g_slist_prepend (list, prop);
4508 			else if (!g_ascii_strcasecmp (pitip->to_address, text))
4509 				found = TRUE;
4510 			g_free (text);
4511 		}
4512 
4513 		for (l = list; l; l = l->next) {
4514 			prop = l->data;
4515 			icalcomponent_remove_property (ical_comp, prop);
4516 			icalproperty_free (prop);
4517 		}
4518 		g_slist_free (list);
4519 
4520 		/* Add a comment if there user set one */
4521 		comment = itip_view_get_rsvp_comment (view);
4522 		if (comment) {
4523 			GSList comments;
4524 			ECalComponentText text;
4525 
4526 			text.value = comment;
4527 			text.altrep = NULL;
4528 
4529 			comments.data = &text;
4530 			comments.next = NULL;
4531 
4532 			e_cal_component_set_comment_list (comp, &comments);
4533 
4534 			g_free (comment);
4535 		}
4536 
4537 		e_cal_component_rescan (comp);
4538 		if (itip_send_comp (
4539 				pitip->registry,
4540 				E_CAL_COMPONENT_METHOD_REPLY,
4541 				comp, pitip->current_client,
4542 				pitip->top_level, NULL, NULL, TRUE, FALSE) &&
4543 				pitip->folder) {
4544 			camel_folder_set_message_flags (
4545 				pitip->folder, pitip->uid,
4546 				CAMEL_MESSAGE_ANSWERED,
4547 				CAMEL_MESSAGE_ANSWERED);
4548 		}
4549 
4550 		g_object_unref (comp);
4551 	}
4552 
4553 	update_item_progress_info (pitip, view, NULL);
4554 }
4555 
4556 static void
4557 receive_objects_ready_cb (GObject *ecalclient,
4558                           GAsyncResult *result,
4559                           gpointer user_data)
4560 {
4561 	ECalClient *client = E_CAL_CLIENT (ecalclient);
4562 	ESource *source = e_client_get_source (E_CLIENT (client));
4563 	ItipView *view = user_data;
4564 	EMailPartItip *pitip = itip_view_get_mail_part (view);
4565 	GError *error = NULL;
4566 
4567 	e_cal_client_receive_objects_finish (client, result, &error);
4568 
4569 	if (error != NULL) {
4570 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
4571 		    !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
4572 			update_item_progress_info (pitip, view, NULL);
4573 			pitip->update_item_error_info_id =
4574 				itip_view_add_lower_info_item_printf (
4575 					view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4576 					_("Unable to send item to calendar '%s'.  %s"),
4577 					e_source_get_display_name (source),
4578 					error->message);
4579 		}
4580 		g_error_free (error);
4581 		return;
4582 	}
4583 
4584 	itip_view_set_extension_name (view, NULL);
4585 
4586 	itip_view_clear_lower_info_items (view);
4587 
4588 	switch (pitip->update_item_response) {
4589 	case ITIP_VIEW_RESPONSE_ACCEPT:
4590 		itip_view_add_lower_info_item_printf (
4591 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4592 			_("Sent to calendar '%s' as accepted"), e_source_get_display_name (source));
4593 		break;
4594 	case ITIP_VIEW_RESPONSE_TENTATIVE:
4595 		itip_view_add_lower_info_item_printf (
4596 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4597 			_("Sent to calendar '%s' as tentative"), e_source_get_display_name (source));
4598 		break;
4599 	case ITIP_VIEW_RESPONSE_DECLINE:
4600 		/* FIXME some calendars just might not save it at all, is this accurate? */
4601 		itip_view_add_lower_info_item_printf (
4602 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4603 			_("Sent to calendar '%s' as declined"), e_source_get_display_name (source));
4604 		break;
4605 	case ITIP_VIEW_RESPONSE_CANCEL:
4606 		/* FIXME some calendars just might not save it at all, is this accurate? */
4607 		itip_view_add_lower_info_item_printf (
4608 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4609 			_("Sent to calendar '%s' as canceled"), e_source_get_display_name (source));
4610 		break;
4611 	default:
4612 		g_assert_not_reached ();
4613 		break;
4614 	}
4615 
4616 	finish_message_delete_with_rsvp (pitip, view, client);
4617 }
4618 
4619 static void
4620 update_item (EMailPartItip *pitip,
4621              ItipView *view,
4622              ItipViewResponse response)
4623 {
4624 	struct icaltimetype stamp;
4625 	icalproperty *prop;
4626 	icalcomponent *clone;
4627 	ECalComponent *clone_comp;
4628 	gchar *str;
4629 
4630 	update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
4631 
4632 	/* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
4633 	 * the user accepted/declined the request. (Outlook ignores
4634 	 * SEQUENCE in REPLY reponses and instead requires that each
4635 	 * updated response have a later REPLYTIME than the previous
4636 	 * one.) This also ends up getting saved in our own copy of
4637 	 * the meeting, though there's currently no way to see that
4638 	 * information (unless it's being saved to an Exchange folder
4639 	 * and you then look at it in Outlook).
4640 	 */
4641 	stamp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
4642 	str = icaltime_as_ical_string_r (stamp);
4643 	prop = icalproperty_new_x (str);
4644 	g_free (str);
4645 	icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME");
4646 	icalcomponent_add_property (pitip->ical_comp, prop);
4647 
4648 	clone = icalcomponent_new_clone (pitip->ical_comp);
4649 	icalcomponent_add_component (pitip->top_level, clone);
4650 	icalcomponent_set_method (pitip->top_level, pitip->method);
4651 
4652 	if (!itip_view_get_inherit_alarm_check_state (view)) {
4653 		icalcomponent *alarm_comp;
4654 		icalcompiter alarm_iter;
4655 
4656 		alarm_iter = icalcomponent_begin_component (clone, ICAL_VALARM_COMPONENT);
4657 		while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
4658 			icalcompiter_next (&alarm_iter);
4659 
4660 			icalcomponent_remove_component (clone, alarm_comp);
4661 			icalcomponent_free (alarm_comp);
4662 		}
4663 	}
4664 
4665 	clone_comp = e_cal_component_new ();
4666 	if (!e_cal_component_set_icalcomponent (clone_comp, clone)) {
4667 		update_item_progress_info (pitip, view, NULL);
4668 		pitip->update_item_error_info_id =
4669 			itip_view_add_lower_info_item (
4670 				view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
4671 				_("Unable to parse item"));
4672 		goto cleanup;
4673 	}
4674 
4675 	if (itip_view_get_keep_alarm_check_state (view)) {
4676 		ECalComponent *real_comp;
4677 		GList *alarms, *l;
4678 		ECalComponentAlarm *alarm;
4679 
4680 		real_comp = get_real_item (pitip);
4681 		if (real_comp != NULL) {
4682 			alarms = e_cal_component_get_alarm_uids (real_comp);
4683 
4684 			for (l = alarms; l; l = l->next) {
4685 				alarm = e_cal_component_get_alarm (
4686 					real_comp, (const gchar *) l->data);
4687 
4688 				if (alarm) {
4689 					ECalComponentAlarm *aclone = e_cal_component_alarm_clone (alarm);
4690 
4691 					if (aclone) {
4692 						e_cal_component_add_alarm (clone_comp, aclone);
4693 						e_cal_component_alarm_free (aclone);
4694 					}
4695 
4696 					e_cal_component_alarm_free (alarm);
4697 				}
4698 			}
4699 
4700 			cal_obj_uid_list_free (alarms);
4701 			g_object_unref (real_comp);
4702 		}
4703 	}
4704 
4705 	if ((response != ITIP_VIEW_RESPONSE_CANCEL)
4706 		&& (response != ITIP_VIEW_RESPONSE_DECLINE)) {
4707 		GSList *attachments = NULL, *new_attachments = NULL, *l;
4708 		CamelMimeMessage *msg = pitip->msg;
4709 
4710 		e_cal_component_get_attachment_list (clone_comp, &attachments);
4711 
4712 		for (l = attachments; l; l = l->next) {
4713 			GSList *parts = NULL, *m;
4714 			gchar *uri, *new_uri;
4715 			CamelMimePart *part;
4716 
4717 			uri = l->data;
4718 
4719 			if (!g_ascii_strncasecmp (uri, "cid:...", 7)) {
4720 				message_foreach_part ((CamelMimePart *) msg, &parts);
4721 
4722 				for (m = parts; m; m = m->next) {
4723 					part = m->data;
4724 
4725 					/* Skip the actual message and the text/calendar part */
4726 					/* FIXME Do we need to skip anything else? */
4727 					if (part == (CamelMimePart *) msg || part == pitip->part)
4728 						continue;
4729 
4730 					new_uri = get_uri_for_part (part);
4731 					if (new_uri != NULL)
4732 						new_attachments = g_slist_append (new_attachments, new_uri);
4733 				}
4734 
4735 				g_slist_free (parts);
4736 
4737 			} else if (!g_ascii_strncasecmp (uri, "cid:", 4)) {
4738 				part = camel_mime_message_get_part_by_content_id (msg, uri + 4);
4739 				if (part) {
4740 					new_uri = get_uri_for_part (part);
4741 					if (new_uri != NULL)
4742 						new_attachments = g_slist_append (new_attachments, new_uri);
4743 				}
4744 
4745 			} else {
4746 				/* Preserve existing non-cid ones */
4747 				new_attachments = g_slist_append (new_attachments, g_strdup (uri));
4748 			}
4749 		}
4750 
4751 		g_slist_foreach (attachments, (GFunc) g_free, NULL);
4752 		g_slist_free (attachments);
4753 
4754 		e_cal_component_set_attachment_list (clone_comp, new_attachments);
4755 	}
4756 
4757 	pitip->update_item_response = response;
4758 
4759 	e_cal_client_receive_objects (
4760 		pitip->current_client,
4761 		pitip->top_level,
4762 		pitip->cancellable,
4763 		receive_objects_ready_cb,
4764 		view);
4765 
4766  cleanup:
4767 	icalcomponent_remove_component (pitip->top_level, clone);
4768 	g_object_unref (clone_comp);
4769 }
4770 
4771 /* TODO These operations should be available in e-cal-component.c */
4772 static void
4773 set_attendee (ECalComponent *comp,
4774               const gchar *address)
4775 {
4776 	icalproperty *prop;
4777 	icalcomponent *icalcomp;
4778 	gboolean found = FALSE;
4779 
4780 	icalcomp = e_cal_component_get_icalcomponent (comp);
4781 
4782 	for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
4783 			prop;
4784 			prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
4785 		const gchar *attendee = icalproperty_get_attendee (prop);
4786 
4787 		if (!(g_str_equal (itip_strip_mailto (attendee), address)))
4788 			icalcomponent_remove_property (icalcomp, prop);
4789 		else
4790 			found = TRUE;
4791 	}
4792 
4793 	if (!found) {
4794 		icalparameter *param;
4795 		gchar *temp = g_strdup_printf ("MAILTO:%s", address);
4796 
4797 		prop = icalproperty_new_attendee ((const gchar *) temp);
4798 		icalcomponent_add_property (icalcomp, prop);
4799 
4800 		param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION);
4801 		icalproperty_add_parameter (prop, param);
4802 
4803 		param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
4804 		icalproperty_add_parameter (prop, param);
4805 
4806 		param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
4807 		icalproperty_add_parameter (prop, param);
4808 
4809 		param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
4810 		icalproperty_add_parameter (prop, param);
4811 
4812 		g_free (temp);
4813 	}
4814 
4815 }
4816 
4817 static gboolean
4818 send_comp_to_attendee (ESourceRegistry *registry,
4819                        ECalComponentItipMethod method,
4820                        ECalComponent *comp,
4821                        const gchar *user,
4822                        ECalClient *client,
4823                        const gchar *comment)
4824 {
4825 	gboolean status;
4826 	ECalComponent *send_comp = e_cal_component_clone (comp);
4827 
4828 	set_attendee (send_comp, user);
4829 
4830 	if (comment) {
4831 		GSList comments;
4832 		ECalComponentText text;
4833 
4834 		text.value = comment;
4835 		text.altrep = NULL;
4836 
4837 		comments.data = &text;
4838 		comments.next = NULL;
4839 
4840 		e_cal_component_set_comment_list (send_comp, &comments);
4841 	}
4842 
4843 	/* FIXME send the attachments in the request */
4844 	status = itip_send_comp (
4845 		registry, method, send_comp,
4846 		client, NULL, NULL, NULL, TRUE, FALSE);
4847 
4848 	g_object_unref (send_comp);
4849 
4850 	return status;
4851 }
4852 
4853 static void
4854 remove_delegate (EMailPartItip *pitip,
4855                  ItipView *view,
4856                  const gchar *delegate,
4857                  const gchar *delegator,
4858                  ECalComponent *comp)
4859 {
4860 	gboolean status;
4861 	gchar *comment = g_strdup_printf (_("Organizer has removed the delegate %s "), itip_strip_mailto (delegate));
4862 
4863 	/* send cancellation notice to delegate */
4864 	status = send_comp_to_attendee (
4865 		pitip->registry,
4866 		E_CAL_COMPONENT_METHOD_CANCEL, pitip->comp,
4867 		delegate, pitip->current_client, comment);
4868 	if (status)
4869 		send_comp_to_attendee (
4870 			pitip->registry,
4871 			E_CAL_COMPONENT_METHOD_REQUEST, pitip->comp,
4872 			delegator, pitip->current_client, comment);
4873 	if (status) {
4874 		itip_view_add_lower_info_item (
4875 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4876 			_("Sent a cancelation notice to the delegate"));
4877 	} else
4878 		itip_view_add_lower_info_item (
4879 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4880 			_("Could not send the cancelation notice to the delegate"));
4881 
4882 	g_free (comment);
4883 
4884 }
4885 
4886 static void
4887 update_x (ECalComponent *pitip_comp,
4888           ECalComponent *comp)
4889 {
4890 	icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (pitip_comp);
4891 	icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
4892 
4893 	icalproperty *prop = icalcomponent_get_first_property (itip_icalcomp, ICAL_X_PROPERTY);
4894 	while (prop) {
4895 		const gchar *name = icalproperty_get_x_name (prop);
4896 		if (!g_ascii_strcasecmp (name, "X-EVOLUTION-IS-REPLY")) {
4897 			icalproperty *new_prop = icalproperty_new_x (icalproperty_get_x (prop));
4898 			icalproperty_set_x_name (new_prop, "X-EVOLUTION-IS-REPLY");
4899 			icalcomponent_add_property (icalcomp, new_prop);
4900 		}
4901 		prop = icalcomponent_get_next_property (itip_icalcomp, ICAL_X_PROPERTY);
4902 	}
4903 
4904 	e_cal_component_set_icalcomponent (comp, icalcomp);
4905 }
4906 
4907 static void
4908 modify_object_cb (GObject *ecalclient,
4909                   GAsyncResult *result,
4910                   gpointer user_data)
4911 {
4912 	ECalClient *client = E_CAL_CLIENT (ecalclient);
4913 	ItipView *view = user_data;
4914 	EMailPartItip *pitip = itip_view_get_mail_part (view);
4915 	GError *error = NULL;
4916 
4917 	e_cal_client_modify_object_finish (client, result, &error);
4918 
4919 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
4920 	    g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
4921 		g_error_free (error);
4922 		return;
4923 	}
4924 
4925 	if (error != NULL) {
4926 		update_item_progress_info (pitip, view, NULL);
4927 		pitip->update_item_error_info_id =
4928 			itip_view_add_lower_info_item_printf (
4929 				view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
4930 				_("Unable to update attendee. %s"),
4931 				error->message);
4932 		g_error_free (error);
4933 	} else {
4934 		update_item_progress_info (pitip, view, NULL);
4935 		itip_view_add_lower_info_item (
4936 			view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4937 			_("Attendee status updated"));
4938 	}
4939 }
4940 
4941 static void
4942 update_attendee_status_icalcomp (EMailPartItip *pitip,
4943                                  ItipView *view,
4944                                  icalcomponent *icalcomp)
4945 {
4946 	ECalComponent *comp;
4947 	const gchar *uid = NULL;
4948 	gchar *rid;
4949 	GSList *attendees;
4950 
4951 	e_cal_component_get_uid (pitip->comp, &uid);
4952 	rid = e_cal_component_get_recurid_as_string (pitip->comp);
4953 
4954 	comp = e_cal_component_new ();
4955 	if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
4956 		icalcomponent_free (icalcomp);
4957 
4958 		itip_view_add_lower_info_item (
4959 			view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
4960 			_("The meeting is invalid and cannot be updated"));
4961 	} else {
4962 		icalcomponent *org_icalcomp;
4963 		const gchar *delegate;
4964 
4965 		org_icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
4966 
4967 		e_cal_component_get_attendee_list (pitip->comp, &attendees);
4968 		if (attendees != NULL) {
4969 			ECalComponentAttendee *a = attendees->data;
4970 			icalproperty *prop, *del_prop;
4971 			EShell *shell = e_shell_get_default ();
4972 
4973 			prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
4974 			if ((a->status == ICAL_PARTSTAT_DELEGATED) && (del_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->delto))) && !(find_attendee (icalcomp, itip_strip_mailto (a->delto)))) {
4975 				gint response;
4976 				delegate = icalproperty_get_attendee (del_prop);
4977 				response = e_alert_run_dialog_for_args (
4978 					e_shell_get_active_window (shell),
4979 					"org.gnome.itip-formatter:add-delegate",
4980 					itip_strip_mailto (a->value),
4981 					itip_strip_mailto (delegate), NULL);
4982 				if (response == GTK_RESPONSE_YES) {
4983 					icalcomponent_add_property (icalcomp, icalproperty_new_clone (del_prop));
4984 					e_cal_component_rescan (comp);
4985 				} else if (response == GTK_RESPONSE_NO) {
4986 					remove_delegate (pitip, view, delegate, itip_strip_mailto (a->value), comp);
4987 					goto cleanup;
4988 				} else {
4989 					goto cleanup;
4990 				}
4991 			}
4992 
4993 			if (prop == NULL) {
4994 				gint response;
4995 
4996 				if (a->delfrom && *a->delfrom) {
4997 					response = e_alert_run_dialog_for_args (
4998 						e_shell_get_active_window (shell),
4999 						"org.gnome.itip-formatter:add-delegate",
5000 						itip_strip_mailto (a->delfrom),
5001 						itip_strip_mailto (a->value), NULL);
5002 					if (response == GTK_RESPONSE_YES) {
5003 						/* Already declared in this function */
5004 						icalproperty *prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
5005 						icalcomponent_add_property (icalcomp,icalproperty_new_clone (prop));
5006 						e_cal_component_rescan (comp);
5007 					} else if (response == GTK_RESPONSE_NO) {
5008 						remove_delegate (
5009 							pitip,
5010 							view,
5011 							itip_strip_mailto (a->value),
5012 							itip_strip_mailto (a->delfrom),
5013 							comp);
5014 						goto cleanup;
5015 					} else {
5016 						goto cleanup;
5017 					}
5018 				}
5019 
5020 				response = e_alert_run_dialog_for_args (
5021 					e_shell_get_active_window (shell),
5022 					"org.gnome.itip-formatter:add-unknown-attendee", NULL);
5023 
5024 				if (response == GTK_RESPONSE_YES) {
5025 					change_status (
5026 						pitip->registry, icalcomp,
5027 						itip_strip_mailto (a->value),
5028 						a->status);
5029 					e_cal_component_rescan (comp);
5030 				} else {
5031 					goto cleanup;
5032 				}
5033 			} else if (a->status == ICAL_PARTSTAT_NONE || a->status == ICAL_PARTSTAT_X) {
5034 				itip_view_add_lower_info_item (
5035 					view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5036 					_("Attendee status could not be updated because the status is invalid"));
5037 				goto cleanup;
5038 			} else {
5039 				if (a->status == ICAL_PARTSTAT_DELEGATED) {
5040 					/* *prop already declared in this function */
5041 					icalproperty *prop, *new_prop;
5042 
5043 					prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
5044 					icalcomponent_remove_property (icalcomp, prop);
5045 
5046 					new_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->value));
5047 					icalcomponent_add_property (icalcomp, icalproperty_new_clone (new_prop));
5048 				} else {
5049 					change_status (
5050 						pitip->registry, icalcomp,
5051 						itip_strip_mailto (a->value),
5052 						a->status);
5053 				}
5054 
5055 				e_cal_component_rescan (comp);
5056 			}
5057 		}
5058 	}
5059 
5060 	update_x (pitip->comp, comp);
5061 
5062 	if (itip_view_get_update (view)) {
5063 		e_cal_component_commit_sequence (comp);
5064 		itip_send_comp (
5065 			pitip->registry,
5066 			E_CAL_COMPONENT_METHOD_REQUEST,
5067 			comp, pitip->current_client,
5068 			NULL, NULL, NULL, TRUE, FALSE);
5069 	}
5070 
5071 	update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
5072 
5073 	e_cal_client_modify_object (
5074 		pitip->current_client,
5075 		icalcomp, rid ? CALOBJ_MOD_THIS : CALOBJ_MOD_ALL,
5076 		pitip->cancellable,
5077 		modify_object_cb,
5078 		view);
5079 
5080  cleanup:
5081 	g_object_unref (comp);
5082 }
5083 
5084 static void
5085 update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
5086                                                   GAsyncResult *result,
5087                                                   gpointer user_data)
5088 {
5089 	ECalClient *client = E_CAL_CLIENT (ecalclient);
5090 	ItipView *view = user_data;
5091 	EMailPartItip *pitip = itip_view_get_mail_part (view);
5092 	icalcomponent *icalcomp = NULL;
5093 	GError *error = NULL;
5094 
5095 	if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
5096 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
5097 		    g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
5098 			g_clear_error (&error);
5099 			return;
5100 		}
5101 
5102 		g_clear_error (&error);
5103 
5104 		update_item_progress_info (pitip, view, NULL);
5105 		pitip->update_item_error_info_id = itip_view_add_lower_info_item (
5106 			view,
5107 			ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
5108 			_("Attendee status can not be updated because the item no longer exists"));
5109 		return;
5110 	}
5111 
5112 	update_attendee_status_icalcomp (pitip, view, icalcomp);
5113 }
5114 
5115 static void
5116 update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
5117                                                GAsyncResult *result,
5118                                                gpointer user_data)
5119 {
5120 	ECalClient *client = E_CAL_CLIENT (ecalclient);
5121 	ItipView *view = user_data;
5122 	EMailPartItip *pitip = itip_view_get_mail_part (view);
5123 	icalcomponent *icalcomp = NULL;
5124 	GError *error = NULL;
5125 
5126 	if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
5127 		const gchar *uid;
5128 		gchar *rid;
5129 
5130 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
5131 		    g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
5132 			g_clear_error (&error);
5133 			return;
5134 		}
5135 
5136 		g_clear_error (&error);
5137 
5138 		e_cal_component_get_uid (pitip->comp, &uid);
5139 		rid = e_cal_component_get_recurid_as_string (pitip->comp);
5140 
5141 		if (!rid || !*rid) {
5142 			g_free (rid);
5143 
5144 			update_item_progress_info (pitip, view, NULL);
5145 			pitip->update_item_error_info_id = itip_view_add_lower_info_item (
5146 				view,
5147 				ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
5148 				_("Attendee status can not be updated because the item no longer exists"));
5149 			return;
5150 		}
5151 
5152 		e_cal_client_get_object (
5153 			pitip->current_client,
5154 			uid,
5155 			NULL,
5156 			pitip->cancellable,
5157 			update_attendee_status_get_object_without_rid_cb,
5158 			view);
5159 
5160 		g_free (rid);
5161 		return;
5162 	}
5163 
5164 	update_attendee_status_icalcomp (pitip, view, icalcomp);
5165 }
5166 
5167 static void
5168 update_attendee_status (EMailPartItip *pitip,
5169                         ItipView *view)
5170 {
5171 	const gchar *uid = NULL;
5172 	gchar *rid;
5173 
5174 	/* Obtain our version */
5175 	e_cal_component_get_uid (pitip->comp, &uid);
5176 	rid = e_cal_component_get_recurid_as_string (pitip->comp);
5177 
5178 	update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
5179 
5180 	/* search for a master object if the detached object doesn't exist in the calendar */
5181 	e_cal_client_get_object (
5182 		pitip->current_client,
5183 		uid, rid,
5184 		pitip->cancellable,
5185 		update_attendee_status_get_object_with_rid_cb,
5186 		view);
5187 
5188 	g_free (rid);
5189 }
5190 
5191 static void
5192 send_item (EMailPartItip *pitip,
5193            ItipView *view)
5194 {
5195 	ECalComponent *comp;
5196 
5197 	comp = get_real_item (pitip);
5198 
5199 	if (comp != NULL) {
5200 		itip_send_comp (
5201 			pitip->registry,
5202 			E_CAL_COMPONENT_METHOD_REQUEST,
5203 			comp, pitip->current_client,
5204 			NULL, NULL, NULL, TRUE, FALSE);
5205 		g_object_unref (comp);
5206 
5207 		switch (pitip->type) {
5208 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
5209 			itip_view_add_lower_info_item (
5210 				view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
5211 				_("Meeting information sent"));
5212 			break;
5213 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
5214 			itip_view_add_lower_info_item (
5215 				view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
5216 				_("Task information sent"));
5217 			break;
5218 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
5219 			itip_view_add_lower_info_item (
5220 				view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
5221 				_("Memo information sent"));
5222 			break;
5223 		default:
5224 			g_assert_not_reached ();
5225 			break;
5226 		}
5227 	} else {
5228 		switch (pitip->type) {
5229 		case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
5230 			itip_view_add_lower_info_item (
5231 				view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5232 				_("Unable to send meeting information, the meeting does not exist"));
5233 			break;
5234 		case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
5235 			itip_view_add_lower_info_item (
5236 				view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5237 				_("Unable to send task information, the task does not exist"));
5238 			break;
5239 		case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
5240 			itip_view_add_lower_info_item (
5241 				view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5242 				_("Unable to send memo information, the memo does not exist"));
5243 			break;
5244 		default:
5245 			g_assert_not_reached ();
5246 			break;
5247 		}
5248 	}
5249 }
5250 
5251 static icalcomponent *
5252 get_next (icalcompiter *iter)
5253 {
5254 	icalcomponent *ret = NULL;
5255 	icalcomponent_kind kind;
5256 
5257 	do {
5258 		icalcompiter_next (iter);
5259 		ret = icalcompiter_deref (iter);
5260 		if (ret == NULL)
5261 			break;
5262 		kind = icalcomponent_isa (ret);
5263 	} while (ret != NULL
5264 		 && kind != ICAL_VEVENT_COMPONENT
5265 		 && kind != ICAL_VTODO_COMPONENT
5266 		 && kind != ICAL_VFREEBUSY_COMPONENT);
5267 
5268 	return ret;
5269 }
5270 
5271 static void
5272 attachment_load_finish (EAttachment *attachment,
5273                         GAsyncResult *result,
5274                         GFile *file)
5275 {
5276 	EShell *shell;
5277 	GtkWindow *parent;
5278 
5279 	/* XXX Theoretically, this should never fail. */
5280 	e_attachment_load_finish (attachment, result, NULL);
5281 
5282 	shell = e_shell_get_default ();
5283 	parent = e_shell_get_active_window (shell);
5284 
5285 	e_attachment_save_async (
5286 		attachment, file, (GAsyncReadyCallback)
5287 		e_attachment_save_handle_error, parent);
5288 
5289 	g_object_unref (file);
5290 }
5291 
5292 static void
5293 save_vcalendar_cb (EMailPartItip *pitip)
5294 {
5295 	EAttachment *attachment;
5296 	EShell *shell;
5297 	GFile *file;
5298 	const gchar *suggestion;
5299 
5300 	g_return_if_fail (pitip != NULL);
5301 	g_return_if_fail (pitip->vcalendar != NULL);
5302 	g_return_if_fail (pitip->part != NULL);
5303 
5304 	suggestion = camel_mime_part_get_filename (pitip->part);
5305 	if (suggestion == NULL) {
5306 		/* Translators: This is a default filename for a calendar. */
5307 		suggestion = _("calendar.ics");
5308 	}
5309 
5310 	shell = e_shell_get_default ();
5311 	file = e_shell_run_save_dialog (
5312 		shell, _("Save Calendar"), suggestion, "*.ics:text/calendar", NULL, NULL);
5313 	if (file == NULL)
5314 		return;
5315 
5316 	attachment = e_attachment_new ();
5317 	e_attachment_set_mime_part (attachment, pitip->part);
5318 
5319 	e_attachment_load_async (
5320 		attachment, (GAsyncReadyCallback)
5321 		attachment_load_finish, file);
5322 }
5323 
5324 static void
5325 set_itip_error (ItipView *view,
5326                 const gchar *primary,
5327                 const gchar *secondary,
5328                 gboolean save_btn)
5329 {
5330 	gchar *error;
5331 
5332 	error = g_strdup_printf (
5333 		"<div class=\"error\">"
5334 		"<p><b>%s</b></p>"
5335 		"<p>%s</p>",
5336 		primary, secondary);
5337 
5338 	itip_view_set_error (view, error, save_btn);
5339 
5340 	g_free (error);
5341 }
5342 
5343 static gboolean
5344 extract_itip_data (EMailPartItip *pitip,
5345                    ItipView *view,
5346                    gboolean *have_alarms)
5347 {
5348 	EShell *shell;
5349 	EShellSettings *shell_settings;
5350 	icalproperty *prop;
5351 	icalcomponent_kind kind = ICAL_NO_COMPONENT;
5352 	icalcomponent *tz_comp;
5353 	icalcompiter tz_iter;
5354 	icalcomponent *alarm_comp;
5355 	icalcompiter alarm_iter;
5356 	ECalComponent *comp;
5357 	gboolean use_default_reminder;
5358 
5359 	shell = e_shell_get_default ();
5360 	shell_settings = e_shell_get_shell_settings (shell);
5361 
5362 	if (!pitip->vcalendar) {
5363 		set_itip_error (
5364 			view,
5365 			_("The calendar attached is not valid"),
5366 			_("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
5367 			FALSE);
5368 
5369 		return FALSE;
5370 	}
5371 
5372 	pitip->top_level = e_cal_util_new_top_level ();
5373 
5374 	pitip->main_comp = icalparser_parse_string (pitip->vcalendar);
5375 	if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) {
5376 		set_itip_error (
5377 			view,
5378 			_("The calendar attached is not valid"),
5379 			_("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
5380 			FALSE);
5381 
5382 		if (pitip->main_comp) {
5383 			icalcomponent_free (pitip->main_comp);
5384 			pitip->main_comp = NULL;
5385 		}
5386 
5387 		return FALSE;
5388 	}
5389 
5390 	prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY);
5391 	if (prop == NULL) {
5392 		pitip->method = ICAL_METHOD_PUBLISH;
5393 	} else {
5394 		pitip->method = icalproperty_get_method (prop);
5395 	}
5396 
5397 	tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT);
5398 	while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) {
5399 		icalcomponent *clone;
5400 
5401 		clone = icalcomponent_new_clone (tz_comp);
5402 		icalcomponent_add_component (pitip->top_level, clone);
5403 
5404 		icalcompiter_next (&tz_iter);
5405 	}
5406 
5407 	pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT);
5408 	pitip->ical_comp = icalcompiter_deref (&pitip->iter);
5409 	if (pitip->ical_comp != NULL) {
5410 		kind = icalcomponent_isa (pitip->ical_comp);
5411 		if (kind != ICAL_VEVENT_COMPONENT
5412 		    && kind != ICAL_VTODO_COMPONENT
5413 		    && kind != ICAL_VFREEBUSY_COMPONENT
5414 		    && kind != ICAL_VJOURNAL_COMPONENT)
5415 			pitip->ical_comp = get_next (&pitip->iter);
5416 	}
5417 
5418 	if (pitip->ical_comp == NULL) {
5419 		set_itip_error (
5420 			view,
5421 			_("The item in the calendar is not valid"),
5422 			_("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
5423 			FALSE);
5424 
5425 		return FALSE;
5426 	}
5427 
5428 	switch (icalcomponent_isa (pitip->ical_comp)) {
5429 	case ICAL_VEVENT_COMPONENT:
5430 		pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
5431 		pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL;
5432 		if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) {
5433 			/* no attendees: assume that that this is not a meeting and organizer doesn't want a reply */
5434 			pitip->no_reply_wanted = TRUE;
5435 		} else {
5436 			/*
5437 			 * if we have attendees, then find_to_address() will check for our RSVP
5438 			 * and set no_reply_wanted=TRUE if RSVP=FALSE for the current user
5439 			 */
5440 		}
5441 		break;
5442 	case ICAL_VTODO_COMPONENT:
5443 		pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
5444 		break;
5445 	case ICAL_VJOURNAL_COMPONENT:
5446 		pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
5447 		break;
5448 	default:
5449 		set_itip_error (
5450 			view,
5451 			_("The item in the calendar is not valid"),
5452 			_("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
5453 			FALSE);
5454 
5455 		return FALSE;
5456 	}
5457 
5458 	pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT);
5459 	pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT);
5460 	pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT);
5461 	pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT);
5462 
5463 	if (pitip->total > 1) {
5464 
5465 		set_itip_error (
5466 			view,
5467 			_("The calendar attached contains multiple items"),
5468 			_("To process all of these items, the file should be saved and the calendar imported"),
5469 			TRUE);
5470 
5471 	} if (pitip->total > 0) {
5472 		pitip->current = 1;
5473 	} else {
5474 		pitip->current = 0;
5475 	}
5476 
5477 	if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) {
5478 		gchar *my_address;
5479 
5480 		prop = NULL;
5481 		comp = e_cal_component_new ();
5482 		e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp));
5483 		my_address = itip_get_comp_attendee (pitip->registry, comp, NULL);
5484 		g_object_unref (comp);
5485 		comp = NULL;
5486 
5487 		if (!prop)
5488 			prop = find_attendee (pitip->ical_comp, my_address);
5489 		if (!prop)
5490 			prop = find_attendee_if_sentby (pitip->ical_comp, my_address);
5491 		if (prop) {
5492 			icalparameter *param;
5493 			const gchar * delfrom;
5494 
5495 			if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) {
5496 				delfrom = icalparameter_get_delegatedfrom (param);
5497 
5498 				pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom));
5499 			}
5500 		}
5501 		g_free (my_address);
5502 		prop = NULL;
5503 
5504 		/* Determine any delegate sections */
5505 		prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY);
5506 		while (prop) {
5507 			const gchar *x_name, *x_val;
5508 
5509 			x_name = icalproperty_get_x_name (prop);
5510 			x_val = icalproperty_get_x (prop);
5511 
5512 			if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID"))
5513 				pitip->calendar_uid = g_strdup (x_val);
5514 			else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI"))
5515 				g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used");
5516 			else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS"))
5517 				pitip->delegator_address = g_strdup (x_val);
5518 			else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME"))
5519 				pitip->delegator_name = g_strdup (x_val);
5520 
5521 			prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY);
5522 		}
5523 
5524 		/* Strip out procedural alarms for security purposes */
5525 		alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
5526 		while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
5527 			icalproperty *p;
5528 
5529 			icalcompiter_next (&alarm_iter);
5530 
5531 			p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY);
5532 			if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE)
5533 				icalcomponent_remove_component (pitip->ical_comp, alarm_comp);
5534 
5535 			icalcomponent_free (alarm_comp);
5536 		}
5537 
5538 		if (have_alarms) {
5539 			alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
5540 			*have_alarms = icalcompiter_deref (&alarm_iter) != NULL;
5541 		}
5542 	}
5543 
5544 	pitip->comp = e_cal_component_new ();
5545 	if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) {
5546 		g_object_unref (pitip->comp);
5547 		pitip->comp = NULL;
5548 
5549 		set_itip_error (
5550 			view,
5551 			_("The item in the calendar is not valid"),
5552 			_("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
5553 			FALSE);
5554 
5555 		return FALSE;
5556 	};
5557 
5558 	/* Add default reminder if the config says so */
5559 
5560 	use_default_reminder = e_shell_settings_get_boolean (
5561 		shell_settings, "cal-use-default-reminder");
5562 
5563 	if (use_default_reminder) {
5564 		ECalComponentAlarm *acomp;
5565 		gint interval;
5566 		EDurationType units;
5567 		ECalComponentAlarmTrigger trigger;
5568 
5569 		interval = e_shell_settings_get_int (
5570 			shell_settings, "cal-default-reminder-interval");
5571 		units = e_shell_settings_get_int (
5572 			shell_settings, "cal-default-reminder-units");
5573 
5574 		acomp = e_cal_component_alarm_new ();
5575 
5576 		e_cal_component_alarm_set_action (acomp, E_CAL_COMPONENT_ALARM_DISPLAY);
5577 
5578 		trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
5579 		memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration));
5580 
5581 		trigger.u.rel_duration.is_neg = TRUE;
5582 
5583 		switch (units) {
5584 			case E_DURATION_MINUTES:
5585 				trigger.u.rel_duration.minutes = interval;
5586 				break;
5587 			case E_DURATION_HOURS:
5588 				trigger.u.rel_duration.hours = interval;
5589 				break;
5590 			case E_DURATION_DAYS:
5591 				trigger.u.rel_duration.days = interval;
5592 				break;
5593 			default:
5594 				g_assert_not_reached ();
5595 		}
5596 
5597 		e_cal_component_alarm_set_trigger (acomp, trigger);
5598 		e_cal_component_add_alarm (pitip->comp, acomp);
5599 
5600 		e_cal_component_alarm_free (acomp);
5601 	}
5602 
5603 	find_from_address (pitip, pitip->ical_comp);
5604 	find_to_address (pitip, pitip->ical_comp, NULL);
5605 
5606 	return TRUE;
5607 }
5608 
5609 static gboolean
5610 idle_open_cb (gpointer data)
5611 {
5612 	EMailPartItip *pitip = data;
5613 	EShell *shell;
5614 	const gchar *uris[2];
5615 	gchar *start, *end, *shell_uri;
5616 
5617 	start = isodate_from_time_t (pitip->start_time ? pitip->start_time : time (NULL));
5618 	end = isodate_from_time_t (pitip->end_time ? pitip->end_time : time (NULL));
5619 	shell_uri = g_strdup_printf ("calendar:///?startdate=%s&enddate=%s", start, end);
5620 
5621 	uris[0] = shell_uri;
5622 	uris[1] = NULL;
5623 
5624 	shell = e_shell_get_default ();
5625 	e_shell_handle_uris (shell, uris, FALSE);
5626 
5627 	g_free (shell_uri);
5628 	g_free (start);
5629 	g_free (end);
5630 
5631 	return FALSE;
5632 }
5633 
5634 static void
5635 view_response_cb (ItipView *view,
5636                   ItipViewResponse response,
5637                   gpointer data)
5638 {
5639 	EMailPartItip *pitip = data;
5640 	gboolean status = FALSE;
5641 	icalproperty *prop;
5642 	ECalComponentTransparency trans;
5643 
5644 	if (response == ITIP_VIEW_RESPONSE_SAVE) {
5645 		save_vcalendar_cb (pitip);
5646 		return;
5647 	}
5648 
5649 	if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method ==  ICAL_METHOD_REQUEST) {
5650 		if (itip_view_get_free_time_check_state (view))
5651 			e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
5652 		else
5653 			e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
5654 	} else {
5655 		e_cal_component_get_transparency (pitip->comp, &trans);
5656 
5657 		if (trans == E_CAL_COMPONENT_TRANSP_NONE)
5658 			e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
5659 	}
5660 
5661 	if (!pitip->to_address && pitip->current_client != NULL)
5662 		e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL);
5663 
5664 	/* check if it is a  recur instance (no master object) and
5665 	 * add a property */
5666 	if (itip_view_get_recur_check_state (view)) {
5667 		prop = icalproperty_new_x ("All");
5668 		icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE");
5669 		icalcomponent_add_property (pitip->ical_comp, prop);
5670 	}
5671 
5672 	switch (response) {
5673 		case ITIP_VIEW_RESPONSE_ACCEPT:
5674 			if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
5675 				status = change_status (
5676 					pitip->registry,
5677 					pitip->ical_comp,
5678 					pitip->to_address,
5679 					ICAL_PARTSTAT_ACCEPTED);
5680 			else
5681 				status = TRUE;
5682 			if (status) {
5683 				e_cal_component_rescan (pitip->comp);
5684 				update_item (pitip, view, response);
5685 			}
5686 			break;
5687 		case ITIP_VIEW_RESPONSE_TENTATIVE:
5688 			status = change_status (
5689 					pitip->registry,
5690 					pitip->ical_comp,
5691 					pitip->to_address,
5692 					ICAL_PARTSTAT_TENTATIVE);
5693 			if (status) {
5694 				e_cal_component_rescan (pitip->comp);
5695 				update_item (pitip, view, response);
5696 			}
5697 			break;
5698 		case ITIP_VIEW_RESPONSE_DECLINE:
5699 			if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
5700 				status = change_status (
5701 					pitip->registry,
5702 					pitip->ical_comp,
5703 					pitip->to_address,
5704 					ICAL_PARTSTAT_DECLINED);
5705 			else {
5706 				prop = icalproperty_new_x ("1");
5707 				icalproperty_set_x_name (prop, "X-GW-DECLINED");
5708 				icalcomponent_add_property (pitip->ical_comp, prop);
5709 				status = TRUE;
5710 			}
5711 
5712 			if (status) {
5713 				e_cal_component_rescan (pitip->comp);
5714 				update_item (pitip, view, response);
5715 			}
5716 			break;
5717 		case ITIP_VIEW_RESPONSE_UPDATE:
5718 			update_attendee_status (pitip, view);
5719 			break;
5720 		case ITIP_VIEW_RESPONSE_CANCEL:
5721 			update_item (pitip, view, response);
5722 			break;
5723 		case ITIP_VIEW_RESPONSE_REFRESH:
5724 			send_item (pitip, view);
5725 			break;
5726 		case ITIP_VIEW_RESPONSE_OPEN:
5727 			/* Prioritize ahead of GTK+ redraws. */
5728 			g_idle_add_full (
5729 				G_PRIORITY_HIGH_IDLE,
5730 				idle_open_cb, pitip, NULL);
5731 			return;
5732 		default:
5733 			break;
5734 	}
5735 }
5736 
5737 static gboolean
5738 check_is_instance (icalcomponent *icalcomp)
5739 {
5740 	icalproperty *icalprop;
5741 
5742 	icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
5743 	while (icalprop) {
5744 		const gchar *x_name;
5745 
5746 		x_name = icalproperty_get_x_name (icalprop);
5747 		if (!strcmp (x_name, "X-GW-RECURRENCE-KEY")) {
5748 			return TRUE;
5749 		}
5750 		icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
5751 	}
5752 
5753 	return FALSE;
5754 }
5755 
5756 static gboolean
5757 in_proper_folder (ESourceRegistry *registry,
5758                   CamelFolder *folder)
5759 {
5760 	EShell *shell;
5761 	EShellBackend *shell_backend;
5762 	EMailBackend *backend;
5763 	EMailSession *session;
5764 	MailFolderCache *folder_cache;
5765 	gboolean res = TRUE;
5766 	CamelFolderInfoFlags flags = 0;
5767 
5768 	if (!folder)
5769 		return FALSE;
5770 
5771 	shell = e_shell_get_default ();
5772 	shell_backend = e_shell_get_backend_by_name (shell, "mail");
5773 	backend = E_MAIL_BACKEND (shell_backend);
5774 	session = e_mail_backend_get_session (backend);
5775 	folder_cache = e_mail_session_get_folder_cache (session);
5776 
5777 	if (mail_folder_cache_get_folder_info_flags (folder_cache, folder, &flags)) {
5778 		/* it should be neither trash nor junk folder, */
5779 		res = ((flags & CAMEL_FOLDER_TYPE_MASK) !=  CAMEL_FOLDER_TYPE_TRASH &&
5780 		       (flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_JUNK &&
5781 			  /* it can be Inbox */
5782 			((flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX ||
5783 			  /* or any other virtual folder */
5784 			  CAMEL_IS_VEE_FOLDER (folder) ||
5785 			  /* or anything else except of sent, outbox or drafts folder */
5786 			  (!em_utils_folder_is_sent (registry, folder) &&
5787 			   !em_utils_folder_is_outbox (registry, folder) &&
5788 			   !em_utils_folder_is_drafts (registry, folder))
5789 			));
5790 	} else {
5791 		/* cannot check for Inbox folder here */
5792 		res = (folder->folder_flags & (CAMEL_FOLDER_IS_TRASH | CAMEL_FOLDER_IS_JUNK)) == 0 && (
5793 		      (CAMEL_IS_VEE_FOLDER (folder)) || (
5794 		      !em_utils_folder_is_sent (registry, folder) &&
5795 		      !em_utils_folder_is_outbox (registry, folder) &&
5796 		      !em_utils_folder_is_drafts (registry, folder)));
5797 	}
5798 
5799 	return res;
5800 }
5801 
5802 void
5803 itip_view_init_view (ItipView *view)
5804 {
5805 	EShell *shell;
5806 	EShellSettings *shell_settings;
5807 	ESourceRegistry *registry;
5808 	ECalComponentText text;
5809 	ECalComponentOrganizer organizer;
5810 	ECalComponentDateTime datetime;
5811 	icaltimezone *from_zone, *to_zone;
5812 	GString *gstring = NULL;
5813 	GSList *list, *l;
5814 	icalcomponent *icalcomp;
5815 	const gchar *string, *org;
5816 	gint i;
5817 	gboolean response_enabled;
5818 	gboolean have_alarms = FALSE;
5819 	EMailPartItip *info;
5820 
5821 	info = view->priv->itip_part;
5822 	g_return_if_fail (info != NULL);
5823 
5824 	shell = e_shell_get_default ();
5825 	registry = e_shell_get_registry (shell);
5826 	shell_settings = e_shell_get_shell_settings (shell);
5827 
5828 	info->registry = g_object_ref (registry);
5829 
5830         /* Reset current client before initializing view */
5831 	info->current_client = NULL;
5832 
5833 	/* Initialize the ecal hashes */
5834 	for (i = 0; i < E_CAL_CLIENT_SOURCE_TYPE_LAST; i++)
5835 		info->clients[i] = g_hash_table_new_full (
5836 			(GHashFunc) g_str_hash,
5837 			(GEqualFunc) g_str_equal,
5838 			(GDestroyNotify) g_free,
5839 			(GDestroyNotify) g_object_unref);
5840 
5841         /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */
5842 	if (!extract_itip_data (info, view, &have_alarms))
5843 		return;
5844 
5845 	response_enabled = in_proper_folder (registry, info->folder);
5846 
5847 	if (!response_enabled) {
5848 		itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL);
5849 	} else {
5850 		itip_view_set_show_inherit_alarm_check (
5851 			view,
5852 			have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method ==  ICAL_METHOD_REQUEST));
5853 
5854 		switch (info->method) {
5855 			case ICAL_METHOD_PUBLISH:
5856 			case ICAL_METHOD_REQUEST:
5857                                 /*
5858                                  * Treat meeting request (sent by organizer directly) and
5859                                  * published evend (forwarded by organizer or attendee) alike:
5860                                  * if the event has an organizer, then it can be replied to and
5861                                  * we show the "accept/tentative/decline" choice.
5862                                  * Otherwise only show "accept".
5863                                  */
5864 				itip_view_set_mode (
5865 					view,
5866 					info->has_organizer ?
5867 					ITIP_VIEW_MODE_REQUEST :
5868 					ITIP_VIEW_MODE_PUBLISH);
5869 				break;
5870 			case ICAL_METHOD_REPLY:
5871 				itip_view_set_mode (view, ITIP_VIEW_MODE_REPLY);
5872 				break;
5873 			case ICAL_METHOD_ADD:
5874 				itip_view_set_mode (view, ITIP_VIEW_MODE_ADD);
5875 				break;
5876 			case ICAL_METHOD_CANCEL:
5877 				itip_view_set_mode (view, ITIP_VIEW_MODE_CANCEL);
5878 				break;
5879 			case ICAL_METHOD_REFRESH:
5880 				itip_view_set_mode (view, ITIP_VIEW_MODE_REFRESH);
5881 				break;
5882 			case ICAL_METHOD_COUNTER:
5883 				itip_view_set_mode (view, ITIP_VIEW_MODE_COUNTER);
5884 				break;
5885 			case ICAL_METHOD_DECLINECOUNTER:
5886 				itip_view_set_mode (view, ITIP_VIEW_MODE_DECLINECOUNTER);
5887 				break;
5888 			case ICAL_METHOD_X :
5889                                 /* Handle appointment requests from Microsoft Live. This is
5890                                  * a best-at-hand-now handling. Must be revisited when we have
5891                                  * better access to the source of such meetings */
5892 				info->method = ICAL_METHOD_REQUEST;
5893 				itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST);
5894 				break;
5895 			default:
5896 				return;
5897 		}
5898 	}
5899 
5900 	itip_view_set_item_type (view, info->type);
5901 
5902 	if (response_enabled) {
5903 		switch (info->method) {
5904 			case ICAL_METHOD_REQUEST:
5905                                 /* FIXME What about the name? */
5906 				itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : info->delegator_address);
5907 			case ICAL_METHOD_PUBLISH:
5908 			case ICAL_METHOD_ADD:
5909 			case ICAL_METHOD_CANCEL:
5910 			case ICAL_METHOD_DECLINECOUNTER:
5911 				itip_view_set_show_update_check (view, FALSE);
5912 
5913                                 /* An organizer sent this */
5914 				e_cal_component_get_organizer (info->comp, &organizer);
5915 				org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value);
5916 
5917 				itip_view_set_organizer (view, org);
5918 				if (organizer.sentby)
5919 					itip_view_set_organizer_sentby (
5920 						view, itip_strip_mailto (organizer.sentby));
5921 
5922 					if (info->my_address) {
5923 						if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), info->my_address))
5924 							&& !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), info->my_address))
5925 							&& (info->to_address && g_ascii_strcasecmp (info->to_address, info->my_address)))
5926 							itip_view_set_proxy (view, info->to_name ? info->to_name : info->to_address);
5927 					}
5928 					break;
5929 			case ICAL_METHOD_REPLY:
5930 			case ICAL_METHOD_REFRESH:
5931 			case ICAL_METHOD_COUNTER:
5932 				itip_view_set_show_update_check (view, TRUE);
5933 
5934                                 /* An attendee sent this */
5935 				e_cal_component_get_attendee_list (info->comp, &list);
5936 				if (list != NULL) {
5937 					ECalComponentAttendee *attendee;
5938 
5939 					attendee = list->data;
5940 
5941 					itip_view_set_attendee (view, attendee->cn ? attendee->cn : itip_strip_mailto (attendee->value));
5942 
5943 					if (attendee->sentby)
5944 						itip_view_set_attendee_sentby (view, itip_strip_mailto (attendee->sentby));
5945 
5946 					if (info->my_address) {
5947 						if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), info->my_address))
5948 							&& !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), info->my_address))
5949 							&& (info->from_address && g_ascii_strcasecmp (info->from_address, info->my_address)))
5950 							itip_view_set_proxy (view, info->from_name ? info->from_name : info->from_address);
5951 					}
5952 
5953 					e_cal_component_free_attendee_list (list);
5954 				}
5955 				break;
5956 			default:
5957 				g_assert_not_reached ();
5958 				break;
5959 		}
5960 	}
5961 
5962 	e_cal_component_get_summary (info->comp, &text);
5963 	itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None"));
5964 
5965 	e_cal_component_get_location (info->comp, &string);
5966 	itip_view_set_location (view, string);
5967 
5968         /* Status really only applies for REPLY */
5969 	if (response_enabled && info->method == ICAL_METHOD_REPLY) {
5970 		e_cal_component_get_attendee_list (info->comp, &list);
5971 		if (list != NULL) {
5972 			ECalComponentAttendee *a = list->data;
5973 
5974 			switch (a->status) {
5975 				case ICAL_PARTSTAT_ACCEPTED:
5976 					itip_view_set_status (view, _("Accepted"));
5977 					break;
5978 				case ICAL_PARTSTAT_TENTATIVE:
5979 					itip_view_set_status (view, _("Tentatively Accepted"));
5980 					break;
5981 				case ICAL_PARTSTAT_DECLINED:
5982 					itip_view_set_status (view, _("Declined"));
5983 					break;
5984 				case ICAL_PARTSTAT_DELEGATED:
5985 					itip_view_set_status (view, _("Delegated"));
5986 					break;
5987 				default:
5988 					itip_view_set_status (view, _("Unknown"));
5989 			}
5990 		}
5991 		e_cal_component_free_attendee_list (list);
5992 	}
5993 
5994 	if (info->method == ICAL_METHOD_REPLY
5995 		|| info->method == ICAL_METHOD_COUNTER
5996 		|| info->method == ICAL_METHOD_DECLINECOUNTER) {
5997                 /* FIXME Check spec to see if multiple comments are actually valid */
5998                 /* Comments for iTIP are limited to one per object */
5999 		e_cal_component_get_comment_list (info->comp, &list);
6000 		if (list) {
6001 			ECalComponentText *text = list->data;
6002 
6003 			if (text->value) {
6004 				gchar *html;
6005 
6006 				html = camel_text_to_html (
6007 					text->value,
6008 					CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
6009 					CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
6010 					CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
6011 					0);
6012 
6013 				itip_view_set_comment (view, html);
6014 
6015 				g_free (html);
6016 			}
6017 		}
6018 		e_cal_component_free_text_list (list);
6019 	}
6020 
6021 	e_cal_component_get_description_list (info->comp, &list);
6022 	for (l = list; l; l = l->next) {
6023 		ECalComponentText *text = l->data;
6024 
6025 		if (!gstring && text->value)
6026 			gstring = g_string_new (text->value);
6027 		else if (text->value)
6028 			g_string_append_printf (gstring, "\n\n%s", text->value);
6029 	}
6030 
6031 	e_cal_component_free_text_list (list);
6032 
6033 	if (gstring) {
6034 		gchar *html;
6035 
6036 		html = camel_text_to_html (
6037 			gstring->str,
6038 			CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
6039 			CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
6040 			CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
6041 			CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
6042 			CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
6043 			0);
6044 
6045 		itip_view_set_description (view, html);
6046 		g_string_free (gstring, TRUE);
6047 		g_free (html);
6048 	}
6049 
6050 	to_zone = e_shell_settings_get_pointer (shell_settings, "cal-timezone");
6051 
6052 	e_cal_component_get_dtstart (info->comp, &datetime);
6053 	info->start_time = 0;
6054 	if (datetime.value) {
6055 		struct tm start_tm;
6056 
6057                 /* If the timezone is not in the component, guess the local time */
6058                 /* Should we guess if the timezone is an olsen name somehow? */
6059 		if (datetime.value->is_utc)
6060 			from_zone = icaltimezone_get_utc_timezone ();
6061 		else if (!datetime.value->is_utc && datetime.tzid)
6062 			from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
6063 		else
6064 			from_zone = NULL;
6065 
6066 		start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
6067 
6068 		itip_view_set_start (view, &start_tm, datetime.value->is_date);
6069 		info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
6070 	}
6071 
6072 	icalcomp = e_cal_component_get_icalcomponent (info->comp);
6073 
6074         /* Set the recurrence id */
6075 	if (check_is_instance (icalcomp) && datetime.value) {
6076 		ECalComponentRange *recur_id;
6077 		struct icaltimetype icaltime = icaltime_convert_to_zone (*datetime.value, to_zone);
6078 
6079 		recur_id = g_new0 (ECalComponentRange, 1);
6080 		recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE;
6081 		recur_id->datetime.value = &icaltime;
6082 		recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone);
6083 		e_cal_component_set_recurid (info->comp, recur_id);
6084 		g_free (recur_id); /* it's ok to call g_free here */
6085 	}
6086 	e_cal_component_free_datetime (&datetime);
6087 
6088 	e_cal_component_get_dtend (info->comp, &datetime);
6089 	info->end_time = 0;
6090 	if (datetime.value) {
6091 		struct tm end_tm;
6092 
6093                 /* If the timezone is not in the component, guess the local time */
6094                 /* Should we guess if the timezone is an olsen name somehow? */
6095 		if (datetime.value->is_utc)
6096 			from_zone = icaltimezone_get_utc_timezone ();
6097 		else if (!datetime.value->is_utc && datetime.tzid)
6098 			from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
6099 		else
6100 			from_zone = NULL;
6101 
6102 		if (datetime.value->is_date) {
6103                         /* RFC says the DTEND is not inclusive, thus subtract one day
6104                          * if we have a date */
6105 
6106 			icaltime_adjust (datetime.value, -1, 0, 0, 0);
6107 		}
6108 
6109 		end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
6110 
6111 		itip_view_set_end (view, &end_tm, datetime.value->is_date);
6112 		info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
6113 	}
6114 	e_cal_component_free_datetime (&datetime);
6115 
6116         /* Recurrence info */
6117         /* FIXME Better recurring description */
6118 	if (e_cal_component_has_recurrences (info->comp)) {
6119                 /* FIXME Tell the user we don't support recurring tasks */
6120 		switch (info->type) {
6121 			case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
6122 				itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This meeting recurs"));
6123 				break;
6124 			case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
6125 				itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This task recurs"));
6126 				break;
6127 			case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
6128 				itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This memo recurs"));
6129 				break;
6130 			default:
6131 				g_assert_not_reached ();
6132 				break;
6133 		}
6134 	}
6135 
6136 	g_signal_connect (
6137 		view, "response",
6138 		G_CALLBACK (view_response_cb), info);
6139 
6140 	if (response_enabled) {
6141 		itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (info->method == ICAL_METHOD_PUBLISH || info->method ==  ICAL_METHOD_REQUEST));
6142 
6143 		if (info->calendar_uid) {
6144 			start_calendar_server_by_uid (info, view, info->calendar_uid, info->type);
6145 		} else {
6146 			find_server (info, view, info->comp);
6147 			set_buttons_sensitive (info, view);
6148 		}
6149 	} else if (view->priv->dom_document) {
6150 		/* The Open Calendar button can be shown, thus enable it */
6151 		WebKitDOMElement *el;
6152 
6153 		el = webkit_dom_document_get_element_by_id (
6154 			view->priv->dom_document, BUTTON_OPEN_CALENDAR);
6155 		webkit_dom_html_button_element_set_disabled (
6156 			WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
6157 	}
6158 }