Commit message: [IMAPx] 'STARTTLS not supported' error ignored

When a user has setup the STARTTLS encryption method, but the server doesn't
support it, then an error should be shown to the user, instead of using
unsecure connection. There had been two bugs in the existing code which
prevented this error from being used and the failure properly reported.

This had been filled at:
https://bugzilla.redhat.com/show_bug.cgi?id=1334842
CVE: None
Vulnerability description: None

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_ensure_selected_sync (CamelIMAPXServer *is,
					 CamelIMAPXMailbox *mailbox,
					 GCancellable *cancellable,
					 GError **error)
{
	CamelIMAPXCommand *ic;
	CamelIMAPXMailbox *selected_mailbox;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);

	if (g_cancellable_set_error_if_cancelled (cancellable, error))
		return FALSE;

	g_mutex_lock (&is->priv->select_lock);
	selected_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
	if (selected_mailbox == mailbox) {
		g_mutex_unlock (&is->priv->select_lock);
		g_clear_object (&selected_mailbox);
		return TRUE;
	}

	g_clear_object (&selected_mailbox);

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SELECT, "SELECT %M", mailbox);

	if (is->priv->use_qresync) {
		CamelFolder *folder;

		folder = imapx_server_ref_folder (is, mailbox);
		camel_imapx_command_add_qresync_parameter (ic, folder);
		g_clear_object (&folder);
	}

	g_weak_ref_set (&is->priv->select_pending, mailbox);
	g_mutex_unlock (&is->priv->select_lock);

	success = camel_imapx_server_process_command_sync (is, ic, _("Failed to select mailbox"), cancellable, error);

	camel_imapx_command_unref (ic);

	g_mutex_lock (&is->priv->select_lock);

	g_weak_ref_set (&is->priv->select_pending, NULL);

	if (success) {
		is->priv->state = IMAPX_SELECTED;
		g_weak_ref_set (&is->priv->select_mailbox, mailbox);
	}

	g_mutex_unlock (&is->priv->select_lock);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_list (CamelIMAPXServer *is,
                     GInputStream *input_stream,
                     GCancellable *cancellable,
                     GError **error)
{
	CamelIMAPXListResponse *response;
	CamelIMAPXStore *imapx_store;
	const gchar *mailbox_name;
	gchar separator;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	response = camel_imapx_list_response_new (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
	if (response == NULL)
		return FALSE;

	mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
	separator = camel_imapx_list_response_get_separator (response);

	/* Record the INBOX separator character once we know it. */
	if (camel_imapx_mailbox_is_inbox (mailbox_name))
		is->priv->inbox_separator = separator;

	imapx_store = camel_imapx_server_ref_store (is);
	camel_imapx_store_handle_list_response (imapx_store, is, response);

	g_clear_object (&imapx_store);
	g_clear_object (&response);

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_sync_changes_sync (CamelIMAPXServer *is,
				      CamelIMAPXMailbox *mailbox,
				      gboolean can_influence_flags,
				      GCancellable *cancellable,
				      GError **error)
{
	guint i, jj, on, on_orset, off_orset;
	GPtrArray *changed_uids;
	GArray *on_user = NULL, *off_user = NULL;
	CamelFolder *folder;
	CamelIMAPXMessageInfo *info;
	GHashTable *changed_meanwhile;
	gulong changed_meanwhile_handler_id;
	guint32 permanentflags;
	struct _uidset_state uidset;
	gint unread_change = 0;
	gboolean use_real_junk_path = FALSE;
	gboolean use_real_trash_path = FALSE;
	gboolean remove_deleted_flags = FALSE;
	gboolean nothing_to_do;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_val_if_fail (folder != NULL, FALSE);

	/* We calculate two masks, a mask of all flags which have been
	 * turned off and a mask of all flags which have been turned
	 * on. If either of these aren't 0, then we have work to do,
	 * and we fire off a job to do it.
	 *
	 * User flags are a bit more tricky, we rely on the user
	 * flags being sorted, and then we create a bunch of lists;
	 * one for each flag being turned off, including each
	 * info being turned off, and one for each flag being turned on.
	 */
	changed_uids = camel_folder_summary_get_changed (folder->summary);

	if (changed_uids->len == 0) {
		camel_folder_free_uids (folder, changed_uids);
		g_object_unref (folder);
		return TRUE;
	}

	changed_meanwhile = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);
	changed_meanwhile_handler_id = g_signal_connect (folder->summary, "info-changed",
		G_CALLBACK (imapx_server_info_changed_cb), changed_meanwhile);

	if (can_influence_flags) {
		CamelIMAPXSettings *settings;

		settings = camel_imapx_server_ref_settings (is);
		use_real_junk_path = camel_imapx_settings_get_use_real_junk_path (settings);
		use_real_trash_path = camel_imapx_settings_get_use_real_trash_path (settings);
		if (use_real_trash_path) {
			CamelFolder *trash_folder = NULL;
			gchar *real_trash_path;

			real_trash_path = camel_imapx_settings_dup_real_trash_path (settings);
			if (real_trash_path)
				trash_folder = camel_store_get_folder_sync (
					camel_folder_get_parent_store (folder),
					real_trash_path, 0, cancellable, NULL);

			/* Remove deleted flags in all but the trash folder itself */
			remove_deleted_flags = !trash_folder || trash_folder != folder;

			use_real_trash_path = trash_folder != NULL;

			g_clear_object (&trash_folder);
			g_free (real_trash_path);
		}
		g_object_unref (settings);
	}

	if (changed_uids->len > 20)
		camel_folder_summary_prepare_fetch_all (folder->summary, NULL);

	off_orset = on_orset = 0;
	for (i = 0; i < changed_uids->len; i++) {
		guint32 flags, sflags;
		CamelFlag *uflags, *suflags;
		const gchar *uid;
		guint j = 0;

		uid = g_ptr_array_index (changed_uids, i);

		info = (CamelIMAPXMessageInfo *)
			camel_folder_summary_get (folder->summary, uid);

		if (info == NULL)
			continue;

		if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
			camel_message_info_unref (info);
			continue;
		}

		flags = info->info.flags & CAMEL_IMAPX_SERVER_FLAGS;
		sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;

		if (can_influence_flags) {
			gboolean move_to_real_junk;
			gboolean move_to_real_trash;

			move_to_real_junk =
				use_real_junk_path &&
				(flags & CAMEL_MESSAGE_JUNK);

			move_to_real_trash =
				use_real_trash_path && remove_deleted_flags &&
				(flags & CAMEL_MESSAGE_DELETED);

			if (move_to_real_junk)
				camel_imapx_folder_add_move_to_real_junk (
					CAMEL_IMAPX_FOLDER (folder), uid);

			if (move_to_real_trash)
				camel_imapx_folder_add_move_to_real_trash (
					CAMEL_IMAPX_FOLDER (folder), uid);
		}

		if (flags != sflags) {
			off_orset |= (flags ^ sflags) & ~flags;
			on_orset |= (flags ^ sflags) & flags;
		}

		uflags = info->info.user_flags;
		suflags = info->server_user_flags;
		while (uflags || suflags) {
			gint res;

			if (uflags) {
				if (suflags)
					res = strcmp (uflags->name, suflags->name);
				else if (*uflags->name)
					res = -1;
				else {
					uflags = uflags->next;
					continue;
				}
			} else {
				res = 1;
			}

			if (res == 0) {
				uflags = uflags->next;
				suflags = suflags->next;
			} else {
				GArray *user_set;
				CamelFlag *user_flag;
				struct _imapx_flag_change *change = NULL, add = { 0 };

				if (res < 0) {
					if (on_user == NULL)
						on_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
					user_set = on_user;
					user_flag = uflags;
					uflags = uflags->next;
				} else {
					if (off_user == NULL)
						off_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
					user_set = off_user;
					user_flag = suflags;
					suflags = suflags->next;
				}

				/* Could sort this and binary search */
				for (j = 0; j < user_set->len; j++) {
					change = &g_array_index (user_set, struct _imapx_flag_change, j);
					if (strcmp (change->name, user_flag->name) == 0)
						goto found;
				}
				add.name = g_strdup (user_flag->name);
				add.infos = g_ptr_array_new ();
				g_array_append_val (user_set, add);
				change = &add;
			found:
				camel_message_info_ref (info);
				g_ptr_array_add (change->infos, info);
			}
		}

		camel_message_info_unref (info);
	}

	nothing_to_do =
		(on_orset == 0) &&
		(off_orset == 0) &&
		(on_user == NULL) &&
		(off_user == NULL);

	if (nothing_to_do) {
		g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id);

		imapx_sync_free_user (on_user);
		imapx_sync_free_user (off_user);
		imapx_unset_folder_flagged_flag (folder->summary, changed_uids, remove_deleted_flags);
		camel_folder_free_uids (folder, changed_uids);
		g_hash_table_destroy (changed_meanwhile);
		g_object_unref (folder);
		return TRUE;
	}

	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) {
		g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id);

		imapx_sync_free_user (on_user);
		imapx_sync_free_user (off_user);
		camel_folder_free_uids (folder, changed_uids);
		g_hash_table_destroy (changed_meanwhile);
		g_object_unref (folder);
		return FALSE;
	}

	permanentflags = camel_imapx_mailbox_get_permanentflags (mailbox);

	success = TRUE;
	for (on = 0; on < 2 && success; on++) {
		guint32 orset = on ? on_orset : off_orset;
		GArray *user_set = on ? on_user : off_user;

		for (jj = 0; jj < G_N_ELEMENTS (flags_table) && success; jj++) {
			guint32 flag = flags_table[jj].flag;
			CamelIMAPXCommand *ic = NULL;

			if ((orset & flag) == 0)
				continue;

			c (is->priv->tagprefix, "checking/storing %s flags '%s'\n", on ? "on" : "off", flags_table[jj].name);
			imapx_uidset_init (&uidset, 0, 100);
			for (i = 0; i < changed_uids->len && success; i++) {
				CamelIMAPXMessageInfo *info;
				gboolean remove_deleted_flag;
				guint32 flags;
				guint32 sflags;
				gint send;

				info = (CamelIMAPXMessageInfo *)
					camel_folder_summary_get (
						folder->summary,
						changed_uids->pdata[i]);

				if (info == NULL)
					continue;

				flags = (info->info.flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
				sflags = (info->server_flags & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
				send = 0;

				remove_deleted_flag =
					remove_deleted_flags &&
					(flags & CAMEL_MESSAGE_DELETED);

				if (remove_deleted_flag) {
					/* Remove the DELETED flag so the
					 * message appears normally in the
					 * real Trash folder when copied. */
					flags &= ~CAMEL_MESSAGE_DELETED;
				}

				if ( (on && (((flags ^ sflags) & flags) & flag))
				     || (!on && (((flags ^ sflags) & ~flags) & flag))) {
					if (ic == NULL) {
						ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SYNC_CHANGES, "UID STORE ");
					}
					send = imapx_uidset_add (&uidset, ic, camel_message_info_uid (info));
				}
				if (send == 1 || (i == changed_uids->len - 1 && ic && imapx_uidset_done (&uidset, ic))) {
					camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on ? "+" : "-", flags_table[jj].name);

					success = camel_imapx_server_process_command_sync (is, ic, _("Error syncing changes"), cancellable, error);

					camel_imapx_command_unref (ic);
					ic = NULL;

					if (!success)
						break;
				}
				if (flag == CAMEL_MESSAGE_SEEN) {
					/* Remember how the server's unread count will change if this
					 * command succeeds */
					if (on)
						unread_change--;
					else
						unread_change++;
				}

				/* The second round and the server doesn't support saving user flags,
				   thus store them at least locally */
				if (on && (permanentflags & CAMEL_MESSAGE_USER) == 0) {
					camel_flag_list_copy (&info->server_user_flags, &info->info.user_flags);
				}

				camel_message_info_unref (info);
			}

			g_warn_if_fail (ic == NULL);
		}

		if (user_set && (permanentflags & CAMEL_MESSAGE_USER) != 0 && success) {
			CamelIMAPXCommand *ic = NULL;

			for (jj = 0; jj < user_set->len && success; jj++) {
				struct _imapx_flag_change *c = &g_array_index (user_set, struct _imapx_flag_change, jj);

				imapx_uidset_init (&uidset, 0, 100);
				for (i = 0; i < c->infos->len; i++) {
					CamelIMAPXMessageInfo *info = c->infos->pdata[i];

					if (ic == NULL)
						ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SYNC_CHANGES, "UID STORE ");

					if (imapx_uidset_add (&uidset, ic, camel_message_info_uid (info)) == 1
					    || (i == c->infos->len - 1 && imapx_uidset_done (&uidset, ic))) {
						gchar *utf7;

						utf7 = camel_utf8_utf7 (c->name);

						camel_imapx_command_add (ic, " %tFLAGS.SILENT (%t)", on ? "+" : "-", utf7 ? utf7 : c->name);

						g_free (utf7);

						success = camel_imapx_server_process_command_sync (is, ic, _("Error syncing changes"), cancellable, error);

						camel_imapx_command_unref (ic);
						ic = NULL;

						if (!success)
							break;
					}
				}
			}
		}
	}

	g_signal_handler_disconnect (folder->summary, changed_meanwhile_handler_id);

	if (success) {
		CamelStore *parent_store;
		guint32 unseen;

		parent_store = camel_folder_get_parent_store (folder);

		camel_folder_summary_lock (folder->summary);

		for (i = 0; i < changed_uids->len; i++) {
			CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) camel_folder_summary_get (folder->summary,
					changed_uids->pdata[i]);

			if (!xinfo)
				continue;

			xinfo->server_flags = xinfo->info.flags & CAMEL_IMAPX_SERVER_FLAGS;
			if (!remove_deleted_flags ||
			    !(xinfo->info.flags & CAMEL_MESSAGE_DELETED)) {
				xinfo->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
			} else {
				/* to stare back the \Deleted flag */
				xinfo->server_flags &= ~CAMEL_MESSAGE_DELETED;
				xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
			}
			xinfo->info.dirty = TRUE;
			if ((permanentflags & CAMEL_MESSAGE_USER) != 0 ||
			    camel_flag_list_size (&xinfo->server_user_flags) == 0)
				camel_flag_list_copy (&xinfo->server_user_flags, &xinfo->info.user_flags);

			if (g_hash_table_lookup (changed_meanwhile, changed_uids->pdata[i]))
				xinfo->info.flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;

			camel_folder_summary_touch (folder->summary);
			camel_message_info_unref (xinfo);
		}

		camel_folder_summary_unlock (folder->summary);

		/* Apply the changes to server-side unread count; it won't tell
		 * us of these changes, of course. */
		unseen = camel_imapx_mailbox_get_unseen (mailbox);
		unseen += unread_change;
		camel_imapx_mailbox_set_unseen (mailbox, unseen);

		if (folder->summary && (folder->summary->flags & CAMEL_FOLDER_SUMMARY_DIRTY) != 0) {
			CamelStoreInfo *si;

			/* ... and store's summary when folder's summary is dirty */
			si = camel_store_summary_path (CAMEL_IMAPX_STORE (parent_store)->summary, camel_folder_get_full_name (folder));
			if (si) {
				if (si->total != camel_folder_summary_get_saved_count (folder->summary) ||
				    si->unread != camel_folder_summary_get_unread_count (folder->summary)) {
					si->total = camel_folder_summary_get_saved_count (folder->summary);
					si->unread = camel_folder_summary_get_unread_count (folder->summary);
					camel_store_summary_touch (CAMEL_IMAPX_STORE (parent_store)->summary);
				}

				camel_store_summary_info_unref (CAMEL_IMAPX_STORE (parent_store)->summary, si);
			}
		}

		camel_folder_summary_save_to_db (folder->summary, NULL);
		camel_store_summary_save (CAMEL_IMAPX_STORE (parent_store)->summary);
	}

	imapx_sync_free_user (on_user);
	imapx_sync_free_user (off_user);
	camel_folder_free_uids (folder, changed_uids);
	g_hash_table_destroy (changed_meanwhile);
	g_object_unref (folder);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_reconnect (CamelIMAPXServer *is,
                 GCancellable *cancellable,
                 GError **error)
{
	CamelIMAPXCommand *ic;
	CamelService *service;
	CamelSession *session;
	CamelIMAPXStore *store;
	CamelSettings *settings;
	gchar *mechanism;
	gboolean use_qresync;
	gboolean use_idle;
	gboolean success = FALSE;

	store = camel_imapx_server_ref_store (is);

	service = CAMEL_SERVICE (store);
	session = camel_service_ref_session (service);
	if (!session) {
		g_set_error_literal (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_UNAVAILABLE,
			_("You must be working online to complete this operation"));
		g_object_unref (store);
		return FALSE;
	}

	settings = camel_service_ref_settings (service);

	mechanism = camel_network_settings_dup_auth_mechanism (
		CAMEL_NETWORK_SETTINGS (settings));

	use_qresync = camel_imapx_settings_get_use_qresync (CAMEL_IMAPX_SETTINGS (settings));
	use_idle = camel_imapx_settings_get_use_idle (CAMEL_IMAPX_SETTINGS (settings));

	g_object_unref (settings);

	if (!imapx_connect_to_server (is, cancellable, error))
		goto exception;

	if (is->priv->state == IMAPX_AUTHENTICATED)
		goto preauthed;

	if (!camel_session_authenticate_sync (
		session, service, mechanism, cancellable, error))
		goto exception;

	/* After login we re-capa unless the server already told us. */
	g_mutex_lock (&is->priv->stream_lock);
	if (is->priv->cinfo == NULL) {
		GError *local_error = NULL;

		g_mutex_unlock (&is->priv->stream_lock);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY");
		camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, &local_error);
		camel_imapx_command_unref (ic);

		if (local_error != NULL) {
			g_propagate_error (error, local_error);
			goto exception;
		}
	} else {
		g_mutex_unlock (&is->priv->stream_lock);
	}

	is->priv->state = IMAPX_AUTHENTICATED;

