Commit message: I#189 - Crash on malformed server response with minimal capabilities

Closes https://gitlab.gnome.org/GNOME/evolution-data-server/issues/189
CVE: CVE-2020-16117
CWE: CWE-476
Vulnerability description: In GNOME evolution-data-server before 3.35.91, a malicious server can crash the mail client with a NULL pointer dereference by sending an invalid (e.g., minimal) CAPABILITY line on a connection attempt. This is related to imapx_free_capability and imapx_connect_to_server.

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

imapx_server_child_process_setup (gpointer user_data)
{
#ifdef TIOCNOTTY
	gint fd;
#endif

	setsid ();

#ifdef TIOCNOTTY
	/* Detach from the controlling tty if we have one.  Otherwise,
	 * SSH might do something stupid like trying to use it instead
	 * of running $SSH_ASKPASS. */
	if ((fd = open ("/dev/tty", O_RDONLY)) != -1) {
		ioctl (fd, TIOCNOTTY, NULL);
		close (fd);
	}
#endif /* TIOCNOTTY */
}

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

imapx_server_set_connection_timeout (GIOStream *connection,
				     gint timeout_seconds)
{
	GSocket *socket;
	gint previous_timeout = -1;

	if (G_IS_TLS_CONNECTION (connection)) {
		GIOStream *base_io_stream = NULL;

		g_object_get (G_OBJECT (connection), "base-io-stream", &base_io_stream, NULL);

		connection = base_io_stream;
	} else if (connection) {
		/* Connection can be NULL, when a custom command (GSubProcess) is used instead */
		g_object_ref (connection);
	}

	if (!G_IS_SOCKET_CONNECTION (connection)) {
		g_clear_object (&connection);
		return previous_timeout;
	}

	socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (connection));
	if (socket) {
		previous_timeout = g_socket_get_timeout (socket);
		g_socket_set_timeout (socket, timeout_seconds);
	}

	g_clear_object (&connection);

	return previous_timeout;
}

Project: evolution-data-server

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));

	/* The camel_imapx_server_refresh_info_sync() gets any piled change
	   notifications and will emit the signal with all of them at once.
	   Similarly message COPY/MOVE command. */
	if (!is->priv->fetch_changes_mailbox && !is->priv->fetch_changes_folder && !is->priv->fetch_changes_infos &&
	    ic->job_kind != CAMEL_IMAPX_JOB_COPY_MESSAGE && ic->job_kind != CAMEL_IMAPX_JOB_MOVE_MESSAGE) {
		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_pending_or_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 (camel_folder_get_folder_summary (folder), 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, FALSE, 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_broken_cyrus = is->priv->is_broken_cyrus || (ic->status->text && imapx_server_check_is_broken_cyrus (ic->status->text, &is->priv->is_cyrus));
		if (is->priv->is_broken_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

camel_imapx_server_init (CamelIMAPXServer *is)
{
	is->priv = camel_imapx_server_get_instance_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->is_broken_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_ref_idle_mailbox (CamelIMAPXServer *is)
{
	CamelIMAPXMailbox *mailbox = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

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

	if (is->priv->idle_state != IMAPX_IDLE_STATE_OFF) {
		if (is->priv->idle_mailbox)
			mailbox = g_object_ref (is->priv->idle_mailbox);
		else
			mailbox = camel_imapx_server_ref_selected (is);
	}

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

	return mailbox;
}

Project: evolution-data-server

camel_imapx_server_skip_old_flags_update (CamelStore *store)
{
	CamelSession *session;
	CamelSettings *settings;
	GNetworkMonitor *network_monitor;
	gboolean skip_old_flags_update = FALSE;

	if (!CAMEL_IS_STORE (store))
		return FALSE;

	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
	if (settings) {
		gboolean allow_update;

		allow_update = camel_imapx_settings_get_full_update_on_metered_network (CAMEL_IMAPX_SETTINGS (settings));

		g_object_unref (settings);

		if (allow_update)
			return FALSE;
	}

	session = camel_service_ref_session (CAMEL_SERVICE (store));
	if (!session)
		return skip_old_flags_update;

	network_monitor = camel_session_ref_network_monitor (session);

	skip_old_flags_update = network_monitor && g_network_monitor_get_network_metered (network_monitor);

	g_clear_object (&network_monitor);
	g_clear_object (&session);

	return skip_old_flags_update;
}

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

get_month_str (gint month)
{
	static const gchar tm_months[][4] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};

	if (month < 1 || month > 12)
		return NULL;

	return tm_months[month - 1];
}

Project: evolution-data-server

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_slice_free (CamelIMAPXServerUntaggedContext, 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);
	g_clear_object (&is->priv->cancellable);

	/* 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_server_inactivity_thread (gpointer user_data)
{
	CamelIMAPXServer *is = user_data;
	GError *local_error = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

	if (camel_imapx_server_is_in_idle (is)) {
		/* Stop and restart the IDLE command. */
		if (!camel_imapx_server_schedule_idle_sync (is, NULL, is->priv->cancellable, &local_error) &&
		    !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
			camel_imapx_debug (io, camel_imapx_server_get_tagprefix (is),
				"%s: Failed to restart IDLE: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
	} else {
		if (!camel_imapx_server_noop_sync (is, NULL, is->priv->cancellable, &local_error) &&
		    !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
			camel_imapx_debug (io, camel_imapx_server_get_tagprefix (is),
				"%s: Failed to issue NOOP: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
	}

	g_clear_error (&local_error);
	g_object_unref (is);

	return NULL;
}

Project: evolution-data-server

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;
	CamelFolderChangeInfo *changes = NULL;
	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);

	if (camel_imapx_mailbox_get_permanentflags (destination) == ~0) {
		/* 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. */
		if (!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 || (camel_folder_get_flags (folder) & CAMEL_FOLDER_IS_TRASH) != 0;
	remove_junk_flags = (camel_folder_get_flags (folder) & 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, g_object_unref);
	data_uids = g_ptr_array_new ();

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

		g_ptr_array_add (data_uids, uid);

		source_info = camel_folder_summary_get (camel_folder_get_folder_summary (folder), uid);
		if (source_info)
			g_hash_table_insert (source_infos, uid, source_info);
	}

	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;
		}

		g_warn_if_fail (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);

		g_mutex_lock (&is->priv->changes_lock);
		if (camel_folder_change_info_changed (is->priv->changes)) {
			if (!changes) {
				changes = is->priv->changes;
			} else {
				camel_folder_change_info_cat (changes, is->priv->changes);
				camel_folder_change_info_free (is->priv->changes);
			}

			is->priv->changes = camel_folder_change_info_new ();
		}
		g_mutex_unlock (&is->priv->changes_lock);

		if (ic->copy_move_expunged) {
			CamelFolderSummary *summary;

			ic->copy_move_expunged = g_slist_reverse (ic->copy_move_expunged);

			summary = camel_folder_get_folder_summary (folder);
			if (summary) {
				GPtrArray *array;

				array = camel_folder_summary_get_array (summary);
				if (array) {
					GSList *slink;
					GList *removed_uids = NULL, *llink;

					camel_folder_sort_uids (folder, array);

					for (slink = ic->copy_move_expunged; slink; slink = g_slist_next (slink)) {
						guint expunged_idx = GPOINTER_TO_UINT (slink->data) - 1;

						if (expunged_idx < array->len) {
							const gchar *uid = g_ptr_array_index (array, expunged_idx);

							if (uid) {
								removed_uids = g_list_prepend (removed_uids, (gpointer) uid);
								g_ptr_array_remove_index (array, expunged_idx);
							}
						}
					}

					if (removed_uids) {
						CamelFolderSummary *summary;

						summary = camel_folder_get_folder_summary (folder);

						camel_folder_summary_remove_uids (summary, removed_uids);

						for (llink = removed_uids; llink; llink = g_list_next (llink)) {
							const gchar *uid = llink->data;

							if (!changes)
								changes = camel_folder_change_info_new ();

							camel_folder_change_info_remove_uid (changes, uid);

							g_ptr_array_add (array, (gpointer) uid);
						}

						g_list_free (removed_uids);
					}

					camel_folder_summary_free_array (array);
				}
			}
		}

		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) {
					CamelFolderSummary *destination_summary;
					CamelMessageInfo *source_info, *destination_info;
					CamelFolderChangeInfo *dest_changes;
					gint ii;

					destination_summary = camel_folder_get_folder_summary (destination_folder);
					camel_folder_summary_lock (destination_summary);

					dest_changes = camel_folder_change_info_new ();

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

						uid = g_strdup_printf ("%u", 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 ("%u", g_array_index (copyuid_status->u.copyuid.copied_uids, guint32, ii));
						destination_info = camel_folder_summary_get (destination_summary, uid);

						if (!destination_info) {
							is_new = TRUE;
							destination_info = camel_message_info_clone (source_info, destination_summary);
							camel_message_info_set_uid (destination_info, uid);
						}

						g_free (uid);

						source_flags = camel_message_info_get_flags (source_info);
						source_user_flags = camel_message_info_dup_user_flags (source_info);
						source_user_tags = camel_message_info_dup_user_tags (source_info);

						imapx_set_message_info_flags_for_new_message (
							destination_info,
							source_flags,
							source_user_flags,
							TRUE,
							source_user_tags,
							camel_imapx_mailbox_get_permanentflags (destination));

						camel_named_flags_free (source_user_flags);
						camel_name_value_array_free (source_user_tags);

						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);
						imapx_copy_move_message_cache (folder, destination_folder, delete_originals || use_move_command,
							camel_message_info_get_uid (source_info),
							camel_message_info_get_uid (destination_info),
							cancellable);
						if (is_new)
							camel_folder_summary_add (destination_summary, destination_info, FALSE);
						camel_folder_change_info_add_uid (dest_changes, camel_message_info_get_uid (destination_info));

						g_clear_object (&destination_info);
					}

					if (camel_folder_change_info_changed (dest_changes)) {
						camel_folder_summary_touch (destination_summary);
						camel_folder_summary_save (destination_summary, NULL);
						imapx_update_store_summary (destination_folder);
						camel_folder_changed (destination_folder, dest_changes);
					}

					camel_folder_summary_unlock (destination_summary);
					camel_folder_change_info_free (dest_changes);
					g_object_unref (destination_folder);
				}
			}

			if (delete_originals || use_move_command) {
				gint jj;

				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 (camel_folder_get_folder_summary (folder), uid)) {
							if (!changes)
								changes = camel_folder_change_info_new ();

							camel_folder_change_info_remove_uid (changes, uid);
						}
					}
				}
			}
		}

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

		camel_imapx_command_unref (ic);

		camel_operation_progress (cancellable, ii * 100 / data_uids->len);
	}

	if (changes) {
		if (camel_folder_change_info_changed (changes)) {
			camel_folder_summary_touch (camel_folder_get_folder_summary (folder));
			camel_folder_summary_save (camel_folder_get_folder_summary (folder), NULL);

			imapx_update_store_summary (folder);

			camel_folder_changed (folder, changes);
		}

		camel_folder_change_info_free (changes);
	}

	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

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;
			CamelFolderSummary *folder_summary;
			const gchar *full_name;

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

			camel_folder_summary_lock (folder_summary);

			camel_folder_summary_save (folder_summary, NULL);
			uids = camel_db_get_folder_deleted_uids (camel_store_get_db (parent_store), 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 (folder_summary, NULL);
				imapx_update_store_summary (folder);
				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_uids_array_cmp (gconstpointer ap,
                      gconstpointer bp)
{
	const gchar **a = (const gchar **) ap;
	const gchar **b = (const gchar **) bp;

	return imapx_refresh_info_uid_cmp (*a, *b, TRUE);
}

Project: evolution-data-server

imapx_untagged_expunge (CamelIMAPXServer *is,
                        GInputStream *input_stream,
                        GCancellable *cancellable,
                        GError **error)
{
	gulong expunged_idx;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	expunged_idx = 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)) {
		gboolean ignored = TRUE;
		gboolean is_idle_command = is->priv->current_command && is->priv->current_command->job_kind == CAMEL_IMAPX_JOB_IDLE;

		COMMAND_UNLOCK (is);

		/* Process only untagged EXPUNGE responses within ongoing IDLE command */
		if (is_idle_command) {
			CamelIMAPXMailbox *mailbox;

			mailbox = camel_imapx_server_ref_selected (is);
			if (mailbox) {
				guint32 messages;

				messages = camel_imapx_mailbox_get_messages (mailbox);
				if (messages > 0) {
					camel_imapx_mailbox_set_messages (mailbox, messages - 1);

					ignored = FALSE;
					c (is->priv->tagprefix, "going to refresh mailbox '%s' due to untagged expunge: %lu\n", camel_imapx_mailbox_get_name (mailbox), expunged_idx);

					g_signal_emit (is, signals[REFRESH_MAILBOX], 0, mailbox);
				}

				g_object_unref (mailbox);
			}
		}

		if (ignored)
			c (is->priv->tagprefix, "ignoring untagged expunge: %lu\n", expunged_idx);

		return TRUE;
	}

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

	is->priv->current_command->copy_move_expunged = g_slist_prepend (
		is->priv->current_command->copy_move_expunged, GUINT_TO_POINTER (expunged_idx));

	COMMAND_UNLOCK (is);

	return TRUE;
}

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

camel_imapx_server_noop_sync (CamelIMAPXServer *is,
			      CamelIMAPXMailbox *mailbox,
			      GCancellable *cancellable,
			      GError **error)
{
	gboolean success = TRUE;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	/* Mailbox may be NULL. */

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

	if (success) {
		CamelIMAPXCommand *ic;

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOOP, "NOOP");

		success = camel_imapx_server_process_command_sync (is, ic, _("Error performing NOOP"), cancellable, error);

		camel_imapx_command_unref (ic);
	}

	return success;
}

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

