예제 #1
0
static void
empty_cache (ECalBackendHttp *cbhttp)
{
	ECalBackendHttpPrivate *priv;
	GSList *comps, *l;

	priv = cbhttp->priv;

	if (!priv->store)
		return;

	comps = e_cal_backend_store_get_components (priv->store);

	for (l = comps; l != NULL; l = g_slist_next (l)) {
		ECalComponentId *id;
		ECalComponent *comp = l->data;

		id = e_cal_component_get_id (comp);

		e_cal_backend_notify_component_removed ((ECalBackend *) cbhttp, id, comp, NULL);

		e_cal_component_free_id (id);
		g_object_unref (comp);
	}
	g_slist_free (comps);

	e_cal_backend_store_put_key_value (priv->store, "ETag", NULL);
	e_cal_backend_store_clean (priv->store);
}
예제 #2
0
static void
e_cal_backend_http_start_view (ECalBackend *backend,
                               EDataCalView *query)
{
	ECalBackendHttp *cbhttp;
	ECalBackendHttpPrivate *priv;
	GSList *components, *l;
	GSList *objects = NULL;
	ECalBackendSExp *cbsexp;
	ETimezoneCache *timezone_cache;
	time_t occur_start = -1, occur_end = -1;
	gboolean prunning_by_time;

	cbhttp = E_CAL_BACKEND_HTTP (backend);
	priv = cbhttp->priv;

	timezone_cache = E_TIMEZONE_CACHE (backend);

	cbsexp = e_data_cal_view_get_sexp (query);

	d (g_message (G_STRLOC ": Starting query (%s)", e_cal_backend_sexp_text (cbsexp)));

	if (!priv->store) {
		GError *error = EDC_ERROR (NoSuchCal);
		e_data_cal_view_notify_complete (query, error);
		g_error_free (error);
		return;
	}

	/* process all components in the cache */
	objects = NULL;
	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (
		cbsexp,
		&occur_start,
		&occur_end);

	components = prunning_by_time ?
		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
		: e_cal_backend_store_get_components (priv->store);

	for (l = components; l != NULL; l = g_slist_next (l)) {
		ECalComponent *comp = l->data;

		if (e_cal_backend_sexp_match_comp (cbsexp, comp, timezone_cache)) {
			objects = g_slist_append (objects, comp);
		}
	}

	e_data_cal_view_notify_components_added (query, objects);

	g_slist_free_full (components, g_object_unref);
	g_slist_free (objects);

	e_data_cal_view_notify_complete (query, NULL /* Success */);
}
예제 #3
0
/* Get_objects_in_range handler for the file backend */
static void
e_cal_backend_http_get_object_list (ECalBackendSync *backend,
                                    EDataCal *cal,
                                    GCancellable *cancellable,
                                    const gchar *sexp,
                                    GSList **objects,
                                    GError **perror)
{
	ECalBackendHttp *cbhttp;
	ECalBackendHttpPrivate *priv;
	GSList *components, *l;
	ECalBackendSExp *cbsexp;
	ETimezoneCache *timezone_cache;
	time_t occur_start = -1, occur_end = -1;
	gboolean prunning_by_time;

	cbhttp = E_CAL_BACKEND_HTTP (backend);
	priv = cbhttp->priv;

	timezone_cache = E_TIMEZONE_CACHE (backend);

	if (!priv->store) {
		g_propagate_error (perror, EDC_ERROR (NoSuchCal));
		return;
	}

	/* process all components in the cache */
	cbsexp = e_cal_backend_sexp_new (sexp);

	*objects = NULL;
	prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (
		cbsexp,
		&occur_start,
		&occur_end);

	components = prunning_by_time ?
		e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
		: e_cal_backend_store_get_components (priv->store);

	for (l = components; l != NULL; l = g_slist_next (l)) {
		if (e_cal_backend_sexp_match_comp (cbsexp, E_CAL_COMPONENT (l->data), timezone_cache)) {
			*objects = g_slist_append (*objects, e_cal_component_get_as_string (l->data));
		}
	}

	g_slist_foreach (components, (GFunc) g_object_unref, NULL);
	g_slist_free (components);
	g_object_unref (cbsexp);
}
예제 #4
0
/** Wrapper for e_cal_backend_store_get_components().
 *
 * Get all components from cache. Components with cache state
 * E_CAL_COMPONENT_CACHE_STATE_REMOVED will be omitted.
 *
 * @param cb 3E calendar backend.
 * @param cache Calendar backend cache object.
 *
 * @return List of ECalComponent objects.
 */