preauthed:
	/* Fetch namespaces (if supported). */
	g_mutex_lock (&is->priv->stream_lock);
	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NAMESPACE)) {
		GError *local_error = NULL;

		g_mutex_unlock (&is->priv->stream_lock);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NAMESPACE, "NAMESPACE");
		camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NAMESPACE"), cancellable, &local_error);
		camel_imapx_command_unref (ic);

		if (local_error != NULL) {
			g_propagate_error (error, local_error);
			goto exception;
		}

		g_mutex_lock (&is->priv->stream_lock);
	}

	/* Enable quick mailbox resynchronization (if supported). */
	if (use_qresync && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, QRESYNC)) {
		GError *local_error = NULL;

		g_mutex_unlock (&is->priv->stream_lock);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_ENABLE, "ENABLE CONDSTORE QRESYNC");
		camel_imapx_server_process_command_sync (is, ic, _("Failed to enable QResync"), cancellable, &local_error);
		camel_imapx_command_unref (ic);

		if (local_error != NULL) {
			g_propagate_error (error, local_error);
			goto exception;
		}

		g_mutex_lock (&is->priv->stream_lock);

		is->priv->use_qresync = TRUE;
	} else {
		is->priv->use_qresync = FALSE;
	}

	/* Set NOTIFY options after enabling QRESYNC (if supported). */
	if (use_idle && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY)) {
		GError *local_error = NULL;

		g_mutex_unlock (&is->priv->stream_lock);

		/* XXX The list of FETCH attributes is negotiable. */
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOTIFY, "NOTIFY SET "
			"(selected "
			"(MessageNew (UID RFC822.SIZE RFC822.HEADER FLAGS)"
			" MessageExpunge"
			" FlagChange)) "
			"(personal "
			"(MessageNew"
			" MessageExpunge"
			" MailboxName"
			" SubscriptionChange))");
		camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NOTIFY"), cancellable, &local_error);
		camel_imapx_command_unref (ic);

		if (local_error != NULL) {
			g_propagate_error (error, local_error);
			goto exception;
		}

		g_mutex_lock (&is->priv->stream_lock);
	}

	g_mutex_unlock (&is->priv->stream_lock);

	is->priv->state = IMAPX_INITIALISED;

	success = TRUE;

	goto exit;

exception:
	imapx_disconnect (is);