imapx_server_wait_idle_stop_cancelled_cb (GCancellable *cancellable,
					  gpointer user_data)
{
	CamelIMAPXServer *is = user_data;

	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));

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

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

imapx_server_get_property (GObject *object,
                           guint property_id,
                           GValue *value,
                           GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_STORE:
			g_value_take_object (
				value,
				camel_imapx_server_ref_store (
				CAMEL_IMAPX_SERVER (object)));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

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

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

	return imapx_connect_to_server (is, cancellable, error);
}

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

imapx_server_reset_inactivity_timer (CamelIMAPXServer *is)
{
	g_mutex_lock (&is->priv->inactivity_timeout_lock);

	if (is->priv->inactivity_timeout != NULL) {
		g_source_destroy (is->priv->inactivity_timeout);
		g_source_unref (is->priv->inactivity_timeout);
	}

	is->priv->inactivity_timeout =
		g_timeout_source_new_seconds (INACTIVITY_TIMEOUT_SECONDS);
	g_source_set_callback (
		is->priv->inactivity_timeout,
		imapx_server_inactivity_timeout_cb,
		imapx_weak_ref_new (is),
		(GDestroyNotify) imapx_weak_ref_free);
	g_source_attach (is->priv->inactivity_timeout, NULL);

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

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

camel_imapx_server_ref_input_stream (CamelIMAPXServer *is)
{
	GInputStream *input_stream = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

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

	if (is->priv->input_stream != NULL)
		input_stream = g_object_ref (is->priv->input_stream);

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

	return input_stream;
}

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

camel_imapx_server_ref_current_command (CamelIMAPXServer *is)
{
	CamelIMAPXCommand *command;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

	COMMAND_LOCK (is);

	command = is->priv->current_command;
	if (command)
		camel_imapx_command_ref (command);

	COMMAND_UNLOCK (is);

	return command;
}

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

imapx_untagged_flags (CamelIMAPXServer *is,
                      GInputStream *input_stream,
                      GCancellable *cancellable,
                      GError **error)
{
	guint32 flags = 0;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	success = imapx_parse_flags (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		&flags, NULL, cancellable, error);

	c (is->priv->tagprefix, "flags: %08x\n", flags);

	return success;
}

Project: evolution-data-server

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, "%u", 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, "%u:%u", 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_untagged_recent (CamelIMAPXServer *is,
                       GInputStream *input_stream,
                       GCancellable *cancellable,
                       GError **error)
{
	CamelIMAPXMailbox *mailbox;
	guint32 recent;

	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;
	}

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

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

	camel_imapx_mailbox_set_recent (mailbox, recent);

	g_object_unref (mailbox);

	return TRUE;
}

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

imapx_untagged_quota (CamelIMAPXServer *is,
                      GInputStream *input_stream,
                      GCancellable *cancellable,
                      GError **error)
{
	gchar *quota_root_name = NULL;
	CamelFolderQuotaInfo *quota_info = NULL;
	gboolean success;

	success = camel_imapx_parse_quota (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		cancellable, &quota_root_name, &quota_info, error);

	/* Sanity check */
	g_return_val_if_fail (
		(success && (quota_root_name != NULL)) ||
		(!success && (quota_root_name == NULL)), FALSE);

	if (success) {
		CamelIMAPXStore *store;

		store = camel_imapx_server_ref_store (is);
		camel_imapx_store_set_quota_info (
			store, quota_root_name, quota_info);
		g_object_unref (store);

		g_free (quota_root_name);
		camel_folder_quota_info_free (quota_info);
	}

	return success;
}

Project: evolution-data-server

imapx_gather_existing_uids_cb (guint32 uid,
			       gpointer user_data)
{
	GatherExistingUidsData *geud = user_data;
	gchar *uid_str;

	g_return_val_if_fail (geud != NULL, FALSE);
	g_return_val_if_fail (geud->is != NULL, FALSE);
	g_return_val_if_fail (geud->summary != NULL, FALSE);

	geud->n_uids++;

	uid_str = g_strdup_printf ("%u", uid);

	if (camel_folder_summary_check_uid (geud->summary, uid_str)) {
		e (geud->is->priv->tagprefix, "vanished known UID: %u\n", uid);
		if (!geud->uid_list)
			g_mutex_lock (&geud->is->priv->changes_lock);

		geud->uid_list = g_list_prepend (geud->uid_list, uid_str);
		camel_folder_change_info_remove_uid (geud->is->priv->changes, uid_str);
	} else {
		e (geud->is->priv->tagprefix, "vanished unknown UID: %u\n", uid);
		g_free (uid_str);
	}

	return TRUE;
}

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

imapx_untagged_search (CamelIMAPXServer *is,
                       GInputStream *input_stream,
                       GCancellable *cancellable,
                       GError **error)
{
	GArray *search_results;
	gint tok;
	guint len;
	guchar *token;
	guint64 number;
	gboolean success = FALSE;

	search_results = g_array_new (FALSE, FALSE, sizeof (guint64));

	while (TRUE) {
		gboolean success;

		/* Peek at the next token, and break
		 * out of the loop if we get a newline. */
		tok = camel_imapx_input_stream_token (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			&token, &len, cancellable, error);
		if (tok == '\n')
			break;
		if (tok == IMAPX_TOK_ERROR)
			goto exit;
		camel_imapx_input_stream_ungettoken (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			tok, token, len);

		success = camel_imapx_input_stream_number (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			&number, cancellable, error);

		if (!success)
			goto exit;

		g_array_append_val (search_results, number);
	}

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

	if (is->priv->search_results == NULL)
		is->priv->search_results = g_array_ref (search_results);
	else
		g_warning ("%s: Conflicting search results", G_STRFUNC);

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

	success = TRUE;

exit:
	g_array_unref (search_results);

	return success;
}

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

camel_imapx_server_disconnect_sync (CamelIMAPXServer *is,
				    GCancellable *cancellable,
				    GError **error)
{
	GCancellable *idle_cancellable;
	gboolean success = TRUE;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	g_mutex_lock (&is->priv->idle_lock);
	idle_cancellable = is->priv->idle_cancellable;
	if (idle_cancellable)
		g_object_ref (idle_cancellable);
	g_mutex_unlock (&is->priv->idle_lock);

	if (idle_cancellable)
		g_cancellable_cancel (idle_cancellable);
	g_clear_object (&idle_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_mutex_unlock (&is->priv->stream_lock);

	/* Ignore errors here. */
	camel_imapx_server_stop_idle_sync (is, cancellable, NULL);

	g_mutex_lock (&is->priv->stream_lock);
	if (is->priv->connection)
		success = g_io_stream_close (is->priv->connection, cancellable, error);
	g_mutex_unlock (&is->priv->stream_lock);

	imapx_disconnect (is);

	return success;
}

Project: evolution-data-server

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_broken_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) || CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, X_GM_EXT_1))
			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_weak_ref_new (gpointer object)
{
	GWeakRef *weak_ref;

	/* XXX Might want to expose this in Camel's public API if it
	 *     proves useful elsewhere.  Based on e_weak_ref_new(). */

	weak_ref = g_slice_new0 (GWeakRef);
	g_weak_ref_init (weak_ref, object);

	return weak_ref;
}

Project: evolution-data-server

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;
	if (!link) {
		g_warn_if_fail (link != NULL);
		return 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_splice_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 (ic->job_kind == CAMEL_IMAPX_JOB_APPEND_MESSAGE && !cp->ends_with_crlf) {
		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;
	}

	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_server_ref_folder (CamelIMAPXServer *is,
                         CamelIMAPXMailbox *mailbox)
{
	CamelFolder *folder;
	CamelIMAPXStore *store;
	gchar *folder_path;
	GError *local_error = NULL;

	store = camel_imapx_server_ref_store (is);

	folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);

	folder = camel_store_get_folder_sync (
		CAMEL_STORE (store), folder_path, 0, NULL, &local_error);

	g_free (folder_path);

	g_object_unref (store);

	/* Sanity check. */
	g_warn_if_fail (
		((folder != NULL) && (local_error == NULL)) ||
		((folder == NULL) && (local_error != NULL)));

	if (local_error != NULL) {
		g_warning (
			"%s: Failed to get folder for '%s': %s",
			G_STRFUNC, camel_imapx_mailbox_get_name (mailbox), local_error->message);
		g_error_free (local_error);
	}

	return folder;
}

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

replace_untagged_descriptor (GHashTable *untagged_handlers,
                             const gchar *key,
                             const CamelIMAPXUntaggedRespHandlerDesc *descr)
{
	const CamelIMAPXUntaggedRespHandlerDesc *prev = NULL;

	g_return_val_if_fail (untagged_handlers != NULL, NULL);
	g_return_val_if_fail (key != NULL, NULL);
	/* descr may be NULL (to delete a handler) */

	prev = g_hash_table_lookup (untagged_handlers, key);
	g_hash_table_replace (
		untagged_handlers,
		g_strdup (key),
		(gpointer) descr);
	return prev;
}

Project: evolution-data-server

idle_thread_data_free (gpointer ptr)
{
	IdleThreadData *itd = ptr;

	if (itd) {
		g_clear_object (&itd->is);
		g_clear_object (&itd->idle_cancellable);
		g_slice_free (IdleThreadData, itd);
	}
}

Project: evolution-data-server

camel_imapx_server_stop_idle_sync (CamelIMAPXServer *is,
				   GCancellable *cancellable,
				   GError **error)
{
	GCancellable *idle_cancellable;
	gulong handler_id = 0;
	gint64 wait_end_time;
	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) {
		g_mutex_unlock (&is->priv->idle_lock);

		/* Do not hold the idle_lock here, because the callback can be called
		   immediately, which leads to a deadlock inside it. */
		handler_id = g_cancellable_connect (cancellable, G_CALLBACK (imapx_server_wait_idle_stop_cancelled_cb), is, NULL);

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

	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);
	}

	/* Give server 10 seconds to process the DONE command, if it fails, then give up and reconnect */
	wait_end_time = g_get_monotonic_time () + 10 * G_TIME_SPAN_SECOND;

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

	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

camel_imapx_server_new (CamelIMAPXStore *store)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);

	return g_object_new (
		CAMEL_TYPE_IMAPX_SERVER,
		"store", store, NULL);
}

Project: evolution-data-server

