evolution-3.6.4/calendar/gui/e-meeting-store.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-meeting-store.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-meeting-store.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   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  *	    Mike Kestner  <mkestner@ximian.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  *
  22  */
  23 
  24 #ifdef HAVE_CONFIG_H
  25 #include <config.h>
  26 #endif
  27 
  28 #include <gio/gio.h>
  29 #include <glib/gi18n.h>
  30 #include <libsoup/soup.h>
  31 
  32 #include <libecal/libecal.h>
  33 #include <libebackend/libebackend.h>
  34 #include <libedataserverui/libedataserverui.h>
  35 
  36 #include <shell/e-shell.h>
  37 #include <e-util/e-util-enumtypes.h>
  38 
  39 #include "itip-utils.h"
  40 #include "e-meeting-utils.h"
  41 #include "e-meeting-attendee.h"
  42 #include "e-meeting-store.h"
  43 
  44 #define ROW_VALID(store, row) \
  45 	(row >= 0 && row < store->priv->attendees->len)
  46 
  47 #define E_MEETING_STORE_GET_PRIVATE(obj) \
  48 	(G_TYPE_INSTANCE_GET_PRIVATE \
  49 	((obj), E_TYPE_MEETING_STORE, EMeetingStorePrivate))
  50 
  51 struct _EMeetingStorePrivate {
  52 	GPtrArray *attendees;
  53 	gint stamp;
  54 
  55 	ECalClient *client;
  56 	icaltimezone *zone;
  57 
  58 	gint default_reminder_interval;
  59 	EDurationType default_reminder_units;
  60 
  61 	gint week_start_day;
  62 
  63 	gchar *fb_uri;
  64 
  65 	GPtrArray *refresh_queue;
  66 	GHashTable *refresh_data;
  67 	GMutex *mutex;
  68 	guint refresh_idle_id;
  69 
  70 	guint num_threads;
  71 	guint num_queries;
  72 };
  73 
  74 #define BUF_SIZE 1024
  75 
  76 typedef struct _EMeetingStoreQueueData EMeetingStoreQueueData;
  77 struct _EMeetingStoreQueueData {
  78 	EMeetingStore *store;
  79 	EMeetingAttendee *attendee;
  80 
  81 	gboolean refreshing;
  82 
  83 	EMeetingTime start;
  84 	EMeetingTime end;
  85 
  86 	gchar buffer[BUF_SIZE];
  87 	GString *string;
  88 
  89 	GPtrArray *call_backs;
  90 	GPtrArray *data;
  91 };
  92 
  93 enum {
  94 	PROP_0,
  95 	PROP_CLIENT,
  96 	PROP_DEFAULT_REMINDER_INTERVAL,
  97 	PROP_DEFAULT_REMINDER_UNITS,
  98 	PROP_FREE_BUSY_TEMPLATE,
  99 	PROP_TIMEZONE,
 100 	PROP_WEEK_START_DAY
 101 };
 102 
 103 /* Forward Declarations */
 104 static void ems_tree_model_init (GtkTreeModelIface *iface);
 105 
 106 G_DEFINE_TYPE_WITH_CODE (
 107 	EMeetingStore, e_meeting_store, GTK_TYPE_LIST_STORE,
 108 	G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL)
 109 	G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, ems_tree_model_init))
 110 
 111 static icalparameter_cutype
 112 text_to_type (const gchar *type)
 113 {
 114 	if (!e_util_utf8_strcasecmp (type, _("Individual")))
 115 		return ICAL_CUTYPE_INDIVIDUAL;
 116 	else if (!e_util_utf8_strcasecmp (type, _("Group")))
 117 		return ICAL_CUTYPE_GROUP;
 118 	else if (!e_util_utf8_strcasecmp (type, _("Resource")))
 119 		return ICAL_CUTYPE_RESOURCE;
 120 	else if (!e_util_utf8_strcasecmp (type, _("Room")))
 121 		return ICAL_CUTYPE_ROOM;
 122 	else
 123 		return ICAL_CUTYPE_NONE;
 124 }
 125 
 126 static gchar *
 127 type_to_text (icalparameter_cutype type)
 128 {
 129 	switch (type) {
 130 	case ICAL_CUTYPE_INDIVIDUAL:
 131 		return _("Individual");
 132 	case ICAL_CUTYPE_GROUP:
 133 		return _("Group");
 134 	case ICAL_CUTYPE_RESOURCE:
 135 		return _("Resource");
 136 	case ICAL_CUTYPE_ROOM:
 137 		return _("Room");
 138 	default:
 139 		return _("Unknown");
 140 	}
 141 
 142 	return NULL;
 143 
 144 }
 145 
 146 static icalparameter_role
 147 text_to_role (const gchar *role)
 148 {
 149 	if (!e_util_utf8_strcasecmp (role, _("Chair")))
 150 		return ICAL_ROLE_CHAIR;
 151 	else if (!e_util_utf8_strcasecmp (role, _("Required Participant")))
 152 		return ICAL_ROLE_REQPARTICIPANT;
 153 	else if (!e_util_utf8_strcasecmp (role, _("Optional Participant")))
 154 		return ICAL_ROLE_OPTPARTICIPANT;
 155 	else if (!e_util_utf8_strcasecmp (role, _("Non-Participant")))
 156 		return ICAL_ROLE_NONPARTICIPANT;
 157 	else
 158 		return ICAL_ROLE_NONE;
 159 }
 160 
 161 static gchar *
 162 role_to_text (icalparameter_role role)
 163 {
 164 	switch (role) {
 165 	case ICAL_ROLE_CHAIR:
 166 		return _("Chair");
 167 	case ICAL_ROLE_REQPARTICIPANT:
 168 		return _("Required Participant");
 169 	case ICAL_ROLE_OPTPARTICIPANT:
 170 		return _("Optional Participant");
 171 	case ICAL_ROLE_NONPARTICIPANT:
 172 		return _("Non-Participant");
 173 	default:
 174 		return _("Unknown");
 175 	}
 176 }
 177 
 178 static gboolean
 179 text_to_boolean (const gchar *role)
 180 {
 181 	if (!e_util_utf8_strcasecmp (role, _("Yes")))
 182 		return TRUE;
 183 	else
 184 		return FALSE;
 185 }
 186 
 187 static gchar *
 188 boolean_to_text (gboolean b)
 189 {
 190 	if (b)
 191 		return _("Yes");
 192 	else
 193 		return _("No");
 194 }
 195 
 196 static icalparameter_partstat
 197 text_to_partstat (const gchar *partstat)
 198 {
 199 	if (!e_util_utf8_strcasecmp (partstat, _("Needs Action")))
 200 		return ICAL_PARTSTAT_NEEDSACTION;
 201 	else if (!e_util_utf8_strcasecmp (partstat, _("Accepted")))
 202 		return ICAL_PARTSTAT_ACCEPTED;
 203 	else if (!e_util_utf8_strcasecmp (partstat, _("Declined")))
 204 		return ICAL_PARTSTAT_DECLINED;
 205 	else if (!e_util_utf8_strcasecmp (partstat, _("Tentative")))
 206 		return ICAL_PARTSTAT_TENTATIVE;
 207 	else if (!e_util_utf8_strcasecmp (partstat, _("Delegated")))
 208 		return ICAL_PARTSTAT_DELEGATED;
 209 	else if (!e_util_utf8_strcasecmp (partstat, _("Completed")))
 210 		return ICAL_PARTSTAT_COMPLETED;
 211 	else if (!e_util_utf8_strcasecmp (partstat, _("In Process")))
 212 		return ICAL_PARTSTAT_INPROCESS;
 213 	else
 214 		return ICAL_PARTSTAT_NONE;
 215 }
 216 
 217 static gchar *
 218 partstat_to_text (icalparameter_partstat partstat)
 219 {
 220 	switch (partstat) {
 221 	case ICAL_PARTSTAT_NEEDSACTION:
 222 		return _("Needs Action");
 223 	case ICAL_PARTSTAT_ACCEPTED:
 224 		return _("Accepted");
 225 	case ICAL_PARTSTAT_DECLINED:
 226 		return _("Declined");
 227 	case ICAL_PARTSTAT_TENTATIVE:
 228 		return _("Tentative");
 229 	case ICAL_PARTSTAT_DELEGATED:
 230 		return _("Delegated");
 231 	case ICAL_PARTSTAT_COMPLETED:
 232 		return _("Completed");
 233 	case ICAL_PARTSTAT_INPROCESS:
 234 		return _("In Process");
 235 	case ICAL_PARTSTAT_NONE:
 236 	default:
 237 		return _("Unknown");
 238 	}
 239 }
 240 
 241 static GtkTreeModelFlags
 242 get_flags (GtkTreeModel *model)
 243 {
 244 	g_return_val_if_fail (E_IS_MEETING_STORE (model), 0);
 245 
 246 	return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
 247 }
 248 
 249 static gint
 250 get_n_columns (GtkTreeModel *model)
 251 {
 252 	g_return_val_if_fail (E_IS_MEETING_STORE (model), 0);
 253 
 254 	return E_MEETING_STORE_COLUMN_COUNT;
 255 }
 256 
 257 static GType
 258 get_column_type (GtkTreeModel *model,
 259                  gint col)
 260 {
 261 	g_return_val_if_fail (E_IS_MEETING_STORE (model), G_TYPE_INVALID);
 262 
 263 	switch (col) {
 264 	case E_MEETING_STORE_ADDRESS_COL:
 265 	case E_MEETING_STORE_MEMBER_COL:
 266 	case E_MEETING_STORE_TYPE_COL:
 267 	case E_MEETING_STORE_ROLE_COL:
 268 	case E_MEETING_STORE_RSVP_COL:
 269 	case E_MEETING_STORE_DELTO_COL:
 270 	case E_MEETING_STORE_DELFROM_COL:
 271 	case E_MEETING_STORE_STATUS_COL:
 272 	case E_MEETING_STORE_CN_COL:
 273 	case E_MEETING_STORE_LANGUAGE_COL:
 274 	case E_MEETING_STORE_ATTENDEE_COL:
 275 		return G_TYPE_STRING;
 276 	case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL:
 277 		return PANGO_TYPE_UNDERLINE;
 278 	default:
 279 		return G_TYPE_INVALID;
 280 	}
 281 }
 282 
 283 static gboolean
 284 get_iter (GtkTreeModel *model,
 285           GtkTreeIter *iter,
 286           GtkTreePath *path)
 287 {
 288 	gint row;
 289 
 290 	g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
 291 	g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
 292 
 293 	row = gtk_tree_path_get_indices (path)[0];
 294 
 295 	if (!ROW_VALID (E_MEETING_STORE (model), row))
 296 	       return FALSE;
 297 
 298 	iter->stamp = E_MEETING_STORE (model)->priv->stamp;
 299 	iter->user_data = GINT_TO_POINTER (row);
 300 
 301 	return TRUE;
 302 }
 303 
 304 static GtkTreePath *
 305 get_path (GtkTreeModel *model,
 306           GtkTreeIter *iter)
 307 {
 308 	gint row;
 309 	GtkTreePath *result;
 310 
 311 	g_return_val_if_fail (E_IS_MEETING_STORE (model), NULL);
 312 	g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, NULL);
 313 
 314 	row = GPOINTER_TO_INT (iter->user_data);
 315 
 316 	g_return_val_if_fail (ROW_VALID (E_MEETING_STORE (model), row), NULL);
 317 
 318 	result = gtk_tree_path_new ();
 319 	gtk_tree_path_append_index (result, row);
 320 	return result;
 321 }
 322 
 323 static void
 324 get_value (GtkTreeModel *model,
 325            GtkTreeIter *iter,
 326            gint col,
 327            GValue *value)
 328 {
 329 	EMeetingStore *store;
 330 	EMeetingAttendee *attendee;
 331 	const gchar *cn;
 332 	gint row;
 333 
 334 	g_return_if_fail (E_IS_MEETING_STORE (model));
 335 	g_return_if_fail (col >= 0 && col < E_MEETING_STORE_COLUMN_COUNT);
 336 
 337 	row = GPOINTER_TO_INT (iter->user_data);
 338 	store = E_MEETING_STORE (model);
 339 
 340 	g_return_if_fail (iter->stamp == store->priv->stamp);
 341 	g_return_if_fail (ROW_VALID (E_MEETING_STORE (model), row));
 342 
 343 	attendee = g_ptr_array_index (store->priv->attendees, row);
 344 
 345 	switch (col) {
 346 	case E_MEETING_STORE_ADDRESS_COL:
 347 		g_value_init (value, G_TYPE_STRING);
 348 		g_value_set_string (
 349 			value, itip_strip_mailto (
 350 			e_meeting_attendee_get_address (attendee)));
 351 		break;
 352 	case E_MEETING_STORE_MEMBER_COL:
 353 		g_value_init (value, G_TYPE_STRING);
 354 		g_value_set_string (
 355 			value, e_meeting_attendee_get_member (attendee));
 356 		break;
 357 	case E_MEETING_STORE_TYPE_COL:
 358 		g_value_init (value, G_TYPE_STRING);
 359 		g_value_set_string (
 360 			value, type_to_text (
 361 			e_meeting_attendee_get_cutype (attendee)));
 362 		break;
 363 	case E_MEETING_STORE_ROLE_COL:
 364 		g_value_init (value, G_TYPE_STRING);
 365 		g_value_set_string (
 366 			value, role_to_text (
 367 			e_meeting_attendee_get_role (attendee)));
 368 		break;
 369 	case E_MEETING_STORE_RSVP_COL:
 370 		g_value_init (value, G_TYPE_STRING);
 371 		g_value_set_string (
 372 			value, boolean_to_text (
 373 			e_meeting_attendee_get_rsvp (attendee)));
 374 		break;
 375 	case E_MEETING_STORE_DELTO_COL:
 376 		g_value_init (value, G_TYPE_STRING);
 377 		g_value_set_string (
 378 			value, itip_strip_mailto (
 379 			e_meeting_attendee_get_delto (attendee)));
 380 		break;
 381 	case E_MEETING_STORE_DELFROM_COL:
 382 		g_value_init (value, G_TYPE_STRING);
 383 		g_value_set_string (
 384 			value, itip_strip_mailto (
 385 			e_meeting_attendee_get_delfrom (attendee)));
 386 		break;
 387 	case E_MEETING_STORE_STATUS_COL:
 388 		g_value_init (value, G_TYPE_STRING);
 389 		g_value_set_string (
 390 			value, partstat_to_text (
 391 			e_meeting_attendee_get_status (attendee)));
 392 		break;
 393 	case E_MEETING_STORE_CN_COL:
 394 		g_value_init (value, G_TYPE_STRING);
 395 		g_value_set_string (
 396 			value, e_meeting_attendee_get_cn (attendee));
 397 		break;
 398 	case E_MEETING_STORE_LANGUAGE_COL:
 399 		g_value_init (value, G_TYPE_STRING);
 400 		g_value_set_string (
 401 			value, e_meeting_attendee_get_language (attendee));
 402 		break;
 403 	case E_MEETING_STORE_ATTENDEE_COL:
 404 		g_value_init (value, G_TYPE_STRING);
 405 		cn = e_meeting_attendee_get_cn (attendee);
 406 		if (strcmp (cn, ""))
 407 			g_value_set_string (value, cn);
 408 		else
 409 			g_value_set_string (
 410 				value, itip_strip_mailto (
 411 				e_meeting_attendee_get_address (attendee)));
 412 		break;
 413 	case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL:
 414 		cn = e_meeting_attendee_get_cn (attendee);
 415 		g_value_init (value, PANGO_TYPE_UNDERLINE);
 416 		g_value_set_enum (
 417 			value, strcmp ("", cn) == 0 ?
 418 			PANGO_UNDERLINE_NONE : PANGO_UNDERLINE_SINGLE);
 419 	}
 420 }
 421 
 422 static gboolean
 423 iter_next (GtkTreeModel *model,
 424            GtkTreeIter *iter)
 425 {
 426 	gint row;
 427 
 428 	g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
 429 	g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, FALSE);
 430 
 431 	row = GPOINTER_TO_INT (iter->user_data) + 1;
 432 	iter->user_data = GINT_TO_POINTER (row);
 433 
 434 	return ROW_VALID (E_MEETING_STORE (model), row);
 435 }
 436 
 437 static gboolean
 438 iter_children (GtkTreeModel *model,
 439                GtkTreeIter *iter,
 440                GtkTreeIter *parent)
 441 {
 442 	EMeetingStore *store;
 443 
 444 	g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
 445 
 446 	store = E_MEETING_STORE (model);
 447 
 448 	if (parent || store->priv->attendees->len <= 0)
 449 		return FALSE;
 450 
 451 	iter->stamp = store->priv->stamp;
 452 	iter->user_data = GINT_TO_POINTER (0);
 453 
 454 	return TRUE;
 455 }
 456 
 457 static gboolean
 458 iter_has_child (GtkTreeModel *model,
 459                 GtkTreeIter *iter)
 460 {
 461 	return FALSE;
 462 }
 463 
 464 static gint
 465 iter_n_children (GtkTreeModel *model,
 466                  GtkTreeIter *iter)
 467 {
 468 	g_return_val_if_fail (E_IS_MEETING_STORE (model), -1);
 469 
 470 	if (!iter)
 471 		return E_MEETING_STORE (model)->priv->attendees->len;
 472 
 473 	g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, -1);
 474 
 475 	return 0;
 476 }
 477 
 478 static gboolean
 479 iter_nth_child (GtkTreeModel *model,
 480                 GtkTreeIter *iter,
 481                 GtkTreeIter *parent,
 482                 gint n)
 483 {
 484 	EMeetingStore *store;
 485 
 486 	g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE);
 487 
 488 	store = E_MEETING_STORE (model);
 489 
 490 	if (parent || !ROW_VALID (store, n))
 491 		return FALSE;
 492 
 493 	iter->stamp = store->priv->stamp;
 494 	iter->user_data = GINT_TO_POINTER (n);
 495 
 496 	return TRUE;
 497 }
 498 
 499 static gboolean
 500 iter_parent (GtkTreeModel *model,
 501              GtkTreeIter *iter,
 502              GtkTreeIter *child)
 503 {
 504 	return FALSE;
 505 }
 506 
 507 static void
 508 ems_tree_model_init (GtkTreeModelIface *iface)
 509 {
 510 	iface->get_flags = get_flags;
 511 	iface->get_n_columns = get_n_columns;
 512 	iface->get_column_type = get_column_type;
 513 	iface->get_iter = get_iter;
 514 	iface->get_path = get_path;
 515 	iface->get_value = get_value;
 516 	iface->iter_next = iter_next;
 517 	iface->iter_children = iter_children;
 518 	iface->iter_has_child = iter_has_child;
 519 	iface->iter_n_children = iter_n_children;
 520 	iface->iter_nth_child = iter_nth_child;
 521 	iface->iter_parent = iter_parent;
 522 }
 523 
 524 void
 525 e_meeting_store_set_value (EMeetingStore *store,
 526                            gint row,
 527                            gint col,
 528                            const gchar *val)
 529 {
 530 	icalparameter_cutype type;
 531 	EMeetingAttendee *attendee = g_ptr_array_index (store->priv->attendees, row);
 532 
 533 	switch (col) {
 534 	case E_MEETING_STORE_ADDRESS_COL:
 535 		if (val != NULL && *((gchar *) val))
 536 			e_meeting_attendee_set_address (
 537 				attendee, g_strdup_printf (
 538 				"MAILTO:%s", (gchar *) val));
 539 		break;
 540 	case E_MEETING_STORE_MEMBER_COL:
 541 		e_meeting_attendee_set_member (attendee, g_strdup (val));
 542 		break;
 543 	case E_MEETING_STORE_TYPE_COL:
 544 		type = text_to_type (val);
 545 		e_meeting_attendee_set_cutype (attendee, text_to_type (val));
 546 		if (type == ICAL_CUTYPE_RESOURCE) {
 547 			e_meeting_attendee_set_role (attendee, ICAL_ROLE_NONPARTICIPANT);
 548 		}
 549 		break;
 550 	case E_MEETING_STORE_ROLE_COL:
 551 		e_meeting_attendee_set_role (attendee, text_to_role (val));
 552 		break;
 553 	case E_MEETING_STORE_RSVP_COL:
 554 		e_meeting_attendee_set_rsvp (attendee, text_to_boolean (val));
 555 		break;
 556 	case E_MEETING_STORE_DELTO_COL:
 557 		e_meeting_attendee_set_delto (attendee, g_strdup (val));
 558 		break;
 559 	case E_MEETING_STORE_DELFROM_COL:
 560 		e_meeting_attendee_set_delfrom (attendee, g_strdup (val));
 561 		break;
 562 	case E_MEETING_STORE_STATUS_COL:
 563 		e_meeting_attendee_set_status (attendee, text_to_partstat (val));
 564 		break;
 565 	case E_MEETING_STORE_CN_COL:
 566 		e_meeting_attendee_set_cn (attendee, g_strdup (val));
 567 		break;
 568 	case E_MEETING_STORE_LANGUAGE_COL:
 569 		e_meeting_attendee_set_language (attendee, g_strdup (val));
 570 		break;
 571 	}
 572 }
 573 
 574 struct FindAttendeeData
 575 {
 576 	EMeetingAttendee *find;
 577 	EMeetingStoreQueueData *qdata;
 578 };
 579 
 580 static void
 581 find_attendee_cb (gpointer key,
 582                   gpointer value,
 583                   gpointer user_data)
 584 {
 585 	EMeetingStoreQueueData *qdata = value;
 586 	struct FindAttendeeData *fad = user_data;
 587 
 588 	g_return_if_fail (qdata != NULL);
 589 	g_return_if_fail (fad != NULL);
 590 
 591 	if (qdata->attendee == fad->find)
 592 		fad->qdata = qdata;
 593 }
 594 
 595 static void
 596 refresh_queue_remove (EMeetingStore *store,
 597                       EMeetingAttendee *attendee)
 598 {
 599 	EMeetingStorePrivate *priv;
 600 	EMeetingStoreQueueData *qdata;
 601 
 602 	priv = store->priv;
 603 
 604 	/* Free the queue data */
 605 	qdata = g_hash_table_lookup (
 606 		priv->refresh_data, itip_strip_mailto (
 607 		e_meeting_attendee_get_address (attendee)));
 608 	if (!qdata) {
 609 		struct FindAttendeeData fad = { 0 };
 610 
 611 		fad.find = attendee;
 612 		fad.qdata = NULL;
 613 
 614 		g_hash_table_foreach (priv->refresh_data, find_attendee_cb, &fad);
 615 
 616 		qdata = fad.qdata;
 617 	}
 618 
 619 	if (qdata) {
 620 		g_mutex_lock (priv->mutex);
 621 		g_hash_table_remove (
 622 			priv->refresh_data, itip_strip_mailto (
 623 			e_meeting_attendee_get_address (attendee)));
 624 		g_mutex_unlock (priv->mutex);
 625 		g_ptr_array_free (qdata->call_backs, TRUE);
 626 		g_ptr_array_free (qdata->data, TRUE);
 627 		g_free (qdata);
 628 	}
 629 
 630 	/* Unref the attendee */
 631 	g_ptr_array_remove (priv->refresh_queue, attendee);
 632 	g_object_unref (attendee);
 633 }
 634 
 635 static void
 636 meeting_store_set_property (GObject *object,
 637                             guint property_id,
 638                             const GValue *value,
 639                             GParamSpec *pspec)
 640 {
 641 	switch (property_id) {
 642 		case PROP_CLIENT:
 643 			e_meeting_store_set_client (
 644 				E_MEETING_STORE (object),
 645 				g_value_get_object (value));
 646 			return;
 647 
 648 		case PROP_DEFAULT_REMINDER_INTERVAL:
 649 			e_meeting_store_set_default_reminder_interval (
 650 				E_MEETING_STORE (object),
 651 				g_value_get_int (value));
 652 			return;
 653 
 654 		case PROP_DEFAULT_REMINDER_UNITS:
 655 			e_meeting_store_set_default_reminder_units (
 656 				E_MEETING_STORE (object),
 657 				g_value_get_enum (value));
 658 			return;
 659 
 660 		case PROP_FREE_BUSY_TEMPLATE:
 661 			e_meeting_store_set_free_busy_template (
 662 				E_MEETING_STORE (object),
 663 				g_value_get_string (value));
 664 			return;
 665 
 666 		case PROP_TIMEZONE:
 667 			e_meeting_store_set_timezone (
 668 				E_MEETING_STORE (object),
 669 				g_value_get_pointer (value));
 670 			return;
 671 
 672 		case PROP_WEEK_START_DAY:
 673 			e_meeting_store_set_week_start_day (
 674 				E_MEETING_STORE (object),
 675 				g_value_get_int (value));
 676 			return;
 677 	}
 678 
 679 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 680 }
 681 
 682 static void
 683 meeting_store_get_property (GObject *object,
 684                             guint property_id,
 685                             GValue *value,
 686                             GParamSpec *pspec)
 687 {
 688 	switch (property_id) {
 689 		case PROP_CLIENT:
 690 			g_value_set_object (
 691 				value,
 692 				e_meeting_store_get_client (
 693 				E_MEETING_STORE (object)));
 694 			return;
 695 
 696 		case PROP_DEFAULT_REMINDER_INTERVAL:
 697 			g_value_set_int (
 698 				value,
 699 				e_meeting_store_get_default_reminder_interval (
 700 				E_MEETING_STORE (object)));
 701 			return;
 702 
 703 		case PROP_DEFAULT_REMINDER_UNITS:
 704 			g_value_set_enum (
 705 				value,
 706 				e_meeting_store_get_default_reminder_units (
 707 				E_MEETING_STORE (object)));
 708 			return;
 709 
 710 		case PROP_FREE_BUSY_TEMPLATE:
 711 			g_value_set_string (
 712 				value,
 713 				e_meeting_store_get_free_busy_template (
 714 				E_MEETING_STORE (object)));
 715 			return;
 716 
 717 		case PROP_TIMEZONE:
 718 			g_value_set_pointer (
 719 				value,
 720 				e_meeting_store_get_timezone (
 721 				E_MEETING_STORE (object)));
 722 			return;
 723 
 724 		case PROP_WEEK_START_DAY:
 725 			g_value_set_int (
 726 				value,
 727 				e_meeting_store_get_week_start_day (
 728 				E_MEETING_STORE (object)));
 729 			return;
 730 	}
 731 
 732 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 733 }
 734 
 735 static void
 736 meeting_store_finalize (GObject *object)
 737 {
 738 	EMeetingStorePrivate *priv;
 739 	gint i;
 740 
 741 	priv = E_MEETING_STORE_GET_PRIVATE (object);
 742 
 743 	for (i = 0; i < priv->attendees->len; i++)
 744 		g_object_unref (g_ptr_array_index (priv->attendees, i));
 745 	g_ptr_array_free (priv->attendees, TRUE);
 746 
 747 	if (priv->client != NULL)
 748 		g_object_unref (priv->client);
 749 
 750 	while (priv->refresh_queue->len > 0)
 751 		refresh_queue_remove (
 752 			E_MEETING_STORE (object),
 753 			g_ptr_array_index (priv->refresh_queue, 0));
 754 	g_ptr_array_free (priv->refresh_queue, TRUE);
 755 	g_hash_table_destroy (priv->refresh_data);
 756 
 757 	if (priv->refresh_idle_id)
 758 		g_source_remove (priv->refresh_idle_id);
 759 
 760 	g_free (priv->fb_uri);
 761 
 762 	g_mutex_free (priv->mutex);
 763 
 764 	/* Chain up to parent's finalize() method. */
 765 	G_OBJECT_CLASS (e_meeting_store_parent_class)->finalize (object);
 766 }
 767 
 768 static void
 769 e_meeting_store_class_init (EMeetingStoreClass *class)
 770 {
 771 	GObjectClass *object_class;
 772 
 773 	g_type_class_add_private (class, sizeof (EMeetingStorePrivate));
 774 
 775 	object_class = G_OBJECT_CLASS (class);
 776 	object_class->set_property = meeting_store_set_property;
 777 	object_class->get_property = meeting_store_get_property;
 778 	object_class->finalize = meeting_store_finalize;
 779 
 780 	g_object_class_install_property (
 781 		object_class,
 782 		PROP_CLIENT,
 783 		g_param_spec_object (
 784 			"client",
 785 			"ECalClient",
 786 			NULL,
 787 			E_TYPE_CAL_CLIENT,
 788 			G_PARAM_READWRITE));
 789 
 790 	g_object_class_install_property (
 791 		object_class,
 792 		PROP_DEFAULT_REMINDER_INTERVAL,
 793 		g_param_spec_int (
 794 			"default-reminder-interval",
 795 			"Default Reminder Interval",
 796 			NULL,
 797 			G_MININT,
 798 			G_MAXINT,
 799 			0,
 800 			G_PARAM_READWRITE));
 801 
 802 	g_object_class_install_property (
 803 		object_class,
 804 		PROP_DEFAULT_REMINDER_UNITS,
 805 		g_param_spec_enum (
 806 			"default-reminder-units",
 807 			"Default Reminder Units",
 808 			NULL,
 809 			E_TYPE_DURATION_TYPE,
 810 			E_DURATION_MINUTES,
 811 			G_PARAM_READWRITE));
 812 
 813 	g_object_class_install_property (
 814 		object_class,
 815 		PROP_FREE_BUSY_TEMPLATE,
 816 		g_param_spec_string (
 817 			"free-busy-template",
 818 			"Free/Busy Template",
 819 			NULL,
 820 			NULL,
 821 			G_PARAM_READWRITE));
 822 
 823 	g_object_class_install_property (
 824 		object_class,
 825 		PROP_TIMEZONE,
 826 		g_param_spec_pointer (
 827 			"timezone",
 828 			"Timezone",
 829 			NULL,
 830 			G_PARAM_READWRITE));
 831 
 832 	g_object_class_install_property (
 833 		object_class,
 834 		PROP_WEEK_START_DAY,
 835 		g_param_spec_int (
 836 			"week-start-day",
 837 			"Week Start Day",
 838 			NULL,
 839 			0,  /* Monday */
 840 			6,  /* Sunday */
 841 			0,
 842 			G_PARAM_READWRITE));
 843 }
 844 
 845 static void
 846 e_meeting_store_init (EMeetingStore *store)
 847 {
 848 	store->priv = E_MEETING_STORE_GET_PRIVATE (store);
 849 
 850 	store->priv->attendees = g_ptr_array_new ();
 851 	store->priv->refresh_queue = g_ptr_array_new ();
 852 	store->priv->refresh_data = g_hash_table_new_full (
 853 		g_str_hash, g_str_equal, g_free, NULL);
 854 
 855 	store->priv->mutex = g_mutex_new ();
 856 
 857 	store->priv->num_queries = 0;
 858 
 859 	e_extensible_load_extensions (E_EXTENSIBLE (store));
 860 }
 861 
 862 GObject *
 863 e_meeting_store_new (void)
 864 {
 865 	return g_object_new (E_TYPE_MEETING_STORE, NULL);
 866 }
 867 
 868 ECalClient *
 869 e_meeting_store_get_client (EMeetingStore *store)
 870 {
 871 	g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
 872 
 873 	return store->priv->client;
 874 }
 875 
 876 void
 877 e_meeting_store_set_client (EMeetingStore *store,
 878                             ECalClient *client)
 879 {
 880 	g_return_if_fail (E_IS_MEETING_STORE (store));
 881 
 882 	if (store->priv->client == client)
 883 		return;
 884 
 885 	if (client != NULL) {
 886 		g_return_if_fail (E_IS_CAL_CLIENT (client));
 887 		g_object_ref (client);
 888 	}
 889 
 890 	if (store->priv->client != NULL)
 891 		g_object_unref (store->priv->client);
 892 
 893 	store->priv->client = client;
 894 
 895 	g_object_notify (G_OBJECT (store), "client");
 896 }
 897 
 898 gint
 899 e_meeting_store_get_default_reminder_interval (EMeetingStore *store)
 900 {
 901 	g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
 902 
 903 	return store->priv->default_reminder_interval;
 904 }
 905 
 906 void
 907 e_meeting_store_set_default_reminder_interval (EMeetingStore *store,
 908                                                gint default_reminder_interval)
 909 {
 910 	g_return_if_fail (E_IS_MEETING_STORE (store));
 911 
 912 	if (store->priv->default_reminder_interval == default_reminder_interval)
 913 		return;
 914 
 915 	store->priv->default_reminder_interval = default_reminder_interval;
 916 
 917 	g_object_notify (G_OBJECT (store), "default-reminder-interval");
 918 }
 919 
 920 EDurationType
 921 e_meeting_store_get_default_reminder_units (EMeetingStore *store)
 922 {
 923 	g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
 924 
 925 	return store->priv->default_reminder_units;
 926 }
 927 
 928 void
 929 e_meeting_store_set_default_reminder_units (EMeetingStore *store,
 930                                             EDurationType default_reminder_units)
 931 {
 932 	g_return_if_fail (E_IS_MEETING_STORE (store));
 933 
 934 	if (store->priv->default_reminder_units == default_reminder_units)
 935 		return;
 936 
 937 	store->priv->default_reminder_units = default_reminder_units;
 938 
 939 	g_object_notify (G_OBJECT (store), "default-reminder-units");
 940 }
 941 
 942 const gchar *
 943 e_meeting_store_get_free_busy_template (EMeetingStore *store)
 944 {
 945 	g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
 946 
 947 	return store->priv->fb_uri;
 948 }
 949 
 950 void
 951 e_meeting_store_set_free_busy_template (EMeetingStore *store,
 952                                         const gchar *free_busy_template)
 953 {
 954 	g_return_if_fail (E_IS_MEETING_STORE (store));
 955 
 956 	if (g_strcmp0 (store->priv->fb_uri, free_busy_template) == 0)
 957 		return;
 958 
 959 	g_free (store->priv->fb_uri);
 960 	store->priv->fb_uri = g_strdup (free_busy_template);
 961 
 962 	g_object_notify (G_OBJECT (store), "free-busy-template");
 963 }
 964 
 965 icaltimezone *
 966 e_meeting_store_get_timezone (EMeetingStore *store)
 967 {
 968 	g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
 969 
 970 	return store->priv->zone;
 971 }
 972 
 973 void
 974 e_meeting_store_set_timezone (EMeetingStore *store,
 975                               icaltimezone *timezone)
 976 {
 977 	g_return_if_fail (E_IS_MEETING_STORE (store));
 978 
 979 	if (store->priv->zone == timezone)
 980 		return;
 981 
 982 	store->priv->zone = timezone;
 983 
 984 	g_object_notify (G_OBJECT (store), "timezone");
 985 }
 986 
 987 gint
 988 e_meeting_store_get_week_start_day (EMeetingStore *store)
 989 {
 990 	g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
 991 
 992 	return store->priv->week_start_day;
 993 }
 994 
 995 void
 996 e_meeting_store_set_week_start_day (EMeetingStore *store,
 997                                     gint week_start_day)
 998 {
 999 	g_return_if_fail (E_IS_MEETING_STORE (store));
1000 
1001 	if (store->priv->week_start_day == week_start_day)
1002 		return;
1003 
1004 	store->priv->week_start_day = week_start_day;
1005 
1006 	g_object_notify (G_OBJECT (store), "week-start-day");
1007 }
1008 
1009 static void
1010 attendee_changed_cb (EMeetingAttendee *attendee,
1011                      gpointer data)
1012 {
1013 	EMeetingStore *store = E_MEETING_STORE (data);
1014 	GtkTreePath *path;
1015 	GtkTreeIter iter;
1016 	gint row = -1, i;
1017 
1018 	for (i = 0; i < store->priv->attendees->len; i++) {
1019 		if (attendee == g_ptr_array_index (store->priv->attendees, i)) {
1020 			row = i;
1021 			break;
1022 		}
1023 	}
1024 
1025 	if (row == -1)
1026 		return;
1027 
1028 	path = gtk_tree_path_new ();
1029 	gtk_tree_path_append_index (path, row);
1030 	get_iter (GTK_TREE_MODEL (store), &iter, path);
1031 	gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, &iter);
1032 	gtk_tree_path_free (path);
1033 }
1034 
1035 void
1036 e_meeting_store_add_attendee (EMeetingStore *store,
1037                               EMeetingAttendee *attendee)
1038 {
1039 	GtkTreePath *path;
1040 	GtkTreeIter iter;
1041 
1042 	g_return_if_fail (E_IS_MEETING_STORE (store));
1043 
1044 	g_object_ref (attendee);
1045 	g_ptr_array_add (store->priv->attendees, attendee);
1046 
1047 	g_signal_connect (
1048 		attendee, "changed",
1049 		G_CALLBACK (attendee_changed_cb), store);
1050 
1051 	path = gtk_tree_path_new ();
1052 	gtk_tree_path_append_index (path, store->priv->attendees->len - 1);
1053 	get_iter (GTK_TREE_MODEL (store), &iter, path);
1054 	gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), path, &iter);
1055 	gtk_tree_path_free (path);
1056 }
1057 
1058 EMeetingAttendee *
1059 e_meeting_store_add_attendee_with_defaults (EMeetingStore *store)
1060 {
1061 	EMeetingAttendee *attendee;
1062 	gchar *str;
1063 
1064 	attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new ());
1065 
1066 	e_meeting_attendee_set_address (attendee, g_strdup (""));
1067 	e_meeting_attendee_set_member (attendee, g_strdup (""));
1068 
1069 	str = g_strdup (_("Individual"));
1070 	e_meeting_attendee_set_cutype (attendee, text_to_type (str));
1071 	g_free (str);
1072 	str = g_strdup (_("Required Participant"));
1073 	e_meeting_attendee_set_role (attendee, text_to_role (str));
1074 	g_free (str);
1075 	str = g_strdup (_("Yes"));
1076 	e_meeting_attendee_set_rsvp (attendee, text_to_boolean (str));
1077 	g_free (str);
1078 
1079 	e_meeting_attendee_set_delto (attendee, g_strdup (""));
1080 	e_meeting_attendee_set_delfrom (attendee, g_strdup (""));
1081 
1082 	str = g_strdup (_("Needs Action"));
1083 	e_meeting_attendee_set_status (attendee, text_to_partstat (str));
1084 	g_free (str);
1085 
1086 	e_meeting_attendee_set_cn (attendee, g_strdup (""));
1087 	e_meeting_attendee_set_language (attendee, g_strdup ("en"));
1088 
1089 	e_meeting_store_add_attendee (store, attendee);
1090 
1091 	return attendee;
1092 }
1093 
1094 void
1095 e_meeting_store_remove_attendee (EMeetingStore *store,
1096                                  EMeetingAttendee *attendee)
1097 {
1098 	gint i, row = -1;
1099 	GtkTreePath *path;
1100 
1101 	for (i = 0; i < store->priv->attendees->len; i++) {
1102 		if (attendee == g_ptr_array_index (store->priv->attendees, i)) {
1103 			row = i;
1104 			break;
1105 		}
1106 	}
1107 
1108 	if (row != -1) {
1109 		g_ptr_array_remove_index (store->priv->attendees, row);
1110 
1111 		path = gtk_tree_path_new ();
1112 		gtk_tree_path_append_index (path, row);
1113 		gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path);
1114 		gtk_tree_path_free (path);
1115 
1116 		g_object_unref (attendee);
1117 	}
1118 }
1119 
1120 void
1121 e_meeting_store_remove_all_attendees (EMeetingStore *store)
1122 {
1123 	gint i, j, k;
1124 
1125 	for (i = 0, j = e_meeting_store_count_actual_attendees (store), k = 0;
1126 	     i < j; i++) {
1127 		/* Always try to remove the attendee at index 0 since
1128 		 * it is the only one that will continue to exist until
1129 		 * all attendees are removed. */
1130 		EMeetingAttendee *attendee;
1131 		GtkTreePath *path;
1132 
1133 		attendee = g_ptr_array_index (store->priv->attendees, k);
1134 		g_ptr_array_remove_index (store->priv->attendees, k);
1135 
1136 		path = gtk_tree_path_new ();
1137 		gtk_tree_path_append_index (path, k);
1138 		gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path);
1139 		gtk_tree_path_free (path);
1140 
1141 		g_object_unref (attendee);
1142 	}
1143 }
1144 
1145 /**
1146  * e_meeting_store_find_self:
1147  * @store: an #EMeetingStore
1148  * @row: return location for the matching row number, or %NULL
1149  *
1150  * Looks for the user in @store by comparing attendee email addresses to
1151  * registered mail identities.  If a matching email address is found and
1152  * @row is not %NULL, @row will be set to the #EMeetingStore row number
1153  * with the matching email address.
1154  *
1155  * Returns: an #EMeetingAttendee, or %NULL
1156  **/
1157 EMeetingAttendee *
1158 e_meeting_store_find_self (EMeetingStore *store,
1159                            gint *row)
1160 {
1161 	EMeetingAttendee *attendee = NULL;
1162 	ESourceRegistry *registry;
1163 	EShell *shell;
1164 	GList *list, *iter;
1165 	const gchar *extension_name;
1166 
1167 	g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
1168 
1169 	/* FIXME Refactor this so we don't need e_shell_get_default(). */
1170 	shell = e_shell_get_default ();
1171 	registry = e_shell_get_registry (shell);
1172 	extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1173 
1174 	list = e_source_registry_list_sources (registry, extension_name);
1175 
1176 	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1177 		ESource *source = E_SOURCE (iter->data);
1178 		ESourceMailIdentity *extension;
1179 		const gchar *address;
1180 
1181 		extension = e_source_get_extension (source, extension_name);
1182 		address = e_source_mail_identity_get_address (extension);
1183 
1184 		if (address != NULL)
1185 			attendee = e_meeting_store_find_attendee (
1186 				store, address, row);
1187 
1188 		if (attendee != NULL)
1189 			break;
1190 	}
1191 
1192 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1193 
1194 	return attendee;
1195 }
1196 
1197 EMeetingAttendee *
1198 e_meeting_store_find_attendee (EMeetingStore *store,
1199                                const gchar *address,
1200                                gint *row)
1201 {
1202 	EMeetingAttendee *attendee;
1203 	gint i;
1204 
1205 	if (address == NULL)
1206 		return NULL;
1207 
1208 	for (i = 0; i < store->priv->attendees->len; i++) {
1209 		const gchar *attendee_address;
1210 
1211 		attendee = g_ptr_array_index (store->priv->attendees, i);
1212 
1213 		attendee_address = e_meeting_attendee_get_address (attendee);
1214 		if (attendee_address && !g_ascii_strcasecmp (
1215 			itip_strip_mailto (attendee_address),
1216 			itip_strip_mailto (address))) {
1217 			if (row != NULL)
1218 				*row = i;
1219 
1220 			return attendee;
1221 		}
1222 	}
1223 
1224 	return NULL;
1225 }
1226 
1227 EMeetingAttendee *
1228 e_meeting_store_find_attendee_at_row (EMeetingStore *store,
1229                                       gint row)
1230 {
1231 	g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
1232 	g_return_val_if_fail (ROW_VALID (store, row), NULL);
1233 
1234 	return g_ptr_array_index (store->priv->attendees, row);
1235 }
1236 
1237 GtkTreePath *
1238 e_meeting_store_find_attendee_path (EMeetingStore *store,
1239                                     EMeetingAttendee *attendee)
1240 {
1241 	GtkTreePath *path;
1242 	gint row = -1, i;
1243 
1244 	for (i = 0; i < store->priv->attendees->len; i++) {
1245 		if (attendee == g_ptr_array_index (store->priv->attendees, i)) {
1246 			row = i;
1247 			break;
1248 		}
1249 	}
1250 
1251 	if (row == -1)
1252 		return NULL;
1253 
1254 	path = gtk_tree_path_new ();
1255 	gtk_tree_path_append_index (path, row);
1256 
1257 	return path;
1258 }
1259 
1260 gint
1261 e_meeting_store_count_actual_attendees (EMeetingStore *store)
1262 {
1263 	g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
1264 
1265 	return store->priv->attendees->len;
1266 }
1267 
1268 const GPtrArray *
1269 e_meeting_store_get_attendees (EMeetingStore *store)
1270 {
1271 	g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL);
1272 
1273 	return store->priv->attendees;
1274 }
1275 
1276 static icaltimezone *
1277 find_zone (icalproperty *ip,
1278            icalcomponent *tz_top_level)
1279 {
1280 	icalparameter *param;
1281 	icalcomponent *sub_comp;
1282 	const gchar *tzid;
1283 	icalcompiter iter;
1284 
1285 	if (tz_top_level == NULL)
1286 		return NULL;
1287 
1288 	param = icalproperty_get_first_parameter (ip, ICAL_TZID_PARAMETER);
1289 	if (param == NULL)
1290 		return NULL;
1291 	tzid = icalparameter_get_tzid (param);
1292 
1293 	iter = icalcomponent_begin_component (tz_top_level, ICAL_VTIMEZONE_COMPONENT);
1294 	while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
1295 		icalcomponent *clone;
1296 		icalproperty *prop;
1297 		const gchar *tz_tzid;
1298 
1299 		prop = icalcomponent_get_first_property (sub_comp, ICAL_TZID_PROPERTY);
1300 		tz_tzid = icalproperty_get_tzid (prop);
1301 		if (!strcmp (tzid, tz_tzid)) {
1302 			icaltimezone *zone;
1303 
1304 			zone = icaltimezone_new ();
1305 			clone = icalcomponent_new_clone (sub_comp);
1306 			icaltimezone_set_component (zone, clone);
1307 
1308 			return zone;
1309 		}
1310 
1311 		icalcompiter_next (&iter);
1312 	}
1313 
1314 	return NULL;
1315 }
1316 
1317 static void
1318 process_callbacks (EMeetingStoreQueueData *qdata)
1319 {
1320 	EMeetingStore *store;
1321 	gint i;
1322 
1323 	store = qdata->store;
1324 
1325 	for (i = 0; i < qdata->call_backs->len; i++) {
1326 		EMeetingStoreRefreshCallback call_back;
1327 		gpointer *data = NULL;
1328 
1329 		call_back = g_ptr_array_index (qdata->call_backs, i);
1330 		data = g_ptr_array_index (qdata->data, i);
1331 
1332 		g_idle_add ((GSourceFunc) call_back, data);
1333 	}
1334 
1335 	g_mutex_lock (store->priv->mutex);
1336 	store->priv->num_threads--;
1337 	g_mutex_unlock (store->priv->mutex);
1338 
1339 	refresh_queue_remove (qdata->store, qdata->attendee);
1340 	g_object_unref (store);
1341 }
1342 
1343 static void
1344 process_free_busy_comp_get_xfb (icalproperty *ip,
1345                                 gchar **summary,
1346                                 gchar **location)
1347 {	
1348 	const gchar *tmp = NULL;
1349 	
1350 	g_return_if_fail (ip != NULL);
1351 	g_return_if_fail (summary != NULL && *summary == NULL);
1352 	g_return_if_fail (location != NULL && *location == NULL);
1353 
1354 	/* We extract extended free/busy information from the icalproperty
1355 	 * here (X-SUMMARY and X-LOCATION). If the property carries such,
1356 	 * it will be displayed as a tooltip for the busy period. Otherwise,
1357 	 * nothing will happen (*summary and/or *location will be NULL)
1358 	 */
1359 	
1360 	tmp = icalproperty_get_parameter_as_string (ip, E_MEETING_FREE_BUSY_XPROP_SUMMARY);
1361 	*summary = e_meeting_xfb_utf8_string_new_from_ical (tmp,
1362 	                                                    E_MEETING_FREE_BUSY_XPROP_MAXLEN);
1363 	tmp = icalproperty_get_parameter_as_string (ip, E_MEETING_FREE_BUSY_XPROP_LOCATION);
1364 	*location = e_meeting_xfb_utf8_string_new_from_ical (tmp,
1365 	                                                     E_MEETING_FREE_BUSY_XPROP_MAXLEN);
1366 }
1367 
1368 static void
1369 process_free_busy_comp (EMeetingAttendee *attendee,
1370                         icalcomponent *fb_comp,
1371                         icaltimezone *zone,
1372                         icalcomponent *tz_top_level)
1373 {
1374 	icalproperty *ip;
1375 
1376 	ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY);
1377 	if (ip != NULL) {
1378 		struct icaltimetype dtstart;
1379 		icaltimezone *ds_zone;
1380 
1381 		dtstart = icalproperty_get_dtstart (ip);
1382 		if (!dtstart.is_utc)
1383 			ds_zone = find_zone (ip, tz_top_level);
1384 		else
1385 			ds_zone = icaltimezone_get_utc_timezone ();
1386 		icaltimezone_convert_time (&dtstart, ds_zone, zone);
1387 		e_meeting_attendee_set_start_busy_range (
1388 			attendee,
1389 			dtstart.year,
1390 			dtstart.month,
1391 			dtstart.day,
1392 			dtstart.hour,
1393 			dtstart.minute);
1394 	}
1395 
1396 	ip = icalcomponent_get_first_property (fb_comp, ICAL_DTEND_PROPERTY);
1397 	if (ip != NULL) {
1398 		struct icaltimetype dtend;
1399 		icaltimezone *de_zone;
1400 
1401 		dtend = icalproperty_get_dtend (ip);
1402 		if (!dtend.is_utc)
1403 			de_zone = find_zone (ip, tz_top_level);
1404 		else
1405 			de_zone = icaltimezone_get_utc_timezone ();
1406 		icaltimezone_convert_time (&dtend, de_zone, zone);
1407 		e_meeting_attendee_set_end_busy_range (
1408 			attendee,
1409 			dtend.year,
1410 			dtend.month,
1411 			dtend.day,
1412 			dtend.hour,
1413 			dtend.minute);
1414 	}
1415 
1416 	ip = icalcomponent_get_first_property (fb_comp, ICAL_FREEBUSY_PROPERTY);
1417 	while (ip != NULL) {
1418 		icalparameter *param;
1419 		struct icalperiodtype fb;
1420 		EMeetingFreeBusyType busy_type = E_MEETING_FREE_BUSY_LAST;
1421 		icalparameter_fbtype fbtype = ICAL_FBTYPE_BUSY;
1422 
1423 		fb = icalproperty_get_freebusy (ip);
1424 		param = icalproperty_get_first_parameter (ip, ICAL_FBTYPE_PARAMETER);
1425 		if (param != NULL)
1426 			fbtype =  icalparameter_get_fbtype (param);
1427 
1428 		switch (fbtype) {
1429 		case ICAL_FBTYPE_BUSY:
1430 			busy_type = E_MEETING_FREE_BUSY_BUSY;
1431 			break;
1432 
1433 		case ICAL_FBTYPE_BUSYUNAVAILABLE:
1434 			busy_type = E_MEETING_FREE_BUSY_OUT_OF_OFFICE;
1435 			break;
1436 
1437 		case ICAL_FBTYPE_BUSYTENTATIVE:
1438 			busy_type = E_MEETING_FREE_BUSY_TENTATIVE;
1439 			break;
1440 
1441 		case ICAL_FBTYPE_FREE:
1442 			busy_type = E_MEETING_FREE_BUSY_FREE;
1443 			break;
1444 
1445 		default:
1446 			break;
1447 		}
1448 
1449 		if (busy_type != E_MEETING_FREE_BUSY_LAST) {
1450 			icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
1451 			gchar *summary = NULL;
1452 			gchar *location = NULL;
1453 
1454 			icaltimezone_convert_time (&fb.start, utc_zone, zone);
1455 			icaltimezone_convert_time (&fb.end, utc_zone, zone);
1456 
1457 			/* Extract extended free/busy (XFB) information from
1458 			 * the icalproperty, if it carries such.
1459 			 * See the comment for the EMeetingXfbData structure
1460 			 * for a reference.
1461 			 */
1462 			process_free_busy_comp_get_xfb (ip, &summary, &location);
1463 			
1464 			e_meeting_attendee_add_busy_period (attendee,
1465 							    fb.start.year,
1466 							    fb.start.month,
1467 							    fb.start.day,
1468 							    fb.start.hour,
1469 							    fb.start.minute,
1470 							    fb.end.year,
1471 							    fb.end.month,
1472 							    fb.end.day,
1473 							    fb.end.hour,
1474 							    fb.end.minute,
1475 			                                    busy_type,
1476 			                                    summary,
1477 			                                    location);
1478 			
1479 			if (summary != NULL)
1480 				g_free (summary);
1481 			if (location != NULL)
1482 				g_free (location);
1483 		}
1484 
1485 		ip = icalcomponent_get_next_property (fb_comp, ICAL_FREEBUSY_PROPERTY);
1486 	}
1487 }
1488 
1489 static void
1490 process_free_busy (EMeetingStoreQueueData *qdata,
1491                    gchar *text)
1492 {
1493 	EMeetingStore *store = qdata->store;
1494 	EMeetingStorePrivate *priv;
1495 	EMeetingAttendee *attendee = qdata->attendee;
1496 	icalcomponent *main_comp;
1497 	icalcomponent_kind kind = ICAL_NO_COMPONENT;
1498 
1499 	priv = store->priv;
1500 
1501 	main_comp = icalparser_parse_string (text);
1502 	if (main_comp == NULL) {
1503 		process_callbacks (qdata);
1504 		return;
1505 	}
1506 
1507 	kind = icalcomponent_isa (main_comp);
1508 	if (kind == ICAL_VCALENDAR_COMPONENT) {
1509 		icalcompiter iter;
1510 		icalcomponent *tz_top_level, *sub_comp;
1511 
1512 		tz_top_level = e_cal_util_new_top_level ();
1513 
1514 		iter = icalcomponent_begin_component (main_comp, ICAL_VTIMEZONE_COMPONENT);
1515 		while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
1516 			icalcomponent *clone;
1517 
1518 			clone = icalcomponent_new_clone (sub_comp);
1519 			icalcomponent_add_component (tz_top_level, clone);
1520 
1521 			icalcompiter_next (&iter);
1522 		}
1523 
1524 		iter = icalcomponent_begin_component (main_comp, ICAL_VFREEBUSY_COMPONENT);
1525 		while ((sub_comp = icalcompiter_deref (&iter)) != NULL) {
1526 			process_free_busy_comp (attendee, sub_comp, priv->zone, tz_top_level);
1527 
1528 			icalcompiter_next (&iter);
1529 		}
1530 		icalcomponent_free (tz_top_level);
1531 	} else if (kind == ICAL_VFREEBUSY_COMPONENT) {
1532 		process_free_busy_comp (attendee, main_comp, priv->zone, NULL);
1533 	}
1534 
1535 	icalcomponent_free (main_comp);
1536 
1537 	process_callbacks (qdata);
1538 }
1539 
1540 /*
1541  * Replace all instances of from_value in string with to_value
1542  * In the returned newly allocated string.
1543 */
1544 static gchar *
1545 replace_string (gchar *string,
1546                 const gchar *from_value,
1547                 gchar *to_value)
1548 {
1549 	gchar *replaced;
1550 	gchar **split_uri;
1551 
1552 	split_uri = g_strsplit (string, from_value, 0);
1553 	replaced = g_strjoinv (to_value, split_uri);
1554 	g_strfreev (split_uri);
1555 
1556 	return replaced;
1557 }
1558 
1559 static void start_async_read (const gchar *uri, gpointer data);
1560 
1561 typedef struct {
1562 	ECalClient *client;
1563 	time_t startt;
1564 	time_t endt;
1565 	GSList *users;
1566 	GSList *fb_data;
1567 	gchar *fb_uri;
1568 	gchar *email;
1569 	EMeetingAttendee *attendee;
1570 	EMeetingStoreQueueData *qdata;
1571 	EMeetingStore *store;
1572 } FreeBusyAsyncData;
1573 
1574 #define USER_SUB   "%u"
1575 #define DOMAIN_SUB "%d"
1576 
1577 static void
1578 client_free_busy_data_cb (ECalClient *client,
1579                           const GSList *ecalcomps,
1580                           FreeBusyAsyncData *fbd)
1581 {
1582 	const GSList *iter;
1583 
1584 	g_return_if_fail (fbd != NULL);
1585 
1586 	for (iter = ecalcomps; iter != NULL; iter = iter->next) {
1587 		ECalComponent *comp = iter->data;
1588 
1589 		if (comp != NULL)
1590 			fbd->fb_data = g_slist_prepend (
1591 				fbd->fb_data, g_object_ref (comp));
1592 	}
1593 }
1594 
1595 static gboolean
1596 freebusy_async (gpointer data)
1597 {
1598 	FreeBusyAsyncData *fbd = data;
1599 	EMeetingAttendee *attendee = fbd->attendee;
1600 	gchar *default_fb_uri = NULL;
1601 	gchar *fburi = NULL;
1602 	static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1603 	EMeetingStorePrivate *priv = fbd->store->priv;
1604 
1605 	if (fbd->client) {
1606 		guint sigid;
1607 		/* FIXME This a workaround for getting all the free busy
1608 		 *       information for the users.  We should be able to
1609 		 *       get free busy asynchronously. */
1610 		g_static_mutex_lock (&mutex);
1611 		priv->num_queries++;
1612 		sigid = g_signal_connect (
1613 			fbd->client, "free-busy-data",
1614 			G_CALLBACK (client_free_busy_data_cb), fbd);
1615 		e_cal_client_get_free_busy_sync (
1616 			fbd->client, fbd->startt,
1617 			fbd->endt, fbd->users, NULL, NULL);
1618 		g_signal_handler_disconnect (fbd->client, sigid);
1619 		priv->num_queries--;
1620 		g_static_mutex_unlock (&mutex);
1621 
1622 		g_slist_foreach (fbd->users, (GFunc) g_free, NULL);
1623 		g_slist_free (fbd->users);
1624 
1625 		if (fbd->fb_data != NULL) {
1626 			ECalComponent *comp = fbd->fb_data->data;
1627 			gchar *comp_str;
1628 
1629 			comp_str = e_cal_component_get_as_string (comp);
1630 			process_free_busy (fbd->qdata, comp_str);
1631 			g_free (comp_str);
1632 
1633 			return TRUE;
1634 		}
1635 	}
1636 
1637 	/* Look for fburl's of attendee with no free busy info on server */
1638 	if (!e_meeting_attendee_is_set_address (attendee)) {
1639 		process_callbacks (fbd->qdata);
1640 		return TRUE;
1641 	}
1642 
1643 	/* Check for free busy info on the default server */
1644 	default_fb_uri = g_strdup (fbd->fb_uri);
1645 	fburi = g_strdup (e_meeting_attendee_get_fburi (attendee));
1646 
1647 	if (fburi && !*fburi) {
1648 		g_free (fburi);
1649 		fburi = NULL;
1650 	}
1651 
1652 	if (fburi) {
1653 		priv->num_queries++;
1654 		start_async_read (fburi, fbd->qdata);
1655 		g_free (fburi);
1656 	} else if (default_fb_uri != NULL && !g_str_equal (default_fb_uri, "")) {
1657 		gchar *tmp_fb_uri;
1658 		gchar **split_email;
1659 
1660 		split_email = g_strsplit (fbd->email, "@", 2);
1661 
1662 		tmp_fb_uri = replace_string (default_fb_uri, USER_SUB, split_email[0]);
1663 		g_free (default_fb_uri);
1664 		default_fb_uri = replace_string (tmp_fb_uri, DOMAIN_SUB, split_email[1]);
1665 
1666 		priv->num_queries++;
1667 		start_async_read (default_fb_uri, fbd->qdata);
1668 		g_free (tmp_fb_uri);
1669 		g_strfreev (split_email);
1670 		g_free (default_fb_uri);
1671 	} else {
1672 		process_callbacks (fbd->qdata);
1673 	}
1674 
1675 	return TRUE;
1676 }
1677 
1678 #undef USER_SUB
1679 #undef DOMAIN_SUB
1680 
1681 static gboolean
1682 refresh_busy_periods (gpointer data)
1683 {
1684 	EMeetingStore *store = E_MEETING_STORE (data);
1685 	EMeetingStorePrivate *priv;
1686 	EMeetingAttendee *attendee = NULL;
1687 	EMeetingStoreQueueData *qdata = NULL;
1688 	gint i;
1689 	GThread *thread;
1690 	GError *error = NULL;
1691 	FreeBusyAsyncData *fbd;
1692 
1693 	priv = store->priv;
1694 
1695 	/* Check to see if there are any remaining attendees in the queue */
1696 	for (i = 0; i < priv->refresh_queue->len; i++) {
1697 		attendee = g_ptr_array_index (priv->refresh_queue, i);
1698 		g_return_val_if_fail (attendee != NULL, FALSE);
1699 
1700 		qdata = g_hash_table_lookup (
1701 			priv->refresh_data, itip_strip_mailto (
1702 			e_meeting_attendee_get_address (attendee)));
1703 		if (!qdata)
1704 			continue;
1705 
1706 		if (!qdata->refreshing)
1707 			break;
1708 	}
1709 
1710 	/* The everything in the queue is being refreshed */
1711 	if (i >= priv->refresh_queue->len) {
1712 		priv->refresh_idle_id = 0;
1713 		return FALSE;
1714 	}
1715 
1716 	/* Indicate we are trying to refresh it */
1717 	qdata->refreshing = TRUE;
1718 
1719 	/* We take a ref in case we get destroyed in the gui during a callback */
1720 	g_object_ref (qdata->store);
1721 
1722 	fbd = g_new0 (FreeBusyAsyncData, 1);
1723 	fbd->client = priv->client;
1724 	fbd->attendee = attendee;
1725 	fbd->users = NULL;
1726 	fbd->fb_data = NULL;
1727 	fbd->qdata = qdata;
1728 	fbd->fb_uri = priv->fb_uri;
1729 	fbd->store = store;
1730 	fbd->email = g_strdup (itip_strip_mailto (
1731 		e_meeting_attendee_get_address (attendee)));
1732 
1733 	/* Check the server for free busy data */
1734 	if (priv->client) {
1735 		struct icaltimetype itt;
1736 
1737 		itt = icaltime_null_time ();
1738 		itt.year = g_date_get_year (&qdata->start.date);
1739 		itt.month = g_date_get_month (&qdata->start.date);
1740 		itt.day = g_date_get_day (&qdata->start.date);
1741 		itt.hour = qdata->start.hour;
1742 		itt.minute = qdata->start.minute;
1743 		fbd->startt = icaltime_as_timet_with_zone (itt, priv->zone);
1744 
1745 		itt = icaltime_null_time ();
1746 		itt.year = g_date_get_year (&qdata->end.date);
1747 		itt.month = g_date_get_month (&qdata->end.date);
1748 		itt.day = g_date_get_day (&qdata->end.date);
1749 		itt.hour = qdata->end.hour;
1750 		itt.minute = qdata->end.minute;
1751 		fbd->endt = icaltime_as_timet_with_zone (itt, priv->zone);
1752 		fbd->qdata = qdata;
1753 
1754 		fbd->users = g_slist_append (fbd->users, g_strdup (fbd->email));
1755 
1756 	}
1757 
1758 	g_mutex_lock (store->priv->mutex);
1759 	store->priv->num_threads++;
1760 	g_mutex_unlock (store->priv->mutex);
1761 
1762 	thread = g_thread_create ((GThreadFunc) freebusy_async, fbd, FALSE, &error);
1763 	if (!thread) {
1764 		/* do clean up stuff here */
1765 		g_slist_foreach (fbd->users, (GFunc) g_free, NULL);
1766 		g_slist_free (fbd->users);
1767 		g_free (fbd->email);
1768 		priv->refresh_idle_id = 0;
1769 
1770 		g_mutex_lock (store->priv->mutex);
1771 		store->priv->num_threads--;
1772 		g_mutex_unlock (store->priv->mutex);
1773 
1774 		return FALSE;
1775 	}
1776 
1777 	return TRUE;
1778 }
1779 
1780 static void
1781 refresh_queue_add (EMeetingStore *store,
1782                    gint row,
1783                    EMeetingTime *start,
1784                    EMeetingTime *end,
1785                    EMeetingStoreRefreshCallback call_back,
1786                    gpointer data)
1787 {
1788 	EMeetingStorePrivate *priv;
1789 	EMeetingAttendee *attendee;
1790 	EMeetingStoreQueueData *qdata;
1791 	gint i;
1792 
1793 	priv = store->priv;
1794 
1795 	attendee = g_ptr_array_index (priv->attendees, row);
1796 	if ((attendee == NULL) || !strcmp (itip_strip_mailto (
1797 		e_meeting_attendee_get_address (attendee)), ""))
1798 		return;
1799 
1800 	/* check the queue if the attendee is already in there*/
1801 	for (i = 0; i < priv->refresh_queue->len; i++) {
1802 		if (attendee == g_ptr_array_index (priv->refresh_queue, i))
1803 			return;
1804 
1805 		if (!strcmp (e_meeting_attendee_get_address (attendee),
1806 			e_meeting_attendee_get_address (
1807 			g_ptr_array_index (priv->refresh_queue, i))))
1808 			return;
1809 	}
1810 
1811 	g_mutex_lock (priv->mutex);
1812 	qdata = g_hash_table_lookup (
1813 		priv->refresh_data, itip_strip_mailto (
1814 		e_meeting_attendee_get_address (attendee)));
1815 
1816 	if (qdata == NULL) {
1817 		qdata = g_new0 (EMeetingStoreQueueData, 1);
1818 
1819 		qdata->store = store;
1820 		qdata->attendee = attendee;
1821 		e_meeting_attendee_clear_busy_periods (attendee);
1822 		e_meeting_attendee_set_has_calendar_info (attendee, FALSE);
1823 
1824 		qdata->start = *start;
1825 		qdata->end = *end;
1826 		qdata->string = g_string_new (NULL);
1827 		qdata->call_backs = g_ptr_array_new ();
1828 		qdata->data = g_ptr_array_new ();
1829 		g_ptr_array_add (qdata->call_backs, call_back);
1830 		g_ptr_array_add (qdata->data, data);
1831 
1832 		g_hash_table_insert (
1833 			priv->refresh_data, g_strdup (itip_strip_mailto (
1834 			e_meeting_attendee_get_address (attendee))), qdata);
1835 	} else {
1836 		if (e_meeting_time_compare_times (start, &qdata->start) == -1)
1837 			qdata->start = *start;
1838 		if (e_meeting_time_compare_times (end, &qdata->end) == -1)
1839 			qdata->end = *end;
1840 		g_ptr_array_add (qdata->call_backs, call_back);
1841 		g_ptr_array_add (qdata->data, data);
1842 	}
1843 	g_mutex_unlock (priv->mutex);
1844 
1845 	g_object_ref (attendee);
1846 	g_ptr_array_add (priv->refresh_queue, attendee);
1847 
1848 	if (priv->refresh_idle_id == 0)
1849 		priv->refresh_idle_id = g_idle_add (refresh_busy_periods, store);
1850 }
1851 
1852 static void
1853 async_read (GObject *source_object,
1854             GAsyncResult *result,
1855             gpointer data)
1856 {
1857 	EMeetingStoreQueueData *qdata = data;
1858 	GError *error = NULL;
1859 	GInputStream *istream;
1860 	gssize read;
1861 
1862 	g_return_if_fail (source_object != NULL);
1863 	g_return_if_fail (G_IS_INPUT_STREAM (source_object));
1864 
1865 	istream = G_INPUT_STREAM (source_object);
1866 
1867 	read = g_input_stream_read_finish (istream, result, &error);
1868 
1869 	if (error != NULL) {
1870 		g_warning (
1871 			"Read finish failed: %s", error->message);
1872 		g_error_free (error);
1873 
1874 		g_input_stream_close (istream, NULL, NULL);
1875 		g_object_unref (istream);
1876 		process_free_busy (qdata, qdata->string->str);
1877 		return;
1878 	}
1879 
1880 	g_return_if_fail (read >= 0);
1881 
1882 	if (read == 0) {
1883 		g_input_stream_close (istream, NULL, NULL);
1884 		g_object_unref (istream);
1885 		process_free_busy (qdata, qdata->string->str);
1886 	} else {
1887 		qdata->buffer[read] = '\0';
1888 		qdata->string = g_string_append (qdata->string, qdata->buffer);
1889 
1890 		g_input_stream_read_async (
1891 			istream, qdata->buffer, BUF_SIZE - 1,
1892 			G_PRIORITY_DEFAULT, NULL, async_read, qdata);
1893 	}
1894 }
1895 
1896 static void
1897 soup_authenticate (SoupSession *session,
1898                    SoupMessage *msg,
1899                    SoupAuth *auth,
1900                    gboolean retrying,
1901                    gpointer data)
1902 {
1903 	SoupURI *suri;
1904 	const gchar *orig_uri;
1905 	gboolean tried = FALSE;
1906 
1907 	g_return_if_fail (msg != NULL);
1908 	g_return_if_fail (auth != NULL);
1909 
1910 	orig_uri = g_object_get_data (G_OBJECT (msg), "orig-uri");
1911 	g_return_if_fail (orig_uri != NULL);
1912 
1913 	suri = soup_uri_new (orig_uri);
1914 	if (!suri)
1915 		return;
1916 
1917 	if (!suri->user || !*suri->user) {
1918 		soup_uri_free (suri);
1919 		return;
1920 	}
1921 
1922 	if (!retrying) {
1923 		if (suri->password) {
1924 			soup_auth_authenticate (auth, suri->user, suri->password);
1925 			tried = TRUE;
1926 		} else {
1927 			gchar *password;
1928 
1929 			password = e_passwords_get_password (NULL, orig_uri);
1930 			if (password) {
1931 				soup_auth_authenticate (auth, suri->user, password);
1932 				tried = TRUE;
1933 
1934 				memset (password, 0, strlen (password));
1935 				g_free (password);
1936 			}
1937 		}
1938 	}
1939 
1940 	if (!tried) {
1941 		gboolean remember = FALSE;
1942 		gchar *password, *bold_host, *bold_user;
1943 		GString *description;
1944 
1945 		bold_host = g_strconcat ("<b>", suri->host, "</b>", NULL);
1946 		bold_user = g_strconcat ("<b>", suri->user, "</b>", NULL);
1947 
1948 		description = g_string_new ("");
1949 
1950 		g_string_append_printf (
1951 			description, _("Enter password to access "
1952 			"free/busy information on server %s as user %s"),
1953 			bold_host, bold_user);
1954 
1955 		g_free (bold_host);
1956 		g_free (bold_user);
1957 
1958 		if (retrying && msg->reason_phrase && *msg->reason_phrase) {
1959 			g_string_append (description, "\n");
1960 			g_string_append_printf (
1961 				description, _("Failure reason: %s"),
1962 				msg->reason_phrase);
1963 		}
1964 
1965 		password = e_passwords_ask_password (
1966 			_("Enter password"), NULL, orig_uri,
1967 			description->str, E_PASSWORDS_REMEMBER_FOREVER |
1968 			E_PASSWORDS_SECRET | E_PASSWORDS_ONLINE |
1969 			(retrying ? E_PASSWORDS_REPROMPT : 0),
1970 			&remember, NULL);
1971 
1972 		g_string_free (description, TRUE);
1973 
1974 		if (password) {
1975 			soup_auth_authenticate (auth, suri->user, password);
1976 			tried = TRUE;
1977 
1978 			memset (password, 0, strlen (password));
1979 			g_free (password);
1980 		}
1981 	}
1982 
1983 	soup_uri_free (suri);
1984 }
1985 
1986 static void
1987 redirect_handler (SoupMessage *msg,
1988                   gpointer user_data)
1989 {
1990 	if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
1991 		SoupSession *soup_session = user_data;
1992 		SoupURI *new_uri;
1993 		const gchar *new_loc;
1994 
1995 		new_loc = soup_message_headers_get (msg->response_headers, "Location");
1996 		if (!new_loc)
1997 			return;
1998 
1999 		new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
2000 		if (!new_uri) {
2001 			soup_message_set_status_full (
2002 				msg,
2003 				SOUP_STATUS_MALFORMED,
2004 				"Invalid Redirect URL");
2005 			return;
2006 		}
2007 
2008 		soup_message_set_uri (msg, new_uri);
2009 		soup_session_requeue_message (soup_session, msg);
2010 
2011 		soup_uri_free (new_uri);
2012 	}
2013 }
2014 
2015 static void
2016 soup_msg_ready_cb (SoupSession *session,
2017                    SoupMessage *msg,
2018                    gpointer user_data)
2019 {
2020 	EMeetingStoreQueueData *qdata = user_data;
2021 
2022 	g_return_if_fail (session != NULL);
2023 	g_return_if_fail (msg != NULL);
2024 	g_return_if_fail (qdata != NULL);
2025 
2026 	if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
2027 		qdata->string = g_string_new_len (
2028 			msg->response_body->data,
2029 			msg->response_body->length);
2030 		process_free_busy (qdata, qdata->string->str);
2031 	} else {
2032 		g_warning (
2033 			"Unable to access free/busy url: %s",
2034 			msg->reason_phrase && *msg->reason_phrase ?
2035 			msg->reason_phrase : (soup_status_get_phrase (
2036 			msg->status_code) ? soup_status_get_phrase (
2037 			msg->status_code) : "Unknown error"));
2038 		process_callbacks (qdata);
2039 	}
2040 }
2041 
2042 static void
2043 download_with_libsoup (const gchar *uri,
2044                        EMeetingStoreQueueData *qdata)
2045 {
2046 	SoupSession *session;
2047 	SoupMessage *msg;
2048 	EProxy *proxy;
2049 
2050 	g_return_if_fail (uri != NULL);
2051 	g_return_if_fail (qdata != NULL);
2052 
2053 	msg = soup_message_new (SOUP_METHOD_GET, uri);
2054 	if (!msg) {
2055 		g_warning ("Unable to access free/busy url '%s'; malformed?", uri);
2056 		process_callbacks (qdata);
2057 		return;
2058 	}
2059 
2060 	g_object_set_data_full (G_OBJECT (msg), "orig-uri", g_strdup (uri), g_free);
2061 
2062 	session = soup_session_async_new ();
2063 	g_object_set (session, SOUP_SESSION_TIMEOUT, 90, NULL);
2064 	g_signal_connect (
2065 		session, "authenticate",
2066 		G_CALLBACK (soup_authenticate), NULL);
2067 
2068 	proxy = e_proxy_new ();
2069 	e_proxy_setup_proxy (proxy);
2070 
2071 	if (e_proxy_require_proxy_for_uri (proxy, uri)) {
2072 		SoupURI *proxy_uri;
2073 
2074 		proxy_uri = e_proxy_peek_uri_for (proxy, uri);
2075 		g_object_set (session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
2076 	}
2077 
2078 	g_object_unref (proxy);
2079 
2080 	soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
2081 	soup_message_add_header_handler (
2082 		msg, "got_body", "Location",
2083 		G_CALLBACK (redirect_handler), session);
2084 	soup_message_headers_append (msg->request_headers, "Connection", "close");
2085 	soup_session_queue_message (session, msg, soup_msg_ready_cb, qdata);
2086 }
2087 
2088 static void
2089 start_async_read (const gchar *uri,
2090                   gpointer data)
2091 {
2092 	EMeetingStoreQueueData *qdata = data;
2093 	GError *error = NULL;
2094 	GFile *file;
2095 	GInputStream *istream;
2096 
2097 	g_return_if_fail (uri != NULL);
2098 	g_return_if_fail (data != NULL);
2099 
2100 	qdata->store->priv->num_queries--;
2101 	file = g_file_new_for_uri (uri);
2102 
2103 	g_return_if_fail (file != NULL);
2104 
2105 	istream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
2106 
2107 	if (error && g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
2108 		download_with_libsoup (uri, qdata);
2109 		g_object_unref (file);
2110 		g_error_free (error);
2111 		return;
2112 	}
2113 
2114 	if (error) {
2115 		g_warning ("Unable to access free/busy url: %s", error->message);
2116 		g_error_free (error);
2117 		process_callbacks (qdata);
2118 		g_object_unref (file);
2119 		return;
2120 	}
2121 
2122 	if (!istream) {
2123 		process_callbacks (qdata);
2124 		g_object_unref (file);
2125 	} else
2126 		g_input_stream_read_async (
2127 			istream, qdata->buffer, BUF_SIZE - 1,
2128 			G_PRIORITY_DEFAULT, NULL, async_read, qdata);
2129 }
2130 
2131 void
2132 e_meeting_store_refresh_all_busy_periods (EMeetingStore *store,
2133                                           EMeetingTime *start,
2134                                           EMeetingTime *end,
2135                                           EMeetingStoreRefreshCallback call_back,
2136                                           gpointer data)
2137 {
2138 	gint i;
2139 
2140 	g_return_if_fail (E_IS_MEETING_STORE (store));
2141 
2142 	for (i = 0; i < store->priv->attendees->len; i++)
2143 		refresh_queue_add (store, i, start, end, call_back, data);
2144 }
2145 
2146 void
2147 e_meeting_store_refresh_busy_periods (EMeetingStore *store,
2148                                       gint row,
2149                                       EMeetingTime *start,
2150                                       EMeetingTime *end,
2151                                       EMeetingStoreRefreshCallback call_back,
2152                                       gpointer data)
2153 {
2154 	g_return_if_fail (E_IS_MEETING_STORE (store));
2155 
2156 	refresh_queue_add (store, row, start, end, call_back, data);
2157 }
2158 
2159 guint
2160 e_meeting_store_get_num_queries (EMeetingStore *store)
2161 {
2162 	g_return_val_if_fail (E_IS_MEETING_STORE (store), 0);
2163 
2164 	return store->priv->num_queries;
2165 }