exit:
	g_free (mechanism);

	g_object_unref (session);
	g_object_unref (store);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_get_message_sync (CamelIMAPXServer *is,
				     CamelIMAPXMailbox *mailbox,
				     CamelFolderSummary *summary,
				     CamelDataCache *message_cache,
				     const gchar *message_uid,
				     GCancellable *cancellable,
				     GError **error)
{
	CamelMessageInfo *mi;
	CamelStream *result_stream = NULL;
	CamelIMAPXSettings *settings;
	GIOStream *cache_stream;
	gsize data_size;
	gboolean use_multi_fetch;
	gboolean success;
	GError *local_error = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), NULL);
	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), NULL);
	g_return_val_if_fail (message_uid != NULL, NULL);

	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error))
		return NULL;

	mi = camel_folder_summary_get (summary, message_uid);
	if (mi == NULL) {
		g_set_error (
			error, CAMEL_FOLDER_ERROR,
			CAMEL_FOLDER_ERROR_INVALID_UID,
			_("Cannot get message with message ID %s: %s"),
			message_uid, _("No such message available."));
		return NULL;
	}

	/* This makes sure that if any file is left on the disk, it is not reused.
	   That can happen when the previous message download had been cancelled
	   or finished with an error. */
	camel_data_cache_remove (message_cache, "tmp", message_uid, NULL);

	cache_stream = camel_data_cache_add (message_cache, "tmp", message_uid, error);
	if (cache_stream == NULL) {
		camel_message_info_unref (mi);
		return NULL;
	}

	settings = camel_imapx_server_ref_settings (is);
	data_size = ((CamelMessageInfoBase *) mi)->size;
	use_multi_fetch = data_size > MULTI_SIZE && camel_imapx_settings_get_use_multi_fetch (settings);
	g_object_unref (settings);

	g_warn_if_fail (is->priv->get_message_stream == NULL);

	is->priv->get_message_stream = cache_stream;

	if (use_multi_fetch) {
		CamelIMAPXCommand *ic;
		gsize fetch_offset = 0;

		do {
			camel_operation_progress (cancellable, fetch_offset * 100 / data_size);

			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_GET_MESSAGE, "UID FETCH %t (BODY.PEEK[]", message_uid);
			camel_imapx_command_add (ic, "<%u.%u>", fetch_offset, MULTI_SIZE);
			camel_imapx_command_add (ic, ")");
			fetch_offset += MULTI_SIZE;

			success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message"), cancellable, &local_error);

			camel_imapx_command_unref (ic);
			ic = NULL;

			if (success) {
				gsize really_fetched = g_seekable_tell (G_SEEKABLE (is->priv->get_message_stream));

				/* Don't automatically stop when we reach the reported message
				 * size -- some crappy servers (like Microsoft Exchange) have
				 * a tendency to lie about it. Keep going (one request at a
				 * time) until the data actually stop coming. */
				if (fetch_offset < data_size ||
				    fetch_offset == really_fetched) {
					/* just continue */
				} else {
					break;
				}
			}
		} while (success);
	} else {
		CamelIMAPXCommand *ic;

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_GET_MESSAGE, "UID FETCH %t (BODY.PEEK[])", message_uid);

		success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message"), cancellable, &local_error);

		camel_imapx_command_unref (ic);
	}

	is->priv->get_message_stream = NULL;

	if (success) {
		if (local_error == NULL) {
			g_io_stream_close (cache_stream, cancellable, &local_error);
			g_prefix_error (
				&local_error, "%s: ",
				_("Failed to close the tmp stream"));
		}

		if (local_error == NULL &&
		    g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
			g_prefix_error (
				&local_error, "%s: ",
				_("Error fetching message"));
		}

		if (local_error == NULL) {
			gchar *cur_filename;
			gchar *tmp_filename;
			gchar *dirname;

			cur_filename = camel_data_cache_get_filename (message_cache, "cur", message_uid);
			tmp_filename = camel_data_cache_get_filename (message_cache, "tmp", message_uid);

			dirname = g_path_get_dirname (cur_filename);
			g_mkdir_with_parents (dirname, 0700);
			g_free (dirname);

			if (g_rename (tmp_filename, cur_filename) == 0) {
				/* Exchange the "tmp" stream for the "cur" stream. */
				g_clear_object (&cache_stream);
				cache_stream = camel_data_cache_get (message_cache, "cur", message_uid, &local_error);
			} else {
				g_set_error (
					&local_error, G_FILE_ERROR,
					g_file_error_from_errno (errno),
					"%s: %s",
					_("Failed to copy the tmp file"),
					g_strerror (errno));
			}

			g_free (cur_filename);
			g_free (tmp_filename);
		}

		/* Delete the 'tmp' file only if the operation succeeded. It's because
		   cancelled operations end before they are properly finished (IMAP-protocol speaking),
		   thus if any other GET_MESSAGE operation was waiting for this job, then it
		   realized that the message was not downloaded and opened its own "tmp" file, but
		   of the same name, thus this remove would drop file which could be used
		   by a different GET_MESSAGE job. */
		if (!local_error && !g_cancellable_is_cancelled (cancellable))
			camel_data_cache_remove (message_cache, "tmp", message_uid, NULL);
	}

	if (!local_error) {
		result_stream = camel_stream_new (cache_stream);
	} else {
		g_propagate_error (error, local_error);
	}

	g_clear_object (&cache_stream);

	return result_stream;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_expunge (CamelIMAPXServer *is,
                        GInputStream *input_stream,
                        GCancellable *cancellable,
                        GError **error)
{
	CamelIMAPXMailbox *mailbox;
	gulong expunge = 0;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	expunge = is->priv->context->id;

	COMMAND_LOCK (is);

	/* Ignore EXPUNGE responses when not running a COPY(MOVE)_MESSAGE job */
	if (!is->priv->current_command || (is->priv->current_command->job_kind != CAMEL_IMAPX_JOB_COPY_MESSAGE &&
	    is->priv->current_command->job_kind != CAMEL_IMAPX_JOB_MOVE_MESSAGE)) {
		COMMAND_UNLOCK (is);

		c (is->priv->tagprefix, "ignoring untagged expunge: %lu\n", expunge);
		return TRUE;
	}

	COMMAND_UNLOCK (is);

	c (is->priv->tagprefix, "expunged: %lu\n", expunge);

	mailbox = camel_imapx_server_ref_pending_or_selected (is);

	if (mailbox != NULL) {
		CamelFolder *folder;
		gchar *uid;

		folder = imapx_server_ref_folder (is, mailbox);
		g_return_val_if_fail (folder != NULL, FALSE);

		uid = camel_imapx_dup_uid_from_summary_index (folder, expunge - 1);

		if (uid != NULL)
			imapx_expunge_uid_from_summary (is, uid, TRUE);

		g_object_unref (folder);
		g_free (uid);
	}

	g_clear_object (&mailbox);

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_can_use_idle (CamelIMAPXServer *is)
{
	gboolean use_idle = FALSE;

	g_mutex_lock (&is->priv->stream_lock);

	/* No need for IDLE if the server supports NOTIFY. */
	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY)) {
		g_mutex_unlock (&is->priv->stream_lock);

		return FALSE;
	}

	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, IDLE)) {
		CamelIMAPXSettings *settings;

		settings = camel_imapx_server_ref_settings (is);
		use_idle = camel_imapx_settings_get_use_idle (settings);
		g_object_unref (settings);
	}

	g_mutex_unlock (&is->priv->stream_lock);

	return use_idle;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_refresh_info_sync (CamelIMAPXServer *is,
				      CamelIMAPXMailbox *mailbox,
				      GCancellable *cancellable,
				      GError **error)
{
	CamelIMAPXCommand *ic;
	CamelIMAPXMailbox *selected_mailbox;
	CamelIMAPXSummary *imapx_summary;
	CamelFolder *folder;
	GHashTable *known_uids;
	guint32 messages;
	guint32 unseen;
	guint32 uidnext;
	guint32 uidvalidity;
	guint64 highestmodseq;
	guint32 total;
	guint64 uidl;
	gboolean need_rescan;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);

	selected_mailbox = camel_imapx_server_ref_pending_or_selected (is);
	if (selected_mailbox == mailbox) {
		success = camel_imapx_server_noop_sync (is, mailbox, cancellable, error);
	} else {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_STATUS, "STATUS %M (%t)", mailbox, is->priv->status_data_items);

		success = camel_imapx_server_process_command_sync (is, ic, _("Error running STATUS"), cancellable, error);

		camel_imapx_command_unref (ic);
	}
	g_clear_object (&selected_mailbox);

	if (!success)
		return FALSE;

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_val_if_fail (folder != NULL, FALSE);

	imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary);

	messages = camel_imapx_mailbox_get_messages (mailbox);
	unseen = camel_imapx_mailbox_get_unseen (mailbox);
	uidnext = camel_imapx_mailbox_get_uidnext (mailbox);
	uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
	highestmodseq = camel_imapx_mailbox_get_highestmodseq (mailbox);
	total = camel_folder_summary_count (folder->summary);

	need_rescan =
		(uidvalidity > 0 && uidvalidity != imapx_summary->validity) ||
		total != messages ||
		imapx_summary->uidnext != uidnext ||
		camel_folder_summary_get_unread_count (folder->summary) != unseen ||
		imapx_summary->modseq != highestmodseq;

	if (!need_rescan) {
		g_object_unref (folder);
		return TRUE;
	}

	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) {
		g_object_unref (folder);
		return FALSE;
	}

	if (is->priv->use_qresync && imapx_summary->modseq > 0 && uidvalidity > 0) {
		imapx_summary->modseq = highestmodseq;
		if (total != messages ||
		    camel_folder_summary_get_unread_count (folder->summary) != unseen ||
		    imapx_summary->modseq != highestmodseq) {
			c (
				is->priv->tagprefix,
				"Eep, after QRESYNC we're out of sync. "
				"total %u / %u, unread %u / %u, modseq %"
				G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n",
				total, messages,
				camel_folder_summary_get_unread_count (folder->summary),
				unseen,
				imapx_summary->modseq,
				highestmodseq);
		} else {
			c (
				is->priv->tagprefix,
				"OK, after QRESYNC we're still in sync. "
				"total %u / %u, unread %u / %u, modseq %"
				G_GUINT64_FORMAT " / %" G_GUINT64_FORMAT "\n",
				total, messages,
				camel_folder_summary_get_unread_count (folder->summary),
				unseen,
				imapx_summary->modseq,
				highestmodseq);
			g_object_unref (folder);
			return TRUE;
		}
	}

	if (total > 0) {
		gchar *uid = camel_imapx_dup_uid_from_summary_index (folder, total - 1);
		if (uid) {
			uidl = g_ascii_strtoull (uid, NULL, 10);
			g_free (uid);
			uidl++;
		} else {
			uidl = 1;
		}
	} else {
		uidl = 1;
	}

	camel_folder_summary_prepare_fetch_all (folder->summary, NULL);

	known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);

	success = imapx_server_fetch_changes (is, mailbox, folder, known_uids, uidl, 0, cancellable, error);
	if (success && uidl != 1)
		success = imapx_server_fetch_changes (is, mailbox, folder, known_uids, 0, uidl, cancellable, error);

	if (success) {
		CamelFolderChangeInfo *changes;
		GList *removed = NULL;
		GPtrArray *array;
		gint ii;

		camel_folder_summary_lock (folder->summary);

		changes = camel_folder_change_info_new ();

		array = camel_folder_summary_get_array (folder->summary);
		for (ii = 0; array && ii < array->len; ii++) {
			const gchar *uid = array->pdata[ii];

			if (!uid)
				continue;

			if (!g_hash_table_contains (known_uids, uid)) {
				removed = g_list_prepend (removed, (gpointer) uid);
				camel_folder_change_info_remove_uid (changes, uid);
			}
		}

		camel_folder_summary_unlock (folder->summary);

		if (removed != NULL) {
			camel_folder_summary_remove_uids (folder->summary, removed);
			camel_folder_summary_touch (folder->summary);

			/* Shares UIDs with the 'array'. */
			g_list_free (removed);
		}

		if (camel_folder_change_info_changed (changes)) {
			camel_folder_summary_save_to_db (folder->summary, NULL);
			imapx_update_store_summary (folder);
			camel_folder_changed (folder, changes);
		}

		camel_folder_change_info_free (changes);
		camel_folder_summary_free_array (array);
	}

	g_hash_table_destroy (known_uids);
	g_object_unref (folder);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_ok_no_bad (CamelIMAPXServer *is,
                          GInputStream *input_stream,
                          GCancellable *cancellable,
                          GError **error)
{
	CamelIMAPXMailbox *mailbox;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	/* TODO: validate which ones of these can happen as unsolicited responses */
	/* TODO: handle bye/preauth differently */
	camel_imapx_input_stream_ungettoken (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		is->priv->context->tok,
		is->priv->context->token,
		is->priv->context->len);

	/* These untagged responses can belong to ongoing SELECT command, thus
	   to the pending select mailbox, not to the currently selected or closing
	   mailbox, thus prefer the select pending mailbox, from the other two.
	   This makes sure that for example UIDVALIDITY is not incorrectly
	   overwritten with a value from a different mailbox, thus the offline
	   cache will persist, instead of being vanished.
	*/
	mailbox = camel_imapx_server_ref_pending_or_selected (is);

	is->priv->context->sinfo = imapx_parse_status (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		mailbox, cancellable, error);

	g_clear_object (&mailbox);

	if (is->priv->context->sinfo == NULL)
		return FALSE;

	switch (is->priv->context->sinfo->condition) {
	case IMAPX_CLOSED:
		c (
			is->priv->tagprefix,
			"previously selected mailbox is now closed\n");
		{
			CamelIMAPXMailbox *select_mailbox;
			CamelIMAPXMailbox *select_pending;

			g_mutex_lock (&is->priv->select_lock);

			select_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
			select_pending = g_weak_ref_get (&is->priv->select_pending);

			if (select_mailbox == NULL)
				g_weak_ref_set (&is->priv->select_mailbox, select_pending);

			g_mutex_unlock (&is->priv->select_lock);

			g_clear_object (&select_mailbox);
			g_clear_object (&select_pending);
		}
		break;
	case IMAPX_ALERT:
		c (is->priv->tagprefix, "ALERT!: %s\n", is->priv->context->sinfo->text);
		{
			const gchar *alert_message;
			gboolean emit_alert = FALSE;

			g_mutex_lock (&is->priv->known_alerts_lock);

			alert_message = is->priv->context->sinfo->text;

			if (alert_message != NULL) {
				emit_alert = !g_hash_table_contains (
					is->priv->known_alerts,
					alert_message);
			}

			if (emit_alert) {
				CamelIMAPXStore *store;
				CamelService *service;
				CamelSession *session;

				store = camel_imapx_server_ref_store (is);

				g_hash_table_add (
					is->priv->known_alerts,
					g_strdup (alert_message));

				service = CAMEL_SERVICE (store);
				session = camel_service_ref_session (service);

				if (session) {
					camel_session_user_alert (
						session, service,
						CAMEL_SESSION_ALERT_WARNING,
						alert_message);

					g_object_unref (session);
				}

				g_object_unref (store);
			}

			g_mutex_unlock (&is->priv->known_alerts_lock);
		}
		break;
	case IMAPX_PARSE:
		c (is->priv->tagprefix, "PARSE: %s\n", is->priv->context->sinfo->text);
		break;
	case IMAPX_CAPABILITY:
		if (is->priv->context->sinfo->u.cinfo) {
			struct _capability_info *cinfo;

			g_mutex_lock (&is->priv->stream_lock);

			cinfo = is->priv->cinfo;
			is->priv->cinfo = is->priv->context->sinfo->u.cinfo;
			is->priv->context->sinfo->u.cinfo = NULL;
			if (cinfo)
				imapx_free_capability (cinfo);
			c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF);

			if (is->priv->context->sinfo->text) {
				guint32 list_extended = imapx_lookup_capability ("LIST-EXTENDED");

				is->priv->is_cyrus = is->priv->is_cyrus || camel_strstrcase (is->priv->context->sinfo->text, "cyrus");
				if (is->priv->is_cyrus && is->priv->cinfo && (is->priv->cinfo->capa & list_extended) != 0) {
					/* Disable LIST-EXTENDED for cyrus servers */
					c (is->priv->tagprefix, "Disabling LIST-EXTENDED extension for a Cyrus server\n");
					is->priv->cinfo->capa &= ~list_extended;
				}
			}

			imapx_server_stash_command_arguments (is);

			g_mutex_unlock (&is->priv->stream_lock);
		}
		break;
	case IMAPX_COPYUID:
		imapx_free_status (is->priv->copyuid_status);
		is->priv->copyuid_status = is->priv->context->sinfo;
		is->priv->context->sinfo = NULL;
		break;
	default:
		break;
	}

	imapx_free_status (is->priv->context->sinfo);
	is->priv->context->sinfo = NULL;

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_expunge_uid_from_summary (CamelIMAPXServer *is,
                                const gchar *uid,
                                gboolean unsolicited)
{
	CamelFolder *folder;
	CamelIMAPXMailbox *mailbox;
	guint32 messages;

	mailbox = camel_imapx_server_ref_pending_or_selected (is);

	g_return_if_fail (mailbox != NULL);

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_if_fail (folder != NULL);

	messages = camel_imapx_mailbox_get_messages (mailbox);

	if (unsolicited && messages > 0)
		camel_imapx_mailbox_set_messages (mailbox, messages - 1);

	g_return_if_fail (is->priv->changes != NULL);

	camel_folder_summary_remove_uid (folder->summary, uid);

	g_mutex_lock (&is->priv->changes_lock);

	camel_folder_change_info_remove_uid (is->priv->changes, uid);

	if (camel_imapx_server_is_in_idle (is)) {
		CamelFolderChangeInfo *changes;

		changes = is->priv->changes;
		is->priv->changes = camel_folder_change_info_new ();

		g_mutex_unlock (&is->priv->changes_lock);

		camel_folder_summary_save_to_db (folder->summary, NULL);
		imapx_update_store_summary (folder);
		camel_folder_changed (folder, changes);

		camel_folder_change_info_free (changes);
	} else {
		g_mutex_unlock (&is->priv->changes_lock);
	}

	g_object_unref (folder);
	g_object_unref (mailbox);
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_fetch_changes (CamelIMAPXServer *is,
			    CamelIMAPXMailbox *mailbox,
			    CamelFolder *folder,
			    GHashTable *known_uids,
			    guint64 from_uidl,
			    guint64 to_uidl,
			    GCancellable *cancellable,
			    GError **error)
{
	GSList *fetch_summary_uids = NULL;
	GHashTable *infos; /* uid ~> FetchChangesInfo */
	CamelIMAPXCommand *ic;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	if (g_cancellable_set_error_if_cancelled (cancellable, error))
		return FALSE;

	if (!from_uidl)
		from_uidl = 1;

	if (to_uidl > 0) {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH %lld:%lld (UID FLAGS)", from_uidl, to_uidl);
	} else {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH %lld:* (UID FLAGS)", from_uidl);
	}

	g_return_val_if_fail (is->priv->fetch_changes_mailbox == NULL, FALSE);
	g_return_val_if_fail (is->priv->fetch_changes_folder == NULL, FALSE);
	g_return_val_if_fail (is->priv->fetch_changes_infos == NULL, FALSE);

	infos = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, fetch_changes_info_free);

	is->priv->fetch_changes_mailbox = mailbox;
	is->priv->fetch_changes_folder = folder;
	is->priv->fetch_changes_infos = infos;
	is->priv->fetch_changes_last_progress = 0;

	camel_operation_push_message (cancellable,
		_("Scanning for changed messages in '%s'"),
		camel_folder_get_display_name (folder));

	success = camel_imapx_server_process_command_sync (is, ic, _("Error scanning changes"), cancellable, error);

	camel_operation_pop_message (cancellable);
	camel_imapx_command_unref (ic);

	/* It can partly succeed. */
	imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, known_uids, &fetch_summary_uids, from_uidl, to_uidl);

	g_hash_table_remove_all (infos);

	if (success && fetch_summary_uids) {
		struct _uidset_state uidset;
		GSList *link;

		ic = NULL;
		imapx_uidset_init (&uidset, 0, 100);

		camel_operation_push_message (cancellable,
			_("Fetching summary information for new messages in '%s'"),
			camel_folder_get_display_name (folder));

		fetch_summary_uids = g_slist_sort (fetch_summary_uids, imapx_uids_desc_cmp);

		for (link = fetch_summary_uids; link; link = g_slist_next (link)) {
			const gchar *uid = link->data;

			if (!uid)
				continue;

			if (!ic)
				ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_REFRESH_INFO, "UID FETCH ");

			if (imapx_uidset_add (&uidset, ic, uid) == 1 || (!link->next && ic && imapx_uidset_done (&uidset, ic))) {
				camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER FLAGS)");

				success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching message info"), cancellable, error);

				camel_imapx_command_unref (ic);
				ic = NULL;

				if (!success)
					break;

				imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, NULL, NULL, 0, 0);
				g_hash_table_remove_all (infos);
			}
		}

		camel_operation_pop_message (cancellable);

		imapx_server_process_fetch_changes_infos (is, mailbox, folder, infos, NULL, NULL, 0, 0);
	}

	g_return_val_if_fail (is->priv->fetch_changes_mailbox == mailbox, FALSE);
	g_return_val_if_fail (is->priv->fetch_changes_folder == folder, FALSE);
	g_return_val_if_fail (is->priv->fetch_changes_infos == infos, FALSE);

	is->priv->fetch_changes_mailbox = NULL;
	is->priv->fetch_changes_folder = NULL;
	is->priv->fetch_changes_infos = NULL;

	g_slist_free_full (fetch_summary_uids, (GDestroyNotify) camel_pstring_free);
	g_hash_table_destroy (infos);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_disconnect (CamelIMAPXServer *is)
{
	g_cancellable_cancel (is->priv->cancellable);

	g_mutex_lock (&is->priv->stream_lock);

	if (is->priv->connection) {
		/* No need to wait for close for too long */
		imapx_server_set_connection_timeout (is->priv->connection, 3);
	}

	g_clear_object (&is->priv->input_stream);
	g_clear_object (&is->priv->output_stream);
	g_clear_object (&is->priv->connection);
	g_clear_object (&is->priv->subprocess);

	if (is->priv->cinfo) {
		imapx_free_capability (is->priv->cinfo);
		is->priv->cinfo = NULL;
	}

	g_mutex_unlock (&is->priv->stream_lock);

	g_mutex_lock (&is->priv->select_lock);
	g_weak_ref_set (&is->priv->select_mailbox, NULL);
	g_weak_ref_set (&is->priv->select_pending, NULL);
	g_mutex_unlock (&is->priv->select_lock);

	is->priv->is_cyrus = FALSE;
	is->priv->state = IMAPX_DISCONNECTED;

	g_mutex_lock (&is->priv->idle_lock);
	is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
	g_cond_broadcast (&is->priv->idle_cond);
	g_mutex_unlock (&is->priv->idle_lock);
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_list_sync (CamelIMAPXServer *is,
			      const gchar *pattern,
			      CamelStoreGetFolderInfoFlags flags,
			      GCancellable *cancellable,
			      GError **error)
{
	CamelIMAPXCommand *ic;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (pattern != NULL, FALSE);

	if (is->priv->list_return_opts != NULL) {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s RETURN (%t)",
			pattern, is->priv->list_return_opts);
	} else {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s",
			pattern);
	}

	success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching folders"), cancellable, error);

	camel_imapx_command_unref (ic);

	if (!success)
		return FALSE;

	if (!is->priv->list_return_opts) {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LSUB, "LSUB \"\" %s",
			pattern);

		success = camel_imapx_server_process_command_sync (is, ic, _("Error fetching subscribed folders"), cancellable, error);

		camel_imapx_command_unref (ic);
	}

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_fetch (CamelIMAPXServer *is,
                      GInputStream *input_stream,
                      GCancellable *cancellable,
                      GError **error)
{
	struct _fetch_info *finfo;
	gboolean got_body_header;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	finfo = imapx_parse_fetch (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
	if (finfo == NULL) {
		imapx_free_fetch (finfo);
		return FALSE;
	}

	/* Some IMAP servers respond with BODY[HEADER] when
	 * asked for RFC822.HEADER.  Treat them equivalently. */
	got_body_header =
		((finfo->got & FETCH_HEADER) == 0) &&
		(finfo->header == NULL) &&
		((finfo->got & FETCH_BODY) != 0) &&
		(g_strcmp0 (finfo->section, "HEADER") == 0);

	if (got_body_header) {
		finfo->got |= FETCH_HEADER;
		finfo->got &= ~FETCH_BODY;
		finfo->header = finfo->body;
		finfo->body = NULL;
	}

	if ((finfo->got & (FETCH_BODY | FETCH_UID)) == (FETCH_BODY | FETCH_UID)) {
		GOutputStream *output_stream;
		gconstpointer body_data;
		gsize body_size;

		g_return_val_if_fail (is->priv->get_message_stream != NULL, FALSE);

		/* Fill out the body stream, in the right spot. */

		g_seekable_seek (
			G_SEEKABLE (is->priv->get_message_stream),
			finfo->offset, G_SEEK_SET,
			NULL, NULL);

		output_stream = g_io_stream_get_output_stream (is->priv->get_message_stream);

		body_data = g_bytes_get_data (finfo->body, &body_size);

		/* Sometimes the server, like Microsoft Exchange, reports larger message
		   size than it actually is, which results in no data being read from
		   the server for that particular offset. */
		if (body_size) {
			g_mutex_lock (&is->priv->stream_lock);
			if (!g_output_stream_write_all (
				output_stream, body_data, body_size,
				NULL, cancellable, error)) {
				g_mutex_unlock (&is->priv->stream_lock);
				g_prefix_error (
					error, "%s: ",
					_("Error writing to cache stream"));
				return FALSE;
			}
			g_mutex_unlock (&is->priv->stream_lock);
		}
	}

	if ((finfo->got & FETCH_FLAGS) && !(finfo->got & FETCH_HEADER)) {
		CamelIMAPXMailbox *select_mailbox;
		CamelIMAPXMailbox *select_pending;

		if (is->priv->fetch_changes_mailbox) {
			g_return_val_if_fail (is->priv->fetch_changes_mailbox != NULL, FALSE);
			g_return_val_if_fail (is->priv->fetch_changes_folder != NULL, FALSE);
			g_return_val_if_fail (is->priv->fetch_changes_infos != NULL, FALSE);
		}

		g_mutex_lock (&is->priv->select_lock);
		select_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
		select_pending = g_weak_ref_get (&is->priv->select_pending);
		g_mutex_unlock (&is->priv->select_lock);

		/* This is either a refresh_info job, check to see if it is
		 * and update if so, otherwise it must've been an unsolicited
		 * response, so update the summary to match. */
		if ((finfo->got & FETCH_UID) != 0 && is->priv->fetch_changes_folder && is->priv->fetch_changes_infos) {
			FetchChangesInfo *nfo;
			gint64 monotonic_time;
			gint n_messages;

			nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
			if (!nfo) {
				nfo = g_new0 (FetchChangesInfo, 1);

				g_hash_table_insert (is->priv->fetch_changes_infos, (gpointer) camel_pstring_strdup (finfo->uid), nfo);
			}

			nfo->server_flags = finfo->flags;
			nfo->server_user_flags = finfo->user_flags;
			finfo->user_flags = NULL;

			monotonic_time = g_get_monotonic_time ();
			n_messages = camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox);

			if (n_messages > 0 && is->priv->fetch_changes_last_progress + G_USEC_PER_SEC / 2 < monotonic_time &&
			    is->priv->context && is->priv->context->id <= n_messages) {
				COMMAND_LOCK (is);

				if (is->priv->current_command) {
					COMMAND_UNLOCK (is);

					is->priv->fetch_changes_last_progress = monotonic_time;

					camel_operation_progress (cancellable, 100 * is->priv->context->id
						/ camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox));
				} else {
					COMMAND_UNLOCK (is);
				}
			}
		} else if (select_mailbox != NULL) {
			CamelFolder *select_folder;
			CamelMessageInfo *mi = NULL;
			gboolean changed = FALSE;
			gchar *uid = NULL;

			c (is->priv->tagprefix, "flag changed: %lu\n", is->priv->context->id);

			select_folder = imapx_server_ref_folder (is, select_mailbox);
			g_return_val_if_fail (select_folder != NULL, FALSE);

			if (finfo->got & FETCH_UID) {
				uid = finfo->uid;
				finfo->uid = NULL;
			} else {
				uid = camel_imapx_dup_uid_from_summary_index (
					select_folder,
					is->priv->context->id - 1);
			}

			if (uid) {
				mi = camel_folder_summary_get (select_folder->summary, uid);
				if (mi) {
					/* It's unsolicited _unless_ select_pending (i.e. during
					 * a QRESYNC SELECT */
					changed = imapx_update_message_info_flags (
						mi, finfo->flags,
						finfo->user_flags,
						camel_imapx_mailbox_get_permanentflags (select_mailbox),
						select_folder,
						(select_pending == NULL));
				} else {
					/* This (UID + FLAGS for previously unknown message) might
					 * happen during a SELECT (QRESYNC). We should use it. */
					c (is->priv->tagprefix, "flags changed for unknown uid %s\n.", uid);
				}
				finfo->user_flags = NULL;
			}

			if (changed) {
				g_return_val_if_fail (is->priv->changes != NULL, FALSE);

				g_mutex_lock (&is->priv->changes_lock);
				camel_folder_change_info_change_uid (is->priv->changes, uid);
				g_mutex_unlock (&is->priv->changes_lock);
			}
			g_free (uid);

			if (changed && camel_imapx_server_is_in_idle (is)) {
				camel_folder_summary_save_to_db (select_folder->summary, NULL);
				imapx_update_store_summary (select_folder);

				g_mutex_lock (&is->priv->changes_lock);

				camel_folder_changed (select_folder, is->priv->changes);
				camel_folder_change_info_clear (is->priv->changes);

				g_mutex_unlock (&is->priv->changes_lock);
			}

			if (mi)
				camel_message_info_unref (mi);

			g_object_unref (select_folder);
		}

		g_clear_object (&select_mailbox);
		g_clear_object (&select_pending);
	}

	if ((finfo->got & (FETCH_HEADER | FETCH_UID)) == (FETCH_HEADER | FETCH_UID)) {
		CamelIMAPXMailbox *mailbox;
		CamelFolder *folder;
		CamelMimeParser *mp;
		CamelMessageInfo *mi;
		guint32 messages;
		guint32 unseen;
		guint32 uidnext;

		/* This must be a refresh info job as well, but it has
		 * asked for new messages to be added to the index. */

		if (is->priv->fetch_changes_mailbox) {
			g_return_val_if_fail (is->priv->fetch_changes_mailbox != NULL, FALSE);
			g_return_val_if_fail (is->priv->fetch_changes_folder != NULL, FALSE);
			g_return_val_if_fail (is->priv->fetch_changes_infos != NULL, FALSE);

			folder = g_object_ref (is->priv->fetch_changes_folder);
			mailbox = g_object_ref (is->priv->fetch_changes_mailbox);
		} else {
			mailbox = camel_imapx_server_ref_selected (is);
			folder = mailbox ? imapx_server_ref_folder (is, mailbox) : NULL;
		}

		if (!mailbox || !folder || (!(finfo->got & FETCH_FLAGS) && !is->priv->fetch_changes_infos)) {
			g_clear_object (&mailbox);
			g_clear_object (&folder);
			imapx_free_fetch (finfo);

			return TRUE;
		}

		messages = camel_imapx_mailbox_get_messages (mailbox);
		unseen = camel_imapx_mailbox_get_unseen (mailbox);
		uidnext = camel_imapx_mailbox_get_uidnext (mailbox);

		/* Do we want to save these headers for later too?  Do we care? */

		mp = camel_mime_parser_new ();
		camel_mime_parser_init_with_bytes (mp, finfo->header);
		mi = camel_folder_summary_info_new_from_parser (folder->summary, mp);
		g_object_unref (mp);

		if (mi != NULL) {
			guint32 server_flags;
			CamelFlag *server_user_flags;
			CamelMessageInfoBase *binfo;
			gboolean free_user_flags = FALSE;

			mi->uid = camel_pstring_strdup (finfo->uid);

			if (!(finfo->got & FETCH_FLAGS) && is->priv->fetch_changes_infos) {
				FetchChangesInfo *nfo;

				nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
				g_return_val_if_fail (nfo != NULL, FALSE);

				server_flags = nfo->server_flags;
				server_user_flags = nfo->server_user_flags;
			} else {
				server_flags = finfo->flags;
				server_user_flags = finfo->user_flags;
				/* free user_flags ? */
				finfo->user_flags = NULL;
				free_user_flags = TRUE;
			}

			/* If the message is a really new one -- equal or higher than what
			 * we know as UIDNEXT for the folder, then it came in since we last
			 * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the
			 * command completion, but update UNREAD count now according to the
			 * message SEEN flag */
			if (!(server_flags & CAMEL_MESSAGE_SEEN)) {
				guint64 uidl;

				uidl = strtoull (mi->uid, NULL, 10);

				if (uidl >= uidnext) {
					c (is->priv->tagprefix, "Updating unseen count for new message %s\n", mi->uid);
					camel_imapx_mailbox_set_unseen (mailbox, unseen + 1);
				} else {
					c (is->priv->tagprefix, "Not updating unseen count for new message %s\n", mi->uid);
				}
			}

			binfo = (CamelMessageInfoBase *) mi;
			binfo->size = finfo->size;

			camel_folder_summary_lock (folder->summary);

			if (!camel_folder_summary_check_uid (folder->summary, mi->uid)) {
				imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, FALSE, NULL, camel_imapx_mailbox_get_permanentflags (mailbox));
				camel_folder_summary_add (folder->summary, mi);

				g_mutex_lock (&is->priv->changes_lock);

				camel_folder_change_info_add_uid (is->priv->changes, mi->uid);
				camel_folder_change_info_recent_uid (is->priv->changes, mi->uid);

				g_mutex_unlock (&is->priv->changes_lock);

				if (messages > 0) {
					gint cnt = (camel_folder_summary_count (folder->summary) * 100) / messages;

					camel_operation_progress (cancellable, cnt ? cnt : 1);
				}
			} else {
				camel_message_info_unref (mi);
			}

			camel_folder_summary_unlock (folder->summary);

			if (free_user_flags && server_user_flags)
				camel_flag_list_free (&server_user_flags);
		}

		g_clear_object (&mailbox);
		g_clear_object (&folder);
	}

	imapx_free_fetch (finfo);

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