camel_imapx_server_class_init (CamelIMAPXServerClass *class)
{
	GObjectClass *object_class;

	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

camel_imapx_server_set_tagprefix (CamelIMAPXServer *is,
				  gchar tagprefix)
{
	g_return_if_fail (CAMEL_IS_IMAPX_SERVER (is));
	g_return_if_fail ((tagprefix >= 'A' && tagprefix <= 'Z') || (tagprefix >= 'a' && tagprefix <= 'z'));

	is->priv->tagprefix = tagprefix;
}

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

imapx_server_set_streams (CamelIMAPXServer *is,
                          GInputStream *input_stream,
                          GOutputStream *output_stream)
{
	GConverter *logger;

	if (input_stream != NULL) {
		GInputStream *temp_stream;

		/* The logger produces debugging output. */
		logger = camel_imapx_logger_new (is->priv->tagprefix);
		input_stream = g_converter_input_stream_new (
			input_stream, logger);
		g_clear_object (&logger);

		/* Buffer the input stream for parsing. */
		temp_stream = camel_imapx_input_stream_new (input_stream);
		camel_binding_bind_property (
			temp_stream, "close-base-stream",
			input_stream, "close-base-stream",
			G_BINDING_SYNC_CREATE);
		g_object_unref (input_stream);
		input_stream = temp_stream;
	}

	if (output_stream != NULL) {
		/* The logger produces debugging output. */
		logger = camel_imapx_logger_new (is->priv->tagprefix);
		output_stream = g_converter_output_stream_new (
			output_stream, logger);
		g_clear_object (&logger);
	}

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

	/* Don't close the base streams so STARTTLS works correctly. */

	if (G_IS_FILTER_INPUT_STREAM (is->priv->input_stream)) {
		g_filter_input_stream_set_close_base_stream (
			G_FILTER_INPUT_STREAM (is->priv->input_stream),
			FALSE);
	}

	if (G_IS_FILTER_OUTPUT_STREAM (is->priv->output_stream)) {
		g_filter_output_stream_set_close_base_stream (
			G_FILTER_OUTPUT_STREAM (is->priv->output_stream),
			FALSE);
	}

	g_clear_object (&is->priv->input_stream);
	is->priv->input_stream = input_stream;

	g_clear_object (&is->priv->output_stream);
	is->priv->output_stream = output_stream;

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

Project: evolution-data-server

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;
	gboolean can_retry_login = FALSE;
	gboolean success;

	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) && (
		    !camel_sasl_is_xoauth2_alias (mechanism) ||
		    !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 if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, LOGINDISABLED)) {
			g_set_error (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
				_("Plaintext authentication disallowed on insecure connections. Change encryption to STARTTLS or TLS for account “%s”."),
				camel_service_get_display_name (service));
			result = CAMEL_AUTHENTICATION_ERROR;
			goto exit;
	} 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);

		can_retry_login = TRUE;
	}

	success = camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, error);
	if (!success && can_retry_login && imapx_password_contains_nonascii (service)) {
		const gchar *password;
		gchar *password_latin1;

		can_retry_login = -1;

		password = camel_service_get_password (service);
		password_latin1 = g_convert_with_fallback (password, -1, "ISO-8859-1", "UTF-8", "", NULL, NULL, NULL);

		if (password_latin1 && g_strcmp0 (password, password_latin1) != 0) {
			camel_imapx_command_unref (ic);
			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LOGIN, "LOGIN %S %S", user, password_latin1);
			g_free (password_latin1);

			success = camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, NULL);
		} else {
			g_free (password_latin1);
		}
	}

	if (!success && (!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 (ic->status->condition != IMAPX_UNKNOWN && ic->status->condition != IMAPX_AUTHENTICATIONFAILED) {
			g_set_error_literal (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_UNAVAILABLE,
				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

imapx_connect_to_server (CamelIMAPXServer *is,
                         GCancellable *cancellable,
                         GError **error)
{
	CamelNetworkSettings *network_settings;
	CamelNetworkSecurityMethod method;
	CamelIMAPXStore *store;
	CamelSettings *settings;
	GIOStream *connection = NULL;
	GIOStream *tls_stream;
	GSocket *socket;
	guint len;
	guchar *token;
	gint tok;
	CamelIMAPXCommand *ic;
	gchar *shell_command = NULL;
	gboolean use_shell_command;
	gboolean success = TRUE;
	gchar *host;

	store = camel_imapx_server_ref_store (is);

	settings = camel_service_ref_settings (CAMEL_SERVICE (store));

	network_settings = CAMEL_NETWORK_SETTINGS (settings);
	host = camel_network_settings_dup_host (network_settings);
	method = camel_network_settings_get_security_method (network_settings);

	use_shell_command = camel_imapx_settings_get_use_shell_command (
		CAMEL_IMAPX_SETTINGS (settings));

	if (use_shell_command)
		shell_command = camel_imapx_settings_dup_shell_command (
			CAMEL_IMAPX_SETTINGS (settings));

	g_object_unref (settings);

	if (shell_command != NULL) {
		success = connect_to_server_process (is, shell_command, error);

		g_free (shell_command);

		if (success)
			goto connected;
		else
			goto exit;
	}

	connection = camel_network_service_connect_sync (
		CAMEL_NETWORK_SERVICE (store), cancellable, error);

	if (connection != NULL) {
		GInputStream *input_stream;
		GOutputStream *output_stream;
		GError *local_error = NULL;

		/* Disable the Nagle algorithm with TCP_NODELAY, since IMAP
		 * commands should be issued immediately even we've not yet
		 * received a response to a previous command. */
		socket = g_socket_connection_get_socket (
			G_SOCKET_CONNECTION (connection));
		g_socket_set_option (
			socket, IPPROTO_TCP, TCP_NODELAY, 1, &local_error);
		if (local_error != NULL) {
			/* Failure to set the socket option is non-fatal. */
			g_warning ("%s: %s", G_STRFUNC, local_error->message);
			g_clear_error (&local_error);
		}

		g_mutex_lock (&is->priv->stream_lock);
		g_warn_if_fail (is->priv->connection == NULL);
		is->priv->connection = g_object_ref (connection);
		g_mutex_unlock (&is->priv->stream_lock);

		input_stream = g_io_stream_get_input_stream (connection);
		output_stream = g_io_stream_get_output_stream (connection);

		imapx_server_set_streams (is, input_stream, output_stream);

		/* Hang on to the connection reference in case we need to
		 * issue STARTTLS below. */
	} else {
		success = FALSE;
		goto exit;
	}

connected:
	while (1) {
		GInputStream *input_stream;

		input_stream = camel_imapx_server_ref_input_stream (is);

		token = NULL;
		tok = camel_imapx_input_stream_token (
			CAMEL_IMAPX_INPUT_STREAM (input_stream),
			&token, &len, cancellable, error);

		if (tok < 0) {
			success = FALSE;

		} else if (tok == '*') {
			success = imapx_untagged (
				is, input_stream, cancellable, error);

			if (success) {
				g_object_unref (input_stream);
				break;
			}

		} else {
			camel_imapx_input_stream_ungettoken (
				CAMEL_IMAPX_INPUT_STREAM (input_stream),
				tok, token, len);

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

			g_free (token);
		}

		g_object_unref (input_stream);

		if (!success)
			goto exit;
	}

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

	if (!is->priv->cinfo) {
		g_mutex_unlock (&is->priv->stream_lock);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY");

		success = camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, error);

		camel_imapx_command_unref (ic);

		if (!success)
			goto exit;
	} else {
		g_mutex_unlock (&is->priv->stream_lock);
	}

	if (method == CAMEL_NETWORK_SECURITY_METHOD_STARTTLS_ON_STANDARD_PORT) {

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

		if (CAMEL_IMAPX_LACK_CAPABILITY (is->priv->cinfo, STARTTLS)) {
			g_mutex_unlock (&is->priv->stream_lock);
			g_set_error (
				error, CAMEL_ERROR,
				CAMEL_ERROR_GENERIC,
				_("Failed to connect to IMAP server %s in secure mode: %s"),
				host, _("STARTTLS not supported"));
			success = FALSE;
			goto exit;
		} else {
			g_mutex_unlock (&is->priv->stream_lock);
		}

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_STARTTLS, "STARTTLS");

		success = camel_imapx_server_process_command_sync (is, ic, _("Failed to issue STARTTLS"), cancellable, error);

		if (success) {
			g_mutex_lock (&is->priv->stream_lock);

			/* See if we got new capabilities
			 * in the STARTTLS response. */
			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 (!success)
			goto exit;

		tls_stream = camel_network_service_starttls (
			CAMEL_NETWORK_SERVICE (store), connection, error);

		if (tls_stream != NULL) {
			GInputStream *input_stream;
			GOutputStream *output_stream;

			g_mutex_lock (&is->priv->stream_lock);
			g_object_unref (is->priv->connection);
			is->priv->connection = g_object_ref (tls_stream);
			g_mutex_unlock (&is->priv->stream_lock);

			input_stream =
				g_io_stream_get_input_stream (tls_stream);
			output_stream =
				g_io_stream_get_output_stream (tls_stream);

			imapx_server_set_streams (
				is, input_stream, output_stream);

			g_object_unref (tls_stream);
		} else {
			g_prefix_error (
				error,
				_("Failed to connect to IMAP server %s in secure mode: "),
				host);
			success = FALSE;
			goto exit;
		}

		/* Get new capabilities if they weren't already given */
		g_mutex_lock (&is->priv->stream_lock);
		if (is->priv->cinfo == NULL) {
			g_mutex_unlock (&is->priv->stream_lock);
			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_CAPABILITY, "CAPABILITY");
			success = camel_imapx_server_process_command_sync (is, ic, _("Failed to get capabilities"), cancellable, error);
			camel_imapx_command_unref (ic);

			if (!success)
				goto exit;
		} else {
			g_mutex_unlock (&is->priv->stream_lock);
		}
	}

exit:
	if (!success) {
		g_mutex_lock (&is->priv->stream_lock);

		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 != NULL) {
			imapx_free_capability (is->priv->cinfo);
			is->priv->cinfo = NULL;
		}

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

	g_free (host);

	g_clear_object (&connection);
	g_clear_object (&store);

	return success;
}

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

connect_to_server_process (CamelIMAPXServer *is,
                           const gchar *cmd,
                           GError **error)
{
	GSubprocessLauncher *launcher;
	GSubprocess *subprocess = NULL;
	CamelNetworkSettings *network_settings;
	CamelProvider *provider;
	CamelSettings *settings;
	CamelIMAPXStore *store;
	CamelURL url;
	gchar **argv = NULL;
	gchar *buf;
	gchar *cmd_copy;
	gchar *full_cmd;
	const gchar *password;
	gchar *host;
	gchar *user;
	guint16 port;

	memset (&url, 0, sizeof (CamelURL));

	launcher = g_subprocess_launcher_new (
		G_SUBPROCESS_FLAGS_STDIN_PIPE |
		G_SUBPROCESS_FLAGS_STDOUT_PIPE |
		G_SUBPROCESS_FLAGS_STDERR_SILENCE);

#ifdef G_OS_UNIX
	g_subprocess_launcher_set_child_setup (
		launcher, imapx_server_child_process_setup,
		NULL, (GDestroyNotify) NULL);
#endif

	store = camel_imapx_server_ref_store (is);

	password = camel_service_get_password (CAMEL_SERVICE (store));
	provider = camel_service_get_provider (CAMEL_SERVICE (store));
	settings = camel_service_ref_settings (CAMEL_SERVICE (store));

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

	/* Put full details in the environment, in case the connection
	 * program needs them */
	camel_url_set_protocol (&url, provider->protocol);
	camel_url_set_host (&url, host);
	camel_url_set_port (&url, port);
	camel_url_set_user (&url, user);
	buf = camel_url_to_string (&url, 0);

	g_subprocess_launcher_setenv (launcher, "URL", buf, TRUE);
	g_subprocess_launcher_setenv (launcher, "URLHOST", host, TRUE);

	if (port > 0) {
		gchar *port_string;

		port_string = g_strdup_printf ("%u", port);
		g_subprocess_launcher_setenv (
			launcher, "URLPORT", port_string, TRUE);
		g_free (port_string);
	}

	if (user != NULL) {
		g_subprocess_launcher_setenv (
			launcher, "URLPORT", user, TRUE);
	}

	if (password != NULL) {
		g_subprocess_launcher_setenv (
			launcher, "URLPASSWD", password, TRUE);
	}

	g_free (buf);

	g_object_unref (settings);
	g_object_unref (store);

	/* Now do %h, %u, etc. substitution in cmd */
	buf = cmd_copy = g_strdup (cmd);

	full_cmd = g_strdup ("");

	for (;;) {
		gchar *pc;
		gchar *tmp;
		const gchar *var;
		gint len;

		pc = strchr (buf, '%');
	ignore:
		if (!pc) {
			tmp = g_strdup_printf ("%s%s", full_cmd, buf);
			g_free (full_cmd);
			full_cmd = tmp;
			break;
		}

		len = pc - buf;

		var = NULL;

		switch (pc[1]) {
		case 'h':
			var = host;
			break;
		case 'u':
			var = user;
			break;
		}
		if (!var) {
			/* If there wasn't a valid %-code, with an actual
			 * variable to insert, pretend we didn't see the % */
			pc = strchr (pc + 1, '%');
			goto ignore;
		}
		tmp = g_strdup_printf ("%s%.*s%s", full_cmd, len, buf, var);
		g_free (full_cmd);
		full_cmd = tmp;
		buf = pc + 2;
	}

	g_free (cmd_copy);

	g_free (host);
	g_free (user);

	if (g_shell_parse_argv (full_cmd, NULL, &argv, error)) {
		subprocess = g_subprocess_launcher_spawnv (
			launcher, (const gchar * const *) argv, error);
		g_strfreev (argv);
	}

	g_free (full_cmd);
	g_object_unref (launcher);

	if (subprocess != NULL) {
		GInputStream *input_stream;
		GOutputStream *output_stream;

		g_mutex_lock (&is->priv->stream_lock);
		g_warn_if_fail (is->priv->subprocess == NULL);
		is->priv->subprocess = g_object_ref (subprocess);
		g_mutex_unlock (&is->priv->stream_lock);

		input_stream = g_subprocess_get_stdout_pipe (subprocess);
		output_stream = g_subprocess_get_stdin_pipe (subprocess);

		imapx_server_set_streams (is, input_stream, output_stream);

		g_object_unref (subprocess);
	}

	return TRUE;
}

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

imapx_uidset_init (struct _uidset_state *ss,
                   gint total,
                   gint limit)
{
	ss->uids = 0;
	ss->entries = 0;
	ss->start = 0;
	ss->last = 0;
	ss->total = total;
	ss->limit = limit;
}

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

camel_imapx_server_sync_message_sync (CamelIMAPXServer *is,
				      CamelIMAPXMailbox *mailbox,
				      CamelFolderSummary *summary,
				      CamelDataCache *message_cache,
				      const gchar *message_uid,
				      GCancellable *cancellable,
				      GError **error)
{
	gchar *cache_file = NULL;
	gboolean is_cached;
	struct stat st;
	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_FOLDER_SUMMARY (summary), FALSE);
	g_return_val_if_fail (CAMEL_IS_DATA_CACHE (message_cache), FALSE);
	g_return_val_if_fail (message_uid != NULL, FALSE);

	/* Check if the cache file already exists and is non-empty. */
	cache_file = camel_data_cache_get_filename (message_cache, "cur", message_uid);
	is_cached = (g_stat (cache_file, &st) == 0 && st.st_size > 0);
	g_free (cache_file);

	if (!is_cached) {
		CamelStream *stream;

		stream = camel_imapx_server_get_message_sync (
			is, mailbox, summary,
			message_cache, message_uid,
			cancellable, error);

		success = (stream != NULL);

		g_clear_object (&stream);
	}

	return success;
}

Project: evolution-data-server

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;
	CamelMessageInfo *info;
	CamelFolderChangeInfo *expunged_changes = NULL;
	GList *expunged_removed_list = NULL;
	GHashTable *stamps;
	guint32 permanentflags;
	struct _uidset_state uidset, uidset_expunge;
	gint unread_change = 0;
	gboolean use_real_junk_path = FALSE;
	gboolean use_real_trash_path = FALSE;
	gboolean remove_deleted_flags = FALSE;
	gboolean is_real_trash_folder = FALSE;
	gboolean is_real_junk_folder = FALSE;
	gboolean has_uidplus_capability;
	gboolean expunge_deleted;
	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 (camel_folder_get_folder_summary (folder));

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

	camel_folder_sort_uids (folder, changed_uids);
	stamps = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) camel_pstring_free, NULL);

	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);
		if (use_real_junk_path) {
			CamelFolder *junk_folder = NULL;
			gchar *real_junk_path;

			real_junk_path = camel_imapx_settings_dup_real_junk_path (settings);
			if (real_junk_path) {
				junk_folder = camel_store_get_folder_sync (
					camel_folder_get_parent_store (folder),
					real_junk_path, 0, cancellable, NULL);
			}

			is_real_junk_folder = junk_folder == folder;

			use_real_junk_path = junk_folder != NULL;

			g_clear_object (&junk_folder);
			g_free (real_junk_path);
		}

		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);

			is_real_trash_folder = trash_folder == folder;

			/* Remove deleted flags in all but the trash folder itself */
			remove_deleted_flags = !trash_folder || !is_real_trash_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 (camel_folder_get_folder_summary (folder), NULL);

	camel_folder_summary_lock (camel_folder_get_folder_summary (folder));

	off_orset = on_orset = 0;
	for (i = 0; i < changed_uids->len; i++) {
		CamelIMAPXMessageInfo *xinfo;
		guint32 flags, sflags;
		const CamelNamedFlags *local_uflags, *server_uflags;
		const gchar *uid;
		guint j = 0;

		uid = g_ptr_array_index (changed_uids, i);

		info = camel_folder_summary_get (camel_folder_get_folder_summary (folder), uid);
		xinfo = info ? CAMEL_IMAPX_MESSAGE_INFO (info) : NULL;

		if (!info || !xinfo) {
			g_clear_object (&info);
			continue;
		}

		if (!camel_message_info_get_folder_flagged (info)) {
			g_clear_object (&info);
			continue;
		}

		camel_message_info_property_lock (info);

		g_hash_table_insert (stamps, (gpointer) camel_message_info_pooldup_uid (info),
			GUINT_TO_POINTER (camel_message_info_get_folder_flagged_stamp (info)));

		flags = camel_message_info_get_flags (info) & CAMEL_IMAPX_SERVER_FLAGS;
		sflags = camel_imapx_message_info_get_server_flags (xinfo) & CAMEL_IMAPX_SERVER_FLAGS;

		if (can_influence_flags) {
			gboolean move_to_real_junk;
			gboolean move_to_real_trash;
			gboolean move_to_inbox;

			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);

			move_to_inbox = is_real_junk_folder &&
				!move_to_real_junk &&
				!move_to_real_trash &&
				(camel_message_info_get_flags (info) & CAMEL_MESSAGE_NOTJUNK) != 0;

			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 (move_to_inbox)
				camel_imapx_folder_add_move_to_inbox (
					CAMEL_IMAPX_FOLDER (folder), uid);
		}

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

		local_uflags = camel_message_info_get_user_flags (info);
		server_uflags = camel_imapx_message_info_get_server_user_flags (xinfo);

		if (!camel_named_flags_equal (local_uflags, server_uflags)) {
			guint ii, jj, llen, slen;

			llen = local_uflags ? camel_named_flags_get_length (local_uflags) : 0;
			slen = server_uflags ? camel_named_flags_get_length (server_uflags) : 0;
			for (ii = 0, jj = 0; ii < llen || jj < slen;) {
				gint res;

				if (ii < llen) {
					const gchar *local_name = camel_named_flags_get (local_uflags, ii);

					if (jj < slen) {
						const gchar *server_name = camel_named_flags_get (server_uflags, jj);

						res = g_strcmp0 (local_name, server_name);
					} else if (local_name && *local_name)
						res = -1;
					else {
						ii++;
						continue;
					}
				} else {
					res = 1;
				}

				if (res == 0) {
					ii++;
					jj++;
				} else {
					GArray *user_set;
					const gchar *user_flag_name;
					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_name = camel_named_flags_get (local_uflags, ii);
						ii++;
					} else {
						if (off_user == NULL)
							off_user = g_array_new (FALSE, FALSE, sizeof (struct _imapx_flag_change));
						user_set = off_user;
						user_flag_name = camel_named_flags_get (server_uflags, jj);
						jj++;
					}

					/* 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 (g_strcmp0 (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:
					g_object_ref (info);
					g_ptr_array_add (change->infos, info);
				}
			}
		}

		camel_message_info_property_unlock (info);

		g_clear_object (&info);
	}

	camel_folder_summary_unlock (camel_folder_get_folder_summary (folder));

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

	if (nothing_to_do) {
		imapx_sync_free_user (on_user);
		imapx_sync_free_user (off_user);
		imapx_unset_folder_flagged_flag (camel_folder_get_folder_summary (folder), changed_uids, remove_deleted_flags);
		camel_folder_free_uids (folder, changed_uids);
		g_hash_table_destroy (stamps);
		g_object_unref (folder);
		return TRUE;
	}

	if (!camel_imapx_server_ensure_selected_sync (is, mailbox, cancellable, error)) {
		imapx_sync_free_user (on_user);
		imapx_sync_free_user (off_user);
		camel_folder_free_uids (folder, changed_uids);
		g_hash_table_destroy (stamps);
		g_object_unref (folder);
		return FALSE;
	}

	imapx_uidset_init (&uidset_expunge, 0, 100);

	has_uidplus_capability = CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, UIDPLUS);
	expunge_deleted = is_real_trash_folder && !remove_deleted_flags;

	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, *ic_expunge = 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++) {
				CamelMessageInfo *info;
				CamelIMAPXMessageInfo *xinfo;
				gboolean remove_deleted_flag;
				guint32 flags;
				guint32 sflags;
				gint send, send_expunge = 0;

				/* the 'stamps' hash table contains only those uid-s,
				   which were also flagged, not only 'dirty' */
				if (!g_hash_table_contains (stamps, changed_uids->pdata[i]))
					continue;

				info = camel_folder_summary_get (camel_folder_get_folder_summary (folder), changed_uids->pdata[i]);
				xinfo = info ? CAMEL_IMAPX_MESSAGE_INFO (info) : NULL;

				if (!info || !xinfo) {
					g_clear_object (&info);
					continue;
				}

				flags = (camel_message_info_get_flags (info) & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags;
				sflags = (camel_imapx_message_info_get_server_flags (xinfo) & 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;
				} else if (expunge_deleted && (flags & CAMEL_MESSAGE_DELETED) != 0) {
					if (has_uidplus_capability) {
						if (!ic_expunge)
							ic_expunge = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_EXPUNGE, "UID EXPUNGE ");

						send_expunge = imapx_uidset_add (&uidset_expunge, ic_expunge, camel_message_info_get_uid (info));
					}

					if (!expunged_changes)
						expunged_changes = camel_folder_change_info_new ();

					camel_folder_change_info_remove_uid (expunged_changes, camel_message_info_get_uid (info));
					expunged_removed_list = g_list_prepend (expunged_removed_list,
						(gpointer) camel_pstring_strdup (camel_message_info_get_uid (info)));
				}

				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_get_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) {
						g_clear_object (&info);
						break;
					}
				}

				if (has_uidplus_capability && (
				    send_expunge == 1 || (i == changed_uids->len - 1 && ic_expunge && imapx_uidset_done (&uidset_expunge, ic_expunge)))) {
					success = camel_imapx_server_process_command_sync (is, ic_expunge, _("Error expunging message"), cancellable, error);

					camel_imapx_command_unref (ic_expunge);
					ic_expunge = NULL;

					if (!success) {
						g_clear_object (&info);
						break;
					}

					camel_folder_changed (folder, expunged_changes);
					camel_folder_summary_remove_uids (camel_folder_get_folder_summary (folder), expunged_removed_list);

					camel_folder_change_info_free (expunged_changes);
					expunged_changes = NULL;

					g_list_free_full (expunged_removed_list, (GDestroyNotify) camel_pstring_free);
					expunged_removed_list = NULL;
				}

				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_imapx_message_info_take_server_user_flags (xinfo,
						camel_message_info_dup_user_flags (info));
				}

				g_clear_object (&info);
			}

			if (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 (has_uidplus_capability && ic_expunge && imapx_uidset_done (&uidset_expunge, ic_expunge)) {
				success = camel_imapx_server_process_command_sync (is, ic_expunge, _("Error expunging message"), cancellable, error);

				camel_imapx_command_unref (ic_expunge);
				ic_expunge = NULL;

				if (!success)
					break;

				camel_folder_changed (folder, expunged_changes);
				camel_folder_summary_remove_uids (camel_folder_get_folder_summary (folder), expunged_removed_list);

				camel_folder_change_info_free (expunged_changes);
				expunged_changes = NULL;

				g_list_free_full (expunged_removed_list, (GDestroyNotify) camel_pstring_free);
				expunged_removed_list = NULL;
			}

			g_warn_if_fail (ic == NULL);
			g_warn_if_fail (ic_expunge == 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++) {
					CamelMessageInfo *info = c->infos->pdata[i];

					/* When expunging deleted in the real Trash folder, then skip those deleted,
					   because they are gone now. */
					if (expunge_deleted && has_uidplus_capability &&
					    (((camel_message_info_get_flags (info) & CAMEL_IMAPX_SERVER_FLAGS) & permanentflags) & CAMEL_MESSAGE_DELETED) != 0) {
						if (ic && i == c->infos->len - 1 && imapx_uidset_done (&uidset, ic))
							goto store_changes;
						continue;
					}

					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_get_uid (info)) == 1
					    || (i == c->infos->len - 1 && imapx_uidset_done (&uidset, ic))) {
						gchar *utf7;
 store_changes:
						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_warn_if_fail (ic == NULL);
		}
	}

	if (success && expunged_changes && expunge_deleted && !has_uidplus_capability) {
		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);
		camel_imapx_command_unref (ic);
	}

	if (success && expunged_changes) {
		camel_folder_changed (folder, expunged_changes);
		camel_folder_summary_remove_uids (camel_folder_get_folder_summary (folder), expunged_removed_list);
	}

	if (expunged_changes) {
		camel_folder_change_info_free (expunged_changes);
		expunged_changes = NULL;
	}

	if (expunged_removed_list) {
		g_list_free_full (expunged_removed_list, (GDestroyNotify) camel_pstring_free);
		expunged_removed_list = NULL;
	}

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

		parent_store = camel_folder_get_parent_store (folder);
		folder_summary = camel_folder_get_folder_summary (folder);

		camel_folder_summary_lock (folder_summary);

		for (i = 0; i < changed_uids->len; i++) {
			CamelMessageInfo *info;
			CamelIMAPXMessageInfo *xinfo;
			gboolean set_folder_flagged;
			guint32 has_flags, set_server_flags;
			gboolean changed_meanwhile;
			const gchar *uid;

			uid = g_ptr_array_index (changed_uids, i);

			/* the 'stamps' hash table contains only those uid-s,
			   which were also flagged, not only 'dirty' */
			if (!g_hash_table_contains (stamps, uid))
				continue;

			info = camel_folder_summary_get (folder_summary, uid);
			xinfo = info ? CAMEL_IMAPX_MESSAGE_INFO (info) : NULL;

			if (!info || !xinfo) {
				g_clear_object (&info);
				continue;
			}

			camel_message_info_property_lock (info);

			changed_meanwhile = camel_message_info_get_folder_flagged_stamp (info) !=
				GPOINTER_TO_UINT (g_hash_table_lookup (stamps, uid));

			has_flags = camel_message_info_get_flags (info);
			set_server_flags = has_flags & CAMEL_IMAPX_SERVER_FLAGS;
			if (!remove_deleted_flags ||
			    !(has_flags & CAMEL_MESSAGE_DELETED)) {
				set_folder_flagged = FALSE;
			} else {
				/* to stare back the \Deleted flag */
				set_server_flags &= ~CAMEL_MESSAGE_DELETED;
				set_folder_flagged = TRUE;
			}

			if ((permanentflags & CAMEL_MESSAGE_USER) != 0 ||
			    !camel_named_flags_get_length (camel_imapx_message_info_get_server_user_flags (xinfo))) {
				camel_imapx_message_info_take_server_user_flags (xinfo, camel_message_info_dup_user_flags (info));
			}

			if (changed_meanwhile)
				set_folder_flagged = TRUE;

			camel_imapx_message_info_set_server_flags (xinfo, set_server_flags);
			camel_message_info_set_folder_flagged (info, set_folder_flagged);

			camel_message_info_property_unlock (info);
			camel_folder_summary_touch (folder_summary);
			g_clear_object (&info);
		}

		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 ((camel_folder_summary_get_flags (folder_summary) & 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 (camel_folder_get_folder_summary (folder), NULL);
	camel_store_summary_save (CAMEL_IMAPX_STORE (camel_folder_get_parent_store (folder))->summary);

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

	return success;
}

Project: evolution-data-server

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);

		if (camel_imapx_server_is_in_idle (is) &&
		    imapx_can_refresh_mailbox_in_idle (is, imapx_store, mailbox))
			g_signal_emit (is, signals[REFRESH_MAILBOX], 0, mailbox);

		g_object_unref (mailbox);
	}

	g_clear_object (&imapx_store);
	g_object_unref (response);

	return TRUE;
}

