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