fetch_changes_info_free (gpointer ptr)
{
	FetchChangesInfo *nfo = ptr;

	if (nfo) {
		camel_flag_list_free (&nfo->server_user_flags);
		g_free (nfo);
	}
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_info_changed_cb (CamelIMAPXSummary *summary,
			      CamelMessageInfo *info,
			      gpointer user_data)
{
	GHashTable *changed_meanwhile = user_data;

	g_return_if_fail (info != NULL);
	g_return_if_fail (changed_meanwhile != NULL);

	/* The UID can be NULL in case of a newly fetched message, for example when creating
	   the message info in imapx_untagged_fetch() by camel_folder_summary_info_new_from_parser() */
	if (camel_message_info_uid (info)) {
		g_hash_table_insert (changed_meanwhile,
			(gpointer) camel_pstring_strdup (camel_message_info_uid (info)),
			GINT_TO_POINTER (1));
	}
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_sync_free_user (GArray *user_set)
{
	gint i;

	if (user_set == NULL)
		return;

	for (i = 0; i < user_set->len; i++) {
		struct _imapx_flag_change *flag_change = &g_array_index (user_set, struct _imapx_flag_change, i);
		GPtrArray *infos = flag_change->infos;
		gint j;

		for (j = 0; j < infos->len; j++) {
			CamelMessageInfo *info = g_ptr_array_index (infos, j);
			camel_message_info_unref (info);
		}

		g_ptr_array_free (infos, TRUE);
		g_free (flag_change->name);
	}
	g_array_free (user_set, TRUE);
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_copy_message_sync (CamelIMAPXServer *is,
				      CamelIMAPXMailbox *mailbox,
				      CamelIMAPXMailbox *destination,
				      GPtrArray *uids,
				      gboolean delete_originals,
				      gboolean remove_deleted_flags,
				      GCancellable *cancellable,
				      GError **error)
{
	GPtrArray *data_uids;
	gint ii;
	gboolean use_move_command = FALSE;
	CamelIMAPXCommand *ic;
	CamelFolder *folder;
	GHashTable *source_infos;
	gboolean remove_junk_flags;
	gboolean success = TRUE;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (destination), FALSE);
	g_return_val_if_fail (uids != NULL, FALSE);

	/* To get permanent flags. That's okay if the "SELECT" fails here, as it can be
	   due to the folder being write-only; just ignore the error and continue. */
	camel_imapx_server_ensure_selected_sync (is, destination, cancellable, NULL);

	if (g_cancellable_set_error_if_cancelled (cancellable, error))
		return FALSE;

	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error))
		return FALSE;

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_val_if_fail (folder != NULL, FALSE);

	remove_deleted_flags = remove_deleted_flags || (folder->folder_flags & CAMEL_FOLDER_IS_TRASH) != 0;
	remove_junk_flags = (folder->folder_flags & CAMEL_FOLDER_IS_JUNK) != 0;

	/* If we're moving messages, prefer "UID MOVE" if supported. */
	if (delete_originals) {
		g_mutex_lock (&is->priv->stream_lock);

		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, MOVE)) {
			delete_originals = FALSE;
			use_move_command = TRUE;
		}

		g_mutex_unlock (&is->priv->stream_lock);
	}

	source_infos = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_message_info_unref);
	data_uids = g_ptr_array_new ();

	for (ii = 0; ii < uids->len; ii++) {
		gchar *uid = (gchar *) camel_pstring_strdup (uids->pdata[ii]);

		g_ptr_array_add (data_uids, uid);
		g_hash_table_insert (source_infos, uid, camel_folder_summary_get (folder->summary, uid));
	}

	g_ptr_array_sort (data_uids, (GCompareFunc) imapx_uids_array_cmp);

	ii = 0;
	while (ii < data_uids->len && success) {
		struct _uidset_state uidset;
		gint last_index = ii;

		imapx_uidset_init (&uidset, 0, MAX_COMMAND_LEN);

		if (use_move_command)
			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_MOVE_MESSAGE, "UID MOVE ");
		else
			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_COPY_MESSAGE, "UID COPY ");

		while (ii < data_uids->len) {
			const gchar *uid = (gchar *) g_ptr_array_index (data_uids, ii);

			ii++;

			if (imapx_uidset_add (&uidset, ic, uid) == 1)
				break;
		}

		imapx_uidset_done (&uidset, ic);

		camel_imapx_command_add (ic, " %M", destination);

		imapx_free_status (is->priv->copyuid_status);
		is->priv->copyuid_status = NULL;

		success = camel_imapx_server_process_command_sync (is, ic,
			use_move_command ? _("Error moving messages") : _("Error copying messages"),
			cancellable, error);

		if (success) {
			struct _status_info *copyuid_status = is->priv->copyuid_status;

			if (ic->status && ic->status->condition == IMAPX_COPYUID)
				copyuid_status = ic->status;

			if (copyuid_status && copyuid_status->u.copyuid.uids &&
			    copyuid_status->u.copyuid.copied_uids &&
			    copyuid_status->u.copyuid.uids->len == copyuid_status->u.copyuid.copied_uids->len) {
				CamelFolder *destination_folder;

				destination_folder = imapx_server_ref_folder (is, destination);
				if (destination_folder) {
					CamelMessageInfo *source_info, *destination_info;
					CamelFolderChangeInfo *changes;
					gint ii;

					changes = camel_folder_change_info_new ();

					for (ii = 0; ii < copyuid_status->u.copyuid.uids->len; ii++) {
						gchar *uid;
						gboolean is_new = FALSE;

						uid = g_strdup_printf ("%d", g_array_index (copyuid_status->u.copyuid.uids, guint32, ii));
						source_info = g_hash_table_lookup (source_infos, uid);
						g_free (uid);

						if (!source_info)
							continue;

						uid = g_strdup_printf ("%d", g_array_index (copyuid_status->u.copyuid.copied_uids, guint32, ii));
						destination_info = camel_folder_summary_get (folder->summary, uid);

						if (!destination_info) {
							is_new = TRUE;
							destination_info = camel_message_info_clone (source_info);
							destination_info->summary = destination_folder->summary;
							camel_pstring_free (destination_info->uid);
							destination_info->uid = camel_pstring_strdup (uid);
						}

						g_free (uid);

						imapx_set_message_info_flags_for_new_message (
							destination_info,
							((CamelMessageInfoBase *) source_info)->flags,
							((CamelMessageInfoBase *) source_info)->user_flags,
							TRUE,
							((CamelMessageInfoBase *) source_info)->user_tags,
							camel_imapx_mailbox_get_permanentflags (destination));
						if (remove_deleted_flags)
							camel_message_info_set_flags (destination_info, CAMEL_MESSAGE_DELETED, 0);
						if (remove_junk_flags)
							camel_message_info_set_flags (destination_info, CAMEL_MESSAGE_JUNK, 0);
						if (is_new)
							camel_folder_summary_add (destination_folder->summary, destination_info);
						camel_folder_change_info_add_uid (changes, destination_info->uid);

						if (!is_new)
							camel_message_info_unref (destination_info);
					}

					if (camel_folder_change_info_changed (changes)) {
						camel_folder_summary_touch (destination_folder->summary);
						camel_folder_summary_save_to_db (destination_folder->summary, NULL);
						camel_folder_changed (destination_folder, changes);
					}

					camel_folder_change_info_free (changes);
					g_object_unref (destination_folder);
				}
			}

			if (delete_originals || use_move_command) {
				CamelFolderChangeInfo *changes = NULL;
				gint jj;

				camel_folder_freeze (folder);

				for (jj = last_index; jj < ii; jj++) {
					const gchar *uid = uids->pdata[jj];

					if (delete_originals) {
						camel_folder_delete_message (folder, uid);
					} else {
						if (camel_folder_summary_remove_uid (folder->summary, uid)) {
							if (!changes)
								changes = camel_folder_change_info_new ();

							camel_folder_change_info_remove_uid (changes, uid);
						}
					}
				}

				if (changes && camel_folder_change_info_changed (changes)) {
					camel_folder_summary_touch (folder->summary);
					camel_folder_summary_save_to_db (folder->summary, NULL);
					camel_folder_changed (folder, changes);
				}

				camel_folder_thaw (folder);

				if (changes)
					camel_folder_change_info_free (changes);
			}
		}

		imapx_free_status (is->priv->copyuid_status);
		is->priv->copyuid_status = NULL;

		camel_imapx_command_unref (ic);
	}

	g_hash_table_destroy (source_infos);
	g_ptr_array_foreach (data_uids, (GFunc) camel_pstring_free, NULL);
	g_ptr_array_free (data_uids, TRUE);
	g_object_unref (folder);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_stop_idle_sync (CamelIMAPXServer *is,
				   GCancellable *cancellable,
				   GError **error)
{
	GCancellable *idle_cancellable;
	gulong handler_id = 0;
	gboolean success = TRUE;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	g_mutex_lock (&is->priv->idle_lock);

	if (is->priv->idle_state == IMAPX_IDLE_STATE_OFF) {
		g_mutex_unlock (&is->priv->idle_lock);
		return TRUE;
	} else if (is->priv->idle_state == IMAPX_IDLE_STATE_SCHEDULED) {
		if (is->priv->idle_pending) {
			g_source_destroy (is->priv->idle_pending);
			g_source_unref (is->priv->idle_pending);
			is->priv->idle_pending = NULL;
		}

		is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
		g_cond_broadcast (&is->priv->idle_cond);
	}

	idle_cancellable = is->priv->idle_cancellable ? g_object_ref (is->priv->idle_cancellable) : NULL;

	g_clear_object (&is->priv->idle_cancellable);
	g_clear_object (&is->priv->idle_mailbox);
	is->priv->idle_stamp++;

	if (cancellable)
		handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_server_wait_idle_stop_cancelled_cb), is, NULL);

	while (is->priv->idle_state == IMAPX_IDLE_STATE_PREPARING &&
	       !g_cancellable_is_cancelled (cancellable)) {
		g_cond_wait (&is->priv->idle_cond, &is->priv->idle_lock);
	}

	if (is->priv->idle_state == IMAPX_IDLE_STATE_RUNNING &&
	    !g_cancellable_is_cancelled (cancellable)) {
		is->priv->idle_state = IMAPX_IDLE_STATE_STOPPING;
		g_cond_broadcast (&is->priv->idle_cond);
		g_mutex_unlock (&is->priv->idle_lock);

		g_mutex_lock (&is->priv->stream_lock);
		if (is->priv->output_stream) {
			gint previous_timeout = -1;

			/* Set the connection timeout to some short time, no need to wait for it for too long */
			if (is->priv->connection)
				previous_timeout = imapx_server_set_connection_timeout (is->priv->connection, 5);

			success = g_output_stream_flush (is->priv->output_stream, cancellable, error);
			success = success && g_output_stream_write_all (is->priv->output_stream, "DONE\r\n", 6, NULL, cancellable, error);
			success = success && g_output_stream_flush (is->priv->output_stream, cancellable, error);

			if (previous_timeout >= 0 && is->priv->connection)
				imapx_server_set_connection_timeout (is->priv->connection, previous_timeout);
		} else {
			success = FALSE;

			/* This message won't get into UI. */
			g_set_error_literal (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
				"Reconnect after couldn't issue DONE command");
		}
		g_mutex_unlock (&is->priv->stream_lock);
		g_mutex_lock (&is->priv->idle_lock);
	}

	while (success && is->priv->idle_state != IMAPX_IDLE_STATE_OFF &&
	       !g_cancellable_is_cancelled (cancellable)) {
		g_cond_wait (&is->priv->idle_cond, &is->priv->idle_lock);
	}

	g_mutex_unlock (&is->priv->idle_lock);

	if (cancellable && handler_id)
		g_cancellable_disconnect (cancellable, handler_id);

	if (success && g_cancellable_is_cancelled (cancellable)) {
		g_clear_error (error);

		success = FALSE;

		/* This message won't get into UI. */
		g_set_error_literal (error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
			"Reconnect after cancelled IDLE stop command");
	}

	if (!success) {
		if (idle_cancellable)
			g_cancellable_cancel (idle_cancellable);

		g_mutex_lock (&is->priv->idle_lock);
		is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
		g_mutex_unlock (&is->priv->idle_lock);

		imapx_disconnect (is);
	}

	g_clear_object (&idle_cancellable);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_exists (CamelIMAPXServer *is,
                       GInputStream *input_stream,
                       GCancellable *cancellable,
                       GError **error)
{
	CamelFolder *folder;
	CamelIMAPXMailbox *mailbox;
	guint32 exists;
	gboolean success = TRUE;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	mailbox = camel_imapx_server_ref_pending_or_selected (is);

	if (mailbox == NULL) {
		g_warning ("%s: No mailbox available", G_STRFUNC);
		return TRUE;
	}

	exists = (guint32) is->priv->context->id;

	c (is->priv->tagprefix, "%s: updating mailbox '%s' messages: %d ~> %d\n", G_STRFUNC,
		camel_imapx_mailbox_get_name (mailbox),
		camel_imapx_mailbox_get_messages (mailbox),
		exists);

	camel_imapx_mailbox_set_messages (mailbox, exists);

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_val_if_fail (folder != NULL, FALSE);

	if (camel_imapx_server_is_in_idle (is)) {
		guint count;

		count = camel_folder_summary_count (folder->summary);
		if (count < exists)
			g_signal_emit (is, signals[REFRESH_MAILBOX], 0, mailbox);
	}

	g_object_unref (folder);
	g_object_unref (mailbox);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_create_mailbox_sync (CamelIMAPXServer *is,
					const gchar *mailbox_name,
					GCancellable *cancellable,
					GError **error)
{
	CamelIMAPXCommand *ic;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (mailbox_name != NULL, FALSE);

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CREATE_MAILBOX, "CREATE %m", mailbox_name);

	success = camel_imapx_server_process_command_sync (is, ic, _("Error creating folder"), cancellable, error);

	camel_imapx_command_unref (ic);

	if (success) {
		gchar *utf7_pattern;

		utf7_pattern = camel_utf8_utf7 (mailbox_name);

		/* List the new mailbox so we trigger our untagged
		 * LIST handler.  This simulates being notified of
		 * a newly-created mailbox, so we can just let the
		 * callback functions handle the bookkeeping. */
		success = camel_imapx_server_list_sync (is, utf7_pattern, 0, cancellable, error);

		g_free (utf7_pattern);
	}

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_uidset_done (struct _uidset_state *ss,
                   CamelIMAPXCommand *ic)
{
	gint ret = FALSE;

	if (ss->last != 0) {
		if (ss->entries > 0)
			camel_imapx_command_add (ic, ",");
		if (ss->last == ss->start)
			camel_imapx_command_add (ic, "%d", ss->last);
		else
			camel_imapx_command_add (ic, "%d:%d", ss->start, ss->last);
	}

	ret = ss->last != 0;

	ss->start = 0;
	ss->last = 0;
	ss->uids = 0;
	ss->entries = 0;

	return ret;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_expunge_sync (CamelIMAPXServer *is,
				 CamelIMAPXMailbox *mailbox,
				 GCancellable *cancellable,
				 GError **error)
{
	CamelFolder *folder;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_val_if_fail (folder != NULL, FALSE);

	success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error);

	if (success) {
		CamelIMAPXCommand *ic;

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_EXPUNGE, "EXPUNGE");

		success = camel_imapx_server_process_command_sync (is, ic, _("Error expunging message"), cancellable, error);
		if (success) {
			GPtrArray *uids;
			CamelStore *parent_store;
			const gchar *full_name;

			full_name = camel_folder_get_full_name (folder);
			parent_store = camel_folder_get_parent_store (folder);

			camel_folder_summary_lock (folder->summary);

			camel_folder_summary_save_to_db (folder->summary, NULL);
			uids = camel_db_get_folder_deleted_uids (parent_store->cdb_r, full_name, NULL);

			if (uids && uids->len) {
				CamelFolderChangeInfo *changes;
				GList *removed = NULL;
				gint i;

				changes = camel_folder_change_info_new ();
				for (i = 0; i < uids->len; i++) {
					camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
					removed = g_list_prepend (removed, (gpointer) uids->pdata[i]);
				}

				camel_folder_summary_remove_uids (folder->summary, removed);
				camel_folder_summary_save_to_db (folder->summary, NULL);

				camel_folder_changed (folder, changes);
				camel_folder_change_info_free (changes);

				g_list_free (removed);
				g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL);
			}

			if (uids)
				g_ptr_array_free (uids, TRUE);

			camel_folder_summary_unlock (folder->summary);
		}

		camel_imapx_command_unref (ic);
	}

	g_clear_object (&folder);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_completion (CamelIMAPXServer *is,
                  GInputStream *input_stream,
                  guchar *token,
                  gint len,
                  GCancellable *cancellable,
                  GError **error)
{
	CamelIMAPXCommand *ic;
	CamelIMAPXMailbox *mailbox;
	gboolean success = FALSE;
	guint tag;

	/* Given "A0001 ...", 'A' = tag prefix, '0001' = tag. */

	if (token[0] != is->priv->tagprefix) {
		g_set_error (
			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
			"Server sent unexpected response: %s", token);
		return FALSE;
	}

	tag = strtoul ((gchar *) token + 1, NULL, 10);

	COMMAND_LOCK (is);

	if (is->priv->current_command != NULL && is->priv->current_command->tag == tag)
		ic = camel_imapx_command_ref (is->priv->current_command);
	else
		ic = NULL;

	COMMAND_UNLOCK (is);

	if (ic == NULL) {
		g_set_error (
			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
			"got response tag unexpectedly: %s", token);
		return FALSE;
	}

	c (is->priv->tagprefix, "Got completion response for command %05u '%s'\n", ic->tag, camel_imapx_job_get_kind_name (ic->job_kind));

	g_mutex_lock (&is->priv->changes_lock);

	if (camel_folder_change_info_changed (is->priv->changes)) {
		CamelFolder *folder = NULL;
		CamelIMAPXMailbox *mailbox;
		CamelFolderChangeInfo *changes;

		changes = is->priv->changes;
		is->priv->changes = camel_folder_change_info_new ();

		g_mutex_unlock (&is->priv->changes_lock);

		mailbox = camel_imapx_server_ref_selected (is);

		g_warn_if_fail (mailbox != NULL);

		if (mailbox) {
			folder = imapx_server_ref_folder (is, mailbox);
			g_return_val_if_fail (folder != NULL, FALSE);

			camel_folder_summary_save_to_db (folder->summary, NULL);

			imapx_update_store_summary (folder);
			camel_folder_changed (folder, changes);
		}

		camel_folder_change_info_free (changes);

		g_clear_object (&folder);
		g_clear_object (&mailbox);
	} else {
		g_mutex_unlock (&is->priv->changes_lock);
	}

	if (g_list_next (ic->current_part) != NULL) {
		g_set_error (
			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
			"command still has unsent parts? %s", camel_imapx_job_get_kind_name (ic->job_kind));
		goto exit;
	}

	mailbox = camel_imapx_server_ref_selected (is);

	ic->status = imapx_parse_status (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		mailbox, cancellable, error);

	g_clear_object (&mailbox);

	if (ic->status == NULL)
		goto exit;

	if (ic->status->condition == IMAPX_CAPABILITY) {
		guint32 list_extended = imapx_lookup_capability ("LIST-EXTENDED");

		is->priv->is_cyrus = is->priv->is_cyrus || (ic->status->text && camel_strstrcase (ic->status->text, "cyrus"));
		if (is->priv->is_cyrus && ic->status->u.cinfo && (ic->status->u.cinfo->capa & list_extended) != 0) {
			/* Disable LIST-EXTENDED for cyrus servers */
			c (is->priv->tagprefix, "Disabling LIST-EXTENDED extension for a Cyrus server\n");
			ic->status->u.cinfo->capa &= ~list_extended;
		}
	}

	success = TRUE;

exit:

	ic->completed = TRUE;
	camel_imapx_command_unref (ic);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_finalize (GObject *object)
{
	CamelIMAPXServer *is = CAMEL_IMAPX_SERVER (object);

	g_mutex_clear (&is->priv->stream_lock);
	g_mutex_clear (&is->priv->select_lock);
	g_mutex_clear (&is->priv->changes_lock);

	camel_folder_change_info_free (is->priv->changes);
	imapx_free_status (is->priv->copyuid_status);

	g_free (is->priv->context);
	g_hash_table_destroy (is->priv->untagged_handlers);

	if (is->priv->inactivity_timeout != NULL)
		g_source_unref (is->priv->inactivity_timeout);
	g_mutex_clear (&is->priv->inactivity_timeout_lock);

	g_free (is->priv->status_data_items);
	g_free (is->priv->list_return_opts);

	if (is->priv->search_results != NULL)
		g_array_unref (is->priv->search_results);
	g_mutex_clear (&is->priv->search_results_lock);

	g_hash_table_destroy (is->priv->known_alerts);
	g_mutex_clear (&is->priv->known_alerts_lock);

	g_mutex_clear (&is->priv->idle_lock);
	g_cond_clear (&is->priv->idle_cond);

	g_rec_mutex_clear (&is->priv->command_lock);

	g_weak_ref_clear (&is->priv->store);
	g_weak_ref_clear (&is->priv->select_mailbox);
	g_weak_ref_clear (&is->priv->select_pending);

	/* Chain up to parent's finalize() method. */
	G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object);
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_uidset_add (struct _uidset_state *ss,
                  CamelIMAPXCommand *ic,
                  const gchar *uid)
{
	guint32 uidn;

	uidn = strtoul (uid, NULL, 10);
	if (uidn == 0)
		return -1;

	ss->uids++;

	e (ic->is->priv->tagprefix, "uidset add '%s'\n", uid);

	if (ss->last == 0) {
		e (ic->is->priv->tagprefix, " start\n");
		ss->start = uidn;
		ss->last = uidn;
	} else {
		if (ss->start - 1 == uidn) {
			ss->start = uidn;
		} else {
			if (ss->last != uidn - 1) {
				if (ss->last == ss->start) {
					e (ic->is->priv->tagprefix, " ,next\n");
					if (ss->entries > 0)
						camel_imapx_command_add (ic, ",");
					camel_imapx_command_add (ic, "%d", ss->start);
					ss->entries++;
				} else {
					e (ic->is->priv->tagprefix, " :range\n");
					if (ss->entries > 0)
						camel_imapx_command_add (ic, ",");
					camel_imapx_command_add (ic, "%d:%d", ss->start, ss->last);
					ss->entries += 2;
				}
				ss->start = uidn;
			}

			ss->last = uidn;
		}
	}

	if ((ss->limit && ss->entries >= ss->limit)
	    || (ss->limit && ss->uids >= ss->limit)
	    || (ss->total && ss->uids >= ss->total)) {
		e (ic->is->priv->tagprefix, " done, %d entries, %d uids\n", ss->entries, ss->uids);
		if (!imapx_uidset_done (ss, ic))
			return -1;
		return 1;
	}

	return 0;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_process_fetch_changes_infos (CamelIMAPXServer *is,
					  CamelIMAPXMailbox *mailbox,
					  CamelFolder *folder,
					  GHashTable *infos,
					  GHashTable *known_uids,
					  GSList **out_fetch_summary_uids,
					  guint64 from_uidl,
					  guint64 to_uidl)
{
	GHashTableIter iter;
	gpointer key, value;
	CamelFolderSummary *summary;

	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
	g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
	g_return_if_fail (CAMEL_IS_FOLDER (folder));
	g_return_if_fail (infos != NULL);

	if (out_fetch_summary_uids)
		g_return_if_fail (*out_fetch_summary_uids == NULL);

	summary = folder->summary;

	g_hash_table_iter_init (&iter, infos);
	while (g_hash_table_iter_next (&iter, &key, &value)) {
		const gchar *uid = key;
		FetchChangesInfo *nfo = value;
		CamelMessageInfo *minfo;

		if (!uid || !nfo)
			continue;

		if (known_uids)
			g_hash_table_insert (known_uids, (gpointer) camel_pstring_strdup (uid), GINT_TO_POINTER (1));

		if (!camel_folder_summary_check_uid (summary, uid) ||
		    !(minfo = camel_folder_summary_get (summary, uid))) {
			if (out_fetch_summary_uids) {
				*out_fetch_summary_uids = g_slist_prepend (*out_fetch_summary_uids,
					(gpointer) camel_pstring_strdup (uid));
			}

			continue;
		}

		if (imapx_update_message_info_flags (
			minfo,
			nfo->server_flags,
			nfo->server_user_flags,
			camel_imapx_mailbox_get_permanentflags (mailbox),
			folder, FALSE)) {
			g_mutex_lock (&is->priv->changes_lock);
			camel_folder_change_info_change_uid (is->priv->changes, camel_message_info_uid (minfo));
			g_mutex_unlock (&is->priv->changes_lock);
		}

		camel_message_info_unref (minfo);
	}
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_run_idle_thread_cb (gpointer user_data)
{
	GWeakRef *is_weakref = user_data;
	CamelIMAPXServer *is;

	g_return_val_if_fail (is_weakref != NULL, FALSE);

	is = g_weak_ref_get (is_weakref);
	if (!is)
		return FALSE;

	g_mutex_lock (&is->priv->idle_lock);

	if (g_main_current_source () == is->priv->idle_pending) {
		if (!g_source_is_destroyed (g_main_current_source ()) &&
		    is->priv->idle_state == IMAPX_IDLE_STATE_SCHEDULED) {
			IdleThreadData *itd;
			GThread *thread;
			GError *local_error = NULL;

			itd = g_new0 (IdleThreadData, 1);
			itd->is = g_object_ref (is);
			itd->idle_cancellable = g_object_ref (is->priv->idle_cancellable);
			itd->idle_stamp = is->priv->idle_stamp;

			thread = g_thread_try_new (NULL, imapx_server_idle_thread, itd, &local_error);
			if (thread) {
				g_thread_unref (thread);
			} else {
				g_warning ("%s: Failed to create IDLE thread: %s", G_STRFUNC, local_error ? local_error->message : "Unknown error");

				g_clear_object (&itd->is);
				g_clear_object (&itd->idle_cancellable);
				g_free (itd);
			}

			g_clear_error (&local_error);
		}

		g_source_unref (is->priv->idle_pending);
		is->priv->idle_pending = NULL;
	}

	g_mutex_unlock (&is->priv->idle_lock);

	return FALSE;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_idle_thread (gpointer user_data)
{
	IdleThreadData *itd = user_data;
	CamelIMAPXServer *is;
	CamelIMAPXMailbox *mailbox;
	CamelIMAPXCommand *ic;
	CamelIMAPXCommandPart *cp;
	GCancellable *idle_cancellable;
	GError *local_error = NULL;
	gint previous_timeout = -1;
	gboolean success = FALSE;
	gboolean rather_disconnect = FALSE;

	g_return_val_if_fail (itd != NULL, NULL);

	is = itd->is;
	idle_cancellable = itd->idle_cancellable;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
	g_return_val_if_fail (G_IS_CANCELLABLE (idle_cancellable), NULL);

	g_mutex_lock (&is->priv->idle_lock);

	if (g_cancellable_is_cancelled (idle_cancellable) ||
	    is->priv->idle_stamp != itd->idle_stamp ||
	    is->priv->idle_state != IMAPX_IDLE_STATE_SCHEDULED) {
		g_cond_broadcast (&is->priv->idle_cond);
		g_mutex_unlock (&is->priv->idle_lock);

		g_clear_object (&itd->is);
		g_clear_object (&itd->idle_cancellable);
		g_free (itd);

		return NULL;
	}

	is->priv->idle_state = IMAPX_IDLE_STATE_PREPARING;
	g_cond_broadcast (&is->priv->idle_cond);

	mailbox = is->priv->idle_mailbox;
	if (mailbox)
		g_object_ref (mailbox);

	g_mutex_unlock (&is->priv->idle_lock);

	if (!mailbox)
		mailbox = camel_imapx_server_ref_selected (is);

	if (!mailbox)
		goto exit;

	success = camel_imapx_server_ensure_selected_sync (is, mailbox, idle_cancellable, &local_error);
	if (!success) {
		rather_disconnect = TRUE;
		goto exit;
	}

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_IDLE, "IDLE");
	camel_imapx_command_close (ic);

	cp = g_queue_peek_head (&ic->parts);
	cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION;

	g_mutex_lock (&is->priv->stream_lock);
	/* Set the connection timeout to one minute more than the inactivity timeout */
	if (is->priv->connection)
		previous_timeout = imapx_server_set_connection_timeout (is->priv->connection, INACTIVITY_TIMEOUT_SECONDS + 60);
	g_mutex_unlock (&is->priv->stream_lock);

	g_mutex_lock (&is->priv->idle_lock);
	if (is->priv->idle_stamp == itd->idle_stamp &&
	    is->priv->idle_state == IMAPX_IDLE_STATE_PREPARING) {
		g_mutex_unlock (&is->priv->idle_lock);

		/* Blocks, until the DONE is issued or on inactivity timeout, error, ... */
		success = camel_imapx_server_process_command_sync (is, ic, _("Error running IDLE"), idle_cancellable, &local_error);

		rather_disconnect = rather_disconnect || !success || g_cancellable_is_cancelled (idle_cancellable);
	} else {
		g_mutex_unlock (&is->priv->idle_lock);
	}

	if (previous_timeout >= 0) {
		g_mutex_lock (&is->priv->stream_lock);
		if (is->priv->connection)
			imapx_server_set_connection_timeout (is->priv->connection, previous_timeout);
		g_mutex_unlock (&is->priv->stream_lock);
	}

	camel_imapx_command_unref (ic);

 exit:
	g_mutex_lock (&is->priv->idle_lock);
	g_clear_object (&is->priv->idle_cancellable);
	is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
	g_cond_broadcast (&is->priv->idle_cond);
	g_mutex_unlock (&is->priv->idle_lock);

	if (success)
		c (camel_imapx_server_get_tagprefix (is), "IDLE finished successfully\n");
	else if (local_error)
		c (camel_imapx_server_get_tagprefix (is), "IDLE finished with error: %s%s\n", local_error->message, rather_disconnect ? "; rather disconnect" : "");
	else
		c (camel_imapx_server_get_tagprefix (is), "IDLE finished without error%s\n", rather_disconnect ? "; rather disconnect" : "");

	if (rather_disconnect) {
		imapx_disconnect (is);
	}

	g_clear_object (&mailbox);
	g_clear_error (&local_error);

	g_clear_object (&itd->is);
	g_clear_object (&itd->idle_cancellable);
	g_free (itd);

	return NULL;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_uid_search_sync (CamelIMAPXServer *is,
				    CamelIMAPXMailbox *mailbox,
				    const gchar *criteria_prefix,
				    const gchar *search_key,
				    const gchar * const *words,
				    GCancellable *cancellable,
				    GError **error)
{
	CamelIMAPXCommand *ic;
	GArray *uid_search_results;
	GPtrArray *results = NULL;
	gint ii;
	gboolean need_charset = FALSE;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), NULL);
	g_return_val_if_fail (criteria_prefix != NULL, NULL);

	success = camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error);
	if (!success)
		return FALSE;

	for (ii = 0; !need_charset && words && words[ii]; ii++) {
		need_charset = !imapx_util_all_is_ascii (words[ii]);
	}

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UID_SEARCH, "UID SEARCH");
	if (need_charset)
		camel_imapx_command_add (ic, " CHARSET UTF-8");
	if (criteria_prefix && *criteria_prefix)
		camel_imapx_command_add (ic, " %t", criteria_prefix);

	if (search_key && words) {
		for (ii = 0; words[ii]; ii++) {
			camel_imapx_command_add (ic, " %t %s", search_key, words[ii]);
		}
	}

	success = camel_imapx_server_process_command_sync (is, ic, _("Search failed"), cancellable, error);

	camel_imapx_command_unref (ic);

	g_mutex_lock (&is->priv->search_results_lock);
	uid_search_results = is->priv->search_results;
	is->priv->search_results = NULL;
	g_mutex_unlock (&is->priv->search_results_lock);

	if (success) {
		guint ii;

		/* Convert the numeric UIDs to strings. */

		g_return_val_if_fail (uid_search_results != NULL, NULL);

		results = g_ptr_array_new_full (uid_search_results->len, (GDestroyNotify) camel_pstring_free);

		for (ii = 0; ii < uid_search_results->len; ii++) {
			const gchar *pooled_uid;
			guint64 numeric_uid;
			gchar *alloced_uid;

			numeric_uid = g_array_index (uid_search_results, guint64, ii);
			alloced_uid = g_strdup_printf ("%" G_GUINT64_FORMAT, numeric_uid);
			pooled_uid = camel_pstring_add (alloced_uid, TRUE);
			g_ptr_array_add (results, (gpointer) pooled_uid);
		}
	}

	if (uid_search_results)
		g_array_unref (uid_search_results);

	return results;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_stash_command_arguments (CamelIMAPXServer *is)
{
	GString *buffer;

	/* Stash some reusable capability-based command arguments. */

	buffer = g_string_new ("MESSAGES UNSEEN UIDVALIDITY UIDNEXT");
	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, CONDSTORE))
		g_string_append (buffer, " HIGHESTMODSEQ");
	g_free (is->priv->status_data_items);
	is->priv->status_data_items = g_string_free (buffer, FALSE);

	g_free (is->priv->list_return_opts);
	if (!is->priv->is_cyrus && CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LIST_EXTENDED)) {
		buffer = g_string_new ("CHILDREN SUBSCRIBED");
		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LIST_STATUS))
			g_string_append_printf (
				buffer, " STATUS (%s)",
				is->priv->status_data_items);
		if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, SPECIAL_USE))
			g_string_append_printf (buffer, " SPECIAL-USE");
		is->priv->list_return_opts = g_string_free (buffer, FALSE);
	} else {
		is->priv->list_return_opts = NULL;
	}
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged (CamelIMAPXServer *is,
                GInputStream *input_stream,
                GCancellable *cancellable,
                GError **error)
{
	CamelIMAPXSettings *settings;
	CamelSortType fetch_order;
	guchar *p = NULL, c;
	const gchar *token = NULL;
	gboolean success = FALSE;

	/* If is->priv->context is not NULL here, it basically means
	 * that imapx_untagged() got called concurrently for the same
	 * CamelIMAPXServer instance. Should this ever happen, then
	 * we will need to protect this data structure with locks
	 */
	g_return_val_if_fail (is->priv->context == NULL, FALSE);
	is->priv->context = g_new0 (CamelIMAPXServerUntaggedContext, 1);

	settings = camel_imapx_server_ref_settings (is);
	fetch_order = camel_imapx_settings_get_fetch_order (settings);
	g_object_unref (settings);

	is->priv->context->lsub = FALSE;
	is->priv->context->fetch_order = fetch_order;

	e (is->priv->tagprefix, "got untagged response\n");
	is->priv->context->id = 0;
	is->priv->context->tok = camel_imapx_input_stream_token (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		&(is->priv->context->token),
		&(is->priv->context->len),
		cancellable, error);
	if (is->priv->context->tok < 0)
		goto exit;

	if (is->priv->context->tok == IMAPX_TOK_INT) {
		is->priv->context->id = strtoul (
			(gchar *) is->priv->context->token, NULL, 10);
		is->priv->context->tok = camel_imapx_input_stream_token (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			&(is->priv->context->token),
			&(is->priv->context->len),
			cancellable, error);
		if (is->priv->context->tok < 0)
			goto exit;
	}

	if (is->priv->context->tok == '\n') {
		g_set_error (
			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
			"truncated server response");
		goto exit;
	}

	e (is->priv->tagprefix, "Have token '%s' id %lu\n", is->priv->context->token, is->priv->context->id);
	p = is->priv->context->token;
	while ((c = *p))
		*p++ = g_ascii_toupper ((gchar) c);

	token = (const gchar *) is->priv->context->token; /* FIXME need 'guchar *token' here */
	while (token != NULL) {
		CamelIMAPXUntaggedRespHandlerDesc *desc = NULL;

		desc = g_hash_table_lookup (is->priv->untagged_handlers, token);
		if (desc == NULL) {
			/* unknown response, just ignore it */
			c (is->priv->tagprefix, "unknown token: %s\n", is->priv->context->token);
			break;
		}
		if (desc->handler == NULL) {
			/* no handler function, ignore token */
			c (is->priv->tagprefix, "no handler for token: %s\n", is->priv->context->token);
			break;
		}

		/* call the handler function */
		success = desc->handler (is, input_stream, cancellable, error);
		if (!success)
			goto exit;

		/* is there another handler next-in-line? */
		token = desc->next_response;
		if (token != NULL) {
			/* TODO do we need to update 'priv->context->token'
			 *      to the value of 'token' here, before
			 *      calling the handler next-in-line for this
			 *      specific run of imapx_untagged()?
			 *      It has not been done in the original code
			 *      in the "fall through" situation in the
			 *      token switch statement, which is what
			 *      we're mimicking here
			 */
			continue;
		}

		if (!desc->skip_stream_when_done)
			goto exit;
	}

	success = camel_imapx_input_stream_skip (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);

exit:
	g_free (is->priv->context);
	is->priv->context = NULL;

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_continuation (CamelIMAPXServer *is,
                    GInputStream *input_stream,
                    GOutputStream *output_stream,
                    gboolean litplus,
                    GCancellable *cancellable,
                    GError **error)
{
	CamelIMAPXCommand *ic, *newic = NULL;
	CamelIMAPXCommandPart *cp;
	GList *link;
	gssize n_bytes_written;
	gboolean success;

	/* The 'literal' pointer is like a write-lock, nothing else
	 * can write while we have it ... so we dont need any
	 * ohter lock here.  All other writes go through
	 * queue-lock */
	if (camel_imapx_server_is_in_idle (is)) {
		success = camel_imapx_input_stream_skip (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			cancellable, error);

		if (!success)
			return FALSE;

		c (is->priv->tagprefix, "Got continuation response for IDLE \n");

		g_mutex_lock (&is->priv->idle_lock);
		is->priv->idle_state = IMAPX_IDLE_STATE_RUNNING;
		g_cond_broadcast (&is->priv->idle_cond);
		g_mutex_unlock (&is->priv->idle_lock);

		return TRUE;
	}

	ic = is->priv->continuation_command;
	if (!litplus) {
		if (ic == NULL) {
			c (is->priv->tagprefix, "got continuation response with no outstanding continuation requests?\n");
			return camel_imapx_input_stream_skip (
				CAMEL_IMAPX_INPUT_STREAM (input_stream),
				cancellable, error);
		}
		c (is->priv->tagprefix, "got continuation response for data\n");
	} else {
		c (is->priv->tagprefix, "sending LITERAL+ continuation\n");
		g_return_val_if_fail (ic != NULL, FALSE);
	}

	/* coverity[deadcode] */
	link = ic ? ic->current_part : NULL;
	g_return_val_if_fail (link != NULL, FALSE);
	cp = (CamelIMAPXCommandPart *) link->data;

	switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) {
	case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
		c (is->priv->tagprefix, "writing data wrapper to literal\n");
		n_bytes_written =
			camel_data_wrapper_write_to_output_stream_sync (
				CAMEL_DATA_WRAPPER (cp->ob),
				output_stream, cancellable, error);
		if (n_bytes_written < 0)
			return FALSE;
		break;
	case CAMEL_IMAPX_COMMAND_AUTH: {
		gchar *resp;
		guchar *token;

		success = camel_imapx_input_stream_text (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			&token, cancellable, error);

		if (!success)
			return FALSE;

		resp = camel_sasl_challenge_base64_sync (
			(CamelSasl *) cp->ob, (const gchar *) token,
			cancellable, error);
		g_free (token);
		if (resp == NULL)
			return FALSE;
		c (is->priv->tagprefix, "got auth continuation, feeding token '%s' back to auth mech\n", resp);

		g_mutex_lock (&is->priv->stream_lock);
		n_bytes_written = g_output_stream_write_all (
			output_stream, resp, strlen (resp),
			NULL, cancellable, error);
		g_mutex_unlock (&is->priv->stream_lock);
		g_free (resp);

		if (n_bytes_written < 0)
			return FALSE;

		/* we want to keep getting called until we get a status reponse from the server
		 * ignore what sasl tells us */
		newic = ic;
		/* We already ate the end of the input stream line */
		goto noskip;
		break; }
	case CAMEL_IMAPX_COMMAND_FILE: {
		GFile *file;
		GFileInfo *file_info;
		GFileInputStream *file_input_stream;
		goffset file_size = 0;

		c (is->priv->tagprefix, "writing file '%s' to literal\n", (gchar *) cp->ob);

		file = g_file_new_for_path (cp->ob);
		file_input_stream = g_file_read (file, cancellable, error);
		g_object_unref (file);

		if (file_input_stream == NULL)
			return FALSE;

		file_info = g_file_input_stream_query_info (file_input_stream,
			G_FILE_ATTRIBUTE_STANDARD_SIZE, cancellable, NULL);
		if (file_info) {
			file_size = g_file_info_get_size (file_info);
			g_object_unref (file_info);
		}

		g_mutex_lock (&is->priv->stream_lock);

		n_bytes_written = imapx_server_write_file_with_progress (
			output_stream, G_INPUT_STREAM (file_input_stream),
			file_size, cancellable, error);

		g_mutex_unlock (&is->priv->stream_lock);

		g_input_stream_close (G_INPUT_STREAM (file_input_stream), cancellable, NULL);
		g_object_unref (file_input_stream);

		if (n_bytes_written < 0)
			return FALSE;

		break; }
	case CAMEL_IMAPX_COMMAND_STRING:
		g_mutex_lock (&is->priv->stream_lock);
		n_bytes_written = g_output_stream_write_all (
			output_stream, cp->ob, cp->ob_size,
			NULL, cancellable, error);
		g_mutex_unlock (&is->priv->stream_lock);
		if (n_bytes_written < 0)
			return FALSE;
		break;
	default:
		/* should we just ignore? */
		g_set_error (
			error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
			"continuation response for non-continuation request");
		return FALSE;
	}

	if (!litplus) {
		success = camel_imapx_input_stream_skip (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			cancellable, error);

		if (!success)
			return FALSE;
	}

noskip:
	link = g_list_next (link);
	if (link != NULL) {
		ic->current_part = link;
		cp = (CamelIMAPXCommandPart *) link->data;

		c (is->priv->tagprefix, "next part of command \"%c%05u: %s\"\n", is->priv->tagprefix, ic->tag, cp->data);

		g_mutex_lock (&is->priv->stream_lock);
		n_bytes_written = g_output_stream_write_all (
			output_stream, cp->data, strlen (cp->data),
			NULL, cancellable, error);
		g_mutex_unlock (&is->priv->stream_lock);
		if (n_bytes_written < 0)
			return FALSE;

		if (cp->type & (CAMEL_IMAPX_COMMAND_CONTINUATION | CAMEL_IMAPX_COMMAND_LITERAL_PLUS)) {
			newic = ic;
		} else {
			g_warn_if_fail (g_list_next (link) == NULL);
		}
	} else {
		c (is->priv->tagprefix, "%p: queueing continuation\n", ic);
	}

	g_mutex_lock (&is->priv->stream_lock);
	n_bytes_written = g_output_stream_write_all (
		output_stream, "\r\n", 2, NULL, cancellable, error);
	g_mutex_unlock (&is->priv->stream_lock);
	if (n_bytes_written < 0)
		return FALSE;

	is->priv->continuation_command = newic;

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_status (CamelIMAPXServer *is,
                       GInputStream *input_stream,
                       GCancellable *cancellable,
                       GError **error)
{
	CamelIMAPXStatusResponse *response;
	CamelIMAPXStore *imapx_store;
	CamelIMAPXMailbox *mailbox;
	const gchar *mailbox_name;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	response = camel_imapx_status_response_new (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		is->priv->inbox_separator, cancellable, error);
	if (response == NULL)
		return FALSE;

	mailbox_name = camel_imapx_status_response_get_mailbox_name (response);

	imapx_store = camel_imapx_server_ref_store (is);
	mailbox = camel_imapx_store_ref_mailbox (imapx_store, mailbox_name);

	if (mailbox != NULL) {
		camel_imapx_mailbox_handle_status_response (mailbox, response);
		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);
		g_object_unref (mailbox);
	}

	g_clear_object (&imapx_store);
	g_object_unref (response);

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_unset_folder_flagged_flag (CamelFolderSummary *summary,
				 GPtrArray *changed_uids,
				 gboolean except_deleted_messages)
{
	CamelMessageInfo *info;
	gboolean changed = FALSE;
	gint ii;

	g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary));
	g_return_if_fail (changed_uids != NULL);

	for (ii = 0; ii < changed_uids->len; ii++) {
		info = camel_folder_summary_get (summary, changed_uids->pdata[ii]);

		if (info) {
			CamelMessageInfoBase *mi = (CamelMessageInfoBase *) info;

			/* some infos could be only 'dirty' (needed to save into summary) */
			if ((mi->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0 &&
			   (!except_deleted_messages || (mi->flags & CAMEL_MESSAGE_DELETED) == 0)) {
				mi->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
				mi->dirty = TRUE;
				changed = TRUE;
			}

			camel_message_info_unref (info);
		}
	}

	if (changed) {
		camel_folder_summary_touch (summary);
		camel_folder_summary_save_to_db (summary, NULL);
	}
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_vanished (CamelIMAPXServer *is,
                         GInputStream *input_stream,
                         GCancellable *cancellable,
                         GError **error)
{
	CamelFolder *folder;
	CamelIMAPXMailbox *mailbox;
	GArray *uids;
	GList *uid_list = NULL;
	gboolean unsolicited = TRUE;
	guint ii = 0;
	guint len = 0;
	guchar *token = NULL;
	gint tok = 0;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	tok = camel_imapx_input_stream_token (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		&token, &len, cancellable, error);
	if (tok < 0)
		return FALSE;
	if (tok == '(') {
		unsolicited = FALSE;
		while (tok != ')') {
			/* We expect this to be 'EARLIER' */
			tok = camel_imapx_input_stream_token (
				CAMEL_IMAPX_INPUT_STREAM (input_stream),
				&token, &len, cancellable, error);
			if (tok < 0)
				return FALSE;
		}
	} else {
		camel_imapx_input_stream_ungettoken (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			tok, token, len);
	}

	uids = imapx_parse_uids (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
	if (uids == NULL)
		return FALSE;

	mailbox = camel_imapx_server_ref_pending_or_selected (is);

	g_return_val_if_fail (mailbox != NULL, FALSE);

	folder = imapx_server_ref_folder (is, mailbox);
	g_return_val_if_fail (folder != NULL, FALSE);

	if (unsolicited) {
		guint32 messages;

		messages = camel_imapx_mailbox_get_messages (mailbox);

		if (messages < uids->len) {
			c (
				is->priv->tagprefix,
				"Error: mailbox messages (%u) is "
				"fewer than vanished %u\n",
				messages, uids->len);
			messages = 0;
		} else {
			messages -= uids->len;
		}

		camel_imapx_mailbox_set_messages (mailbox, messages);
	}

	g_return_val_if_fail (is->priv->changes != NULL, FALSE);

	g_mutex_lock (&is->priv->changes_lock);

	for (ii = 0; ii < uids->len; ii++) {
		guint32 uid;
		gchar *str;

		uid = g_array_index (uids, guint32, ii);

		e (is->priv->tagprefix, "vanished: %u\n", uid);

		str = g_strdup_printf ("%u", uid);
		uid_list = g_list_prepend (uid_list, str);
		camel_folder_change_info_remove_uid (is->priv->changes, str);
	}

	g_mutex_unlock (&is->priv->changes_lock);

	uid_list = g_list_reverse (uid_list);
	camel_folder_summary_remove_uids (folder->summary, uid_list);

	/* If the response is truly unsolicited (e.g. via NOTIFY)
	 * then go ahead and emit the change notification now. */
	COMMAND_LOCK (is);
	if (!is->priv->current_command) {
		COMMAND_UNLOCK (is);

		g_mutex_lock (&is->priv->changes_lock);
		if (is->priv->changes->uid_removed &&
		    is->priv->changes->uid_removed->len >= 100) {
			CamelFolderChangeInfo *changes;

			changes = is->priv->changes;
			is->priv->changes = camel_folder_change_info_new ();

			g_mutex_unlock (&is->priv->changes_lock);

			camel_folder_summary_save_to_db (folder->summary, NULL);
			imapx_update_store_summary (folder);

			camel_folder_changed (folder, changes);
			camel_folder_change_info_free (changes);
		} else {
			g_mutex_unlock (&is->priv->changes_lock);
		}
	} else {
		COMMAND_UNLOCK (is);
	}

	g_list_free_full (uid_list, (GDestroyNotify) g_free);
	g_array_free (uids, TRUE);

	g_object_unref (folder);
	g_object_unref (mailbox);

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_init (CamelIMAPXServer *is)
{
	is->priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);

	is->priv->untagged_handlers = create_initial_untagged_handler_table ();

	g_mutex_init (&is->priv->stream_lock);
	g_mutex_init (&is->priv->inactivity_timeout_lock);
	g_mutex_init (&is->priv->select_lock);
	g_mutex_init (&is->priv->changes_lock);
	g_mutex_init (&is->priv->search_results_lock);
	g_mutex_init (&is->priv->known_alerts_lock);

	g_weak_ref_init (&is->priv->store, NULL);
	g_weak_ref_init (&is->priv->select_mailbox, NULL);
	g_weak_ref_init (&is->priv->select_pending, NULL);

	is->priv->cancellable = g_cancellable_new ();

	is->priv->state = IMAPX_DISCONNECTED;
	is->priv->is_cyrus = FALSE;
	is->priv->copyuid_status = NULL;

	is->priv->changes = camel_folder_change_info_new ();

	is->priv->known_alerts = g_hash_table_new_full (
		(GHashFunc) g_str_hash,
		(GEqualFunc) g_str_equal,
		(GDestroyNotify) g_free,
		(GDestroyNotify) NULL);

	/* Initialize IDLE members. */
	g_mutex_init (&is->priv->idle_lock);
	g_cond_init (&is->priv->idle_cond);
	is->priv->idle_state = IMAPX_IDLE_STATE_OFF;
	is->priv->idle_stamp = 0;

	g_rec_mutex_init (&is->priv->command_lock);
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_connect_sync (CamelIMAPXServer *is,
				 GCancellable *cancellable,
				 GError **error)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	if (is->priv->state == IMAPX_SHUTDOWN) {
		g_set_error (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_UNAVAILABLE,
			"Shutting down");
		return FALSE;
	}

	if (is->priv->state >= IMAPX_INITIALISED)
		return TRUE;

	is->priv->is_cyrus = FALSE;

	if (!imapx_reconnect (is, cancellable, error))
		return FALSE;

	g_mutex_lock (&is->priv->stream_lock);

	if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, NAMESPACE)) {
		g_mutex_unlock (&is->priv->stream_lock);

		/* This also creates a needed faux NAMESPACE */
		if (!camel_imapx_server_list_sync (is, "INBOX", 0, cancellable, error))
			return FALSE;
	} else {
		g_mutex_unlock (&is->priv->stream_lock);
	}

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
				      const gchar *mechanism,
				      GCancellable *cancellable,
				      GError **error)
{
	CamelNetworkSettings *network_settings;
	CamelIMAPXStore *store;
	CamelService *service;
	CamelSettings *settings;
	CamelAuthenticationResult result;
	CamelIMAPXCommand *ic;
	CamelSasl *sasl = NULL;
	gchar *host;
	gchar *user;

	g_return_val_if_fail (
		CAMEL_IS_IMAPX_SERVER (is),
		CAMEL_AUTHENTICATION_ERROR);

	store = camel_imapx_server_ref_store (is);

	service = CAMEL_SERVICE (store);
	settings = camel_service_ref_settings (service);

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	user = camel_network_settings_dup_user (network_settings);

	g_object_unref (settings);

	if (mechanism != NULL) {
		g_mutex_lock (&is->priv->stream_lock);

		if (is->priv->cinfo && !g_hash_table_lookup (is->priv->cinfo->auth_types, mechanism) && (
		    !g_str_equal (mechanism, "Google") || !g_hash_table_lookup (is->priv->cinfo->auth_types, "XOAUTH2"))) {
			g_mutex_unlock (&is->priv->stream_lock);
			g_set_error (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("IMAP server %s does not support %s "
				"authentication"), host, mechanism);
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		} else {
			g_mutex_unlock (&is->priv->stream_lock);
		}

		sasl = camel_sasl_new ("imap", mechanism, service);
		if (sasl == NULL) {
			g_set_error (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("No support for %s authentication"),
				mechanism);
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		}
	}

	if (sasl != NULL) {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_AUTHENTICATE, "AUTHENTICATE %A", sasl);
	} else {
		const gchar *password;

		password = camel_service_get_password (service);

		if (user == NULL) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("Cannot authenticate without a username"));
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		}

		if (password == NULL) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("Authentication password not available"));
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
		}

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LOGIN, "LOGIN %s %s", user, password);
	}

	if (!camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, error) && (
	    !ic->status || ic->status->result != IMAPX_NO))
		result = CAMEL_AUTHENTICATION_ERROR;
	else if (ic->status->result == IMAPX_OK)
		result = CAMEL_AUTHENTICATION_ACCEPTED;
	else if (ic->status->result == IMAPX_NO) {
		g_clear_error (error);

		if (camel_imapx_store_is_connecting_concurrent_connection (store)) {
			/* At least one connection succeeded, probably max connection limit
			   set on the server had been reached, thus use special error code
			   for it, to instruct the connection manager to decrease the limit
			   and use already created connection. */
			g_set_error_literal (
				error, CAMEL_IMAPX_SERVER_ERROR,
				CAMEL_IMAPX_SERVER_ERROR_CONCURRENT_CONNECT_FAILED,
				ic->status->text ? ic->status->text : _("Unknown error"));
			result = CAMEL_AUTHENTICATION_ERROR;
		} else if (sasl) {
			CamelSaslClass *sasl_class;

			sasl_class = CAMEL_SASL_GET_CLASS (sasl);
			if (sasl_class && sasl_class->auth_type && !sasl_class->auth_type->need_password) {
				g_set_error_literal (
					error, CAMEL_SERVICE_ERROR,
					CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
					ic->status->text ? ic->status->text : _("Unknown error"));
				result = CAMEL_AUTHENTICATION_ERROR;
			} else {
				result = CAMEL_AUTHENTICATION_REJECTED;
			}
		} else {
			result = CAMEL_AUTHENTICATION_REJECTED;
		}
	} else {
		g_set_error_literal (
			error, CAMEL_SERVICE_ERROR,
			CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
			ic->status->text ? ic->status->text : _("Unknown error"));
		result = CAMEL_AUTHENTICATION_ERROR;
	}

	/* Forget old capabilities after login. */
	if (result == CAMEL_AUTHENTICATION_ACCEPTED) {
		g_mutex_lock (&is->priv->stream_lock);

		if (is->priv->cinfo) {
			imapx_free_capability (is->priv->cinfo);
			is->priv->cinfo = NULL;
		}

		if (ic->status->condition == IMAPX_CAPABILITY) {
			is->priv->cinfo = ic->status->u.cinfo;
			ic->status->u.cinfo = NULL;
			c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo ? is->priv->cinfo->capa : 0xFFFFFFFF);
			imapx_server_stash_command_arguments (is);
		}

		g_mutex_unlock (&is->priv->stream_lock);
	}

	camel_imapx_command_unref (ic);

	if (sasl != NULL)
		g_object_unref (sasl);