Project: evolution-data-server

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

	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);

	changed = camel_imapx_mailbox_get_messages (mailbox) != exists;
	camel_imapx_mailbox_set_messages (mailbox, exists);

	if (changed && camel_imapx_server_is_in_idle (is))
		g_signal_emit (is, signals[REFRESH_MAILBOX], 0, mailbox);

	g_object_unref (mailbox);

	return success;
}

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

camel_imapx_server_ref_output_stream (CamelIMAPXServer *is)
{
	GOutputStream *output_stream = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

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

	if (is->priv->output_stream != NULL)
		output_stream = g_object_ref (is->priv->output_stream);

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

	return output_stream;
}

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

imapx_server_constructed (GObject *object)
{
	CamelIMAPXServer *server;

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

	server = CAMEL_IMAPX_SERVER (object);
	server->priv->tagprefix = 'Z';
}

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

add_initial_untagged_descriptor (GHashTable *untagged_handlers,
                                 guint untagged_id)
{
	const CamelIMAPXUntaggedRespHandlerDesc *prev = NULL;
	const CamelIMAPXUntaggedRespHandlerDesc *cur = NULL;

	g_return_if_fail (untagged_handlers != NULL);
	g_return_if_fail (untagged_id < IMAPX_UNTAGGED_LAST_ID);

	cur = &(_untagged_descr[untagged_id]);
	prev = replace_untagged_descriptor (
		untagged_handlers,
		cur->untagged_response,
		cur);
	/* there must not be any previous handler here */
	g_return_if_fail (prev == NULL);
}

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

