/** Load calendar name, owner and permission from the ESource. * * @param cb 3E calendar backend. * * @return TRUE on success, FALSE otherwise. */ gboolean e_cal_backend_3e_calendar_info_load(ECalBackend3e *cb) { ESource *source; const char *immediate_sync; source = e_cal_backend_get_source(E_CAL_BACKEND(cb)); immediate_sync = e_source_get_property(source, "eee-immediate-sync"); g_free(cb->priv->calname); g_free(cb->priv->owner); g_free(cb->priv->perm); cb->priv->calname = g_strdup(e_source_get_property(source, "eee-calname")); cb->priv->owner = g_strdup(e_source_get_property(source, "eee-owner")); cb->priv->perm = g_strdup(e_source_get_property(source, "eee-perm")); cb->priv->sync_immediately = (immediate_sync == NULL || !strcmp(immediate_sync, "1")); g_free(cb->priv->calspec); cb->priv->calspec = g_strdup_printf("%s:%s", cb->priv->owner, cb->priv->calname); if (cb->priv->calname == NULL || cb->priv->owner == NULL) { return FALSE; } return TRUE; }
const char *e_cal_backend_3e_get_cache_path(ECalBackend3e *cb) { if (cb->priv->cache_path == NULL) { char *mangled_uri = g_strdup(e_cal_backend_get_cache_dir(E_CAL_BACKEND(cb))); guint i; for (i = 0; i < strlen(mangled_uri); i++) { if (mangled_uri[i] == ':' || mangled_uri[i] == '/') { mangled_uri[i] = '_'; } } cb->priv->cache_path = g_build_filename(e_cal_backend_get_cache_dir (E_CAL_BACKEND(cb)), "calendar3e", mangled_uri, NULL); g_free(mangled_uri); } return cb->priv->cache_path; }
static gchar * subprocess_cal_factory_open (ESubprocessFactory *subprocess_factory, EBackend *backend, GDBusConnection *connection, gpointer data, GCancellable *cancellable, GError **error) { EDataCal *data_cal; gchar *object_path; /* If the backend already has an EDataCal installed, return its * object path. Otherwise we need to install a new EDataCal. */ data_cal = e_cal_backend_ref_data_cal (E_CAL_BACKEND (backend)); if (data_cal != NULL) { object_path = g_strdup (e_data_cal_get_object_path (data_cal)); } else { object_path = e_subprocess_factory_construct_path (); /* The EDataCal will attach itself to ECalBackend, * so no need to call e_cal_backend_set_data_cal(). */ data_cal = e_data_cal_new ( E_CAL_BACKEND (backend), connection, object_path, error); if (data_cal != NULL) { e_subprocess_factory_set_backend_callbacks ( subprocess_factory, backend, data); } else { g_free (object_path); object_path = NULL; } } g_clear_object (&data_cal); return object_path; }
/* Get_free_busy handler for the file backend */ static void e_cal_backend_http_get_free_busy (ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const GSList *users, time_t start, time_t end, GSList **freebusy, GError **error) { ESourceRegistry *registry; ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; gchar *address, *name; icalcomponent *vfb; gchar *calobj; cbhttp = E_CAL_BACKEND_HTTP (backend); priv = cbhttp->priv; if (!priv->store) { g_propagate_error (error, EDC_ERROR (NoSuchCal)); return; } registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend)); if (users == NULL) { if (e_cal_backend_mail_account_get_default (registry, &address, &name)) { vfb = create_user_free_busy (cbhttp, address, name, start, end); calobj = icalcomponent_as_ical_string_r (vfb); *freebusy = g_slist_append (*freebusy, calobj); icalcomponent_free (vfb); g_free (address); g_free (name); } } else { const GSList *l; for (l = users; l != NULL; l = l->next ) { address = l->data; if (e_cal_backend_mail_account_is_valid (registry, address, &name)) { vfb = create_user_free_busy (cbhttp, address, name, start, end); calobj = icalcomponent_as_ical_string_r (vfb); *freebusy = g_slist_append (*freebusy, calobj); icalcomponent_free (vfb); g_free (name); } } } }
static void e_data_cal_view_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { EDataCalView *query; EDataCalViewPrivate *priv; query = QUERY (object); priv = query->priv; switch (property_id) { case PROP_BACKEND: priv->backend = E_CAL_BACKEND (g_value_dup_object (value)); break; case PROP_SEXP: priv->sexp = E_CAL_BACKEND_SEXP (g_value_dup_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } }
static gboolean notify_and_remove_from_cache (gpointer key, gpointer value, gpointer user_data) { const gchar *calobj = value; ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (user_data); ECalComponent *comp = e_cal_component_new_from_string (calobj); ECalComponentId *id = e_cal_component_get_id (comp); if (id) { e_cal_backend_store_remove_component (cbhttp->priv->store, id->uid, id->rid); e_cal_backend_notify_component_removed (E_CAL_BACKEND (cbhttp), id, comp, NULL); e_cal_component_free_id (id); } g_object_unref (comp); return TRUE; }
/** * e_cal_backend_sync_get_timezone: * @backend: An ECalBackendSync object. * @cal: An EDataCal object. * @cancellable: a #GCancellable for the operation * @tzid: ID of the timezone to retrieve. * @tzobject: Placeholder for the returned timezone. * @error: Out parameter for a #GError. * * Calls the get_timezone_sync method on the given backend. * This method is not mandatory on the backend, because here * is used internal_get_timezone call to fetch timezone from * it and that is transformed to a string. In other words, * any object deriving from ECalBackendSync can implement only * internal_get_timezone and can skip implementation of * get_timezone_sync completely. */ void e_cal_backend_sync_get_timezone (ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, const gchar *tzid, gchar **tzobject, GError **error) { e_return_data_cal_error_if_fail (E_IS_CAL_BACKEND_SYNC (backend), InvalidArg); if (E_CAL_BACKEND_SYNC_GET_CLASS (backend)->get_timezone_sync) { LOCK_WRAPPER (get_timezone_sync, (backend, cal, cancellable, tzid, tzobject, error)); } if (tzobject && !*tzobject) { icaltimezone *zone = NULL; if (backend->priv->mutex_lock) g_mutex_lock (backend->priv->sync_mutex); zone = e_cal_backend_internal_get_timezone (E_CAL_BACKEND (backend), tzid); if (backend->priv->mutex_lock) g_mutex_unlock (backend->priv->sync_mutex); if (!zone) { g_propagate_error (error, e_data_cal_create_error (ObjectNotFound, NULL)); } else { icalcomponent *icalcomp; icalcomp = icaltimezone_get_component (zone); if (!icalcomp) { g_propagate_error (error, e_data_cal_create_error (InvalidObject, NULL)); } else { *tzobject = icalcomponent_as_ical_string_r (icalcomp); } } } }
static gchar * e_cal_backend_http_get_backend_property (ECalBackend *backend, const gchar *prop_name) { g_return_val_if_fail (prop_name != NULL, NULL); if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { return g_strjoin ( "," CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED, NULL); } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) || g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { /* A HTTP backend has no particular email address associated * with it (although that would be a useful feature some day). */ return NULL; } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT)) { icalcomponent *icalcomp; icalcomponent_kind kind; gchar *prop_value; kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend)); icalcomp = e_cal_util_new_component (kind); prop_value = icalcomponent_as_ical_string_r (icalcomp); icalcomponent_free (icalcomp); return prop_value; } /* Chain up to parent's get_backend_property() method. */ return E_CAL_BACKEND_CLASS (e_cal_backend_http_parent_class)-> get_backend_property (backend, prop_name); }
/** Setup 3E server connection information. * * @param cb 3E calendar backend. * @param username Username used for authentication. * @param password Password. * @param err Error pointer. */ gboolean e_cal_backend_3e_setup_connection(ECalBackend3e *cb, const char *username, const char *password) { ESource *source; g_return_val_if_fail(cb != NULL, FALSE); g_return_val_if_fail(username != NULL, FALSE); g_return_val_if_fail(password != NULL, FALSE); source = e_cal_backend_get_source(E_CAL_BACKEND(cb)); g_free(cb->priv->username); g_free(cb->priv->password); g_free(cb->priv->server_uri); cb->priv->server_uri = NULL; if (e_source_get_property(source, "eee-server")) { cb->priv->server_uri = g_strdup_printf("https://%s/RPC2", e_source_get_property(source, "eee-server")); } cb->priv->username = g_strdup(username); cb->priv->password = g_strdup(password); return TRUE; }
/** Check if calendar is online (i.e. network operations should be enabled). * * @param cb 3e calendar backend. * * @return TRUE if it is OK to run network communication. */ gboolean e_cal_backend_3e_calendar_is_online(ECalBackend3e *cb) { return e_cal_backend_is_online (E_CAL_BACKEND(cb)); // return cb->priv->mode == CAL_MODE_REMOTE; }
static void begin_retrieval_cb (GTask *task, gpointer source_object, gpointer task_tada, GCancellable *cancellable) { ECalBackendHttp *backend = source_object; const gchar *uri; gchar *certificate_pem = NULL; GTlsCertificateFlags certificate_errors = 0; GError *error = NULL; if (!e_backend_get_online (E_BACKEND (backend)) || backend->priv->is_loading) return; d (g_message ("Starting retrieval...\n")); backend->priv->is_loading = TRUE; uri = cal_backend_http_ensure_uri (backend); cal_backend_http_load (backend, uri, &certificate_pem, &certificate_errors, cancellable, &error); if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) || g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { GError *local_error = NULL; ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED; if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED; } e_backend_credentials_required_sync (E_BACKEND (backend), reason, certificate_pem, certificate_errors, error, cancellable, &local_error); g_clear_error (&error); error = local_error; } else if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) { GError *local_error = NULL; e_backend_credentials_required_sync (E_BACKEND (backend), E_SOURCE_CREDENTIALS_REASON_REJECTED, certificate_pem, certificate_errors, error, cancellable, &local_error); g_clear_error (&error); error = local_error; } g_free (certificate_pem); backend->priv->is_loading = FALSE; /* Ignore cancellations. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); } else if (error != NULL) { e_cal_backend_notify_error ( E_CAL_BACKEND (backend), error->message); empty_cache (backend); g_error_free (error); } d (g_message ("Retrieval really done.\n")); }
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 gboolean put_component_to_store (ECalBackendHttp *cb, ECalComponent *comp) { time_t time_start, time_end; ECalBackendHttpPrivate *priv; ECalComponent *cache_comp; const gchar *uid; gchar *rid; priv = cb->priv; e_cal_component_get_uid (comp, &uid); rid = e_cal_component_get_recurid_as_string (comp); cache_comp = e_cal_backend_store_get_component (priv->store, uid, rid); g_free (rid); if (cache_comp) { gboolean changed = TRUE; struct icaltimetype stamp1, stamp2; stamp1 = icaltime_null_time (); stamp2 = icaltime_null_time (); e_cal_component_get_dtstamp (comp, &stamp1); e_cal_component_get_dtstamp (cache_comp, &stamp2); changed = (icaltime_is_null_time (stamp1) && !icaltime_is_null_time (stamp2)) || (!icaltime_is_null_time (stamp1) && icaltime_is_null_time (stamp2)) || (icaltime_compare (stamp1, stamp2) != 0); if (!changed) { struct icaltimetype *last_modified1 = NULL, *last_modified2 = NULL; e_cal_component_get_last_modified (comp, &last_modified1); e_cal_component_get_last_modified (cache_comp, &last_modified2); changed = (last_modified1 != NULL && last_modified2 == NULL) || (last_modified1 == NULL && last_modified2 != NULL) || (last_modified1 != NULL && last_modified2 != NULL && icaltime_compare (*last_modified1, *last_modified2) != 0); if (last_modified1) e_cal_component_free_icaltimetype (last_modified1); if (last_modified2) e_cal_component_free_icaltimetype (last_modified2); if (!changed) { gint *sequence1 = NULL, *sequence2 = NULL; e_cal_component_get_sequence (comp, &sequence1); e_cal_component_get_sequence (cache_comp, &sequence2); changed = (sequence1 != NULL && sequence2 == NULL) || (sequence1 == NULL && sequence2 != NULL) || (sequence1 != NULL && sequence2 != NULL && *sequence1 != *sequence2); if (sequence1) e_cal_component_free_sequence (sequence1); if (sequence2) e_cal_component_free_sequence (sequence2); } } g_object_unref (cache_comp); if (!changed) return FALSE; } e_cal_util_get_component_occur_times ( comp, &time_start, &time_end, resolve_tzid, cb, icaltimezone_get_utc_timezone (), e_cal_backend_get_kind (E_CAL_BACKEND (cb))); e_cal_backend_store_put_component_with_time_range (priv->store, comp, time_start, time_end); return TRUE; }
/** 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; GList *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; } sync_timezones_to_server(cb); g_static_rw_lock_reader_lock(&cb->priv->cache_lock); components = e_cal_backend_cache_get_components(cb->priv->cache); 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); ECalComponentCacheState state = e_cal_component_get_cache_state(comp); /* remove client properties before sending component to the server */ e_cal_component_set_x_property(comp, "X-EVOLUTION-STATUS", NULL); e_cal_component_set_cache_state(comp, E_CAL_COMPONENT_CACHE_STATE_NONE); e_cal_component_set_x_property(comp, "X-3E-DELETED", NULL); remote_comp = e_cal_component_clone(comp); gboolean attachments_converted = e_cal_backend_3e_convert_attachment_uris_to_remote(cb, remote_comp); char *remote_object = e_cal_component_get_as_string(remote_comp); char *object = e_cal_component_get_as_string(comp); if (!attachments_converted) { goto next; } if (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; } } 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_cache_put_component(cb->priv->cache, 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_cache_put_component(cb->priv->cache, 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_cache_remove_component(cb->priv->cache, 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_list_free(components); e_cal_backend_3e_close_connection(cb); return TRUE; }
/* Open handler for the file backend */ static void e_cal_backend_http_open (ECalBackendSync *backend, EDataCal *cal, GCancellable *cancellable, gboolean only_if_exists, GError **perror) { ECalBackendHttp *cbhttp; ECalBackendHttpPrivate *priv; ESource *source; ESourceWebdav *webdav_extension; const gchar *extension_name; const gchar *cache_dir; gboolean opened = TRUE; gchar *tmp; GError *local_error = NULL; cbhttp = E_CAL_BACKEND_HTTP (backend); priv = cbhttp->priv; /* already opened, thus can skip all this initialization */ if (priv->opened) return; source = e_backend_get_source (E_BACKEND (backend)); cache_dir = e_cal_backend_get_cache_dir (E_CAL_BACKEND (backend)); extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND; webdav_extension = e_source_get_extension (source, extension_name); e_source_webdav_unset_temporary_ssl_trust (webdav_extension); if (priv->source_changed_id == 0) { priv->source_changed_id = g_signal_connect ( source, "changed", G_CALLBACK (source_changed_cb), cbhttp); } /* always read uri again */ tmp = priv->uri; priv->uri = NULL; g_free (tmp); if (priv->store == NULL) { /* remove the old cache while migrating to ECalBackendStore */ e_cal_backend_cache_remove (cache_dir, "cache.xml"); priv->store = e_cal_backend_store_new ( cache_dir, E_TIMEZONE_CACHE (backend)); e_cal_backend_store_load (priv->store); if (!priv->store) { g_propagate_error ( perror, EDC_ERROR_EX (OtherError, _("Could not create cache file"))); return; } } e_cal_backend_set_writable (E_CAL_BACKEND (backend), FALSE); if (e_backend_get_online (E_BACKEND (backend))) { gchar *certificate_pem = NULL; GTlsCertificateFlags certificate_errors = 0; const gchar *uri; uri = cal_backend_http_ensure_uri (cbhttp); opened = cal_backend_http_load (cbhttp, uri, &certificate_pem, &certificate_errors, cancellable, &local_error); if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) || g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) || (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN) && !cbhttp->priv->password)) { GError *local_error2 = NULL; ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED; if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED; } e_backend_credentials_required_sync (E_BACKEND (cbhttp), reason, certificate_pem, certificate_errors, local_error, cancellable, &local_error2); g_clear_error (&local_error); local_error = local_error2; } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) { GError *local_error2 = NULL; e_backend_credentials_required_sync (E_BACKEND (cbhttp), E_SOURCE_CREDENTIALS_REASON_REJECTED, certificate_pem, certificate_errors, local_error, cancellable, &local_error2); g_clear_error (&local_error); local_error = local_error2; } g_free (certificate_pem); if (local_error != NULL) g_propagate_error (perror, local_error); } if (opened) { if (!priv->reload_timeout_id) priv->reload_timeout_id = e_source_refresh_add_timeout (source, NULL, http_cal_reload_cb, backend, NULL); } }
/** Sync changes from the server to the cache. * * @param cb 3E calendar backend. * * @return TRUE on success. * * @todo Handle UID/RID. * @todo Better server error handling. * @todo Conflict resolution. */ gboolean e_cal_backend_3e_sync_server_to_cache(ECalBackend3e *cb) { GError *local_err = NULL; gboolean update_sync = TRUE; icalcomponent *ical; icalcomponent *icomp; char filter[128]; struct tm tm; time_t stamp = MAX(e_cal_backend_3e_get_sync_timestamp(cb) - 60 * 60 * 24, 0); /*XXX: always add 1 day padding to prevent timezone problems */ /* prepare query filter string */ gmtime_r(&stamp, &tm); strftime(filter, sizeof(filter), "modified_since('%F %T')", &tm); ical = get_server_objects(cb, filter); if (ical == NULL) { return FALSE; } for (icomp = icalcomponent_get_first_component(ical, ICAL_ANY_COMPONENT); icomp; icomp = icalcomponent_get_next_component(ical, ICAL_ANY_COMPONENT)) { icalcomponent_kind kind = icalcomponent_isa(icomp); icalcomponent_set_cache_state(icomp, E_CAL_COMPONENT_CACHE_STATE_NONE); if (kind == ICAL_VEVENT_COMPONENT) { ECalComponent *comp; const char *uid = icalcomponent_get_uid(icomp); gboolean server_deleted = icalcomponent_3e_status_is_deleted(icomp); ECalComponentCacheState comp_state = E_CAL_COMPONENT_CACHE_STATE_NONE; g_static_rw_lock_reader_lock(&cb->priv->cache_lock); comp = e_cal_backend_cache_get_component(cb->priv->cache, uid, NULL); g_static_rw_lock_reader_unlock(&cb->priv->cache_lock); if (comp) { comp_state = e_cal_component_get_cache_state(comp); } if (server_deleted) { /* deleted by the server */ if (comp && e_cal_component_get_cache_state(comp) != E_CAL_COMPONENT_CACHE_STATE_CREATED && e_cal_component_get_cache_state(comp) != E_CAL_COMPONENT_CACHE_STATE_MODIFIED) { char *object = e_cal_component_get_as_string(comp); ECalComponentId *id = e_cal_component_get_id(comp); g_static_rw_lock_writer_lock(&cb->priv->cache_lock); e_cal_backend_cache_remove_component(cb->priv->cache, uid, NULL); g_static_rw_lock_writer_unlock(&cb->priv->cache_lock); e_cal_backend_notify_object_removed(E_CAL_BACKEND(cb), id, object, NULL); e_cal_component_free_id(id); g_free(object); } } else { char *old_object = NULL; char *object; ECalComponent *new_comp = e_cal_component_new(); e_cal_component_set_icalcomponent(new_comp, icalcomponent_new_clone(icomp)); e_cal_component_set_cache_state(new_comp, E_CAL_COMPONENT_CACHE_STATE_NONE); e_cal_backend_3e_convert_attachment_uris_to_local(cb, new_comp); if (comp) { old_object = e_cal_component_get_as_string(comp); } object = e_cal_component_get_as_string(new_comp); if (old_object == NULL) { if (e_cal_backend_3e_download_attachments(cb, new_comp, &local_err)) { /* not in cache yet */ g_static_rw_lock_writer_lock(&cb->priv->cache_lock); e_cal_backend_cache_put_component(cb->priv->cache, new_comp); g_static_rw_lock_writer_unlock(&cb->priv->cache_lock); e_cal_backend_notify_object_created(E_CAL_BACKEND(cb), object); } else { e_cal_backend_notify_gerror_error(E_CAL_BACKEND(cb), "Can't download attachment.", local_err); g_clear_error(&local_err); update_sync = FALSE; } } else if (strcmp(old_object, object)) { /* what is in cache and what is on server differs */ if (comp_state != E_CAL_COMPONENT_CACHE_STATE_NONE) { /* modified in cache, don't do anything */ } else { if (e_cal_backend_3e_download_attachments(cb, new_comp, &local_err)) { /* sync with server */ g_static_rw_lock_writer_lock(&cb->priv->cache_lock); e_cal_backend_cache_put_component(cb->priv->cache, new_comp); g_static_rw_lock_writer_unlock(&cb->priv->cache_lock); e_cal_backend_notify_object_modified(E_CAL_BACKEND(cb), old_object, object); } else { e_cal_backend_notify_gerror_error(E_CAL_BACKEND(cb), "Can't download attachment.", local_err); g_clear_error(&local_err); update_sync = FALSE; } } } g_free(old_object); g_free(object); g_object_unref(new_comp); } if (comp) { g_object_unref(comp); } } else if (kind == ICAL_VTIMEZONE_COMPONENT) { const char *tzid = icalcomponent_get_tzid(icomp); /* import non-existing timezones from the server */ if (!e_cal_backend_cache_get_timezone(cb->priv->cache, tzid)) { icaltimezone *zone = icaltimezone_new(); icalcomponent *zone_comp = icalcomponent_new_clone(icomp); if (icaltimezone_set_component(zone, zone_comp)) { g_static_rw_lock_writer_lock(&cb->priv->cache_lock); e_cal_backend_cache_put_timezone(cb->priv->cache, zone); g_static_rw_lock_writer_unlock(&cb->priv->cache_lock); } else { icalcomponent_free(zone_comp); } icaltimezone_free(zone, 1); } } else { g_warning("Unsupported component kind (%d) found on the 3e server.", kind); } } if (update_sync) { e_cal_backend_3e_set_sync_timestamp(cb, time(NULL)); } icalcomponent_free(ical); return TRUE; }
static gboolean impl_CalFactory_get_cal (EGdbusCalFactory *object, GDBusMethodInvocation *invocation, const gchar * const *in_source_type, EDataCalFactory *factory) { EDataCal *calendar; EBackend *backend; EDataCalFactoryPrivate *priv = factory->priv; GDBusConnection *connection; ESource *source; gchar *uri; gchar *path = NULL; const gchar *sender; GList *list; GError *error = NULL; gchar *source_xml = NULL; guint type = 0; sender = g_dbus_method_invocation_get_sender (invocation); connection = g_dbus_method_invocation_get_connection (invocation); if (!e_gdbus_cal_factory_decode_get_cal (in_source_type, &source_xml, &type)) { error = g_error_new ( E_DATA_CAL_ERROR, NoSuchCal, _("Invalid call")); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return TRUE; } source = e_source_new_from_standalone_xml (source_xml); g_free (source_xml); if (!source) { error = g_error_new ( E_DATA_CAL_ERROR, NoSuchCal, _("Invalid source")); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return TRUE; } uri = e_source_get_uri (source); if (uri == NULL || *uri == '\0') { g_object_unref (source); g_free (uri); error = g_error_new ( E_DATA_CAL_ERROR, NoSuchCal, _("Empty URI")); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return TRUE; } backend = e_data_cal_factory_get_backend (factory, source, uri, type); if (backend == NULL) { error = g_error_new ( E_DATA_CAL_ERROR, NoSuchCal, _("Invalid source")); g_dbus_method_invocation_return_gerror (invocation, error); g_error_free (error); return TRUE; } g_mutex_lock (priv->calendars_lock); e_dbus_server_hold (E_DBUS_SERVER (factory)); path = construct_cal_factory_path (); calendar = e_data_cal_new (E_CAL_BACKEND (backend)); g_hash_table_insert (priv->calendars, g_strdup (path), calendar); e_cal_backend_add_client (E_CAL_BACKEND (backend), calendar); e_data_cal_register_gdbus_object (calendar, connection, path, &error); g_object_weak_ref ( G_OBJECT (calendar), (GWeakNotify) calendar_freed_cb, factory); /* Update the hash of open connections. */ g_mutex_lock (priv->connections_lock); list = g_hash_table_lookup (priv->connections, sender); list = g_list_prepend (list, calendar); g_hash_table_insert (priv->connections, g_strdup (sender), list); g_mutex_unlock (priv->connections_lock); g_mutex_unlock (priv->calendars_lock); g_object_unref (source); g_free (uri); e_gdbus_cal_factory_complete_get_cal ( object, invocation, path, error); if (error) g_error_free (error); g_free (path); return TRUE; }