exit:
	g_free (host);
	g_free (user);

	g_object_unref (store);

	return result;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_class_init (CamelIMAPXServerClass *class)
{
	GObjectClass *object_class;

	g_type_class_add_private (class, sizeof (CamelIMAPXServerPrivate));

	object_class = G_OBJECT_CLASS (class);
	object_class->set_property = imapx_server_set_property;
	object_class->get_property = imapx_server_get_property;
	object_class->finalize = imapx_server_finalize;
	object_class->dispose = imapx_server_dispose;
	object_class->constructed = imapx_server_constructed;

	g_object_class_install_property (
		object_class,
		PROP_STORE,
		g_param_spec_object (
			"store",
			"Store",
			"IMAPX store for this server",
			CAMEL_TYPE_IMAPX_STORE,
			G_PARAM_READWRITE |
			G_PARAM_CONSTRUCT_ONLY |
			G_PARAM_STATIC_STRINGS));

	signals[REFRESH_MAILBOX] = g_signal_new (
		"refresh-mailbox",
		G_OBJECT_CLASS_TYPE (class),
		G_SIGNAL_RUN_LAST,
		G_STRUCT_OFFSET (CamelIMAPXServerClass, refresh_mailbox),
		NULL, NULL, NULL,
		G_TYPE_NONE, 1,
		CAMEL_TYPE_IMAPX_MAILBOX);
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_untagged_lsub (CamelIMAPXServer *is,
                     GInputStream *input_stream,
                     GCancellable *cancellable,
                     GError **error)
{
	CamelIMAPXListResponse *response;
	CamelIMAPXStore *imapx_store;
	const gchar *mailbox_name;
	gchar separator;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	/* LSUB response is syntactically compatible with LIST response. */
	response = camel_imapx_list_response_new (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
	if (response == NULL)
		return FALSE;

	camel_imapx_list_response_add_attribute (
		response, CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED);

	mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
	separator = camel_imapx_list_response_get_separator (response);

	/* Record the INBOX separator character once we know it. */
	if (camel_imapx_mailbox_is_inbox (mailbox_name))
		is->priv->inbox_separator = separator;

	imapx_store = camel_imapx_server_ref_store (is);
	camel_imapx_store_handle_lsub_response (imapx_store, is, response);

	g_clear_object (&imapx_store);
	g_clear_object (&response);

	return TRUE;
}

Project: evolution-data-server
File: camel-imapx-server.c

camel_imapx_server_append_message_sync (CamelIMAPXServer *is,
					CamelIMAPXMailbox *mailbox,
					CamelFolderSummary *summary,
					CamelDataCache *message_cache,
					CamelMimeMessage *message,
					const CamelMessageInfo *mi,
					gchar **appended_uid,
					GCancellable *cancellable,
					GError **error)
{
	gchar *uid = NULL, *path = NULL;
	CamelMimeFilter *filter;
	CamelIMAPXCommand *ic;
	CamelMessageInfo *info;
	GIOStream *base_stream;
	GOutputStream *output_stream;
	GOutputStream *filter_stream;
	gint res;
	time_t date_time;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);
	g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE);
	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
	/* CamelMessageInfo can be NULL. */

	/* That's okay if the "SELECT" fails here, as it can be due to
	   the folder being write-only; just ignore the error and continue. */
	camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, NULL);

	if (g_cancellable_set_error_if_cancelled (cancellable, error))
		return FALSE;

	/* Append just assumes we have no/a dodgy connection.  We dump
	 * stuff into the 'new' directory, and let the summary know it's
	 * there.  Then we fire off a no-reply job which will asynchronously
	 * upload the message at some point in the future, and fix up the
	 * summary to match */

	/* chen cleanup this later */
	uid = imapx_get_temp_uid ();
	base_stream = camel_data_cache_add (message_cache, "new", uid, error);
	if (base_stream == NULL) {
		g_prefix_error (error, _("Cannot create spool file: "));
		g_free (uid);
		return FALSE;
	}

	output_stream = g_io_stream_get_output_stream (base_stream);
	filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF);
	filter_stream = camel_filter_output_stream_new (output_stream, filter);

	g_filter_output_stream_set_close_base_stream (
		G_FILTER_OUTPUT_STREAM (filter_stream), FALSE);

	res = camel_data_wrapper_write_to_output_stream_sync (
		CAMEL_DATA_WRAPPER (message),
		filter_stream, cancellable, error);

	g_object_unref (base_stream);
	g_object_unref (filter_stream);
	g_object_unref (filter);

	if (res == -1) {
		g_prefix_error (error, _("Cannot create spool file: "));
		camel_data_cache_remove (message_cache, "new", uid, NULL);
		g_free (uid);
		return FALSE;
	}

	date_time = camel_mime_message_get_date (message, NULL);
	path = camel_data_cache_get_filename (message_cache, "new", uid);
	info = camel_folder_summary_info_new_from_message (summary, message, NULL);
	info->uid = camel_pstring_strdup (uid);

	if (mi != NULL) {
		struct icaltimetype icaltime;
		CamelMessageInfoBase *base_info = (CamelMessageInfoBase *) info;
		const CamelFlag *flag;
		const CamelTag *tag;

		base_info->flags = camel_message_info_flags (mi);
		base_info->size = camel_message_info_size (mi);

		flag = camel_message_info_user_flags (mi);
		while (flag != NULL) {
			if (*flag->name != '\0')
				camel_flag_set (
					&base_info->user_flags,
					flag->name, TRUE);
			flag = flag->next;
		}

		tag = camel_message_info_user_tags (mi);
		while (tag != NULL) {
			if (*tag->name != '\0')
				camel_tag_set (
					&base_info->user_tags,
					tag->name, tag->value);
			tag = tag->next;
		}

		if (date_time > 0) {
			icaltime = icaltime_from_timet (date_time, FALSE);
			if (!icaltime_is_valid_time (icaltime))
				date_time = -1;
		}

		if (date_time <= 0)
			date_time = camel_message_info_date_received (mi);

		if (date_time > 0) {
			icaltime = icaltime_from_timet (date_time, FALSE);
			if (!icaltime_is_valid_time (icaltime))
				date_time = -1;
		}
	}

	g_free (uid);

	if (camel_mime_message_has_attachment (message))
		((CamelMessageInfoBase *) info)->flags |= CAMEL_MESSAGE_ATTACHMENTS;

	if (date_time > 0) {
		gchar *date_time_str;
		struct tm stm;

		gmtime_r (&date_time, &stm);

		/* Store always in UTC */
		date_time_str = g_strdup_printf (
			"\"%02d-%s-%04d %02d:%02d:%02d +0000\"",
			stm.tm_mday,
			get_month_str (stm.tm_mon + 1),
			stm.tm_year + 1900,
			stm.tm_hour,
			stm.tm_min,
			stm.tm_sec);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_APPEND_MESSAGE, "APPEND %M %F %t %P",
			mailbox,
			((CamelMessageInfoBase *) info)->flags,
			((CamelMessageInfoBase *) info)->user_flags,
			date_time_str,
			path);

		g_free (date_time_str);
	} else {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_APPEND_MESSAGE, "APPEND %M %F %P",
			mailbox,
			((CamelMessageInfoBase *) info)->flags,
			((CamelMessageInfoBase *) info)->user_flags,
			path);
	}

	success = camel_imapx_server_process_command_sync (is, ic, _("Error appending message"), cancellable, error);

	if (success) {
		CamelIMAPXFolder *imapx_folder;
		CamelFolder *folder;
		CamelMessageInfo *mi;
		gchar *cur, *old_uid;
		guint32 uidvalidity;

		folder = imapx_server_ref_folder (is, mailbox);
		g_return_val_if_fail (folder != NULL, FALSE);

		uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);

		imapx_folder = CAMEL_IMAPX_FOLDER (folder);

		/* Append done.  If we the server supports UIDPLUS we will get
		 * an APPENDUID response with the new uid.  This lets us move the
		 * message we have directly to the cache and also create a correctly
		 * numbered MessageInfo, without losing any information.  Otherwise
		 * we have to wait for the server to let us know it was appended. */

		mi = camel_message_info_clone (info);
		old_uid = g_strdup (info->uid);

		if (ic->status && ic->status->condition == IMAPX_APPENDUID) {
			c (is->priv->tagprefix, "Got appenduid %d %d\n", (gint) ic->status->u.appenduid.uidvalidity, (gint) ic->status->u.appenduid.uid);
			if (ic->status->u.appenduid.uidvalidity == uidvalidity) {
				if (appended_uid)
					*appended_uid = g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid);
				mi->uid = camel_pstring_add (g_strdup_printf ("%u", (guint) ic->status->u.appenduid.uid), TRUE);

				cur = camel_data_cache_get_filename  (imapx_folder->cache, "cur", mi->uid);
				if (g_rename (path, cur) == -1 && errno != ENOENT) {
					g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, path, cur, g_strerror (errno));
				}

				imapx_set_message_info_flags_for_new_message (
					mi,
					((CamelMessageInfoBase *) info)->flags,
					((CamelMessageInfoBase *) info)->user_flags,
					TRUE,
					((CamelMessageInfoBase *) info)->user_tags,
					camel_imapx_mailbox_get_permanentflags (mailbox));

				camel_folder_summary_add (folder->summary, mi);

				g_mutex_lock (&is->priv->changes_lock);
				camel_folder_change_info_add_uid (is->priv->changes, mi->uid);
				g_mutex_unlock (&is->priv->changes_lock);

				mi = NULL;

				g_free (cur);
			} else {
				c (is->priv->tagprefix, "but uidvalidity changed \n");
			}
		}

		camel_data_cache_remove (imapx_folder->cache, "new", old_uid, NULL);
		g_free (old_uid);

		camel_imapx_command_unref (ic);
		if (mi)
			camel_message_info_unref (mi);
		g_object_unref (folder);
	}

	g_free (path);

	return success;
}