camel_imapx_server_get_capability_info (CamelIMAPXServer *is)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

	return is->priv->cinfo;
}

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

imapx_step (CamelIMAPXServer *is,
            GInputStream *input_stream,
	    GOutputStream *output_stream,
            GCancellable *cancellable,
            GError **error)
{
	guint len;
	guchar *token;
	gint tok;
	gboolean success = FALSE;

	tok = camel_imapx_input_stream_token (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		&token, &len, cancellable, error);

	switch (tok) {
		case IMAPX_TOK_ERROR:
			/* GError is already set. */
			break;
		case '*':
			success = imapx_untagged (
				is, input_stream, cancellable, error);
			break;
		case IMAPX_TOK_TOKEN:
			success = imapx_completion (
				is, input_stream,
				token, len, cancellable, error);
			break;
		case '+':
			success = imapx_continuation (
				is, input_stream, output_stream,
				FALSE, cancellable, error);
			break;
		default:
			g_set_error (
				error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED,
				"unexpected server response:");
			break;
	}

	return success;
}

Project: evolution-data-server

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, retrying = FALSE;
	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);

	/* Check whether the message is already downloaded by another job */
	cache_stream = camel_data_cache_get (message_cache, "cur", message_uid, NULL);
	if (cache_stream) {
		result_stream = camel_stream_new (cache_stream);

		g_clear_object (&cache_stream);
		g_clear_object (&mi);

		return result_stream;
	}

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

	settings = camel_imapx_server_ref_settings (is);
	data_size = camel_message_info_get_size (mi);
	use_multi_fetch = data_size > MULTI_SIZE && camel_imapx_settings_get_use_multi_fetch (settings);
	g_object_unref (settings);
	g_clear_object (&mi);

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

	is->priv->get_message_stream = cache_stream;

 try_again:
	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);
	}

	if (success && !retrying && !g_seekable_tell (G_SEEKABLE (is->priv->get_message_stream))) {
		/* Nothing had been read from the server. Maybe this connection
		   doesn't know about the message on the server side yet, thus
		   invoke NOOP and retry. */
		CamelIMAPXCommand *ic;

		retrying = TRUE;

		c (is->priv->tagprefix, "%s: Returned no message data, retrying after NOOP\n", G_STRFUNC);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOOP, "NOOP");

		success = camel_imapx_server_process_command_sync (is, ic, _("Error performing NOOP"), cancellable, &local_error);

		camel_imapx_command_unref (ic);

		if (success)
			goto try_again;
	}

	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

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

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

	/* We don't want to select the mailbox to be subscribed. */
	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_SUBSCRIBE_MAILBOX, "SUBSCRIBE %M", mailbox);

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

	camel_imapx_command_unref (ic);

	if (success) {
		CamelIMAPXStore *imapx_store;

		/* Perform the same processing as imapx_untagged_list()
		 * would if the server notified us of a subscription. */

		imapx_store = camel_imapx_server_ref_store (is);

		camel_imapx_mailbox_subscribed (mailbox);
		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);

		g_clear_object (&imapx_store);
	}

	return success;
}

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

camel_imapx_server_update_quota_info_sync (CamelIMAPXServer *is,
					   CamelIMAPXMailbox *mailbox,
					   GCancellable *cancellable,
					   GError **error)
{
	CamelIMAPXCommand *ic;
	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_mutex_lock (&is->priv->stream_lock);

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

		g_set_error_literal (
			error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
			_("IMAP server does not support quotas"));
		return FALSE;
	} else {
		g_mutex_unlock (&is->priv->stream_lock);
	}

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

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UPDATE_QUOTA_INFO, "GETQUOTAROOT %M", mailbox);

	success = camel_imapx_server_process_command_sync (is, ic, _("Error retrieving quota information"), cancellable, error);

	camel_imapx_command_unref (ic);

	return success;
}

Project: evolution-data-server

imapx_copy_move_message_cache (CamelFolder *source_folder,
			       CamelFolder *destination_folder,
			       gboolean delete_originals,
			       const gchar *source_uid,
			       const gchar *destination_uid,
			       GCancellable *cancellable)
{
	CamelIMAPXFolder *imapx_source_folder, *imapx_destination_folder;
	gchar *source_filename, *destination_filename;

	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (source_folder));
	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (destination_folder));
	g_return_if_fail (source_uid != NULL);
	g_return_if_fail (destination_uid != NULL);

	imapx_source_folder = CAMEL_IMAPX_FOLDER (source_folder);
	imapx_destination_folder = CAMEL_IMAPX_FOLDER (destination_folder);

	source_filename = camel_data_cache_get_filename  (imapx_source_folder->cache, "cur", source_uid);
	if (!g_file_test (source_filename, G_FILE_TEST_EXISTS)) {
		g_free (source_filename);
		return;
	}

	destination_filename = camel_data_cache_get_filename  (imapx_destination_folder->cache, "cur", destination_uid);
	if (!g_file_test (destination_filename, G_FILE_TEST_EXISTS)) {
		GIOStream *stream;

		/* To create the cache folder structure for the message file */
		stream = camel_data_cache_add (imapx_destination_folder->cache, "cur", destination_uid, NULL);
		if (stream) {
			g_clear_object (&stream);

			/* Remove the empty file, it's gonna be replaced with actual message */
			g_unlink (destination_filename);

			if (delete_originals) {
				if (g_rename (source_filename, destination_filename) == -1 && errno != ENOENT) {
					g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, source_filename, destination_filename, g_strerror (errno));
				}
			} else {
				GFile *source, *destination;
				GError *local_error = NULL;

				source = g_file_new_for_path (source_filename);
				destination = g_file_new_for_path (destination_filename);

				if (source && destination &&
				    !g_file_copy (source, destination, G_FILE_COPY_NONE, cancellable, NULL, NULL, &local_error)) {
					if (local_error) {
						g_warning ("%s: Failed to copy '%s' to '%s': %s", G_STRFUNC, source_filename, destination_filename, local_error->message);
					}
				}

				g_clear_object (&source);
				g_clear_object (&destination);
				g_clear_error (&local_error);
			}
		}
	}

	g_free (source_filename);
	g_free (destination_filename);
}

Project: evolution-data-server

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,
		/* Translators: The first “%s” is replaced with an account name and the second “%s”
		   is replaced with a full path name. The spaces around “:” are intentional, as
		   the whole “%s : %s” is meant as an absolute identification of the folder. */
		_("Scanning for changed messages in “%s : %s”"),
		camel_service_get_display_name (CAMEL_SERVICE (camel_folder_get_parent_store (folder))),
		camel_folder_get_full_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,
			/* Translators: The first “%s” is replaced with an account name and the second “%s”
			   is replaced with a full path name. The spaces around “:” are intentional, as
			   the whole “%s : %s” is meant as an absolute identification of the folder. */
			_("Fetching summary information for new messages in “%s : %s”"),
			camel_service_get_display_name (CAMEL_SERVICE (camel_folder_get_parent_store (folder))),
			camel_folder_get_full_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))) {
				GError *local_error = NULL;
				gboolean bodystructure_enabled;
				CamelIMAPXStore *imapx_store;

				imapx_store = camel_imapx_server_ref_store (is);
				bodystructure_enabled = imapx_store && camel_imapx_store_get_bodystructure_enabled (imapx_store);

				if (bodystructure_enabled)
					camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER BODYSTRUCTURE FLAGS)");
				else
					camel_imapx_command_add (ic, " (RFC822.SIZE RFC822.HEADER FLAGS)");

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

				camel_imapx_command_unref (ic);
				ic = NULL;

				/* Some servers can return broken BODYSTRUCTURE response, thus disable it
				   even when it's not 100% sure the BODYSTRUCTURE response was the broken one. */
				if (bodystructure_enabled && !success &&
				    g_error_matches (local_error, CAMEL_IMAPX_ERROR, CAMEL_IMAPX_ERROR_SERVER_RESPONSE_MALFORMED)) {
					camel_imapx_store_set_bodystructure_enabled (imapx_store, FALSE);
					local_error->domain = CAMEL_IMAPX_SERVER_ERROR;
					local_error->code = CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT;
				}

				g_clear_object (&imapx_store);

				if (local_error)
					g_propagate_error (error, local_error);

				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);

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

	/* Notify about new messages, thus they are shown in the UI early. */
	if (camel_folder_change_info_changed (is->priv->changes)) {
		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 (camel_folder_get_folder_summary (folder), 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);
	}

	return success;
}

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

camel_imapx_server_process_command_sync (CamelIMAPXServer *is,
					 CamelIMAPXCommand *ic,
					 const gchar *error_prefix,
					 GCancellable *cancellable,
					 GError **error)
{
	CamelIMAPXCommandPart *cp;
	GInputStream *input_stream = NULL;
	GOutputStream *output_stream = NULL;
	gboolean cp_literal_plus;
	GList *head;
	gchar *string;
	gboolean success = FALSE;
	GError *local_error = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_COMMAND (ic), FALSE);

	camel_imapx_command_close (ic);
	if (ic->status) {
		imapx_free_status (ic->status);
		ic->status = NULL;
	}
	ic->completed = FALSE;

	head = g_queue_peek_head_link (&ic->parts);
	g_return_val_if_fail (head != NULL, FALSE);
	cp = (CamelIMAPXCommandPart *) head->data;
	ic->current_part = head;

	if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
		if (error_prefix && local_error)
			g_prefix_error (&local_error, "%s: ", error_prefix);

		if (local_error)
			g_propagate_error (error, local_error);

		return FALSE;
	}

	cp_literal_plus = ((cp->type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) != 0);

	COMMAND_LOCK (is);

	if (is->priv->current_command != NULL) {
		g_warning ("%s: [%c] %p: Starting command %p (%s) while still processing %p (%s)", G_STRFUNC,
			is->priv->tagprefix, is, ic, camel_imapx_job_get_kind_name (ic->job_kind),
			is->priv->current_command, camel_imapx_job_get_kind_name (is->priv->current_command->job_kind));
	}

	if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
		c (is->priv->tagprefix, "%s: command %p (%s) cancelled\n", G_STRFUNC, ic, camel_imapx_job_get_kind_name (ic->job_kind));

		COMMAND_UNLOCK (is);

		if (error_prefix && local_error)
			g_prefix_error (&local_error, "%s: ", error_prefix);

		if (local_error)
			g_propagate_error (error, local_error);

		return FALSE;
	}

	c (is->priv->tagprefix, "%s: %p (%s) ~> %p (%s)\n", G_STRFUNC, is->priv->current_command,
		is->priv->current_command ? camel_imapx_job_get_kind_name (is->priv->current_command->job_kind) : "",
		ic, camel_imapx_job_get_kind_name (ic->job_kind));

	is->priv->current_command = ic;
	is->priv->continuation_command = ic;

	COMMAND_UNLOCK (is);

	input_stream = camel_imapx_server_ref_input_stream (is);
	output_stream = camel_imapx_server_ref_output_stream (is);

	if (output_stream == NULL) {
		local_error = g_error_new_literal (
			CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
			_("Cannot issue command, no stream available"));
		goto exit;
	}

	c (
		is->priv->tagprefix,
		"Starting command (%s) %c%05u %s\r\n",
		is->priv->current_command ? " literal" : "",
		is->priv->tagprefix,
		ic->tag,
		cp->data && g_str_has_prefix (cp->data, "LOGIN") ?
			"LOGIN..." : cp->data);

	if (ic->job_kind == CAMEL_IMAPX_JOB_DONE)
		string = g_strdup_printf ("%s\r\n", cp->data);
	else
		string = g_strdup_printf ("%c%05u %s\r\n", is->priv->tagprefix, ic->tag, cp->data);
	g_mutex_lock (&is->priv->stream_lock);
	success = g_output_stream_write_all (
		output_stream, string, strlen (string),
		NULL, cancellable, &local_error);
	g_mutex_unlock (&is->priv->stream_lock);
	g_free (string);

	if (local_error != NULL || !success)
		goto exit;

	while (is->priv->continuation_command == ic && cp_literal_plus) {
		/* Sent LITERAL+ continuation immediately */
		imapx_continuation (
			is, input_stream, output_stream,
			TRUE, cancellable, &local_error);
		if (local_error != NULL)
			goto exit;
	}

	while (success && !ic->completed)
		success = imapx_step (is, input_stream, output_stream, cancellable, &local_error);

	imapx_server_reset_inactivity_timer (is);

 exit:

	COMMAND_LOCK (is);

	if (is->priv->current_command == ic) {
		c (is->priv->tagprefix, "%s: %p ~> %p; success:%d local-error:%s result:%s status-text:'%s'\n", G_STRFUNC,
			is->priv->current_command, NULL, success, local_error ? local_error->message : "[null]",
			ic->status ? (
				ic->status->result == IMAPX_OK ? "OK" :
				ic->status->result == IMAPX_NO ? "NO" :
				ic->status->result == IMAPX_BAD ? "BAD" :
				ic->status->result == IMAPX_PREAUTH ? "PREAUTH" :
				ic->status->result == IMAPX_BYE ? "BYE" : "???") : "[null]",
			ic->status ? ic->status->text : "[null]");

		is->priv->current_command = NULL;
		is->priv->continuation_command = NULL;
	} else {
		c (is->priv->tagprefix, "%s: current command:%p doesn't match passed-in command:%p success:%d local-error:%s result:%s status-text:'%s'\n", G_STRFUNC,
			is->priv->current_command, ic, success, local_error ? local_error->message : "[null]",
			ic->status ? (
				ic->status->result == IMAPX_OK ? "OK" :
				ic->status->result == IMAPX_NO ? "NO" :
				ic->status->result == IMAPX_BAD ? "BAD" :
				ic->status->result == IMAPX_PREAUTH ? "PREAUTH" :
				ic->status->result == IMAPX_BYE ? "BYE" : "???") : "[null]",
			ic->status ? ic->status->text : "[null]");
	}

	COMMAND_UNLOCK (is);

	/* Server reported error. */
	if (success && ic->status && ic->status->result != IMAPX_OK) {
		g_set_error (
			&local_error, CAMEL_ERROR,
			CAMEL_ERROR_GENERIC,
			"%s", ic->status->text);
	}

	if (local_error) {
		/* Sadly, G_IO_ERROR_FAILED is also used for 'Connection reset by peer' error;
		   since GLib 2.44 is used G_IO_ERROR_CONNECTION_CLOSED, which is the same as G_IO_ERROR_BROKEN_PIPE */
		if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_FAILED) ||
		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE) ||
		    g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT)) {
			local_error->domain = CAMEL_IMAPX_SERVER_ERROR;
			local_error->code = CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT;
		}

		if (error_prefix && local_error)
			g_prefix_error (&local_error, "%s: ", error_prefix);

		g_propagate_error (error, local_error);

		success = FALSE;
	}

	g_clear_object (&input_stream);
	g_clear_object (&output_stream);

	return success;
}

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