GSList *e_cal_backend_3e_store_get_components(ECalBackend3e *cb, ECalBackendStore *store)
{
    GSList *iter, *iter_next;
    GSList *list;

    g_static_rw_lock_reader_lock(&cb->priv->cache_lock);
    list = e_cal_backend_store_get_components(store);
    g_static_rw_lock_reader_unlock(&cb->priv->cache_lock);
    for (iter = list; iter; iter = iter_next)
    {
        ECalComponent *comp = E_CAL_COMPONENT(iter->data);
        iter_next = iter->next;

        if (e_cal_component_get_cache_state(comp) == E_CAL_COMPONENT_CACHE_STATE_REMOVED)
        {
            list = g_slist_remove_link(list, iter);
            g_object_unref(comp);
        }
    }

    return list;
}
예제 #5
0
/** Sync cache changes to the server and unmark them.
 *
 * @param cb 3E calendar backend.
 *
 * @return TRUE on success.
 *
 * @todo Conflict resolution.
 */
gboolean e_cal_backend_3e_sync_cache_to_server(ECalBackend3e *cb)
{
    GError *local_err = NULL;
    GSList *components, *iter;

    if (!e_cal_backend_3e_open_connection(cb, &local_err))
    {
        g_warning("Sync failed. Can't open connection to the 3e server. (%s)", local_err ? local_err->message : "Unknown error");
        g_clear_error(&local_err);
        return FALSE;
    }

    g_static_rw_lock_reader_lock(&cb->priv->cache_lock);
    components = e_cal_backend_store_get_components(cb->priv->store);
    g_static_rw_lock_reader_unlock(&cb->priv->cache_lock);

    for (iter = components; iter && !e_cal_backend_3e_sync_should_stop(cb); iter = iter->next)
    {
        ECalComponent *comp = E_CAL_COMPONENT(iter->data);
        ECalComponent *remote_comp;
        ECalComponentId *id = e_cal_component_get_id(comp);
        ECalComponentVType type = e_cal_component_get_vtype (comp);
        ECalComponentCacheState state = e_cal_component_get_cache_state(comp);

        /* remove client properties before sending component to the server */
        e_cal_component_set_outofsync (comp, FALSE);
        e_cal_component_set_cache_state(comp, E_CAL_COMPONENT_CACHE_STATE_NONE);

        remote_comp = e_cal_component_clone(comp);
        char *remote_object = e_cal_component_get_as_string(remote_comp);
        char *object = e_cal_component_get_as_string(comp);

        if (type == E_CAL_COMPONENT_EVENT && !e_cal_backend_3e_convert_attachment_uris_to_remote(cb, remote_comp))
            goto next;

        if (type == E_CAL_COMPONENT_EVENT && (state == E_CAL_COMPONENT_CACHE_STATE_CREATED || state == E_CAL_COMPONENT_CACHE_STATE_MODIFIED))
        {
            if (!e_cal_backend_3e_upload_attachments(cb, remote_comp, &local_err))
            {
                e_cal_backend_notify_gerror_error(E_CAL_BACKEND(cb), "3e attachemnts sync failure", local_err);
                g_clear_error(&local_err);
                goto next;
            }

            /* add timezone */
            const icaltimezone *zone = NULL;
            ECalComponentDateTime datetime;

            e_cal_component_get_dtstart (comp, &datetime);
            g_static_rw_lock_reader_lock (&cb->priv->cache_lock);
            zone = e_cal_backend_store_get_timezone (cb->priv->store, datetime.tzid);
            g_static_rw_lock_reader_unlock (&cb->priv->cache_lock);
            e_cal_component_free_datetime (&datetime);

            if (zone)
            {
                icalcomponent *zone_comp = icaltimezone_get_component (zone);
                char *object = icalcomponent_as_ical_string (zone_comp);

                ESClient_addObject (cb->priv->conn, cb->priv->calspec, object, &local_err);
                if (local_err)
                    g_clear_error (&local_err);
            }
        }

        switch (state)
        {
        case E_CAL_COMPONENT_CACHE_STATE_CREATED:
        {
            ESClient_addObject(cb->priv->conn, cb->priv->calspec, remote_object, &local_err);
            if (local_err)
            {
                e_cal_backend_notify_gerror_error(E_CAL_BACKEND(cb), "3e sync failure", local_err);
                g_clear_error(&local_err);
                break;
            }

            char *new_object = e_cal_component_get_as_string(comp);
            e_cal_backend_notify_object_modified(E_CAL_BACKEND(cb), object, new_object);
            g_free(new_object);

            g_static_rw_lock_writer_lock(&cb->priv->cache_lock);
            e_cal_backend_store_put_component(cb->priv->store, comp);
            g_static_rw_lock_writer_unlock(&cb->priv->cache_lock);
            break;
        }

        case E_CAL_COMPONENT_CACHE_STATE_MODIFIED:
        {
            ESClient_updateObject(cb->priv->conn, cb->priv->calspec, remote_object, &local_err);
            if (local_err)
            {
                e_cal_backend_notify_gerror_error(E_CAL_BACKEND(cb), "3e sync failure", local_err);
                g_clear_error(&local_err);
                break;
            }

            char *new_object = e_cal_component_get_as_string(comp);
            e_cal_backend_notify_object_modified(E_CAL_BACKEND(cb), object, new_object);
            g_free(new_object);

            g_static_rw_lock_writer_lock(&cb->priv->cache_lock);
            e_cal_backend_store_put_component(cb->priv->store, comp);
            g_static_rw_lock_writer_unlock(&cb->priv->cache_lock);
            break;
        }

        case E_CAL_COMPONENT_CACHE_STATE_REMOVED:
        {
            char *oid = id->rid ? g_strdup_printf("%s@%s", id->uid, id->rid) : g_strdup(id->uid);
            ESClient_deleteObject(cb->priv->conn, cb->priv->calspec, oid, &local_err);
            g_free(oid);
            if (local_err)
            {
                // ignore the error if component doesn't exist anymore
                if (local_err->code == ES_XMLRPC_ERROR_UNKNOWN_COMPONENT)
                {
                    g_clear_error(&local_err);
                    local_err = NULL;
                }
                else 
                {
                    e_cal_backend_notify_gerror_error(E_CAL_BACKEND(cb), "3e sync failure", local_err);
                    g_clear_error(&local_err);
                    break;
                }
            }

            g_static_rw_lock_writer_lock(&cb->priv->cache_lock);
            e_cal_backend_store_remove_component(cb->priv->store, id->uid, id->rid);
            g_static_rw_lock_writer_unlock(&cb->priv->cache_lock);
            break;
        }

        case E_CAL_COMPONENT_CACHE_STATE_NONE:
        default:
            break;
        }

next:
        g_object_unref(comp);
        g_object_unref(remote_comp);
        e_cal_component_free_id(id);
        g_free(object);
        g_free(remote_object);
    }

    g_slist_free(components);

    e_cal_backend_3e_close_connection(cb);

    return TRUE;
}
예제 #6
0
static gboolean
cal_backend_http_load (ECalBackendHttp *backend,
                       const gchar *uri,
		       gchar **out_certificate_pem,
		       GTlsCertificateFlags *out_certificate_errors,
                       GCancellable *cancellable,
                       GError **error)
{
	ECalBackendHttpPrivate *priv = backend->priv;
	ETimezoneCache *timezone_cache;
	SoupMessage *soup_message;
	SoupSession *soup_session;
	icalcomponent *icalcomp, *subcomp;
	icalcomponent_kind kind;
	const gchar *newuri;
	SoupURI *uri_parsed;
	GHashTable *old_cache;
	GSList *comps_in_cache;
	ESource *source;
	guint status_code;
	gulong cancel_id = 0;

	struct {
		SoupSession *soup_session;
		SoupMessage *soup_message;
	} cancel_data;

	timezone_cache = E_TIMEZONE_CACHE (backend);

	soup_session = backend->priv->soup_session;
	soup_message = cal_backend_http_new_message (backend, uri);

	if (soup_message == NULL) {
		g_set_error (
			error, SOUP_HTTP_ERROR,
			SOUP_STATUS_MALFORMED,
			_("Malformed URI: %s"), uri);
		return FALSE;
	}

	if (G_IS_CANCELLABLE (cancellable)) {
		cancel_data.soup_session = soup_session;
		cancel_data.soup_message = soup_message;

		cancel_id = g_cancellable_connect (
			cancellable,
			G_CALLBACK (cal_backend_http_cancelled),
			&cancel_data, (GDestroyNotify) NULL);
	}

	source = e_backend_get_source (E_BACKEND (backend));

	e_soup_ssl_trust_connect (soup_message, source);

	e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);

	status_code = soup_session_send_message (soup_session, soup_message);

	if (G_IS_CANCELLABLE (cancellable))
		g_cancellable_disconnect (cancellable, cancel_id);

	if (status_code == SOUP_STATUS_NOT_MODIFIED) {
		e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);

		/* attempts with ETag can result in 304 status code */
		g_object_unref (soup_message);
		priv->opened = TRUE;
		return TRUE;
	}

	/* Handle redirection ourselves */
	if (SOUP_STATUS_IS_REDIRECTION (status_code)) {
		gboolean success;

		newuri = soup_message_headers_get_list (
			soup_message->response_headers, "Location");

		d (g_message ("Redirected from %s to %s\n", async_context->uri, newuri));

		if (newuri != NULL) {
			gchar *redirected_uri;

			if (newuri[0]=='/') {
				g_warning ("Hey! Relative URI returned! Working around...\n");

				uri_parsed = soup_uri_new (uri);
				soup_uri_set_path (uri_parsed, newuri);
				soup_uri_set_query (uri_parsed, NULL);
				/* g_free (newuri); */

				newuri = soup_uri_to_string (uri_parsed, FALSE);
				g_message ("Translated URI: %s\n", newuri);
				soup_uri_free (uri_parsed);
			}

			redirected_uri =
				webcal_to_http_method (newuri, FALSE);
			success = cal_backend_http_load (
				backend, redirected_uri, out_certificate_pem, out_certificate_errors, cancellable, error);
			g_free (redirected_uri);

		} else {
			g_set_error (
				error, SOUP_HTTP_ERROR,
				SOUP_STATUS_BAD_REQUEST,
				_("Redirected to Invalid URI"));
			success = FALSE;
		}

		if (success) {
			e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
		} else {
			e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
		}

		g_object_unref (soup_message);
		return success;
	}

	/* check status code */
	if (!SOUP_STATUS_IS_SUCCESSFUL (status_code)) {
		/* because evolution knows only G_IO_ERROR_CANCELLED */
		if (status_code == SOUP_STATUS_CANCELLED)
			g_set_error (
				error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
				"%s", soup_message->reason_phrase);
		else
			g_set_error (
				error, SOUP_HTTP_ERROR, status_code,
				"%s", soup_message->reason_phrase);

		if (status_code == SOUP_STATUS_SSL_FAILED) {
			e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
			cal_backend_http_extract_ssl_failed_data (soup_message, out_certificate_pem, out_certificate_errors);
		} else {
			e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
		}

		g_object_unref (soup_message);
		empty_cache (backend);
		return FALSE;
	}

	e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);

	if (priv->store) {
		const gchar *etag;

		etag = soup_message_headers_get_one (
			soup_message->response_headers, "ETag");

		if (etag != NULL && *etag == '\0')
			etag = NULL;

		e_cal_backend_store_put_key_value (priv->store, "ETag", etag);
	}

	/* get the calendar from the response */
	icalcomp = icalparser_parse_string (soup_message->response_body->data);

	if (!icalcomp) {
		g_set_error (
			error, SOUP_HTTP_ERROR,
			SOUP_STATUS_MALFORMED,
			_("Bad file format."));
		g_object_unref (soup_message);
		empty_cache (backend);
		return FALSE;
	}

	if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) {
		g_set_error (
			error, SOUP_HTTP_ERROR,
			SOUP_STATUS_MALFORMED,
			_("Not a calendar."));
		icalcomponent_free (icalcomp);
		g_object_unref (soup_message);
		empty_cache (backend);
		return FALSE;
	}

	g_object_unref (soup_message);
	soup_message = NULL;

	/* Update cache */
	old_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);

	comps_in_cache = e_cal_backend_store_get_components (priv->store);
	while (comps_in_cache != NULL) {
		const gchar *uid;
		ECalComponent *comp = comps_in_cache->data;

		e_cal_component_get_uid (comp, &uid);
		g_hash_table_insert (old_cache, g_strdup (uid), e_cal_component_get_as_string (comp));

		comps_in_cache = g_slist_remove (comps_in_cache, comps_in_cache->data);
		g_object_unref (comp);
	}

	kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
	subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT);
	e_cal_backend_store_freeze_changes (priv->store);
	while (subcomp) {
		ECalComponent *comp;
		icalcomponent_kind subcomp_kind;
		icalproperty *prop = NULL;

		subcomp_kind = icalcomponent_isa (subcomp);
		prop = icalcomponent_get_first_property (subcomp, ICAL_UID_PROPERTY);
		if (!prop && subcomp_kind == kind) {
			gchar *new_uid = e_cal_component_gen_uid ();
			icalcomponent_set_uid (subcomp, new_uid);
			g_free (new_uid);
		}

		if (subcomp_kind == kind) {
			comp = e_cal_component_new ();
			if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp))) {
				const gchar *uid;
				gpointer orig_key, orig_value;

				e_cal_component_get_uid (comp, &uid);

				if (!put_component_to_store (backend, comp)) {
					g_hash_table_remove (old_cache, uid);
				} else if (g_hash_table_lookup_extended (old_cache, uid, &orig_key, &orig_value)) {
					ECalComponent *orig_comp = e_cal_component_new_from_string (orig_value);

					e_cal_backend_notify_component_modified (E_CAL_BACKEND (backend), orig_comp, comp);

					g_hash_table_remove (old_cache, uid);
					if (orig_comp)
						g_object_unref (orig_comp);
				} else {
					e_cal_backend_notify_component_created (E_CAL_BACKEND (backend), comp);
				}
			}

			g_object_unref (comp);
		} else if (subcomp_kind == ICAL_VTIMEZONE_COMPONENT) {
			icaltimezone *zone;

			zone = icaltimezone_new ();
			icaltimezone_set_component (zone, icalcomponent_new_clone (subcomp));
			e_timezone_cache_add_timezone (timezone_cache, zone);

			icaltimezone_free (zone, 1);
		}

		subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT);
	}

	e_cal_backend_store_thaw_changes (priv->store);

	/* notify the removals */
	g_hash_table_foreach_remove (old_cache, (GHRFunc) notify_and_remove_from_cache, backend);
	g_hash_table_destroy (old_cache);

	/* free memory */
	icalcomponent_free (icalcomp);

	priv->opened = TRUE;

	return TRUE;
}
예제 #7
0
static icalcomponent *
create_user_free_busy (ECalBackendHttp *cbhttp,
                       const gchar *address,
                       const gchar *cn,
                       time_t start,
                       time_t end)
{
	GSList *slist = NULL, *l;
	icalcomponent *vfb;
	icaltimezone *utc_zone;
	ECalBackendSExp *obj_sexp;
	ECalBackendHttpPrivate *priv;
	ECalBackendStore *store;
	gchar *query, *iso_start, *iso_end;

	priv = cbhttp->priv;
	store = priv->store;

        /* create the (unique) VFREEBUSY object that we'll return */
	vfb = icalcomponent_new_vfreebusy ();
	if (address != NULL) {
		icalproperty *prop;
		icalparameter *param;

		prop = icalproperty_new_organizer (address);
		if (prop != NULL && cn != NULL) {
			param = icalparameter_new_cn (cn);
			icalproperty_add_parameter (prop, param);
		}
		if (prop != NULL)
			icalcomponent_add_property (vfb, prop);
	}
	utc_zone = icaltimezone_get_utc_timezone ();
	icalcomponent_set_dtstart (vfb, icaltime_from_timet_with_zone (start, FALSE, utc_zone));
	icalcomponent_set_dtend (vfb, icaltime_from_timet_with_zone (end, FALSE, utc_zone));

        /* add all objects in the given interval */
	iso_start = isodate_from_time_t (start);
	iso_end = isodate_from_time_t (end);
	query = g_strdup_printf (
		"occur-in-time-range? (make-time \"%s\") (make-time \"%s\")",
		iso_start, iso_end);
	obj_sexp = e_cal_backend_sexp_new (query);
	g_free (query);
	g_free (iso_start);
	g_free (iso_end);

	if (!obj_sexp)
		return vfb;

	slist = e_cal_backend_store_get_components (store);

	for (l = slist; l; l = g_slist_next (l)) {
		ECalComponent *comp = l->data;
		icalcomponent *icalcomp, *vcalendar_comp;
		icalproperty *prop;

		icalcomp = e_cal_component_get_icalcomponent (comp);
		if (!icalcomp)
			continue;

                /* If the event is TRANSPARENT, skip it. */
		prop = icalcomponent_get_first_property (
			icalcomp,
			ICAL_TRANSP_PROPERTY);
		if (prop) {
			icalproperty_transp transp_val = icalproperty_get_transp (prop);
			if (transp_val == ICAL_TRANSP_TRANSPARENT ||
			    transp_val == ICAL_TRANSP_TRANSPARENTNOCONFLICT)
				continue;
		}

		if (!e_cal_backend_sexp_match_comp (
			obj_sexp, l->data,
			E_TIMEZONE_CACHE (cbhttp)))
			continue;

		vcalendar_comp = icalcomponent_get_parent (icalcomp);
		if (!vcalendar_comp)
			vcalendar_comp = icalcomp;
		e_cal_recur_generate_instances (
			comp, start, end,
			free_busy_instance,
			vfb,
			resolve_tzid,
			vcalendar_comp,
			icaltimezone_get_utc_timezone ());
	}
	g_object_unref (obj_sexp);

	return vfb;
}