Project: evolution-data-server
File: camel-imapx-server.c

imapx_server_write_file_with_progress (GOutputStream *output_stream,
				       GInputStream *input_stream,
				       goffset file_size,
				       GCancellable *cancellable,
				       GError **error)
{
	gssize n_read;
	gsize bytes_copied, n_written;
	gchar buffer[8192];
	goffset file_offset;
	gboolean res;

	g_return_val_if_fail (G_IS_OUTPUT_STREAM (output_stream), -1);
	g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), -1);

	if (g_cancellable_set_error_if_cancelled (cancellable, error))
		return FALSE;

	file_offset = 0;
	bytes_copied = 0;
	res = TRUE;
	do {
		n_read = g_input_stream_read (input_stream, buffer, sizeof (buffer), cancellable, error);
		if (n_read == -1) {
			res = FALSE;
			break;
		}

		if (n_read == 0)
			break;

		if (!g_output_stream_write_all (output_stream, buffer, n_read, &n_written, cancellable, error) || n_written == -1) {
			res = FALSE;
			break;
		}

		file_offset += n_read;

		if (file_size > 0) {
			gdouble divd = (gdouble) file_offset / (gdouble) file_size;
			camel_operation_progress (cancellable, (gint) (100 * divd));
		}

		bytes_copied += n_written;
		if (bytes_copied > G_MAXSSIZE)
			bytes_copied = G_MAXSSIZE;
	} while (res);

	if (res)
		return bytes_copied;

	return -1;
}