imapx_untagged_capability (CamelIMAPXServer *is,
                           GInputStream *input_stream,
                           GCancellable *cancellable,
                           GError **error)
{
	struct _capability_info *cinfo;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

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

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

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

	cinfo = imapx_parse_capability (CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);

	if (!cinfo)
		return FALSE;

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

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

	c (is->priv->tagprefix, "got capability flags %08x\n", is->priv->cinfo->capa);

	imapx_server_stash_command_arguments (is);

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

	return TRUE;
}

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

imapx_server_set_property (GObject *object,
                           guint property_id,
                           const GValue *value,
                           GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_STORE:
			imapx_server_set_store (
				CAMEL_IMAPX_SERVER (object),
				g_value_get_object (value));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}

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

camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE);

	return imapx_server->priv->state >= IMAPX_CONNECTED;
}

Project: evolution-data-server

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_slice_new0 (CamelIMAPXServerUntaggedContext);

	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 (p && *p) {
		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_slice_free (CamelIMAPXServerUntaggedContext, is->priv->context);
	is->priv->context = NULL;

	return success;
}

Project: evolution-data-server

imapx_untagged_lsub (CamelIMAPXServer *is,
                     GInputStream *input_stream,
                     GCancellable *cancellable,
                     GError **error)
{
	CamelIMAPXListResponse *response;
	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;

	if (is->priv->list_responses_hash) {
		CamelIMAPXListResponse *list_response;

		is->priv->lsub_responses = g_slist_prepend (is->priv->lsub_responses, response);

		list_response = g_hash_table_lookup (is->priv->list_responses_hash, camel_imapx_list_response_get_mailbox_name (response));
		if (list_response)
			camel_imapx_list_response_add_attribute (list_response, CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED);
	} else {
		CamelIMAPXStore *imapx_store;

		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

imapx_password_contains_nonascii (CamelService *service)
{
	const gchar *password;

	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);

	password = camel_service_get_password (service);

	while (password && *password) {
		if (*password < 0)
			return TRUE;

		password++;
	}

	return FALSE;
}

Project: evolution-data-server

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;

		if (!is->priv->get_message_stream) {
			g_warn_if_fail (is->priv->get_message_stream != NULL);
			imapx_free_fetch (finfo);
			return 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"));
				imapx_free_fetch (finfo);
				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) {
			if (!is->priv->fetch_changes_mailbox ||
			    !is->priv->fetch_changes_folder ||
			    !is->priv->fetch_changes_infos) {
				g_warn_if_fail (is->priv->fetch_changes_mailbox != NULL);
				g_warn_if_fail (is->priv->fetch_changes_folder != NULL);
				g_warn_if_fail (is->priv->fetch_changes_infos != NULL);
				imapx_free_fetch (finfo);
				return 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_slice_new0 (FetchChangesInfo);

				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) {
					guint32 n_messages;

					COMMAND_UNLOCK (is);

					is->priv->fetch_changes_last_progress = monotonic_time;

					n_messages = camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox);
					if (n_messages > 0)
						camel_operation_progress (cancellable, 100 * is->priv->context->id / n_messages);
				} 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);

			if (select_pending)
				select_folder = imapx_server_ref_folder (is, select_pending);
			else
				select_folder = imapx_server_ref_folder (is, select_mailbox);
			if (!select_folder) {
				g_warn_if_fail (select_folder != NULL);

				g_clear_object (&select_mailbox);
				g_clear_object (&select_pending);
				imapx_free_fetch (finfo);

				return 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 (camel_folder_get_folder_summary (select_folder), 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));
					c (is->priv->tagprefix, "found uid %s in '%s', changed:%d\n", uid,
						camel_folder_get_full_name (select_folder), changed);
				} 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 in '%s'\n", uid,
						camel_folder_get_full_name (select_folder));
				}
			}

			if (changed) {
				CamelIMAPXSummary *imapx_summary;

				imapx_summary = CAMEL_IMAPX_SUMMARY (camel_folder_get_folder_summary (select_folder));

				if (imapx_summary && (finfo->got & FETCH_MODSEQ) != 0 &&
				    imapx_summary->modseq < finfo->modseq) {
					c (is->priv->tagprefix, "updating summary modseq %" G_GUINT64_FORMAT "~>%" G_GUINT64_FORMAT " in '%s'\n",
						imapx_summary->modseq, finfo->modseq, camel_folder_get_full_name (select_folder));
					imapx_summary->modseq = finfo->modseq;

					camel_folder_summary_touch (CAMEL_FOLDER_SUMMARY (imapx_summary));
				}

				g_mutex_lock (&is->priv->changes_lock);
				if (is->priv->changes)
					camel_folder_change_info_change_uid (is->priv->changes, uid);
				else
					g_warn_if_fail (is->priv->changes != NULL);
				g_mutex_unlock (&is->priv->changes_lock);
			}
			g_free (uid);

			if (changed && camel_imapx_server_is_in_idle (is)) {
				camel_folder_summary_save (camel_folder_get_folder_summary (select_folder), 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);
			}

			g_clear_object (&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) {
			if (!is->priv->fetch_changes_mailbox ||
			    !is->priv->fetch_changes_folder ||
			    !is->priv->fetch_changes_infos) {
				g_warn_if_fail (is->priv->fetch_changes_mailbox != NULL);
				g_warn_if_fail (is->priv->fetch_changes_folder != NULL);
				g_warn_if_fail (is->priv->fetch_changes_infos != NULL);
				imapx_free_fetch (finfo);
				return 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 (camel_folder_get_folder_summary (folder), mp);
		g_object_unref (mp);

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

			camel_message_info_set_abort_notifications (mi, TRUE);

			camel_message_info_set_uid (mi, finfo->uid);

			if ((finfo->got & FETCH_CINFO) && finfo->cinfo) {
				gboolean has_attachment = FALSE;

				camel_message_content_info_traverse (finfo->cinfo, imapx_server_cinfo_has_attachment_cb, &has_attachment);

				camel_message_info_set_flags (mi, CAMEL_MESSAGE_ATTACHMENTS, has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
			}

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

				nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
				if (!nfo) {
					g_warn_if_fail (nfo != NULL);

					camel_message_info_set_abort_notifications (mi, FALSE);
					g_clear_object (&mi);
					g_clear_object (&mailbox);
					g_clear_object (&folder);
					imapx_free_fetch (finfo);

					return 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 (finfo->uid, NULL, 10);

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

			camel_message_info_set_size (mi, finfo->size);
			camel_message_info_set_abort_notifications (mi, FALSE);

			camel_folder_summary_lock (camel_folder_get_folder_summary (folder));

			if (!camel_folder_summary_check_uid (camel_folder_get_folder_summary (folder), finfo->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 (camel_folder_get_folder_summary (folder), mi, TRUE);

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

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

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

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

					camel_operation_progress (cancellable, cnt ? cnt : 1);
				}

				if (camel_imapx_server_is_in_idle (is) && !camel_folder_is_frozen (folder))
					camel_folder_summary_save (camel_folder_get_folder_summary (folder), NULL);
			}

			g_clear_object (&mi);
			camel_folder_summary_unlock (camel_folder_get_folder_summary (folder));

			if (free_user_flags)
				camel_named_flags_free (server_user_flags);

			if (camel_imapx_server_is_in_idle (is) && !camel_folder_is_frozen (folder)) {
				CamelFolderChangeInfo *changes = NULL;

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

				if (camel_folder_change_info_changed (is->priv->changes)) {
					changes = is->priv->changes;
					is->priv->changes = camel_folder_change_info_new ();
				}

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

				if (changes) {
					imapx_update_store_summary (folder);
					camel_folder_changed (folder, changes);

					camel_folder_change_info_free (changes);
				}
			}
		}

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

	imapx_free_fetch (finfo);

	return TRUE;
}

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

imapx_server_dispose (GObject *object)
{
	CamelIMAPXServer *server = CAMEL_IMAPX_SERVER (object);

	g_cancellable_cancel (server->priv->cancellable);

	imapx_disconnect (server);

	g_weak_ref_set (&server->priv->store, NULL);

	g_clear_object (&server->priv->subprocess);

	g_mutex_lock (&server->priv->idle_lock);
	g_clear_object (&server->priv->idle_cancellable);
	g_clear_object (&server->priv->idle_mailbox);
	if (server->priv->idle_pending) {
		g_source_destroy (server->priv->idle_pending);
		g_source_unref (server->priv->idle_pending);
		server->priv->idle_pending = NULL;
	}
	g_mutex_unlock (&server->priv->idle_lock);

	g_clear_object (&server->priv->subprocess);

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

Project: evolution-data-server

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) {
		gboolean request_noop;
		gint change_stamp;

		change_stamp = selected_mailbox ? camel_imapx_mailbox_get_change_stamp (selected_mailbox) : 0;
		request_noop = selected_mailbox && is->priv->last_selected_mailbox_change_stamp != change_stamp;

		if (request_noop)
			is->priv->last_selected_mailbox_change_stamp = change_stamp;

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

		if (request_noop) {
			c (is->priv->tagprefix, "%s: Selected mailbox '%s' changed, do NOOP instead\n", G_STRFUNC, camel_imapx_mailbox_get_name (mailbox));

			return camel_imapx_server_noop_sync (is, mailbox, cancellable, error);
		}

		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;
		is->priv->last_selected_mailbox_change_stamp = camel_imapx_mailbox_get_change_stamp (mailbox);
		g_weak_ref_set (&is->priv->select_mailbox, mailbox);
	} else {
		is->priv->state = IMAPX_INITIALISED;
		is->priv->last_selected_mailbox_change_stamp = 0;
		g_weak_ref_set (&is->priv->select_mailbox, NULL);
	}

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

	return success;
}

Project: evolution-data-server

imapx_server_cinfo_has_attachment_cb (CamelMessageContentInfo *ci,
				      gint depth,
				      gpointer user_data)
{
	gboolean *pbool = user_data;

	g_return_val_if_fail (pbool != NULL, FALSE);

	*pbool = camel_content_disposition_is_attachment_ex (ci->disposition, ci->type, ci->parent ? ci->parent->type : NULL);

	return !*pbool;
}

Project: evolution-data-server

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. */
	if (!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);

	camel_message_info_set_abort_notifications (info, TRUE);
	camel_message_info_set_uid (info, uid);

	if (mi != NULL) {
		struct icaltimetype icaltime;

		camel_message_info_property_lock (mi);

		camel_message_info_set_flags (info, ~0, camel_message_info_get_flags (mi));
		camel_message_info_set_size (info, camel_message_info_get_size (mi));
		camel_message_info_take_user_flags (info,
			camel_named_flags_copy (camel_message_info_get_user_flags (mi)));
		camel_message_info_take_user_tags (info,
			camel_name_value_array_copy (camel_message_info_get_user_tags (mi)));

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

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

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

		camel_message_info_property_unlock (mi);
	}

	if (!camel_message_info_get_size (info)) {
		camel_message_info_set_size (info, camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (message), NULL, NULL));
	}

	g_free (uid);

	if (camel_mime_message_has_attachment (message))
		camel_message_info_set_flags (info, CAMEL_MESSAGE_ATTACHMENTS, 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,
			camel_message_info_get_flags (info),
			camel_message_info_get_user_flags (info),
			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,
			camel_message_info_get_flags (info),
			camel_message_info_get_user_flags (info),
			path);
	}

	camel_message_info_set_abort_notifications (info, FALSE);

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

	if (success) {
		CamelIMAPXFolder *imapx_folder;
		CamelFolder *folder;
		CamelMessageInfo *clone;
		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. */

		clone = camel_message_info_clone (info, camel_folder_get_folder_summary (folder));
		old_uid = g_strdup (camel_message_info_get_uid (info));

		if (ic->status && ic->status->condition == IMAPX_APPENDUID) {
			c (is->priv->tagprefix, "Got appenduid %u %u\n", (guint32) ic->status->u.appenduid.uidvalidity, ic->status->u.appenduid.uid);
			if (ic->status->u.appenduid.uidvalidity == uidvalidity) {
				CamelFolderChangeInfo *dest_changes;
				gchar *uid;

				uid = g_strdup_printf ("%u", ic->status->u.appenduid.uid);
				camel_message_info_set_uid (clone, uid);

				cur = camel_data_cache_get_filename  (imapx_folder->cache, "cur", 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 (
					clone,
					camel_message_info_get_flags (info),
					camel_message_info_get_user_flags (info),
					TRUE,
					camel_message_info_get_user_tags (info),
					camel_imapx_mailbox_get_permanentflags (mailbox));

				camel_folder_summary_add (camel_folder_get_folder_summary (folder), clone, TRUE);

				dest_changes = camel_folder_change_info_new ();
				camel_folder_change_info_add_uid (dest_changes, camel_message_info_get_uid (clone));

				camel_folder_summary_save (camel_folder_get_folder_summary (folder), NULL);
				imapx_update_store_summary (folder);
				camel_folder_changed (folder, dest_changes);
				camel_folder_change_info_free (dest_changes);

				if (appended_uid)
					*appended_uid = uid;
				else
					g_free (uid);

				g_clear_object (&clone);

				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);
		g_clear_object (&clone);
		g_object_unref (folder);
	}

	g_clear_object (&info);
	g_free (path);

	return success;
}

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

imapx_untagged_quotaroot (CamelIMAPXServer *is,
                          GInputStream *input_stream,
                          GCancellable *cancellable,
                          GError **error)
{
	CamelIMAPXStore *imapx_store;
	CamelIMAPXMailbox *mailbox;
	gchar *mailbox_name = NULL;
	gchar **quota_roots = NULL;
	gboolean success;

	success = camel_imapx_parse_quotaroot (
		CAMEL_IMAPX_INPUT_STREAM (input_stream),
		cancellable, &mailbox_name, &quota_roots, error);

	/* Sanity check */
	g_return_val_if_fail (
		(success && (mailbox_name != NULL)) ||
		(!success && (mailbox_name == NULL)), FALSE);

	if (!success)
		return FALSE;

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

	if (mailbox != NULL) {
		camel_imapx_mailbox_set_quota_roots (
			mailbox, (const gchar **) quota_roots);
		g_object_unref (mailbox);
	} else {
		g_warning (
			"%s: Unknown mailbox '%s'",
			G_STRFUNC, mailbox_name);
	}

	g_free (mailbox_name);
	g_strfreev (quota_roots);

	return TRUE;
}

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

camel_imapx_server_ref_pending_or_selected (CamelIMAPXServer *is)
{
	CamelIMAPXMailbox *mailbox;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

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

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

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

	return mailbox;
}

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

camel_imapx_server_ref_settings (CamelIMAPXServer *server)
{
	CamelIMAPXStore *store;
	CamelSettings *settings;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL);

	store = camel_imapx_server_ref_store (server);
	settings = camel_service_ref_settings (CAMEL_SERVICE (store));
	g_object_unref (store);

	return CAMEL_IMAPX_SETTINGS (settings);
}

