/** Wrapper for e_cal_backend_store_remove_component().
 *
 * This function does not actually remove component from cache in most cases. If
 * component already existed in cache and did not have cache state
 * E_CAL_COMPONENT_CACHE_STATE_CREATED, its state will be set to
 * E_CAL_COMPONENT_CACHE_STATE_REMOVED, otherwise it will be removed from cache.
 *
 * @param cb 3e calendar backend.
 * @param cache Calendar backend cache object.
 * @param uid UID of the calendar component.
 * @param rid RID of the detached instance of recurring event.
 *
 * @return TRUE if successfully removed, FALSE on failure (storage problem or not
 * found).
 */
gboolean e_cal_backend_3e_store_remove_component(ECalBackend3e *cb, ECalBackendStore *store, const char *uid, const char *rid)
{
    ECalComponent *existing;
    gboolean retval;

    g_static_rw_lock_writer_lock(&cb->priv->cache_lock);
    existing = e_cal_backend_store_get_component(store, uid, rid);
    if (existing)
    {
        if (e_cal_component_get_cache_state(existing) == E_CAL_COMPONENT_CACHE_STATE_CREATED)
        {
            retval = e_cal_backend_store_remove_component(store, uid, rid);
        }
        else
        {
            e_cal_component_set_cache_state(existing, E_CAL_COMPONENT_CACHE_STATE_REMOVED);
            retval = e_cal_backend_store_put_component(store, existing);
        }

        g_object_unref(existing);
    }
    else
    {
        retval = FALSE;
    }
    g_static_rw_lock_writer_unlock(&cb->priv->cache_lock);

    return retval;
}
Example #2
0
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;
}
/** 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_store_get_component(cb->priv->store, 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_store_remove_component(cb->priv->store, 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_store_put_component(cb->priv->store, 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 (g_strcmp0(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_store_put_component(cb->priv->store, 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_store_get_timezone(cb->priv->store, 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_store_put_timezone(cb->priv->store, 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;
}
/** 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;
}