evolution-3.6.4/calendar/gui/itip-utils.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@ximian.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 <time.h>
  28 #include <glib/gi18n.h>
  29 #include <libical/ical.h>
  30 #include <libsoup/soup.h>
  31 
  32 #include <e-util/e-dialog-utils.h>
  33 
  34 #include <composer/e-msg-composer.h>
  35 
  36 #include "itip-utils.h"
  37 #include "dialogs/comp-editor-util.h"
  38 
  39 #define d(x)
  40 
  41 static const gchar *itip_methods[] = {
  42 	"PUBLISH",
  43 	"REQUEST",
  44 	"REPLY",
  45 	"ADD",
  46 	"CANCEL",
  47 	"RERESH",
  48 	"COUNTER",
  49 	"DECLINECOUNTER"
  50 };
  51 
  52 static icalproperty_method itip_methods_enum[] = {
  53     ICAL_METHOD_PUBLISH,
  54     ICAL_METHOD_REQUEST,
  55     ICAL_METHOD_REPLY,
  56     ICAL_METHOD_ADD,
  57     ICAL_METHOD_CANCEL,
  58     ICAL_METHOD_REFRESH,
  59     ICAL_METHOD_COUNTER,
  60     ICAL_METHOD_DECLINECOUNTER,
  61 };
  62 
  63 /**
  64  * itip_get_default_name_and_address:
  65  * @registry: an #ESourceRegistry
  66  * @name: return location for the user's real name, or %NULL
  67  * @address: return location for the user's email address, or %NULL
  68  *
  69  * Returns the real name and email address of the default mail identity,
  70  * if available.  If no default mail identity is available, @name and
  71  * @address are set to %NULL and the function returns %FALSE.
  72  *
  73  * Returns: %TRUE if @name and/or @address were set
  74  **/
  75 gboolean
  76 itip_get_default_name_and_address (ESourceRegistry *registry,
  77                                    gchar **name,
  78                                    gchar **address)
  79 {
  80 	ESource *source;
  81 	ESourceExtension *extension;
  82 	const gchar *extension_name;
  83 	gboolean success;
  84 
  85 	source = e_source_registry_ref_default_mail_identity (registry);
  86 
  87 	if (source != NULL) {
  88 		extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
  89 		extension = e_source_get_extension (source, extension_name);
  90 
  91 		if (name != NULL)
  92 			*name = e_source_mail_identity_dup_name (
  93 				E_SOURCE_MAIL_IDENTITY (extension));
  94 
  95 		if (address != NULL)
  96 			*address = e_source_mail_identity_dup_address (
  97 				E_SOURCE_MAIL_IDENTITY (extension));
  98 
  99 		g_object_unref (source);
 100 
 101 		success = TRUE;
 102 
 103 	} else {
 104 		if (name != NULL)
 105 			*name = NULL;
 106 
 107 		if (address != NULL)
 108 			*address = NULL;
 109 
 110 		success = FALSE;
 111 	}
 112 
 113 	return success;
 114 }
 115 
 116 /**
 117  * itip_get_user_identities:
 118  * @registry: an #ESourceRegistry
 119  *
 120  * Returns a %NULL-terminated array of name + address strings based on
 121  * registered mail identities.  Free the returned array with g_strfreev().
 122  *
 123  * Returns: an %NULL-terminated array of mail identity strings
 124  **/
 125 gchar **
 126 itip_get_user_identities (ESourceRegistry *registry)
 127 {
 128 	GList *list, *iter;
 129 	const gchar *extension_name;
 130 	gchar **identities;
 131 	guint ii = 0;
 132 
 133 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
 134 
 135 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 136 
 137 	list = e_source_registry_list_sources (registry, extension_name);
 138 
 139 	identities = g_new0 (gchar *, g_list_length (list) + 1);
 140 
 141 	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
 142 		ESource *source = E_SOURCE (iter->data);
 143 		ESourceMailIdentity *extension;
 144 		const gchar *name, *address;
 145 
 146 		extension = e_source_get_extension (source, extension_name);
 147 
 148 		name = e_source_mail_identity_get_name (extension);
 149 		address = e_source_mail_identity_get_address (extension);
 150 
 151 		if (name == NULL || address == NULL)
 152 			continue;
 153 
 154 		identities[ii++] = g_strdup_printf ("%s <%s>", name, address);
 155 	}
 156 
 157 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 158 
 159 	return identities;
 160 }
 161 
 162 /**
 163  * itip_get_fallback_identity:
 164  * @registry: an #ESourceRegistry
 165  *
 166  * Returns a name + address string taken from the default mail identity,
 167  * but only if the corresponding account is enabled.  If the account is
 168  * disabled, the function returns %NULL.  This is meant to be used as a
 169  * fallback identity for organizers.  Free the returned string with
 170  * g_free().
 171  *
 172  * Returns: a fallback mail identity, or %NULL
 173  **/
 174 gchar *
 175 itip_get_fallback_identity (ESourceRegistry *registry)
 176 {
 177 	ESource *source;
 178 	ESourceMailIdentity *mail_identity;
 179 	const gchar *extension_name;
 180 	const gchar *address;
 181 	const gchar *name;
 182 	gchar *identity = NULL;
 183 
 184 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
 185 
 186 	source = e_source_registry_ref_default_mail_identity (registry);
 187 
 188 	if (source == NULL)
 189 		return NULL;
 190 
 191 	if (!e_source_get_enabled (source)) {
 192 		g_object_unref (source);
 193 		return NULL;
 194 	}
 195 
 196 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 197 	mail_identity = e_source_get_extension (source, extension_name);
 198 
 199 	name = e_source_mail_identity_get_name (mail_identity);
 200 	address = e_source_mail_identity_get_address (mail_identity);
 201 
 202 	if (name != NULL && address != NULL)
 203 		identity = g_strdup_printf ("%s <%s>", name, address);
 204 
 205 	g_object_unref (source);
 206 
 207 	return identity;
 208 }
 209 
 210 /**
 211  * itip_address_is_user:
 212  * @registry: an #ESourceRegistry
 213  * @address: an email address
 214  *
 215  * Looks for a registered mail identity with a matching email address.
 216  *
 217  * Returns: %TRUE if a match was found, %FALSE if not
 218  **/
 219 gboolean
 220 itip_address_is_user (ESourceRegistry *registry,
 221                       const gchar *address)
 222 {
 223 	GList *list, *iter;
 224 	const gchar *extension_name;
 225 	gboolean match = FALSE;
 226 
 227 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
 228 	g_return_val_if_fail (address != NULL, FALSE);
 229 
 230 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 231 
 232 	list = e_source_registry_list_sources (registry, extension_name);
 233 
 234 	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
 235 		ESource *source = E_SOURCE (iter->data);
 236 		ESourceMailIdentity *extension;
 237 		const gchar *id_address;
 238 
 239 		extension = e_source_get_extension (source, extension_name);
 240 		id_address = e_source_mail_identity_get_address (extension);
 241 
 242 		if (id_address == NULL)
 243 			continue;
 244 
 245 		if (g_ascii_strcasecmp (address, id_address) == 0) {
 246 			match = TRUE;
 247 			break;
 248 		}
 249 	}
 250 
 251 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 252 
 253 	return match;
 254 }
 255 
 256 gboolean
 257 itip_organizer_is_user (ESourceRegistry *registry,
 258                         ECalComponent *comp,
 259                         ECalClient *cal_client)
 260 {
 261 	return itip_organizer_is_user_ex (registry, comp, cal_client, FALSE);
 262 }
 263 
 264 gboolean
 265 itip_organizer_is_user_ex (ESourceRegistry *registry,
 266                            ECalComponent *comp,
 267                            ECalClient *cal_client,
 268                            gboolean skip_cap_test)
 269 {
 270 	ECalComponentOrganizer organizer;
 271 	const gchar *strip;
 272 	gboolean user_org = FALSE;
 273 
 274 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
 275 
 276 	if (!e_cal_component_has_organizer (comp) ||
 277 		(!skip_cap_test && e_client_check_capability (
 278 		E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER)))
 279 		return FALSE;
 280 
 281 	e_cal_component_get_organizer (comp, &organizer);
 282 	if (organizer.value != NULL) {
 283 
 284 		strip = itip_strip_mailto (organizer.value);
 285 
 286 		if (e_client_check_capability (E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) {
 287 			gchar *email = NULL;
 288 
 289 			if (e_client_get_backend_property_sync (E_CLIENT (cal_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &email, NULL, NULL) &&
 290 				!g_ascii_strcasecmp (email, strip)) {
 291 				g_free (email);
 292 
 293 				return TRUE;
 294 			}
 295 
 296 			g_free (email);
 297 			return FALSE;
 298 		}
 299 
 300 		user_org = itip_address_is_user (registry, strip);
 301 	}
 302 
 303 	return user_org;
 304 }
 305 
 306 gboolean
 307 itip_sentby_is_user (ESourceRegistry *registry,
 308                      ECalComponent *comp,
 309                      ECalClient *cal_client)
 310 {
 311 	ECalComponentOrganizer organizer;
 312 	const gchar *strip;
 313 	gboolean user_sentby = FALSE;
 314 
 315 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
 316 
 317 	if (!e_cal_component_has_organizer (comp) ||
 318 		e_client_check_capability (
 319 		E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER))
 320 		return FALSE;
 321 
 322 	e_cal_component_get_organizer (comp, &organizer);
 323 	if (organizer.sentby != NULL) {
 324 		strip = itip_strip_mailto (organizer.sentby);
 325 		user_sentby = itip_address_is_user (registry, strip);
 326 	}
 327 
 328 	return user_sentby;
 329 }
 330 
 331 static ECalComponentAttendee *
 332 get_attendee (GSList *attendees,
 333               gchar *address)
 334 {
 335 	GSList *l;
 336 
 337 	if (!address)
 338 		return NULL;
 339 
 340 	for (l = attendees; l; l = l->next) {
 341 		ECalComponentAttendee *attendee = l->data;
 342 
 343 		if (!g_ascii_strcasecmp (itip_strip_mailto (attendee->value), address)) {
 344 			return attendee;
 345 		}
 346 	}
 347 
 348 	return NULL;
 349 }
 350 
 351 static ECalComponentAttendee *
 352 get_attendee_if_attendee_sentby_is_user (GSList *attendees,
 353                                          gchar *address)
 354 {
 355 	GSList *l;
 356 
 357 	for (l = attendees; l; l = l->next) {
 358 		ECalComponentAttendee *attendee = l->data;
 359 
 360 		if (attendee->sentby && g_str_equal (
 361 			itip_strip_mailto (attendee->sentby), address)) {
 362 			return attendee;
 363 		}
 364 	}
 365 
 366 	return NULL;
 367 }
 368 
 369 static gchar *
 370 html_new_lines_for (const gchar *string)
 371 {
 372 	gchar **lines;
 373 	gchar *joined;
 374 
 375 	lines = g_strsplit_set (string, "\n", -1);
 376 	joined = g_strjoinv ("<br>", lines);
 377 	g_strfreev (lines);
 378 
 379 	return joined;
 380 }
 381 
 382 gchar *
 383 itip_get_comp_attendee (ESourceRegistry *registry,
 384                         ECalComponent *comp,
 385                         ECalClient *cal_client)
 386 {
 387 	ESource *source;
 388 	GSList *attendees;
 389 	ECalComponentAttendee *attendee = NULL;
 390 	GList *list, *link;
 391 	const gchar *extension_name;
 392 	gchar *address = NULL;
 393 
 394 	e_cal_component_get_attendee_list (comp, &attendees);
 395 
 396 	if (cal_client)
 397 		e_client_get_backend_property_sync (
 398 			E_CLIENT (cal_client),
 399 			CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
 400 			&address, NULL, NULL);
 401 
 402 	if (address != NULL && *address != '\0') {
 403 		attendee = get_attendee (attendees, address);
 404 
 405 		if (attendee) {
 406 			gchar *user_email;
 407 
 408 			user_email = g_strdup (
 409 				itip_strip_mailto (attendee->value));
 410 			e_cal_component_free_attendee_list (attendees);
 411 			g_free (address);
 412 
 413 			return user_email;
 414 		}
 415 
 416 		attendee = get_attendee_if_attendee_sentby_is_user (
 417 			attendees, address);
 418 
 419 		if (attendee != NULL) {
 420 			gchar *user_email;
 421 
 422 			user_email = g_strdup (
 423 				itip_strip_mailto (attendee->sentby));
 424 			e_cal_component_free_attendee_list (attendees);
 425 			g_free (address);
 426 
 427 			return user_email;
 428 		}
 429 
 430 		g_free (address);
 431 		address = NULL;
 432 	}
 433 
 434 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 435 	list = e_source_registry_list_sources (registry, extension_name);
 436 
 437 	for (link = list; link != NULL; link = g_list_next (link)) {
 438 		ESourceExtension *extension;
 439 
 440 		source = E_SOURCE (link->data);
 441 
 442 		if (!e_source_get_enabled (source))
 443 			continue;
 444 
 445 		extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
 446 		extension = e_source_get_extension (source, extension_name);
 447 
 448 		address = e_source_mail_identity_dup_address (
 449 			E_SOURCE_MAIL_IDENTITY (extension));
 450 
 451 		if (address == NULL)
 452 			continue;
 453 
 454 		attendee = get_attendee (attendees, address);
 455 		if (attendee != NULL) {
 456 			gchar *user_email;
 457 
 458 			user_email = g_strdup (
 459 				itip_strip_mailto (attendee->value));
 460 			e_cal_component_free_attendee_list (attendees);
 461 
 462 			g_free (address);
 463 
 464 			return user_email;
 465 		}
 466 
 467 		/* If the account was not found in the attendees list, then
 468 		 * let's check the 'sentby' fields of the attendees if we can
 469 		 * find the account. */
 470 		attendee = get_attendee_if_attendee_sentby_is_user (
 471 			attendees, address);
 472 		if (attendee) {
 473 			gchar *user_email;
 474 
 475 			user_email = g_strdup (
 476 				itip_strip_mailto (attendee->sentby));
 477 			e_cal_component_free_attendee_list (attendees);
 478 
 479 			g_free (address);
 480 
 481 			return user_email;
 482 		}
 483 
 484 		g_free (address);
 485 	}
 486 
 487 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
 488 
 489 	/* We could not find the attendee in the component, so just give
 490 	 * the default account address if the email address is not set in
 491 	 * the backend. */
 492 	/* FIXME do we have a better way ? */
 493 	itip_get_default_name_and_address (registry, NULL, &address);
 494 
 495 	e_cal_component_free_attendee_list (attendees);
 496 
 497 	if (address == NULL)
 498 		address = g_strdup ("");
 499 
 500 	return address;
 501 }
 502 
 503 const gchar *
 504 itip_strip_mailto (const gchar *address)
 505 {
 506 	if (address == NULL)
 507 		return NULL;
 508 
 509 	if (!g_ascii_strncasecmp (address, "mailto:", 7))
 510 		address += 7;
 511 
 512 	return address;
 513 }
 514 
 515 static gchar *
 516 get_label (struct icaltimetype *tt,
 517            gboolean use_24_hour_format)
 518 {
 519 	gchar buffer[1000];
 520 	struct tm tmp_tm;
 521 
 522 	tmp_tm = icaltimetype_to_tm (tt);
 523 
 524 	e_time_format_date_and_time (
 525 		&tmp_tm, use_24_hour_format, FALSE, FALSE, buffer, 1000);
 526 
 527 	return g_strdup (buffer);
 528 }
 529 
 530 typedef struct {
 531 	GHashTable *tzids;
 532 	icalcomponent *icomp;
 533 	ECalClient *client;
 534 	icalcomponent *zones;
 535 } ItipUtilTZData;
 536 
 537 static void
 538 foreach_tzid_callback (icalparameter *param,
 539                        gpointer data)
 540 {
 541 	ItipUtilTZData *tz_data = data;
 542 	const gchar *tzid;
 543 	icaltimezone *zone = NULL;
 544 	icalcomponent *vtimezone_comp;
 545 
 546 	/* Get the TZID string from the parameter. */
 547 	tzid = icalparameter_get_tzid (param);
 548 	if (!tzid || g_hash_table_lookup (tz_data->tzids, tzid))
 549 		return;
 550 
 551 	/* Look for the timezone */
 552 	if (tz_data->zones != NULL)
 553 		zone = icalcomponent_get_timezone (tz_data->zones, tzid);
 554 	if (zone == NULL)
 555 		zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
 556 	if (zone == NULL && tz_data->client != NULL)
 557 		e_cal_client_get_timezone_sync (tz_data->client, tzid, &zone, NULL, NULL);
 558 	if (zone == NULL)
 559 		return;
 560 
 561 	/* Convert it to a string and add it to the hash. */
 562 	vtimezone_comp = icaltimezone_get_component (zone);
 563 	if (!vtimezone_comp)
 564 		return;
 565 
 566 	icalcomponent_add_component (
 567 		tz_data->icomp, icalcomponent_new_clone (vtimezone_comp));
 568 	g_hash_table_insert (tz_data->tzids, (gchar *) tzid, (gchar *) tzid);
 569 }
 570 
 571 static icalcomponent *
 572 comp_toplevel_with_zones (ECalComponentItipMethod method,
 573                           ECalComponent *comp,
 574                           ECalClient *cal_client,
 575                           icalcomponent *zones)
 576 {
 577 	icalcomponent *top_level, *icomp;
 578 	icalproperty *prop;
 579 	icalvalue *value;
 580 	ItipUtilTZData tz_data;
 581 
 582 	top_level = e_cal_util_new_top_level ();
 583 
 584 	prop = icalproperty_new (ICAL_METHOD_PROPERTY);
 585 	value = icalvalue_new_method (itip_methods_enum[method]);
 586 	icalproperty_set_value (prop, value);
 587 	icalcomponent_add_property (top_level, prop);
 588 
 589 	icomp = e_cal_component_get_icalcomponent (comp);
 590 	icomp = icalcomponent_new_clone (icomp);
 591 
 592 	tz_data.tzids = g_hash_table_new (g_str_hash, g_str_equal);
 593 	tz_data.icomp = top_level;
 594 	tz_data.client = cal_client;
 595 	tz_data.zones = zones;
 596 	icalcomponent_foreach_tzid (icomp, foreach_tzid_callback, &tz_data);
 597 	g_hash_table_destroy (tz_data.tzids);
 598 
 599 	icalcomponent_add_component (top_level, icomp);
 600 
 601 	return top_level;
 602 }
 603 
 604 static gboolean
 605 users_has_attendee (const GSList *users,
 606                     const gchar *address)
 607 {
 608 	const GSList *l;
 609 
 610 	for (l = users; l != NULL; l = l->next) {
 611 		if (!g_ascii_strcasecmp (address, l->data))
 612 			return TRUE;
 613 	}
 614 
 615 	return FALSE;
 616 }
 617 
 618 static gchar *
 619 comp_from (ECalComponentItipMethod method,
 620            ECalComponent *comp,
 621            ESourceRegistry *registry)
 622 {
 623 	ECalComponentOrganizer organizer;
 624 	ECalComponentAttendee *attendee;
 625 	GSList *attendees;
 626 	gchar *from;
 627 	gchar *sender = NULL;
 628 
 629 	switch (method) {
 630 	case E_CAL_COMPONENT_METHOD_PUBLISH:
 631 		return NULL;
 632 
 633 	case E_CAL_COMPONENT_METHOD_REQUEST:
 634 		return itip_get_comp_attendee (registry, comp, NULL);
 635 
 636 	case E_CAL_COMPONENT_METHOD_REPLY:
 637 		sender = itip_get_comp_attendee (registry, comp, NULL);
 638 		if (sender != NULL)
 639 			return sender;
 640 		if (!e_cal_component_has_attendees (comp))
 641 			return NULL;
 642 
 643 	case E_CAL_COMPONENT_METHOD_CANCEL:
 644 
 645 	case E_CAL_COMPONENT_METHOD_ADD:
 646 
 647 		e_cal_component_get_organizer (comp, &organizer);
 648 		if (organizer.value == NULL) {
 649 			e_notice (
 650 				NULL, GTK_MESSAGE_ERROR,
 651 				_("An organizer must be set."));
 652 			return NULL;
 653 		}
 654 		return g_strdup (itip_strip_mailto (organizer.value));
 655 
 656 	default:
 657 		if (!e_cal_component_has_attendees (comp))
 658 			return NULL;
 659 
 660 		e_cal_component_get_attendee_list (comp, &attendees);
 661 		attendee = attendees->data;
 662 		if (attendee->value != NULL)
 663 			from = g_strdup (itip_strip_mailto (attendee->value));
 664 		else
 665 			from = NULL;
 666 		e_cal_component_free_attendee_list (attendees);
 667 
 668 		return from;
 669 	}
 670 }
 671 
 672 static EDestination **
 673 comp_to_list (ESourceRegistry *registry,
 674               ECalComponentItipMethod method,
 675               ECalComponent *comp,
 676               const GSList *users,
 677               gboolean reply_all,
 678               const GSList *only_attendees)
 679 {
 680 	ECalComponentOrganizer organizer;
 681 	GSList *attendees, *l;
 682 	GPtrArray *array = NULL;
 683 	EDestination *destination;
 684 	gint len;
 685 	gchar *sender = NULL;
 686 
 687 	union {
 688 		gpointer *pdata;
 689 		EDestination **destinations;
 690 	} convert;
 691 
 692 	switch (method) {
 693 	case E_CAL_COMPONENT_METHOD_REQUEST:
 694 	case E_CAL_COMPONENT_METHOD_CANCEL:
 695 		e_cal_component_get_attendee_list (comp, &attendees);
 696 		len = g_slist_length (attendees);
 697 		if (len <= 0) {
 698 			e_notice (
 699 				NULL, GTK_MESSAGE_ERROR,
 700 				_("At least one attendee is necessary"));
 701 			e_cal_component_free_attendee_list (attendees);
 702 			return NULL;
 703 		}
 704 
 705 		e_cal_component_get_organizer (comp, &organizer);
 706 		if (organizer.value == NULL) {
 707 			e_notice (
 708 				NULL, GTK_MESSAGE_ERROR,
 709 				_("An organizer must be set."));
 710 			return NULL;
 711 		}
 712 
 713 		array = g_ptr_array_new ();
 714 
 715 		sender = itip_get_comp_attendee (registry, comp, NULL);
 716 
 717 		for (l = attendees; l != NULL; l = l->next) {
 718 			ECalComponentAttendee *att = l->data;
 719 
 720 			/* Bugfix: 688711 - Varadhan
 721 			 * Resource is also considered as a "attendee". If the respective backend
 722 			 * is able to successfully book resources automagically, it will appear
 723 			 * in the users list and thereby won't get added to the list of destinations
 724 			 * to send the meeting invite, otherwise, as a safety measure, a meeting
 725 			 * invite will be sent to the resources as well. */
 726 			if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
 727 			    att->cutype != ICAL_CUTYPE_GROUP &&
 728 			    att->cutype != ICAL_CUTYPE_RESOURCE)
 729 				continue;
 730 			else if (users_has_attendee (users, att->value))
 731 				continue;
 732 			else if (att->sentby &&
 733 				users_has_attendee (users, att->sentby))
 734 				continue;
 735 			else if (!g_ascii_strcasecmp (
 736 				att->value, organizer.value))
 737 				continue;
 738 			else if (att->sentby && !g_ascii_strcasecmp (
 739 				att->sentby, organizer.sentby))
 740 				continue;
 741 			else if (!g_ascii_strcasecmp (
 742 				itip_strip_mailto (att->value), sender))
 743 				continue;
 744 			else if (att->status == ICAL_PARTSTAT_DELEGATED &&
 745 				(att->delto && *att->delto) && !(att->rsvp) &&
 746 				method == E_CAL_COMPONENT_METHOD_REQUEST)
 747 				continue;
 748 			else if (only_attendees &&
 749 				!comp_editor_have_in_new_attendees_lst (
 750 				only_attendees, itip_strip_mailto (att->value)))
 751 				continue;
 752 
 753 			destination = e_destination_new ();
 754 			if (att->cn != NULL)
 755 				e_destination_set_name (destination, att->cn);
 756 			e_destination_set_email (
 757 				destination, itip_strip_mailto (att->value));
 758 			g_ptr_array_add (array, destination);
 759 		}
 760 		g_free (sender);
 761 		e_cal_component_free_attendee_list (attendees);
 762 		break;
 763 
 764 	case E_CAL_COMPONENT_METHOD_REPLY:
 765 
 766 		if (reply_all) {
 767 			e_cal_component_get_attendee_list (comp, &attendees);
 768 			len = g_slist_length (attendees);
 769 
 770 			if (len <= 0)
 771 				return NULL;
 772 
 773 			array = g_ptr_array_new ();
 774 
 775 			e_cal_component_get_organizer (comp, &organizer);
 776 			sender = itip_get_comp_attendee (registry, comp, NULL);
 777 
 778 			for (l = attendees; l != NULL; l = l->next) {
 779 				ECalComponentAttendee *att = l->data;
 780 
 781 				if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
 782 				    att->cutype != ICAL_CUTYPE_GROUP)
 783 					continue;
 784 				else if (only_attendees &&
 785 					!comp_editor_have_in_new_attendees_lst (
 786 					only_attendees, itip_strip_mailto (att->value)))
 787 					continue;
 788 
 789 				destination = e_destination_new ();
 790 				if (att->cn != NULL)
 791 					e_destination_set_name (destination, att->cn);
 792 				e_destination_set_email (
 793 					destination, itip_strip_mailto (att->value));
 794 				g_ptr_array_add (array, destination);
 795 			}
 796 
 797 			g_free (sender);
 798 			e_cal_component_free_attendee_list (attendees);
 799 
 800 		} else {
 801 			array = g_ptr_array_new ();
 802 
 803 			destination = e_destination_new ();
 804 			e_cal_component_get_organizer (comp, &organizer);
 805 			if (organizer.value)
 806 				e_destination_set_email (
 807 					destination, itip_strip_mailto (organizer.value));
 808 			g_ptr_array_add (array, destination);
 809 		}
 810 		break;
 811 
 812 	case E_CAL_COMPONENT_METHOD_ADD:
 813 	case E_CAL_COMPONENT_METHOD_REFRESH:
 814 	case E_CAL_COMPONENT_METHOD_COUNTER:
 815 	case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
 816 		e_cal_component_get_organizer (comp, &organizer);
 817 		if (organizer.value == NULL) {
 818 			e_notice (
 819 				NULL, GTK_MESSAGE_ERROR,
 820 				_("An organizer must be set."));
 821 			return NULL;
 822 		}
 823 
 824 		array = g_ptr_array_new ();
 825 
 826 		destination = e_destination_new ();
 827 		if (organizer.cn != NULL)
 828 			e_destination_set_name (destination, organizer.cn);
 829 		e_destination_set_email (
 830 			destination, itip_strip_mailto (organizer.value));
 831 		g_ptr_array_add (array, destination);
 832 
 833 		/* send the status to delegatee to the delegate also*/
 834 		e_cal_component_get_attendee_list (comp, &attendees);
 835 		sender = itip_get_comp_attendee (registry, comp, NULL);
 836 
 837 		for (l = attendees; l != NULL; l = l->next) {
 838 			ECalComponentAttendee *att = l->data;
 839 
 840 			if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
 841 			    att->cutype != ICAL_CUTYPE_GROUP)
 842 				continue;
 843 
 844 			if (!g_ascii_strcasecmp (
 845 				itip_strip_mailto (att->value), sender) ||
 846 				(att->sentby && !g_ascii_strcasecmp (
 847 				itip_strip_mailto (att->sentby), sender))) {
 848 
 849 				if (!(att->delfrom && *att->delfrom))
 850 					break;
 851 
 852 				destination = e_destination_new ();
 853 				e_destination_set_email (
 854 					destination, itip_strip_mailto (att->delfrom));
 855 				g_ptr_array_add (array, destination);
 856 			}
 857 
 858 		}
 859 		e_cal_component_free_attendee_list (attendees);
 860 
 861 		break;
 862 	case E_CAL_COMPONENT_METHOD_PUBLISH:
 863 		if (users) {
 864 			const GSList *list;
 865 
 866 			array = g_ptr_array_new ();
 867 
 868 			for (list = users; list != NULL; list = list->next) {
 869 				destination = e_destination_new ();
 870 				e_destination_set_email (destination, list->data);
 871 				g_ptr_array_add (array, destination);
 872 			}
 873 
 874 			break;
 875 		}
 876 	default:
 877 		break;
 878 	}
 879 
 880 	if (array == NULL)
 881 		return NULL;
 882 
 883 	g_ptr_array_add (array, NULL);
 884 	convert.pdata = g_ptr_array_free (array, FALSE);
 885 
 886 	return convert.destinations;
 887 }
 888 
 889 static gchar *
 890 comp_subject (ESourceRegistry *registry,
 891               ECalComponentItipMethod method,
 892               ECalComponent *comp)
 893 {
 894 	ECalComponentText caltext;
 895 	const gchar *description, *prefix = NULL;
 896 	GSList *alist, *l;
 897 	gchar *subject;
 898 	gchar *sender;
 899 	ECalComponentAttendee *a = NULL;
 900 
 901 	e_cal_component_get_summary (comp, &caltext);
 902 	if (caltext.value != NULL)
 903 		description = caltext.value;
 904 	else {
 905 		switch (e_cal_component_get_vtype (comp)) {
 906 		case E_CAL_COMPONENT_EVENT:
 907 			description = _("Event information");
 908 			break;
 909 		case E_CAL_COMPONENT_TODO:
 910 			description = _("Task information");
 911 			break;
 912 		case E_CAL_COMPONENT_JOURNAL:
 913 			description = _("Memo information");
 914 			break;
 915 		case E_CAL_COMPONENT_FREEBUSY:
 916 			description = _("Free/Busy information");
 917 			break;
 918 		default:
 919 			description = _("Calendar information");
 920 		}
 921 	}
 922 
 923 	switch (method) {
 924 	case E_CAL_COMPONENT_METHOD_PUBLISH:
 925 	case E_CAL_COMPONENT_METHOD_REQUEST:
 926 		/* FIXME: If this is an update to a previous
 927 		 * PUBLISH or REQUEST, then
 928 			prefix = U_("Updated");
 929 		 */
 930 		break;
 931 
 932 	case E_CAL_COMPONENT_METHOD_REPLY:
 933 		e_cal_component_get_attendee_list (comp, &alist);
 934 		sender = itip_get_comp_attendee (registry, comp, NULL);
 935 		if (sender) {
 936 
 937 			for (l = alist; l != NULL; l = l->next) {
 938 				a = l->data;
 939 				if ((sender && *sender) && (g_ascii_strcasecmp (
 940 					itip_strip_mailto (a->value), sender) ||
 941 					(a->sentby && g_ascii_strcasecmp (
 942 					itip_strip_mailto (a->sentby), sender))))
 943 					break;
 944 			}
 945 			g_free (sender);
 946 		}
 947 
 948 		if (alist != NULL) {
 949 
 950 			switch (a->status) {
 951 			case ICAL_PARTSTAT_ACCEPTED:
 952 				/* Translators: This is part of the subject
 953 				 * line of a meeting request or update email.
 954 				 * The full subject line would be:
 955 				 * "Accepted: Meeting Name". */
 956 				prefix = C_("Meeting", "Accepted");
 957 				break;
 958 			case ICAL_PARTSTAT_TENTATIVE:
 959 				/* Translators: This is part of the subject
 960 				 * line of a meeting request or update email.
 961 				 * The full subject line would be:
 962 				 * "Tentatively Accepted: Meeting Name". */
 963 				prefix = C_("Meeting", "Tentatively Accepted");
 964 				break;
 965 			case ICAL_PARTSTAT_DECLINED:
 966 				/* Translators: This is part of the subject
 967 				 * line of a meeting request or update email.
 968 				 * The full subject line would be:
 969 				 * "Declined: Meeting Name". */
 970 				prefix = C_("Meeting", "Declined");
 971 				break;
 972 			case ICAL_PARTSTAT_DELEGATED:
 973 				/* Translators: This is part of the subject
 974 				 * line of a meeting request or update email.
 975 				 * The full subject line would be:
 976 				 * "Delegated: Meeting Name". */
 977 				prefix = C_("Meeting", "Delegated");
 978 				break;
 979 			default:
 980 				break;
 981 			}
 982 			e_cal_component_free_attendee_list (alist);
 983 		}
 984 		break;
 985 
 986 	case E_CAL_COMPONENT_METHOD_ADD:
 987 		/* Translators: This is part of the subject line of a
 988 		 * meeting request or update email.  The full subject
 989 		 * line would be: "Updated: Meeting Name". */
 990 		prefix = C_("Meeting", "Updated");
 991 		break;
 992 
 993 	case E_CAL_COMPONENT_METHOD_CANCEL:
 994 		/* Translators: This is part of the subject line of a
 995 		 * meeting request or update email.  The full subject
 996 		 * line would be: "Cancel: Meeting Name". */
 997 		prefix = C_("Meeting", "Cancel");
 998 		break;
 999 
1000 	case E_CAL_COMPONENT_METHOD_REFRESH:
1001 		/* Translators: This is part of the subject line of a
1002 		 * meeting request or update email.  The full subject
1003 		 * line would be: "Refresh: Meeting Name". */
1004 		prefix = C_("Meeting", "Refresh");
1005 		break;
1006 
1007 	case E_CAL_COMPONENT_METHOD_COUNTER:
1008 		/* Translators: This is part of the subject line of a
1009 		 * meeting request or update email.  The full subject
1010 		 * line would be: "Counter-proposal: Meeting Name". */
1011 		prefix = C_("Meeting", "Counter-proposal");
1012 		break;
1013 
1014 	case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1015 		/* Translators: This is part of the subject line of a
1016 		 * meeting request or update email.  The full subject
1017 		 * line would be: "Declined: Meeting Name". */
1018 		prefix = C_("Meeting", "Declined");
1019 		break;
1020 
1021 	default:
1022 		break;
1023 	}
1024 
1025 	if (prefix != NULL)
1026 		subject = g_strdup_printf ("%s: %s", prefix, description);
1027 	else
1028 		subject = g_strdup (description);
1029 
1030 	return subject;
1031 }
1032 
1033 static gchar *
1034 comp_content_type (ECalComponent *comp,
1035                    ECalComponentItipMethod method)
1036 {
1037 	const gchar *name;
1038 
1039 	if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1040 		name = "freebusy.ifb";
1041 	else
1042 		name = "calendar.ics";
1043 
1044 	return g_strdup_printf (
1045 		"text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s",
1046 		name, itip_methods[method]);
1047 }
1048 
1049 static const gchar *
1050 comp_filename (ECalComponent *comp)
1051 {
1052 	if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1053 		return "freebusy.ifb";
1054 	else
1055 		return "calendar.ics";
1056 }
1057 
1058 static gchar *
1059 comp_description (ECalComponent *comp,
1060                   gboolean use_24_hour_format)
1061 {
1062 	gchar *description;
1063 	ECalComponentDateTime dt;
1064 	gchar *start = NULL, *end = NULL;
1065 
1066 	switch (e_cal_component_get_vtype (comp)) {
1067 	case E_CAL_COMPONENT_EVENT:
1068 		description = g_strdup (_("Event information"));
1069 		break;
1070 	case E_CAL_COMPONENT_TODO:
1071 		description = g_strdup (_("Task information"));
1072 		break;
1073 	case E_CAL_COMPONENT_JOURNAL:
1074 		description = g_strdup (_("Memo information"));
1075 		break;
1076 	case E_CAL_COMPONENT_FREEBUSY:
1077 		e_cal_component_get_dtstart (comp, &dt);
1078 		if (dt.value)
1079 			start = get_label (dt.value, use_24_hour_format);
1080 		e_cal_component_free_datetime (&dt);
1081 
1082 		e_cal_component_get_dtend (comp, &dt);
1083 		if (dt.value)
1084 			end = get_label (dt.value, use_24_hour_format);
1085 		e_cal_component_free_datetime (&dt);
1086 
1087 		if (start != NULL && end != NULL)
1088 			description = g_strdup_printf (
1089 				_("Free/Busy information (%s to %s)"),
1090 				start, end);
1091 		else
1092 			description = g_strdup (_("Free/Busy information"));
1093 		g_free (start);
1094 		g_free (end);
1095 		break;
1096 	default:
1097 		description = g_strdup (_("iCalendar information"));
1098 		break;
1099 	}
1100 
1101 	return description;
1102 }
1103 
1104 static gboolean
1105 comp_server_send (ECalComponentItipMethod method,
1106                   ECalComponent *comp,
1107                   ECalClient *cal_client,
1108                   icalcomponent *zones,
1109                   GSList **users)
1110 {
1111 	icalcomponent *top_level, *returned_icalcomp = NULL;
1112 	gboolean retval = TRUE;
1113 	GError *error = NULL;
1114 
1115 	top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
1116 	d (printf ("itip-utils.c: comp_server_send: calling e_cal_send_objects... \n"));
1117 	if (!e_cal_client_send_objects_sync (cal_client, top_level, users, &returned_icalcomp, NULL, &error)) {
1118 		/* FIXME Really need a book problem status code */
1119 		d (printf ("itip-utils.c: return value from e_cal_send_objects is: %d", error->code));
1120 		if (error) {
1121 			if (g_error_matches (error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_ID_ALREADY_EXISTS)) {
1122 				e_notice (
1123 					NULL, GTK_MESSAGE_ERROR,
1124 					_("Unable to book a resource, the "
1125 					"new event collides with some other."));
1126 			} else {
1127 				gchar *msg = g_strconcat (
1128 					_("Unable to book a resource, error: "),
1129 					error->message, NULL);
1130 				e_notice (NULL, GTK_MESSAGE_ERROR, msg);
1131 				g_free (msg);
1132 			}
1133 
1134 			retval = FALSE;
1135 		}
1136 	} else {
1137 	  d (printf ("itip-utils.c: e_cal_send_objects returned without errors\n"));
1138 	}
1139 
1140 	g_clear_error (&error);
1141 
1142 	if (returned_icalcomp)
1143 		icalcomponent_free (returned_icalcomp);
1144 	icalcomponent_free (top_level);
1145 
1146 	return retval;
1147 }
1148 
1149 static gboolean
1150 comp_limit_attendees (ESourceRegistry *registry,
1151                       ECalComponent *comp)
1152 {
1153 	icalcomponent *icomp;
1154 	icalproperty *prop;
1155 	gboolean found = FALSE, match = FALSE;
1156 	GSList *l, *list = NULL;
1157 
1158 	icomp = e_cal_component_get_icalcomponent (comp);
1159 
1160 	for (prop = icalcomponent_get_first_property (icomp, ICAL_ATTENDEE_PROPERTY);
1161 	     prop != NULL;
1162 	     prop = icalcomponent_get_next_property (icomp, ICAL_ATTENDEE_PROPERTY))
1163 	{
1164 		gchar *attendee;
1165 		gchar *attendee_text;
1166 		icalparameter *param;
1167 		const gchar *attendee_sentby;
1168 		gchar *attendee_sentby_text = NULL;
1169 
1170 		/* If we've already found something, just erase the rest */
1171 		if (found) {
1172 			list = g_slist_prepend (list, prop);
1173 			continue;
1174 		}
1175 
1176 		attendee = icalproperty_get_value_as_string_r (prop);
1177 		if (!attendee)
1178 			continue;
1179 
1180 		attendee_text = g_strdup (itip_strip_mailto (attendee));
1181 		g_free (attendee);
1182 		attendee_text = g_strstrip (attendee_text);
1183 		found = match = itip_address_is_user (registry, attendee_text);
1184 
1185 		if (!found) {
1186 			param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
1187 			if (param) {
1188 				attendee_sentby =
1189 					icalparameter_get_sentby (param);
1190 				attendee_sentby =
1191 					itip_strip_mailto (attendee_sentby);
1192 				attendee_sentby_text =
1193 					g_strstrip (g_strdup (attendee_sentby));
1194 				found = match = itip_address_is_user (
1195 					registry, attendee_sentby_text);
1196 			}
1197 		}
1198 
1199 		g_free (attendee_text);
1200 		g_free (attendee_sentby_text);
1201 
1202 		if (!match)
1203 			list = g_slist_prepend (list, prop);
1204 	}
1205 
1206 	for (l = list; l != NULL; l = l->next) {
1207 		prop = l->data;
1208 
1209 		icalcomponent_remove_property (icomp, prop);
1210 		icalproperty_free (prop);
1211 	}
1212 	g_slist_free (list);
1213 
1214 	return found;
1215 }
1216 
1217 static void
1218 comp_sentby (ECalComponent *comp,
1219              ECalClient *cal_client,
1220              ESourceRegistry *registry)
1221 {
1222 	ECalComponentOrganizer organizer;
1223 	GSList * attendees, *l;
1224 	gchar *name;
1225 	gchar *address;
1226 	gchar *user = NULL;
1227 
1228 	itip_get_default_name_and_address (registry, &name, &address);
1229 
1230 	e_cal_component_get_organizer (comp, &organizer);
1231 	if (!organizer.value && name != NULL && address != NULL) {
1232 		organizer.value = g_strdup_printf ("MAILTO:%s", address);
1233 		organizer.sentby = NULL;
1234 		organizer.cn = name;
1235 		organizer.language = NULL;
1236 
1237 		e_cal_component_set_organizer (comp, &organizer);
1238 		g_free ((gchar *) organizer.value);
1239 
1240 		g_free (name);
1241 		g_free (address);
1242 		return;
1243 	}
1244 
1245 	e_cal_component_get_attendee_list (comp, &attendees);
1246 	user = itip_get_comp_attendee (registry, comp, cal_client);
1247 	for (l = attendees; l; l = l->next) {
1248 		ECalComponentAttendee *a = l->data;
1249 
1250 		if (!g_ascii_strcasecmp (
1251 			itip_strip_mailto (a->value), user) ||
1252 			(a->sentby && !g_ascii_strcasecmp (
1253 			itip_strip_mailto (a->sentby), user))) {
1254 			g_free (user);
1255 
1256 			g_free (name);
1257 			g_free (address);
1258 			return;
1259 		}
1260 	}
1261 
1262 	if (!itip_organizer_is_user (registry, comp, cal_client) &&
1263 	    !itip_sentby_is_user (registry, comp, cal_client) &&
1264 	    address != NULL) {
1265 		organizer.value = g_strdup (organizer.value);
1266 		organizer.sentby = g_strdup_printf ("MAILTO:%s", address);
1267 		organizer.cn = g_strdup (organizer.cn);
1268 		organizer.language = g_strdup (organizer.language);
1269 
1270 		e_cal_component_set_organizer (comp, &organizer);
1271 
1272 		g_free ((gchar *) organizer.value);
1273 		g_free ((gchar *) organizer.sentby);
1274 		g_free ((gchar *) organizer.cn);
1275 		g_free ((gchar *) organizer.language);
1276 	}
1277 
1278 	g_free (name);
1279 	g_free (address);
1280 }
1281 
1282 static ECalComponent *
1283 comp_minimal (ESourceRegistry *registry,
1284               ECalComponent *comp,
1285               gboolean attendee)
1286 {
1287 	ECalComponent *clone;
1288 	icalcomponent *icomp, *icomp_clone;
1289 	icalproperty *prop;
1290 	ECalComponentOrganizer organizer;
1291 	const gchar *uid;
1292 	GSList *comments;
1293 	struct icaltimetype itt;
1294 	ECalComponentRange recur_id;
1295 
1296 	clone = e_cal_component_new ();
1297 	e_cal_component_set_new_vtype (clone, e_cal_component_get_vtype (comp));
1298 
1299 	if (attendee) {
1300 		GSList *attendees;
1301 
1302 		e_cal_component_get_attendee_list (comp, &attendees);
1303 		e_cal_component_set_attendee_list (clone, attendees);
1304 
1305 		if (!comp_limit_attendees (registry, clone)) {
1306 			e_notice (
1307 				NULL, GTK_MESSAGE_ERROR,
1308 				_("You must be an attendee of the event."));
1309 			goto error;
1310 		}
1311 	}
1312 
1313 	itt = icaltime_from_timet_with_zone (
1314 		time (NULL), FALSE,
1315 		icaltimezone_get_utc_timezone ());
1316 	e_cal_component_set_dtstamp (clone, &itt);
1317 
1318 	e_cal_component_get_organizer (comp, &organizer);
1319 	if (organizer.value == NULL)
1320 		goto error;
1321 	e_cal_component_set_organizer (clone, &organizer);
1322 
1323 	e_cal_component_get_uid (comp, &uid);
1324 	e_cal_component_set_uid (clone, uid);
1325 
1326 	e_cal_component_get_comment_list (comp, &comments);
1327 	if (g_slist_length (comments) <= 1) {
1328 		e_cal_component_set_comment_list (clone, comments);
1329 	} else {
1330 		GSList *l = comments;
1331 
1332 		comments = g_slist_remove_link (comments, l);
1333 		e_cal_component_set_comment_list (clone, l);
1334 		e_cal_component_free_text_list (l);
1335 	}
1336 	e_cal_component_free_text_list (comments);
1337 
1338 	e_cal_component_get_recurid (comp, &recur_id);
1339 	if (recur_id.datetime.value != NULL)
1340 		e_cal_component_set_recurid (clone, &recur_id);
1341 
1342 	icomp = e_cal_component_get_icalcomponent (comp);
1343 	icomp_clone = e_cal_component_get_icalcomponent (clone);
1344 	for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
1345 	     prop != NULL;
1346 	     prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
1347 	{
1348 		icalproperty *p;
1349 
1350 		p = icalproperty_new_clone (prop);
1351 		icalcomponent_add_property (icomp_clone, p);
1352 	}
1353 
1354 	e_cal_component_rescan (clone);
1355 
1356 	return clone;
1357 
1358  error:
1359 	g_object_unref (clone);
1360 	return NULL;
1361 }
1362 
1363 static void
1364 strip_x_microsoft_props (ECalComponent *comp)
1365 {
1366 	GSList *lst = NULL, *l;
1367 	icalcomponent *icalcomp;
1368 	icalproperty *icalprop;
1369 
1370 	g_return_if_fail (comp != NULL);
1371 
1372 	icalcomp = e_cal_component_get_icalcomponent (comp);
1373 	g_return_if_fail (icalcomp != NULL);
1374 
1375 	for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
1376 	     icalprop;
1377 	     icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
1378 		const gchar *x_name = icalproperty_get_x_name (icalprop);
1379 
1380 		if (x_name && g_ascii_strncasecmp (x_name, "X-MICROSOFT-", 12) == 0)
1381 			lst = g_slist_prepend (lst, icalprop);
1382 	}
1383 
1384 	for (l = lst; l != NULL; l = l->next) {
1385 		icalprop = l->data;
1386 		icalcomponent_remove_property (icalcomp, icalprop);
1387 		icalproperty_free (icalprop);
1388 	}
1389 
1390 	g_slist_free (lst);
1391 }
1392 
1393 static ECalComponent *
1394 comp_compliant (ESourceRegistry *registry,
1395                 ECalComponentItipMethod method,
1396                 ECalComponent *comp,
1397                 ECalClient *client,
1398                 icalcomponent *zones,
1399                 icaltimezone *default_zone,
1400                 gboolean strip_alarms)
1401 {
1402 	ECalComponent *clone, *temp_clone;
1403 	struct icaltimetype itt;
1404 
1405 	clone = e_cal_component_clone (comp);
1406 	itt = icaltime_from_timet_with_zone (
1407 		time (NULL), FALSE,
1408 		icaltimezone_get_utc_timezone ());
1409 	e_cal_component_set_dtstamp (clone, &itt);
1410 
1411 	/* Make UNTIL date a datetime in a simple recurrence */
1412 	if (e_cal_component_has_recurrences (clone)
1413 	    && e_cal_component_has_simple_recurrence (clone)) {
1414 		GSList *rrule_list;
1415 		struct icalrecurrencetype *r;
1416 
1417 		e_cal_component_get_rrule_list (clone, &rrule_list);
1418 		r = rrule_list->data;
1419 
1420 		if (!icaltime_is_null_time (r->until) && r->until.is_date) {
1421 			ECalComponentDateTime dt;
1422 			icaltimezone *from_zone = NULL, *to_zone;
1423 
1424 			e_cal_component_get_dtstart (clone, &dt);
1425 
1426 			if (dt.value->is_date) {
1427 				from_zone = default_zone;
1428 			} else if (dt.tzid == NULL) {
1429 				from_zone = icaltimezone_get_utc_timezone ();
1430 			} else {
1431 				if (zones != NULL)
1432 					from_zone = icalcomponent_get_timezone (zones, dt.tzid);
1433 				if (from_zone == NULL)
1434 					from_zone = icaltimezone_get_builtin_timezone_from_tzid (dt.tzid);
1435 				if (from_zone == NULL && client != NULL)
1436 					/* FIXME Error checking */
1437 					e_cal_client_get_timezone_sync (
1438 						client, dt.tzid,
1439 						&from_zone, NULL, NULL);
1440 			}
1441 
1442 			to_zone = icaltimezone_get_utc_timezone ();
1443 
1444 			r->until.hour = dt.value->hour;
1445 			r->until.minute = dt.value->minute;
1446 			r->until.second = dt.value->second;
1447 			r->until.is_date = FALSE;
1448 
1449 			icaltimezone_convert_time (&r->until, from_zone, to_zone);
1450 			r->until.is_utc = TRUE;
1451 
1452 			e_cal_component_free_datetime (&dt);
1453 			e_cal_component_set_rrule_list (clone, rrule_list);
1454 			e_cal_component_abort_sequence (clone);
1455 		}
1456 
1457 		e_cal_component_free_recur_list (rrule_list);
1458 	}
1459 
1460 	/* We delete incoming alarms if requested, even this helps with outlook */
1461 	if (strip_alarms) {
1462 		e_cal_component_remove_all_alarms (clone);
1463 	} else {
1464 		/* Always strip procedure alarms, because of security */
1465 		GList *uids, *l;
1466 
1467 		uids = e_cal_component_get_alarm_uids (clone);
1468 
1469 		for (l = uids; l; l = l->next) {
1470 			ECalComponentAlarm *alarm;
1471 			ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN;
1472 
1473 			alarm = e_cal_component_get_alarm (clone, (const gchar *) l->data);
1474 			if (alarm) {
1475 				e_cal_component_alarm_get_action (alarm, &action);
1476 				e_cal_component_alarm_free (alarm);
1477 
1478 				if (action == E_CAL_COMPONENT_ALARM_PROCEDURE)
1479 					e_cal_component_remove_alarm (clone, (const gchar *) l->data);
1480 			}
1481 		}
1482 
1483 		cal_obj_uid_list_free (uids);
1484 	}
1485 
1486 	strip_x_microsoft_props (clone);
1487 
1488 	/* Strip X-LIC-ERROR stuff */
1489 	e_cal_component_strip_errors (clone);
1490 
1491 	/* Comply with itip spec */
1492 	switch (method) {
1493 	case E_CAL_COMPONENT_METHOD_PUBLISH:
1494 		comp_sentby (clone, client, registry);
1495 		e_cal_component_set_attendee_list (clone, NULL);
1496 		break;
1497 	case E_CAL_COMPONENT_METHOD_REQUEST:
1498 		comp_sentby (clone, client, registry);
1499 		break;
1500 	case E_CAL_COMPONENT_METHOD_CANCEL:
1501 		comp_sentby (clone, client, registry);
1502 		break;
1503 	case E_CAL_COMPONENT_METHOD_REPLY:
1504 		break;
1505 	case E_CAL_COMPONENT_METHOD_ADD:
1506 		break;
1507 	case E_CAL_COMPONENT_METHOD_REFRESH:
1508 		/* Need to remove almost everything */
1509 		temp_clone = comp_minimal (registry, clone, TRUE);
1510 		g_object_unref (clone);
1511 		clone = temp_clone;
1512 		break;
1513 	case E_CAL_COMPONENT_METHOD_COUNTER:
1514 		break;
1515 	case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1516 		/* Need to remove almost everything */
1517 		temp_clone = comp_minimal (registry, clone, FALSE);
1518 		g_object_unref (clone);
1519 		clone = temp_clone;
1520 		break;
1521 	default:
1522 		break;
1523 	}
1524 
1525 	return clone;
1526 }
1527 
1528 static void
1529 append_cal_attachments (EMsgComposer *composer,
1530                         ECalComponent *comp,
1531                         GSList *attach_list)
1532 {
1533 	struct CalMimeAttach *mime_attach;
1534 	GSList *l;
1535 
1536 	for (l = attach_list; l; l = l->next) {
1537 		CamelMimePart *attachment;
1538 
1539 		mime_attach = (struct CalMimeAttach *) l->data;
1540 
1541 		attachment = camel_mime_part_new ();
1542 		camel_mime_part_set_content (
1543 			attachment, mime_attach->encoded_data,
1544 			mime_attach->length, mime_attach->content_type);
1545 		if (mime_attach->content_id)
1546 			camel_mime_part_set_content_id (
1547 				attachment, mime_attach->content_id);
1548 		if (mime_attach->filename != NULL)
1549 			camel_mime_part_set_filename (
1550 				attachment, mime_attach->filename);
1551 		if (mime_attach->description != NULL)
1552 			camel_mime_part_set_description (
1553 				attachment, mime_attach->description);
1554 		if (mime_attach->disposition)
1555 			camel_mime_part_set_disposition (
1556 				attachment, "inline");
1557 		else
1558 			camel_mime_part_set_disposition (
1559 				attachment, "attachment");
1560 		e_msg_composer_attach (composer, attachment);
1561 		g_object_unref (attachment);
1562 
1563 		g_free (mime_attach->filename);
1564 		g_free (mime_attach->content_type);
1565 		g_free (mime_attach->content_id);
1566 		g_free (mime_attach->description);
1567 		g_free (mime_attach->encoded_data);
1568 		g_free (mime_attach);
1569 	}
1570 
1571 	g_slist_free (attach_list);
1572 }
1573 
1574 static ESource *
1575 find_enabled_identity (ESourceRegistry *registry,
1576                        const gchar *id_address)
1577 {
1578 	GList *list, *link;
1579 	ESource *mail_identity = NULL;
1580 	const gchar *extension_name;
1581 
1582 	if (id_address == NULL)
1583 		return NULL;
1584 
1585 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1586 	list = e_source_registry_list_sources (registry, extension_name);
1587 
1588 	for (link = list; link != NULL; link = g_list_next (link)) {
1589 		ESource *source = E_SOURCE (link->data);
1590 		ESourceMailIdentity *extension;
1591 		const gchar *address;
1592 
1593 		if (!e_source_get_enabled (source))
1594 			continue;
1595 
1596 		extension = e_source_get_extension (source, extension_name);
1597 		address = e_source_mail_identity_get_address (extension);
1598 
1599 		if (address == NULL)
1600 			continue;
1601 
1602 		if (g_ascii_strcasecmp (address, id_address) == 0) {
1603 			mail_identity = g_object_ref (source);
1604 			break;
1605 		}
1606 	}
1607 
1608 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1609 
1610 	return mail_identity;
1611 }
1612 
1613 static void
1614 setup_from (ECalComponentItipMethod method,
1615             ECalComponent *comp,
1616             ECalClient *cal_client,
1617             EComposerHeaderTable *table)
1618 {
1619 	ESourceRegistry *registry;
1620 	ESource *source = NULL;
1621 
1622 	registry = e_composer_header_table_get_registry (table);
1623 
1624 	/* always use organizer's email when user is an organizer */
1625 	if (itip_organizer_is_user (registry, comp, cal_client)) {
1626 		ECalComponentOrganizer organizer = {0};
1627 
1628 		e_cal_component_get_organizer (comp, &organizer);
1629 		if (organizer.value != NULL)
1630 			source = find_enabled_identity (
1631 				registry,
1632 				itip_strip_mailto (organizer.value));
1633 	}
1634 
1635 	if (source == NULL) {
1636 		gchar *from = comp_from (method, comp, registry);
1637 
1638 		if (from != NULL)
1639 			source = find_enabled_identity (registry, from);
1640 
1641 		g_free (from);
1642 	}
1643 
1644 	if (source != NULL) {
1645 		const gchar *uid;
1646 
1647 		uid = e_source_get_uid (source);
1648 		e_composer_header_table_set_identity_uid (table, uid);
1649 
1650 		g_object_unref (source);
1651 	}
1652 }
1653 
1654 gboolean
1655 itip_send_comp (ESourceRegistry *registry,
1656                 ECalComponentItipMethod method,
1657                 ECalComponent *send_comp,
1658                 ECalClient *cal_client,
1659                 icalcomponent *zones,
1660                 GSList *attachments_list,
1661                 GSList *users,
1662                 gboolean strip_alarms,
1663                 gboolean only_new_attendees)
1664 {
1665 	EShell *shell;
1666 	EShellSettings *shell_settings;
1667 	EMsgComposer *composer;
1668 	EComposerHeaderTable *table;
1669 	EDestination **destinations;
1670 	ECalComponent *comp = NULL;
1671 	icalcomponent *top_level = NULL;
1672 	icaltimezone *default_zone;
1673 	gchar *ical_string = NULL;
1674 	gchar *content_type = NULL;
1675 	gchar *subject = NULL;
1676 	gboolean use_24_hour_format;
1677 	gboolean retval = FALSE;
1678 
1679 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
1680 
1681 	/* FIXME Pass this in. */
1682 	shell = e_shell_get_default ();
1683 	shell_settings = e_shell_get_shell_settings (shell);
1684 
1685 	default_zone = e_shell_settings_get_pointer (
1686 		shell_settings, "cal-timezone");
1687 
1688 	use_24_hour_format = e_shell_settings_get_boolean (
1689 		shell_settings, "cal-use-24-hour-format");
1690 
1691 	/* check whether backend could handle auto-saving requests/updates */
1692 	if (method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_client_check_save_schedules (cal_client))
1693 		return TRUE;
1694 
1695 	/* Give the server a chance to manipulate the comp */
1696 	if (method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1697 		d (printf ("itip-utils.c: itip_send_comp: calling comp_server_send... \n"));
1698 		if (!comp_server_send (method, send_comp, cal_client, zones, &users))
1699 			goto cleanup;
1700 	}
1701 
1702 	/* check whether backend could handle sending requests/updates */
1703 	if (method != E_CAL_COMPONENT_METHOD_PUBLISH &&
1704 		e_client_check_capability (
1705 			E_CLIENT (cal_client),
1706 		CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
1707 		if (users) {
1708 			g_slist_foreach (users, (GFunc) g_free, NULL);
1709 			g_slist_free (users);
1710 		}
1711 		return TRUE;
1712 	}
1713 
1714 	/* Tidy up the comp */
1715 	comp = comp_compliant (
1716 		registry, method, send_comp, cal_client,
1717 		zones, default_zone, strip_alarms);
1718 
1719 	if (comp == NULL)
1720 		goto cleanup;
1721 
1722 	/* Recipients */
1723 	destinations = comp_to_list (
1724 		registry, method, comp, users, FALSE,
1725 		only_new_attendees ?  g_object_get_data (
1726 		G_OBJECT (send_comp), "new-attendees") : NULL);
1727 	if (method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1728 		if (destinations == NULL) {
1729 			/* We sent them all via the server */
1730 			retval = TRUE;
1731 			goto cleanup;
1732 		}
1733 	}
1734 
1735 	/* Subject information */
1736 	subject = comp_subject (registry, method, comp);
1737 
1738 	composer = e_msg_composer_new (shell);
1739 	table = e_msg_composer_get_header_table (composer);
1740 
1741 	setup_from (method, send_comp, cal_client, table);
1742 	e_composer_header_table_set_subject (table, subject);
1743 	e_composer_header_table_set_destinations_to (table, destinations);
1744 
1745 	e_destination_freev (destinations);
1746 
1747 	/* Content type */
1748 	content_type = comp_content_type (comp, method);
1749 
1750 	top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
1751 	ical_string = icalcomponent_as_ical_string_r (top_level);
1752 
1753 	if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
1754 		e_msg_composer_set_body (composer, ical_string, content_type);
1755 	} else {
1756 		CamelMimePart *attachment;
1757 		const gchar *filename;
1758 		gchar *description;
1759 		gchar *body;
1760 
1761 		filename = comp_filename (comp);
1762 		description = comp_description (comp, use_24_hour_format);
1763 
1764 		body = camel_text_to_html (
1765 			description, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
1766 		e_msg_composer_set_body_text (composer, body, TRUE);
1767 		g_free (body);
1768 
1769 		attachment = camel_mime_part_new ();
1770 		camel_mime_part_set_content (
1771 			attachment, ical_string,
1772 			strlen (ical_string), content_type);
1773 		if (filename != NULL && *filename != '\0')
1774 			camel_mime_part_set_filename (attachment, filename);
1775 		if (description != NULL && *description != '\0')
1776 			camel_mime_part_set_description (attachment, description);
1777 		camel_mime_part_set_disposition (attachment, "inline");
1778 		e_msg_composer_attach (composer, attachment);
1779 		g_object_unref (attachment);
1780 
1781 		g_free (description);
1782 	}
1783 
1784 	append_cal_attachments (composer, comp, attachments_list);
1785 
1786 	if ((method == E_CAL_COMPONENT_METHOD_PUBLISH) && !users)
1787 		gtk_widget_show (GTK_WIDGET (composer));
1788 	else
1789 		e_msg_composer_send (composer);
1790 
1791 	retval = TRUE;
1792 
1793 cleanup:
1794 	if (comp != NULL)
1795 		g_object_unref (comp);
1796 	if (top_level != NULL)
1797 		icalcomponent_free (top_level);
1798 
1799 	if (users) {
1800 		g_slist_foreach (users, (GFunc) g_free, NULL);
1801 		g_slist_free (users);
1802 	}
1803 
1804 	g_free (content_type);
1805 	g_free (subject);
1806 	g_free (ical_string);
1807 
1808 	return retval;
1809 }
1810 
1811 gboolean
1812 reply_to_calendar_comp (ESourceRegistry *registry,
1813                         ECalComponentItipMethod method,
1814                         ECalComponent *send_comp,
1815                         ECalClient *cal_client,
1816                         gboolean reply_all,
1817                         icalcomponent *zones,
1818                         GSList *attachments_list)
1819 {
1820 	EShell *shell;
1821 	EShellSettings *shell_settings;
1822 	EMsgComposer *composer;
1823 	EComposerHeaderTable *table;
1824 	EDestination **destinations;
1825 	ECalComponent *comp = NULL;
1826 	icalcomponent *top_level = NULL;
1827 	icaltimezone *default_zone;
1828 	GSList *users = NULL;
1829 	gchar *subject = NULL;
1830 	gchar *ical_string = NULL;
1831 	gboolean retval = FALSE;
1832 
1833 	g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
1834 
1835 	/* FIXME Pass this in. */
1836 	shell = e_shell_get_default ();
1837 	shell_settings = e_shell_get_shell_settings (shell);
1838 
1839 	default_zone = e_shell_settings_get_pointer (
1840 		shell_settings, "cal-timezone");
1841 
1842 	/* Tidy up the comp */
1843 	comp = comp_compliant (
1844 		registry, method, send_comp, cal_client,
1845 		zones, default_zone, TRUE);
1846 	if (comp == NULL)
1847 		goto cleanup;
1848 
1849 	/* Recipients */
1850 	destinations = comp_to_list (
1851 		registry, method, comp, users, reply_all, NULL);
1852 
1853 	/* Subject information */
1854 	subject = comp_subject (registry, method, comp);
1855 
1856 	composer = e_msg_composer_new (shell);
1857 	table = e_msg_composer_get_header_table (composer);
1858 
1859 	setup_from (method, send_comp, cal_client, table);
1860 	e_composer_header_table_set_subject (table, subject);
1861 	e_composer_header_table_set_destinations_to (table, destinations);
1862 
1863 	e_destination_freev (destinations);
1864 
1865 	top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
1866 	ical_string = icalcomponent_as_ical_string_r (top_level);
1867 
1868 	if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
1869 
1870 		GString *body;
1871 		gchar *orig_from = NULL;
1872 		const gchar *description = NULL;
1873 		gchar *subject = NULL;
1874 		const gchar *location = NULL;
1875 		gchar *time = NULL;
1876 		gchar *html_description = NULL;
1877 		GSList *text_list = NULL;
1878 		ECalComponentOrganizer organizer;
1879 		ECalComponentText text;
1880 		ECalComponentDateTime dtstart;
1881 		icaltimezone *start_zone = NULL;
1882 		time_t start;
1883 
1884 		e_cal_component_get_description_list (comp, &text_list);
1885 
1886 		if (text_list) {
1887 			ECalComponentText text = *((ECalComponentText *) text_list->data);
1888 			if (text.value)
1889 				description = text.value;
1890 			else
1891 				description = "";
1892 		} else {
1893 			description = "";
1894 		}
1895 
1896 		e_cal_component_free_text_list (text_list);
1897 
1898 		e_cal_component_get_summary (comp, &text);
1899 		if (text.value)
1900 			subject = g_strdup (text.value);
1901 
1902 		e_cal_component_get_organizer (comp, &organizer);
1903 		if (organizer.value)
1904 			orig_from = g_strdup (itip_strip_mailto (organizer.value));
1905 
1906 		e_cal_component_get_location (comp, &location);
1907 		if (!location)
1908 			location = "Unspecified";
1909 
1910 		e_cal_component_get_dtstart (comp, &dtstart);
1911 		if (dtstart.value) {
1912 			start_zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
1913 			if (!start_zone && dtstart.tzid) {
1914 				GError *error = NULL;
1915 
1916 				e_cal_client_get_timezone_sync (
1917 					cal_client, dtstart.tzid,
1918 					&start_zone, NULL, &error);
1919 
1920 				if (error != NULL) {
1921 					g_warning (
1922 						"%s: Couldn't get timezone '%s' from server: %s",
1923 						G_STRFUNC,
1924 						dtstart.tzid ? dtstart.tzid : "",
1925 						error->message);
1926 					g_error_free (error);
1927 				}
1928 			}
1929 
1930 			if (!start_zone || dtstart.value->is_date)
1931 				start_zone = default_zone;
1932 
1933 			start = icaltime_as_timet_with_zone (*dtstart.value, start_zone);
1934 			time = g_strdup (ctime (&start));
1935 		}
1936 
1937 		body = g_string_new (
1938 			"<br><br><hr><br><b>"
1939 			"______ Original Appointment ______ "
1940 			"</b><br><br><table>");
1941 
1942 		if (orig_from && *orig_from)
1943 			g_string_append_printf (
1944 				body,
1945 				"<tr><td><b>From</b></td>"
1946 				"<td>:</td><td>%s</td></tr>", orig_from);
1947 		g_free (orig_from);
1948 
1949 		if (subject)
1950 			g_string_append_printf (
1951 				body,
1952 				"<tr><td><b>Subject</b></td>"
1953 				"<td>:</td><td>%s</td></tr>", subject);
1954 		g_free (subject);
1955 
1956 		g_string_append_printf (
1957 			body,
1958 			"<tr><td><b>Location</b></td>"
1959 			"<td>:</td><td>%s</td></tr>", location);
1960 
1961 		if (time)
1962 			g_string_append_printf (
1963 				body,
1964 				"<tr><td><b>Time</b></td>"
1965 				"<td>:</td><td>%s</td></tr>", time);
1966 		g_free (time);
1967 
1968 		g_string_append_printf (body, "</table><br>");
1969 
1970 		html_description = html_new_lines_for (description);
1971 		g_string_append (body, html_description);
1972 		g_free (html_description);
1973 
1974 		e_msg_composer_set_body_text (composer, body->str, TRUE);
1975 		g_string_free (body, TRUE);
1976 	}
1977 
1978 	gtk_widget_show (GTK_WIDGET (composer));
1979 
1980 	retval = TRUE;
1981 
1982  cleanup:
1983 
1984 	if (comp != NULL)
1985 		g_object_unref (comp);
1986 	if (top_level != NULL)
1987 		icalcomponent_free (top_level);
1988 
1989 	if (users) {
1990 		g_slist_foreach (users, (GFunc) g_free, NULL);
1991 		g_slist_free (users);
1992 	}
1993 
1994 	g_free (subject);
1995 	g_free (ical_string);
1996 	return retval;
1997 }
1998 
1999 gboolean
2000 itip_publish_begin (ECalComponent *pub_comp,
2001                     ECalClient *cal_client,
2002                     gboolean cloned,
2003                     ECalComponent **clone)
2004 {
2005 	icalcomponent *icomp = NULL, *icomp_clone = NULL;
2006 	icalproperty *prop;
2007 
2008 	if (e_cal_component_get_vtype (pub_comp) == E_CAL_COMPONENT_FREEBUSY) {
2009 
2010 		if (!cloned)
2011 			*clone = e_cal_component_clone (pub_comp);
2012 		else {
2013 
2014 			icomp = e_cal_component_get_icalcomponent (pub_comp);
2015 			icomp_clone = e_cal_component_get_icalcomponent (*clone);
2016 			for (prop = icalcomponent_get_first_property (icomp,
2017 						      ICAL_FREEBUSY_PROPERTY);
2018 				prop != NULL;
2019 				prop = icalcomponent_get_next_property (
2020 					icomp,
2021 					ICAL_FREEBUSY_PROPERTY))
2022 			{
2023 				icalproperty *p;
2024 
2025 				p = icalproperty_new_clone (prop);
2026 				icalcomponent_add_property (icomp_clone, p);
2027 			}
2028 		}
2029 	}
2030 
2031 	return TRUE;
2032 }
2033 
2034 static void
2035 fb_sort (struct icalperiodtype *ipt,
2036          gint fb_count)
2037 {
2038 	gint i,j;
2039 
2040 	if (ipt == NULL || fb_count == 0)
2041 		return;
2042 
2043 	for (i = 0; i < fb_count - 1; i++) {
2044 		for (j = i + 1; j < fb_count; j++) {
2045 			struct icalperiodtype temp;
2046 
2047 			if (icaltime_compare (ipt[i].start, ipt[j].start) < 0)
2048 				continue;
2049 
2050 			if (icaltime_compare (ipt[i].start, ipt[j].start) == 0) {
2051 				if (icaltime_compare (ipt[i].end,
2052 						     ipt[j].start) < 0)
2053 					continue;
2054 			}
2055 			temp = ipt[i];
2056 			ipt[i] = ipt[j];
2057 			ipt[j] = temp;
2058 		}
2059 	}
2060 }
2061 
2062 static icalcomponent *
2063 comp_fb_normalize (icalcomponent *icomp)
2064 {
2065 	icalcomponent *iclone;
2066 	icalproperty *prop, *p;
2067 	const gchar *uid,  *comment;
2068 	struct icaltimetype itt;
2069 	gint fb_count, i = 0, j;
2070 	struct icalperiodtype *ipt;
2071 
2072 	iclone = icalcomponent_new (ICAL_VFREEBUSY_COMPONENT);
2073 
2074 	prop = icalcomponent_get_first_property (
2075 		icomp, ICAL_ORGANIZER_PROPERTY);
2076 	if (prop) {
2077 		p = icalproperty_new_clone (prop);
2078 		icalcomponent_add_property (iclone, p);
2079 	}
2080 
2081 	itt = icalcomponent_get_dtstart (icomp);
2082 	icalcomponent_set_dtstart (iclone, itt);
2083 
2084 	itt = icalcomponent_get_dtend (icomp);
2085 	icalcomponent_set_dtend (iclone, itt);
2086 
2087 	fb_count = icalcomponent_count_properties (
2088 		icomp, ICAL_FREEBUSY_PROPERTY);
2089 	ipt = g_new0 (struct icalperiodtype, fb_count + 1);
2090 
2091 	for (prop = icalcomponent_get_first_property (icomp, ICAL_FREEBUSY_PROPERTY);
2092 		prop != NULL;
2093 		prop = icalcomponent_get_next_property (icomp, ICAL_FREEBUSY_PROPERTY))
2094 	{
2095 		ipt[i] = icalproperty_get_freebusy (prop);
2096 		i++;
2097 	}
2098 
2099 	fb_sort (ipt, fb_count);
2100 
2101 	for (j = 0; j <= fb_count - 1; j++) {
2102 		icalparameter *param;
2103 
2104 		prop = icalproperty_new_freebusy (ipt[j]);
2105 		param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
2106 		icalproperty_add_parameter (prop, param);
2107 		icalcomponent_add_property (iclone, prop);
2108 	}
2109 	g_free (ipt);
2110 
2111 	/* Should I strip this RFC 2446 says there must not be a UID
2112 		if the METHOD is PUBLISH?? */
2113 	uid = icalcomponent_get_uid (icomp);
2114 	if (uid)
2115 		icalcomponent_set_uid (iclone, uid);
2116 
2117 	itt = icaltime_from_timet_with_zone (
2118 		time (NULL), FALSE,
2119 		icaltimezone_get_utc_timezone ());
2120 	icalcomponent_set_dtstamp (iclone, itt);
2121 
2122 	prop = icalcomponent_get_first_property (icomp, ICAL_URL_PROPERTY);
2123 	if (prop) {
2124 		p = icalproperty_new_clone (prop);
2125 		icalcomponent_add_property (iclone, p);
2126 	}
2127 
2128 	comment =  icalcomponent_get_comment (icomp);
2129 	if (comment)
2130 		icalcomponent_set_comment (iclone, comment);
2131 
2132 	for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
2133 	     prop != NULL;
2134 	     prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
2135 	{
2136 		p = icalproperty_new_clone (prop);
2137 		icalcomponent_add_property (iclone, p);
2138 	}
2139 
2140 	return iclone;
2141 }
2142 
2143 gboolean
2144 itip_publish_comp (ECalClient *cal_client,
2145                    gchar *uri,
2146                    gchar *username,
2147                    gchar *password,
2148                    ECalComponent **pub_comp)
2149 {
2150 	icalcomponent *toplevel = NULL, *icalcomp = NULL;
2151 	icalcomponent *icomp = NULL;
2152 	SoupSession *session;
2153 	SoupMessage *msg;
2154 	SoupURI *real_uri;
2155 	gchar *ical_string = NULL;
2156 
2157 	toplevel = e_cal_util_new_top_level ();
2158 	icalcomponent_set_method (toplevel, ICAL_METHOD_PUBLISH);
2159 
2160 	e_cal_component_set_url (*pub_comp, uri);
2161 
2162 	icalcomp = e_cal_component_get_icalcomponent (*pub_comp);
2163 
2164 	icomp = comp_fb_normalize (icalcomp);
2165 
2166 	icalcomponent_add_component (toplevel, icomp);
2167 
2168 	/* Publish the component */
2169 	session = soup_session_async_new ();
2170 	g_object_set (session, SOUP_SESSION_TIMEOUT, 90, NULL);
2171 
2172 	real_uri = soup_uri_new (uri);
2173 	if (!real_uri || !real_uri->host) {
2174 		g_warning (G_STRLOC ": Invalid URL: %s", uri);
2175 		g_object_unref (session);
2176 		return FALSE;
2177 	}
2178 
2179 	soup_uri_set_user (real_uri, username);
2180 	soup_uri_set_password (real_uri, password);
2181 
2182 	/* build the message */
2183 	msg = soup_message_new_from_uri (SOUP_METHOD_PUT, real_uri);
2184 	soup_uri_free (real_uri);
2185 	if (!msg) {
2186 		g_warning (G_STRLOC ": Could not build SOAP message");
2187 		g_object_unref (session);
2188 		return FALSE;
2189 	}
2190 
2191 	soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
2192 	ical_string = icalcomponent_as_ical_string_r (toplevel);
2193 	soup_message_set_request (
2194 		msg, "text/calendar", SOUP_MEMORY_TEMPORARY,
2195 		ical_string, strlen (ical_string));
2196 
2197 	/* send message to server */
2198 	soup_session_send_message (session, msg);
2199 	if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
2200 		g_warning (
2201 			G_STRLOC ": Could not publish Free/Busy: %d: %s",
2202 			msg->status_code,
2203 			msg->reason_phrase);
2204 		g_object_unref (msg);
2205 		g_object_unref (session);
2206 		g_free (ical_string);
2207 		return FALSE;
2208 	}
2209 
2210 	g_object_unref (msg);
2211 	g_object_unref (session);
2212 	g_free (ical_string);
2213 
2214 	return TRUE;
2215 }
2216 
2217 static gboolean
2218 check_time (const struct icaltimetype tmval,
2219             gboolean can_null_time)
2220 {
2221 	if (icaltime_is_null_time (tmval))
2222 		return can_null_time;
2223 
2224 	return  icaltime_is_valid_time (tmval) &&
2225 		tmval.month >= 1 && tmval.month <= 12 &&
2226 		tmval.day >= 1 && tmval.day <= 31 &&
2227 		tmval.hour >= 0 && tmval.hour < 24 &&
2228 		tmval.minute >= 0 && tmval.minute < 60 &&
2229 		tmval.second >= 0 && tmval.second < 60;
2230 }
2231 
2232 /* Returns whether the passed-in icalcomponent is valid or not.
2233  * It does some sanity checks on values too. */
2234 gboolean
2235 is_icalcomp_valid (icalcomponent *icalcomp)
2236 {
2237 	if (!icalcomp || !icalcomponent_is_valid (icalcomp))
2238 		return FALSE;
2239 
2240 	switch (icalcomponent_isa (icalcomp)) {
2241 	case ICAL_VEVENT_COMPONENT:
2242 		return	check_time (icalcomponent_get_dtstart (icalcomp), FALSE) &&
2243 			check_time (icalcomponent_get_dtend (icalcomp), TRUE);
2244 	case ICAL_VTODO_COMPONENT:
2245 		return	check_time (icalcomponent_get_dtstart (icalcomp), TRUE) &&
2246 			check_time (icalcomponent_get_due (icalcomp), TRUE);
2247 	case ICAL_VJOURNAL_COMPONENT:
2248 		return	check_time (icalcomponent_get_dtstart (icalcomp), TRUE) &&
2249 			check_time (icalcomponent_get_dtend (icalcomp), TRUE);
2250 	default:
2251 		break;
2252 	}
2253 
2254 	return TRUE;
2255 }