Project: evolution-data-server

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

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

	g_warn_if_fail (is->priv->list_responses_hash == NULL);
	g_warn_if_fail (is->priv->list_responses == NULL);
	g_warn_if_fail (is->priv->lsub_responses == NULL);

	if (!camel_imapx_server_get_utf8_accept (is))
		utf7_pattern = camel_utf8_utf7 (in_pattern);

	if (is->priv->list_return_opts != NULL) {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s RETURN (%t)",
			utf7_pattern ? utf7_pattern : in_pattern, is->priv->list_return_opts);
	} else {
		is->priv->list_responses_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LIST, "LIST \"\" %s",
			utf7_pattern ? utf7_pattern : in_pattern);
	}

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

	camel_imapx_command_unref (ic);

	if (success && !is->priv->list_return_opts) {
		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LSUB, "LSUB \"\" %s",
			utf7_pattern ? utf7_pattern : in_pattern);

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

		camel_imapx_command_unref (ic);
	}

	g_free (utf7_pattern);

	if (is->priv->list_responses_hash) {
		CamelIMAPXStore *imapx_store;
		GSList *link;

		imapx_store = camel_imapx_server_ref_store (is);
		if (imapx_store) {
			/* Preserve order in which these had been received from the server */
			is->priv->list_responses = g_slist_reverse (is->priv->list_responses);
			is->priv->lsub_responses = g_slist_reverse (is->priv->lsub_responses);

			for (link = is->priv->list_responses; link; link = g_slist_next (link)) {
				CamelIMAPXListResponse *response = link->data;

				camel_imapx_store_handle_list_response (imapx_store, is, response);
			}

			for (link = is->priv->lsub_responses; link; link = g_slist_next (link)) {
				CamelIMAPXListResponse *response = link->data;

				camel_imapx_store_handle_lsub_response (imapx_store, is, response);
			}

			g_clear_object (&imapx_store);
		}

		g_hash_table_destroy (is->priv->list_responses_hash);
		is->priv->list_responses_hash = NULL;
		g_slist_free_full (is->priv->list_responses, g_object_unref);
		is->priv->list_responses = NULL;
		g_slist_free_full (is->priv->lsub_responses, g_object_unref);
		is->priv->lsub_responses = NULL;
	}

	return success;
}

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

camel_imapx_server_get_tagprefix (CamelIMAPXServer *is)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), 0);

	return is->priv->tagprefix;
}

Project: evolution-data-server

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_slice_new0 (IdleThreadData);
			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");

				idle_thread_data_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);
	g_object_unref (is);

	return FALSE;
}

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

imapx_untagged_preauth (CamelIMAPXServer *is,
                        GInputStream *input_stream,
                        GCancellable *cancellable,
                        GError **error)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	c (is->priv->tagprefix, "preauthenticated\n");
	if (is->priv->state < IMAPX_AUTHENTICATED)
		is->priv->state = IMAPX_AUTHENTICATED;

	return TRUE;
}

Project: evolution-data-server

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, "%u", ss->last);
		else
			camel_imapx_command_add (ic, "%u:%u", 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

imapx_untagged_namespace (CamelIMAPXServer *is,
                          GInputStream *input_stream,
                          GCancellable *cancellable,
                          GError **error)
{
	CamelIMAPXNamespaceResponse *response;
	CamelIMAPXStore *imapx_store;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	response = camel_imapx_namespace_response_new (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);

	if (response == NULL)
		return FALSE;

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

	g_clear_object (&imapx_store);
	g_object_unref (response);

	return TRUE;
}

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

imapx_untagged_bye (CamelIMAPXServer *is,
                    GInputStream *input_stream,
                    GCancellable *cancellable,
                    GError **error)
{
	guchar *token = NULL;
	gboolean success;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

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

	/* XXX It's weird to be setting an error on success,
	 *     but it's to indicate the server hung up on us. */
	if (success) {
		g_strstrip ((gchar *) token);

		c (is->priv->tagprefix, "BYE: %s\n", token);
		g_set_error (
			error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT,
			"IMAP server said BYE: %s", token);
	}

	g_free (token);

	is->priv->state = IMAPX_SHUTDOWN;

	return FALSE;
}

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

camel_imapx_server_register_untagged_handler (CamelIMAPXServer *is,
                                              const gchar *untagged_response,
                                              const CamelIMAPXUntaggedRespHandlerDesc *desc)
{
	const CamelIMAPXUntaggedRespHandlerDesc *previous = NULL;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);
	g_return_val_if_fail (untagged_response != NULL, NULL);
	/* desc may be NULL */

	previous = replace_untagged_descriptor (
		is->priv->untagged_handlers,
		untagged_response, desc);

	return previous;
}

Project: evolution-data-server

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;

	if (!camel_imapx_server_get_utf8_accept (is)) {
		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) {
		gboolean is_gmail_server = FALSE;

		if (g_strcasecmp (search_key, "BODY") == 0) {
			CamelIMAPXStore *imapx_store;

			imapx_store = camel_imapx_server_ref_store (is);
			if (imapx_store) {
				is_gmail_server = camel_imapx_store_is_gmail_server (imapx_store);
				g_object_unref (imapx_store);
			}
		}

		for (ii = 0; words[ii]; ii++) {
			guchar mask = is_gmail_server ? imapx_is_mask (words[ii]) : 0;
			if (is_gmail_server && !(mask & IMAPX_TYPE_ATOM_CHAR) && (mask & IMAPX_TYPE_TEXT_CHAR) != 0)
				camel_imapx_command_add (ic, " X-GM-RAW %s", words[ii]);
			else
				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

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);
			g_clear_object (&info);
		}

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

Project: evolution-data-server

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 = camel_folder_get_folder_summary (folder);

	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_get_uid (minfo));
			g_mutex_unlock (&is->priv->changes_lock);
		}

		g_clear_object (&minfo);
	}
}

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

imapx_refresh_info_uid_cmp (gconstpointer ap,
                            gconstpointer bp,
                            gboolean ascending)
{
	guint av, bv;

	av = g_ascii_strtoull ((const gchar *) ap, NULL, 10);
	bv = g_ascii_strtoull ((const gchar *) bp, NULL, 10);

	if (av < bv)
		return ascending ? -1 : 1;
	else if (av > bv)
		return ascending ? 1 : -1;
	else
		return 0;
}

Project: evolution-data-server

camel_imapx_server_can_use_idle (CamelIMAPXServer *is)
{
	gboolean use_idle;
	CamelIMAPXSettings *settings;

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

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

	/* Run IDLE if the server supports NOTIFY, to have
	   a constant read on the stream, thus to be notified. */
	if (!CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, NOTIFY) &&
	    !CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, IDLE)) {
		use_idle = FALSE;
	}

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

	return use_idle;
}

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

camel_imapx_server_schedule_idle_sync (CamelIMAPXServer *is,
				       CamelIMAPXMailbox *mailbox,
				       GCancellable *cancellable,
				       GError **error)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
	if (mailbox)
		g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);

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

	if (!camel_imapx_server_can_use_idle (is))
		return TRUE;

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

	if (is->priv->idle_state != IMAPX_IDLE_STATE_OFF) {
		g_warn_if_fail (is->priv->idle_state == IMAPX_IDLE_STATE_OFF);

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

		return FALSE;
	}

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

	is->priv->idle_cancellable = g_cancellable_new ();
	is->priv->idle_stamp++;

	if (is->priv->idle_pending) {
		g_source_destroy (is->priv->idle_pending);
		g_source_unref (is->priv->idle_pending);
	}

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

	is->priv->idle_state = IMAPX_IDLE_STATE_SCHEDULED;
	is->priv->idle_pending = g_timeout_source_new_seconds (IMAPX_IDLE_WAIT_SECONDS);
	g_source_set_callback (
		is->priv->idle_pending, imapx_server_run_idle_thread_cb,
		imapx_weak_ref_new (is), (GDestroyNotify) imapx_weak_ref_free);
	g_source_attach (is->priv->idle_pending, NULL);

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

	return TRUE;
}

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

camel_imapx_server_ref_selected (CamelIMAPXServer *is)
{
	CamelIMAPXMailbox *mailbox;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), NULL);

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

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

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

	return mailbox;
}

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

imapx_server_set_store (CamelIMAPXServer *server,
                        CamelIMAPXStore *store)
{
	g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));

	g_weak_ref_set (&server->priv->store, store);
}

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

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

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

	/* We don't want to select the mailbox to be unsubscribed. */
	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_UNSUBSCRIBE_MAILBOX, "UNSUBSCRIBE %M", mailbox);

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

	camel_imapx_command_unref (ic);

	if (success) {
		CamelIMAPXStore *imapx_store;

		/* Perform the same processing as imapx_untagged_list()
		 * would if the server notified us of an unsubscription. */

		imapx_store = camel_imapx_server_ref_store (is);

		camel_imapx_mailbox_unsubscribed (mailbox);
		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);

		g_clear_object (&imapx_store);
	}

	return success;
}

Project: evolution-data-server

camel_imapx_server_get_utf8_accept (CamelIMAPXServer *is)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	return is->priv->utf8_accept;
}

