static gboolean
nntp_folder_refresh_info_sync (CamelFolder *folder,
                               GCancellable *cancellable,
                               GError **error)
{
	CamelStore *parent_store;
	CamelNNTPStore *nntp_store;
	CamelFolderChangeInfo *changes = NULL;
	CamelNNTPFolder *nntp_folder;
	gchar *line;
	gboolean success;

	parent_store = camel_folder_get_parent_store (folder);

	nntp_folder = CAMEL_NNTP_FOLDER (folder);
	nntp_store = CAMEL_NNTP_STORE (parent_store);

	/* When invoked with no fmt, camel_nntp_command() just selects the folder
	 * and should return zero. */
	success = !camel_nntp_command (
		nntp_store, cancellable, error, nntp_folder, &line, NULL);

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

	if (changes) {
		camel_folder_changed (folder, changes);
		camel_folder_change_info_free (changes);
	}

	return success;
}
static void
nntp_folder_refresh_info_online (CamelFolder *folder, CamelException *ex)
{
	CamelNNTPStore *nntp_store;
	CamelFolderChangeInfo *changes = NULL;
	CamelNNTPFolder *nntp_folder;
	char *line;

	nntp_store = (CamelNNTPStore *) folder->parent_store;
	nntp_folder = (CamelNNTPFolder *) folder;

	CAMEL_SERVICE_REC_LOCK(nntp_store, connect_lock);

	camel_nntp_command(nntp_store, ex, nntp_folder, &line, NULL);

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

	CAMEL_SERVICE_REC_UNLOCK(nntp_store, connect_lock);

	if (changes) {
		camel_object_trigger_event ((CamelObject *) folder, "folder_changed", changes);
		camel_folder_change_info_free (changes);
	}
}
gboolean
camel_nntp_folder_selected (CamelNNTPFolder *nntp_folder,
                            gchar *line,
                            GCancellable *cancellable,
                            GError **error)
{
	CamelFolder *folder;
	CamelStore *parent_store;
	gboolean res;

	folder = CAMEL_FOLDER (nntp_folder);
	parent_store = camel_folder_get_parent_store (folder);

	res = camel_nntp_summary_check (
		CAMEL_NNTP_SUMMARY (folder->summary),
		CAMEL_NNTP_STORE (parent_store),
		line, nntp_folder->changes,
		cancellable, error);

	if (camel_folder_change_info_changed (nntp_folder->changes)) {
		CamelFolderChangeInfo *changes;

		changes = nntp_folder->changes;
		nntp_folder->changes = camel_folder_change_info_new ();

		camel_folder_changed (CAMEL_FOLDER (nntp_folder), changes);
		camel_folder_change_info_free (changes);
	}

	return res;
}
void
groupwise_summary_clear (CamelFolderSummary *summary, gboolean uncache)
{
	CamelFolderChangeInfo *changes;
	CamelMessageInfo *info;
	int i, count;
	const char *uid;

	changes = camel_folder_change_info_new ();
	count = camel_folder_summary_count (summary);
	for (i = 0; i < count; i++) {
		if (!(info = camel_folder_summary_index (summary, i)))
			continue;

		uid = camel_message_info_uid (info);
		camel_folder_change_info_remove_uid (changes, uid);
		camel_folder_summary_remove_uid (summary, uid);
		camel_message_info_free(info);
	}

	camel_folder_summary_clear (summary);
	camel_folder_summary_save (summary);

	if (uncache)
		camel_data_cache_clear (((CamelGroupwiseFolder *) summary->folder)->cache, "cache", NULL);

	if (camel_folder_change_info_changed (changes))
		camel_object_trigger_event (summary->folder, "folder_changed", changes);
	camel_folder_change_info_free (changes);
}
static void
nntp_folder_init (CamelNNTPFolder *nntp_folder, CamelNNTPFolderClass *klass)
{
	struct _CamelNNTPFolderPrivate *p;

	nntp_folder->changes = camel_folder_change_info_new ();
	p = nntp_folder->priv = g_malloc0 (sizeof (*nntp_folder->priv));
	p->search_lock = g_mutex_new ();
	p->cache_lock = g_mutex_new ();
}
static gboolean
gw_info_set_flags (CamelMessageInfo *info, guint32 flags, guint32 set)
{
	guint32 old;
	CamelMessageInfoBase *mi = (CamelMessageInfoBase *)info;

	/* TODO: locking? */

	old = mi->flags;
	/* we don't set flags which aren't appropriate for the folder*/
	if ((set == (CAMEL_MESSAGE_JUNK|CAMEL_MESSAGE_JUNK_LEARN|CAMEL_MESSAGE_SEEN)) && (old & CAMEL_GW_MESSAGE_JUNK))
		return FALSE;

	mi->flags = (old & ~flags) | (set & flags);
	if (old != mi->flags) {
		mi->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
		if (mi->summary)
			camel_folder_summary_touch(mi->summary);
	}
	/* This is a hack, we are using CAMEL_MESSAGE_JUNK justo to hide the item
	 * we make sure this doesn't have any side effects*/

	if ((set == CAMEL_MESSAGE_JUNK_LEARN) && (old & CAMEL_GW_MESSAGE_JUNK)) {
		mi->flags |= CAMEL_GW_MESSAGE_NOJUNK | CAMEL_MESSAGE_JUNK;

		/* This has ugly side-effects. Evo will never learn unjunk.

		   We need to create one CAMEL_MESSAGE_HIDDEN flag which must be used for all hiding operations. We must also get rid of the seperate file that is maintained somewhere in evolution/mail/em-folder-browser.c for hidden messages
		 */

		if (mi->summary) {
			camel_folder_summary_touch(mi->summary);
		}

	} else	if ((old & ~CAMEL_MESSAGE_SYSTEM_MASK) == (mi->flags & ~CAMEL_MESSAGE_SYSTEM_MASK))
		return FALSE;

	if (mi->summary && mi->summary->folder && mi->uid) {
		CamelFolderChangeInfo *changes = camel_folder_change_info_new();

		camel_folder_change_info_change_uid(changes, camel_message_info_uid(info));
		camel_object_trigger_event(mi->summary->folder, "folder_changed", changes);
		camel_folder_change_info_free(changes);
	}

	return TRUE;

}
static gboolean
nntp_folder_expunge_sync (CamelFolder *folder,
                          GCancellable *cancellable,
                          GError **error)
{
	CamelFolderSummary *summary;
	CamelFolderChangeInfo *changes;
	GPtrArray *known_uids;
	guint ii;

	summary = folder->summary;

	camel_folder_summary_prepare_fetch_all (summary, NULL);
	known_uids = camel_folder_summary_get_array (summary);

	if (known_uids == NULL)
		return TRUE;

	changes = camel_folder_change_info_new ();

	for (ii = 0; ii < known_uids->len; ii++) {
		CamelMessageInfo *info;
		const gchar *uid;

		uid = g_ptr_array_index (known_uids, ii);
		info = camel_folder_summary_get (summary, uid);

		if (camel_message_info_flags (info) & CAMEL_MESSAGE_DELETED) {
			camel_folder_change_info_remove_uid (changes, uid);
			camel_folder_summary_remove (summary, info);
		}

		camel_message_info_unref (info);
	}

	camel_folder_summary_save_to_db (summary, NULL);
	camel_folder_changed (folder, changes);

	camel_folder_change_info_free (changes);
	camel_folder_summary_free_array (known_uids);

	return TRUE;
}
static void
offline_folder_changed (CamelFolder *folder, CamelFolderChangeInfo *changes, void *dummy)
{
	CamelOfflineFolder *offline = (CamelOfflineFolder *) folder;
	CamelService *service = (CamelService *) folder->parent_store;

	if (changes->uid_added->len > 0 && (offline->sync_offline || camel_url_get_param (service->url, "sync_offline"))) {
		CamelSession *session = service->session;
		struct _offline_downsync_msg *m;

		m = camel_session_thread_msg_new (session, &offline_downsync_ops, sizeof (*m));
		m->changes = camel_folder_change_info_new ();
		camel_folder_change_info_cat (m->changes, changes);
		camel_object_ref (folder);
		m->folder = folder;

		camel_session_thread_queue (session, &m->msg, 0);
	}
}
static gboolean
vee_info_set_flags (CamelMessageInfo *mi,
                    guint32 flags,
                    guint32 set)
{
	gint res = FALSE;
	CamelVeeFolder *vf = (CamelVeeFolder *)mi->summary->folder;
	gboolean hacked_unread_folder = FALSE;

	if (camel_debug("vfolderexp"))
		printf (
			"Expression for vfolder '%s' is '%s'\n",
			camel_folder_get_full_name (mi->summary->folder),
			g_strescape (vf->expression, ""));

	if (camel_vee_folder_get_unread_vfolder (vf) == -1)
		camel_vee_summary_load_check_unread_vfolder (CAMEL_VEE_SUMMARY (mi->summary));

	if (camel_vee_folder_get_unread_vfolder (vf) == 1)
		hacked_unread_folder = TRUE;

	if (mi->uid) {
		guint32 old_visible, visible, old_unread;
		CamelMessageInfo *rmi = camel_folder_summary_uid (((CamelVeeMessageInfo *)mi)->summary, mi->uid+8);
		CamelVeeSummary *vsummary = (CamelVeeSummary *)mi->summary;

		HANDLE_NULL_INFO (FALSE);

		old_visible = rmi->summary->visible_count;
		old_unread = mi->summary->unread_count;
		camel_folder_summary_update_counts_by_flags (mi->summary, camel_message_info_flags (rmi), TRUE);

		if (hacked_unread_folder)
			camel_vee_folder_mask_event_folder_changed ((CamelVeeFolder *)mi->summary->folder, rmi->summary->folder);

		camel_folder_freeze (rmi->summary->folder);
		res = camel_message_info_set_flags (rmi, flags, set);
		((CamelVeeMessageInfo *) mi)->old_flags = camel_message_info_flags (rmi);
		camel_folder_thaw (rmi->summary->folder);

		if (hacked_unread_folder)
			camel_vee_folder_unmask_event_folder_changed ((CamelVeeFolder *)mi->summary->folder, rmi->summary->folder);

		visible = rmi->summary->visible_count;

		/* Keep the summary in sync */
		camel_folder_summary_update_counts_by_flags (mi->summary, camel_message_info_flags (rmi), FALSE);

		if (hacked_unread_folder && !vsummary->fake_visible_count)
			vsummary->fake_visible_count = mi->summary->visible_count;

		if (vsummary->fake_visible_count || hacked_unread_folder)
			vsummary->fake_visible_count += visible - old_visible;

		d(printf("VF %d %d %d %d %d\n", mi->summary->unread_count, mi->summary->deleted_count, mi->summary->junk_count, mi->summary->junk_not_deleted_count, mi->summary->visible_count));

		/* This is where the ugly-created-hack is used */
		if (hacked_unread_folder && mi->summary->unread_count - old_unread != 0) {
			CamelFolderChangeInfo *changes = camel_folder_change_info_new ();
			GPtrArray *match, *array;

			camel_folder_change_info_change_uid (changes, mi->uid);

			array = g_ptr_array_new ();
			g_ptr_array_add (array, (gpointer)rmi->uid);

			match = camel_folder_search_by_uids (rmi->summary->folder, vf->expression, array, NULL);
			if ((match && !match->len) || !match) {
				vsummary->fake_visible_count--;
			} else {
				vsummary->fake_visible_count++;
			}

			g_ptr_array_free (array, TRUE);
			if (match)
				camel_folder_search_free (rmi->summary->folder, match);

			camel_folder_changed (mi->summary->folder, changes);
			camel_folder_change_info_free (changes);
		}
		camel_message_info_free (rmi);
	}

	return res;
}
static CamelMimeMessage *
nntp_folder_get_message_sync (CamelFolder *folder,
                              const gchar *uid,
                              GCancellable *cancellable,
                              GError **error)
{
	CamelStore *parent_store;
	CamelMimeMessage *message = NULL;
	CamelDataCache *nntp_cache;
	CamelNNTPStore *nntp_store;
	CamelFolderChangeInfo *changes;
	CamelNNTPFolder *nntp_folder;
	CamelStream *stream = NULL;
	GIOStream *base_stream;
	gchar *article, *msgid;
	gsize article_len;

	parent_store = camel_folder_get_parent_store (folder);

	nntp_folder = CAMEL_NNTP_FOLDER (folder);
	nntp_store = CAMEL_NNTP_STORE (parent_store);

	article_len = strlen (uid) + 1;
	article = alloca (article_len);
	g_strlcpy (article, uid, article_len);
	msgid = strchr (article, ',');
	if (msgid == NULL) {
		g_set_error (
			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
			_("Internal error: UID in invalid format: %s"), uid);
		return NULL;
	}
	*msgid++ = 0;

	/* Lookup in cache, NEWS is global messageid's so use a global cache path */
	nntp_cache = camel_nntp_store_ref_cache (nntp_store);
	base_stream = camel_data_cache_get (nntp_cache, "cache", msgid, NULL);
	g_clear_object (&nntp_cache);

	if (base_stream != NULL) {
		stream = camel_stream_new (base_stream);
		g_object_unref (base_stream);
	} else {
		CamelServiceConnectionStatus connection_status;

		connection_status = camel_service_get_connection_status (
			CAMEL_SERVICE (parent_store));

		if (connection_status != CAMEL_SERVICE_CONNECTED) {
			g_set_error (
				error, CAMEL_SERVICE_ERROR,
				CAMEL_SERVICE_ERROR_UNAVAILABLE,
				_("This message is not currently available"));
			goto fail;
		}

		stream = nntp_folder_download_message (nntp_folder, article, msgid, cancellable, error);
		if (stream == NULL)
			goto fail;
	}

	message = camel_mime_message_new ();
	if (!camel_data_wrapper_construct_from_stream_sync ((CamelDataWrapper *) message, stream, cancellable, error)) {
		g_prefix_error (error, _("Cannot get message %s: "), uid);
		g_object_unref (message);
		message = NULL;
	}

	g_object_unref (stream);
fail:
	if (camel_folder_change_info_changed (nntp_folder->changes)) {
		changes = nntp_folder->changes;
		nntp_folder->changes = camel_folder_change_info_new ();
	} else {
		changes = NULL;
	}

	if (changes) {
		camel_folder_changed (folder, changes);
		camel_folder_change_info_free (changes);
	}

	return message;
}
static CamelMimeMessage *
nntp_folder_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
{
	CamelMimeMessage *message = NULL;
	CamelNNTPStore *nntp_store;
	CamelFolderChangeInfo *changes;
	CamelNNTPFolder *nntp_folder;
	CamelStream *stream = NULL;
	char *article, *msgid;

	nntp_store = (CamelNNTPStore *) folder->parent_store;
	nntp_folder = (CamelNNTPFolder *) folder;

	article = alloca(strlen(uid)+1);
	strcpy(article, uid);
	msgid = strchr (article, ',');
	if (msgid == NULL) {
		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
				      _("Internal error: UID in invalid format: %s"), uid);
		return NULL;
	}
	*msgid++ = 0;

	CAMEL_SERVICE_REC_LOCK(nntp_store, connect_lock);

	/* Lookup in cache, NEWS is global messageid's so use a global cache path */
	stream = camel_data_cache_get (nntp_store->cache, "cache", msgid, NULL);
	if (stream == NULL) {
		if (camel_disco_store_status ((CamelDiscoStore *) nntp_store) == CAMEL_DISCO_STORE_OFFLINE) {
			camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
					     _("This message is not currently available"));
			goto fail;
		}

		stream = nntp_folder_download_message (nntp_folder, article, msgid, ex);
		if (stream == NULL)
			goto fail;
	}

	message = camel_mime_message_new ();
	if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream) == -1) {
		if (errno == EINTR)
			camel_exception_setv (ex, CAMEL_EXCEPTION_USER_CANCEL, _("User canceled"));
		else
			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, g_strerror (errno));
		camel_object_unref(message);
		message = NULL;
	}

	camel_object_unref (stream);
fail:
	if (camel_folder_change_info_changed (nntp_folder->changes)) {
		changes = nntp_folder->changes;
		nntp_folder->changes = camel_folder_change_info_new ();
	} else {
		changes = NULL;
	}

	CAMEL_SERVICE_REC_UNLOCK(nntp_store, connect_lock);

	if (changes) {
		camel_object_trigger_event ((CamelObject *) folder, "folder_changed", changes);
		camel_folder_change_info_free (changes);
	}

	return message;
}