Project: evolution-data-server

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, TRUE, 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);

				if (select_pending)
					is->priv->last_selected_mailbox_change_stamp = camel_imapx_mailbox_get_change_stamp (select_pending);
				else
					is->priv->last_selected_mailbox_change_stamp = 0;
			}

			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_broken_cyrus = is->priv->is_broken_cyrus || imapx_server_check_is_broken_cyrus (is->priv->context->sinfo->text, &is->priv->is_cyrus);
				if (is->priv->is_broken_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

imapx_untagged_list (CamelIMAPXServer *is,
                     GInputStream *input_stream,
                     GCancellable *cancellable,
                     GError **error)
{
	CamelIMAPXListResponse *response;
	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;

	if (is->priv->list_responses_hash) {
		is->priv->list_responses = g_slist_prepend (is->priv->list_responses, response);
		g_hash_table_insert (is->priv->list_responses_hash, (gpointer) camel_imapx_list_response_get_mailbox_name (response), response);
	} else {
		CamelIMAPXStore *imapx_store;

		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_is_in_idle (CamelIMAPXServer *is)
{
	gboolean in_idle;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

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

	return in_idle;
}

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

create_initial_untagged_handler_table (void)
{
	GHashTable *uh = g_hash_table_new_full (
		camel_strcase_hash,
		camel_strcase_equal,
		g_free,
		NULL);
	guint32 ii = 0;

	/* CamelIMAPXServer predefined handlers*/
	for (ii = 0; ii < IMAPX_UNTAGGED_LAST_ID; ii++)
		add_initial_untagged_descriptor (uh, ii);

	g_return_val_if_fail (g_hash_table_size (uh) == IMAPX_UNTAGGED_LAST_ID, NULL);

	return uh;
}

Project: evolution-data-server

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);
	is->priv->last_selected_mailbox_change_stamp = 0;
	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->is_broken_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

imapx_uids_desc_cmp (gconstpointer ap,
		     gconstpointer bp)
{
	const gchar *a = (const gchar *) ap;
	const gchar *b = (const gchar *) bp;

	return imapx_refresh_info_uid_cmp (a, b, FALSE);
}

Project: evolution-data-server

imapx_untagged_vanished (CamelIMAPXServer *is,
                         GInputStream *input_stream,
                         GCancellable *cancellable,
                         GError **error)
{
	CamelFolder *folder;
	CamelIMAPXMailbox *mailbox;
	GatherExistingUidsData geud;
	gboolean unsolicited = TRUE;
	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);
	}

	g_return_val_if_fail (is->priv->changes != NULL, 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);

	geud.is = is;
	geud.summary = camel_folder_get_folder_summary (folder);
	geud.uid_list = NULL;
	geud.n_uids = 0;

	if (!imapx_parse_uids_with_callback (CAMEL_IMAPX_INPUT_STREAM (input_stream), imapx_gather_existing_uids_cb, &geud, cancellable, error)) {
		g_object_unref (folder);
		g_object_unref (mailbox);
		return FALSE;
	}

	/* It's locked by imapx_gather_existing_uids_cb() when the first known UID is found */
	if (geud.uid_list)
		g_mutex_unlock (&is->priv->changes_lock);

	if (unsolicited) {
		guint32 messages;

		messages = camel_imapx_mailbox_get_messages (mailbox);

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

		camel_imapx_mailbox_set_messages (mailbox, messages);
	}

	if (geud.uid_list) {
		geud.uid_list = g_list_reverse (geud.uid_list);
		camel_folder_summary_remove_uids (geud.summary, geud.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 (geud.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 (geud.uid_list, g_free);
	g_object_unref (folder);
	g_object_unref (mailbox);

	return TRUE;
}

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

camel_imapx_server_rename_mailbox_sync (CamelIMAPXServer *is,
					CamelIMAPXMailbox *mailbox,
					const gchar *new_mailbox_name,
					GCancellable *cancellable,
					GError **error)
{
	CamelIMAPXCommand *ic;
	CamelIMAPXMailbox *inbox;
	CamelIMAPXStore *imapx_store;
	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 (new_mailbox_name != NULL, FALSE);

	imapx_store = camel_imapx_server_ref_store (is);
	inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
	g_return_val_if_fail (inbox != NULL, FALSE);

	/* We don't want to select the mailbox to be renamed. */
	success = camel_imapx_server_ensure_selected_sync (is, inbox, cancellable, error);
	if (!success) {
		g_clear_object (&inbox);
		g_clear_object (&imapx_store);
		return FALSE;
	}

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_RENAME_MAILBOX, "RENAME %M %m", mailbox, new_mailbox_name);

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

	camel_imapx_command_unref (ic);

	if (success) {
		/* Perform the same processing as imapx_untagged_list()
		 * would if the server notified us of a renamed mailbox. */

		camel_imapx_store_handle_mailbox_rename (imapx_store, mailbox, new_mailbox_name);
	}

	g_clear_object (&inbox);
	g_clear_object (&imapx_store);

	return success;
}

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

camel_imapx_server_ref_store (CamelIMAPXServer *server)
{
	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (server), NULL);

	return g_weak_ref_get (&server->priv->store);
}

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

camel_imapx_server_delete_mailbox_sync (CamelIMAPXServer *is,
					CamelIMAPXMailbox *mailbox,
					GCancellable *cancellable,
					GError **error)
{
	CamelIMAPXCommand *ic;
	CamelIMAPXMailbox *inbox;
	CamelIMAPXStore *imapx_store;
	gboolean success;

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

	/* Avoid camel_imapx_job_set_mailbox() here.  We
	 * don't want to select the mailbox to be deleted. */

	imapx_store = camel_imapx_server_ref_store (is);
	/* Keep going, even if this returns NULL. */
	inbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");

	/* Make sure the to-be-deleted folder is not
	 * selected by selecting INBOX for this operation. */
	success = camel_imapx_server_ensure_selected_sync (is, inbox, cancellable, error);
	if (!success) {
		g_clear_object (&inbox);
		g_clear_object (&imapx_store);
		return FALSE;
	}

	/* Just to make sure it'll not disappeare before the end of this function */
	g_object_ref (mailbox);

	ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_DELETE_MAILBOX, "DELETE %M", mailbox);

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

	camel_imapx_command_unref (ic);

	if (success) {
		camel_imapx_mailbox_deleted (mailbox);
		camel_imapx_store_emit_mailbox_updated (imapx_store, mailbox);
	}

	g_clear_object (&inbox);
	g_clear_object (&imapx_store);
	g_clear_object (&mailbox);

	return success;
}

Project: evolution-data-server

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);

		idle_thread_data_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);

	idle_thread_data_free (itd);

	return NULL;
}

Project: evolution-data-server

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) {
		/* 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, mailbox_name, 0, cancellable, error);
	}

	return success;
}

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

camel_imapx_server_mailbox_selected (CamelIMAPXServer *is,
				     CamelIMAPXMailbox *mailbox)
{
	CamelIMAPXMailbox *selected_mailbox;
	gboolean res;

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

	g_mutex_lock (&is->priv->select_lock);
	selected_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
	res = selected_mailbox == mailbox;
	g_clear_object (&selected_mailbox);
	g_mutex_unlock (&is->priv->select_lock);

	return res;
}

Project: evolution-data-server

imapx_server_check_is_broken_cyrus (const gchar *response_text,
				    gboolean *inout_is_cyrus)
{
	const gchar *pp, *from;
	gint vermajor = 0, verminor = 0, vermicro = 0;

	g_return_val_if_fail (inout_is_cyrus != NULL, FALSE);

	/* If already known that this is cyrus server, then it had been
	   identified as a good server, thus just return here. */
	if (*inout_is_cyrus)
		return FALSE;

	if (!response_text || !*response_text)
		return FALSE;

	/* Expects "Cyrus IMAP v1.2.3", eventually "Cyrus IMAP 4.5.6" (with or without 'v' prefix) */
	pp = response_text;
	while (pp = camel_strstrcase (pp, "cyrus"), pp) {
		/* It's a whole word */
		if ((pp == response_text || g_ascii_isspace (pp[-1])) && g_ascii_isspace (pp[5]))
			break;
		pp++;
	}

	if (!pp)
		return FALSE;

	from = pp;

	/* In case there is the 'cyrus' word multiple times */
	while (pp = from, pp && *pp) {
		#define skip_word() \
			while (*pp && *pp != ' ') {	\
				pp++;			\
			}				\
							\
			if (!*pp)			\
				return TRUE;		\
							\
			pp++;

		/* Skip the 'Cyrus' word */
		skip_word ();

		/* Skip the 'IMAP' word */
		skip_word ();

		#undef skip_word

		/* Now is at version with or without 'v' prefix */
		if (*pp == 'v')
			pp++;

		if (sscanf (pp, "%d.%d.%d", &vermajor, &verminor, &vermicro) == 3) {
			*inout_is_cyrus = TRUE;
			break;
		}

		vermajor = 0;

		pp = from + 1;
		from = NULL;

		while (pp = camel_strstrcase (pp, "cyrus"), pp) {
			/* It's a whole word */
			if (g_ascii_isspace (pp[-1]) && g_ascii_isspace (pp[5])) {
				from = pp;
				break;
			}

			pp++;
		}
	}

	/* The 2.5.11, inclusive, has the issue fixed, thus check for that version. */
	return !(vermajor > 2 || (vermajor == 2 && (verminor > 5 || (verminor == 5 && vermicro >= 11))));
}

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

camel_imapx_server_lack_capability (CamelIMAPXServer *is,
				    guint32 capability)
{
	gboolean lack;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	g_mutex_lock (&is->priv->stream_lock);
	lack = is->priv->cinfo != NULL && (is->priv->cinfo->capa & capability) == 0;
	g_mutex_unlock (&is->priv->stream_lock);

	return lack;
}

Project: evolution-data-server

imapx_can_refresh_mailbox_in_idle (CamelIMAPXServer *imapx_server,
				   CamelIMAPXStore *imapx_store,
				   CamelIMAPXMailbox *mailbox)
{
	CamelIMAPXSettings *imapx_settings;
	gboolean can_refresh = FALSE;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), FALSE);
	g_return_val_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox), FALSE);

	if (camel_imapx_mailbox_is_inbox (camel_imapx_mailbox_get_name (mailbox)))
		return TRUE;

	imapx_settings = camel_imapx_server_ref_settings (imapx_server);

	if (camel_imapx_settings_get_use_subscriptions (imapx_settings)) {
		can_refresh = camel_imapx_mailbox_has_attribute (mailbox, CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED);
	} else if (camel_imapx_settings_get_check_all (imapx_settings)) {
		can_refresh = TRUE;
	} else if (camel_imapx_settings_get_check_subscribed (imapx_settings)) {
		can_refresh = camel_imapx_mailbox_has_attribute (mailbox, CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED);
	}

	if (!can_refresh &&
	    !camel_imapx_settings_get_use_subscriptions (imapx_settings)) {
		/* Refresh opened folders when viewing both subscribed and unsubscribed,
		   even if they would not be refreshed otherwise. */
		gchar *folder_path;

		can_refresh = FALSE;

		folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
		if (folder_path) {
			GPtrArray *opened_folders = camel_store_dup_opened_folders (CAMEL_STORE (imapx_store));

			if (opened_folders) {
				gint ii;

				for (ii = 0; !can_refresh && ii < opened_folders->len; ii++) {
					CamelFolder *folder = g_ptr_array_index (opened_folders, ii);

					can_refresh = g_strcmp0 (camel_folder_get_full_name (folder), folder_path) == 0;
				}

				g_ptr_array_foreach (opened_folders, (GFunc) g_object_unref, NULL);
				g_ptr_array_free (opened_folders, TRUE);
			}
		}

		g_free (folder_path);
	}

	g_clear_object (&imapx_settings);

	return can_refresh;
}

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

imapx_weak_ref_free (GWeakRef *weak_ref)
{
	g_return_if_fail (weak_ref != NULL);

	/* XXX Might want to expose this in Camel's public API if it
	 *     proves useful elsewhere.  Based on e_weak_ref_free(). */

	g_weak_ref_clear (weak_ref);
	g_slice_free (GWeakRef, weak_ref);
}

Project: evolution-data-server

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) {
			/* some infos could be only 'dirty' (needed to save into summary) */
			if (camel_message_info_get_folder_flagged (info) &&
			   (!except_deleted_messages || (camel_message_info_get_flags (info) & CAMEL_MESSAGE_DELETED) == 0)) {
				camel_message_info_set_folder_flagged (info, FALSE);
				changed = TRUE;
			}

			g_clear_object (&info);
		}
	}

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

Project: evolution-data-server

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);

	is->priv->utf8_accept = FALSE;

	/* RFC 6855 */
	if (CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, UTF8_ACCEPT) ||
	    CAMEL_IMAPX_HAVE_CAPABILITY (is->priv->cinfo, UTF8_ONLY)) {
		GError *local_error = NULL;

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

		ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NAMESPACE, "ENABLE UTF8=ACCEPT");
		camel_imapx_server_process_command_sync (is, ic, _("Failed to issue ENABLE UTF8=ACCEPT"), 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->utf8_accept = TRUE;

		if (CAMEL_IS_IMAPX_INPUT_STREAM (is->priv->input_stream))
			camel_imapx_input_stream_set_utf8_accept (CAMEL_IMAPX_INPUT_STREAM (is->priv->input_stream), TRUE);
	}

	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);

		#define NOTIFY_CMD(x) "NOTIFY SET " \
			"(selected " \
			"(MessageNew (UID RFC822.SIZE RFC822.HEADER FLAGS" x ")" \
			" MessageExpunge" \
			" FlagChange)) " \
			"(personal " \
			"(MessageNew" \
			" MessageExpunge" \
			" MailboxName" \
			" SubscriptionChange))"

		/* XXX The list of FETCH attributes is negotiable. */
		if (camel_imapx_store_get_bodystructure_enabled (store))
			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOTIFY, NOTIFY_CMD (" BODYSTRUCTURE"));
		else
			ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_NOTIFY, NOTIFY_CMD (""));
		camel_imapx_server_process_command_sync (is, ic, _("Failed to issue NOTIFY"), cancellable, &local_error);
		camel_imapx_command_unref (ic);

		#undef NOTIFY_CMD

		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

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;
	is->priv->is_broken_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

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

	if (nfo) {
		camel_named_flags_free (nfo->server_user_flags);
		g_slice_free (FetchChangesInfo, nfo);
	}
}