[evolution-data-server] I#10 - [CalDAV/CardDAV] Avoid GET after PUT when the server didn't modify the component



commit f755e9f4c1a5a4d0e29de0d1da3673eddfd8479c
Author: Milan Crha <mcrha redhat com>
Date:   Tue Jun 19 23:32:52 2018 +0200

    I#10 - [CalDAV/CardDAV] Avoid GET after PUT when the server didn't modify the component
    
    Closes https://gitlab.gnome.org/GNOME/evolution-data-server/issues/10

 .../backends/webdav/e-book-backend-webdav.c        | 55 +++++++++++-
 .../backends/caldav/e-cal-backend-caldav.c         | 98 ++++++++++++++++++----
 src/libedataserver/e-webdav-session.c              |  2 +
 3 files changed, 134 insertions(+), 21 deletions(-)
---
diff --git a/src/addressbook/backends/webdav/e-book-backend-webdav.c 
b/src/addressbook/backends/webdav/e-book-backend-webdav.c
index 94d198bb7..9664306ca 100644
--- a/src/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/src/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -929,8 +929,30 @@ ebb_webdav_load_contact_sync (EBookMetaBackend *meta_backend,
        g_return_val_if_fail (E_IS_BOOK_BACKEND_WEBDAV (meta_backend), FALSE);
        g_return_val_if_fail (uid != NULL, FALSE);
        g_return_val_if_fail (out_contact != NULL, FALSE);
+       g_return_val_if_fail (out_extra != NULL, FALSE);
 
        bbdav = E_BOOK_BACKEND_WEBDAV (meta_backend);
+
+       /* When called immediately after save and the server didn't change the vCard,
+          then the 'extra' contains "href" + "\n" + "vCard", to avoid unneeded GET
+          from the server. */
+       if (extra && *extra) {
+               const gchar *newline;
+
+               newline = strchr (extra, '\n');
+               if (newline && newline[1] && newline != extra) {
+                       EContact *contact;
+
+                       contact = e_contact_new_from_vcard (newline + 1);
+                       if (contact) {
+                               *out_extra = g_strndup (extra, newline - extra);
+                               *out_contact = contact;
+
+                               return TRUE;
+                       }
+               }
+       }
+
        webdav = ebb_webdav_ref_session (bbdav);
 
        if (extra && *extra) {
@@ -1061,6 +1083,7 @@ ebb_webdav_save_contact_sync (EBookMetaBackend *meta_backend,
        vcard_string = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
 
        if (uid && vcard_string && (!overwrite_existing || (extra && *extra))) {
+               gchar *new_extra = NULL, *new_etag = NULL;
                gboolean force_write = FALSE;
 
                if (!extra || !*extra)
@@ -1081,11 +1104,37 @@ ebb_webdav_save_contact_sync (EBookMetaBackend *meta_backend,
 
                success = e_webdav_session_put_data_sync (webdav, (extra && *extra) ? extra : href,
                        force_write ? "" : overwrite_existing ? etag : NULL, E_WEBDAV_CONTENT_TYPE_VCARD,
-                       vcard_string, -1, out_new_extra, NULL, cancellable, &local_error);
+                       vcard_string, -1, &new_extra, &new_etag, cancellable, &local_error);
+
+               if (success) {
+                       /* Only if both are returned and it's not a weak ETag */
+                       if (new_extra && *new_extra && new_etag && *new_etag &&
+                           g_ascii_strncasecmp (new_etag, "W/", 2) != 0) {
+                               gchar *tmp;
 
-               /* To read the component back, because server can change it */
-               if (success)
+                               e_vcard_util_set_x_attribute (E_VCARD (contact), E_WEBDAV_X_ETAG, new_etag);
+
+                               g_free (vcard_string);
+                               vcard_string = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+
+                               /* Encodes the href and the vCard into one string, which
+                                  will be decoded in the load function */
+                               tmp = g_strconcat (new_extra, "\n", vcard_string, NULL);
+                               g_free (new_extra);
+                               new_extra = tmp;
+                       }
+
+                       /* To read the vCard back, either from the new_extra
+                          or from the server, because the server could change it */
                        *out_new_uid = g_strdup (uid);
+
+                       if (out_new_extra)
+                               *out_new_extra = new_extra;
+                       else
+                               g_free (new_extra);
+               }
+
+               g_free (new_etag);
        } else {
                success = FALSE;
                g_propagate_error (error, EDB_ERROR_EX (E_DATA_BOOK_STATUS_OTHER_ERROR, _("Object to save is 
not a valid vCard")));
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.c 
b/src/calendar/backends/caldav/e-cal-backend-caldav.c
index fc9d5539a..00f30ac99 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -1040,6 +1040,30 @@ ecb_caldav_uid_to_uri (ECalBackendCalDAV *cbdav,
        return uri;
 }
 
+static void
+ecb_caldav_store_component_etag (icalcomponent *icalcomp,
+                                const gchar *etag)
+{
+       icalcomponent *subcomp;
+
+       g_return_if_fail (icalcomp != NULL);
+       g_return_if_fail (etag != NULL);
+
+       e_cal_util_set_x_property (icalcomp, E_CALDAV_X_ETAG, etag);
+
+       for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT);
+            subcomp;
+            subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT)) {
+               icalcomponent_kind kind = icalcomponent_isa (subcomp);
+
+               if (kind == ICAL_VEVENT_COMPONENT ||
+                   kind == ICAL_VJOURNAL_COMPONENT ||
+                   kind == ICAL_VTODO_COMPONENT) {
+                       e_cal_util_set_x_property (subcomp, E_CALDAV_X_ETAG, etag);
+               }
+       }
+}
+
 static gboolean
 ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
                                const gchar *uid,
@@ -1059,8 +1083,30 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
        g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
        g_return_val_if_fail (uid != NULL, FALSE);
        g_return_val_if_fail (out_component != NULL, FALSE);
+       g_return_val_if_fail (out_extra != NULL, FALSE);
 
        cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
+
+       /* When called immediately after save and the server didn't change the component,
+          then the 'extra' contains "href" + "\n" + "vCalendar", to avoid unneeded GET
+          from the server. */
+       if (extra && *extra) {
+               const gchar *newline;
+
+               newline = strchr (extra, '\n');
+               if (newline && newline[1] && newline != extra) {
+                       icalcomponent *vcalendar;
+
+                       vcalendar = icalcomponent_new_from_string (newline + 1);
+                       if (vcalendar) {
+                               *out_extra = g_strndup (extra, newline - extra);
+                               *out_component = vcalendar;
+
+                               return TRUE;
+                       }
+               }
+       }
+
        webdav = ecb_caldav_ref_session (cbdav);
 
        if (extra && *extra) {
@@ -1127,23 +1173,12 @@ ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
                *out_component = NULL;
 
                if (href && etag && bytes && length != ((gsize) -1)) {
-                       icalcomponent *icalcomp, *subcomp;
+                       icalcomponent *icalcomp;
 
                        icalcomp = icalcomponent_new_from_string (bytes);
-                       if (icalcomp) {
-                               e_cal_util_set_x_property (icalcomp, E_CALDAV_X_ETAG, etag);
 
-                               for (subcomp = icalcomponent_get_first_component (icalcomp, 
ICAL_ANY_COMPONENT);
-                                    subcomp;
-                                    subcomp = icalcomponent_get_next_component (icalcomp, 
ICAL_ANY_COMPONENT)) {
-                                       icalcomponent_kind kind = icalcomponent_isa (subcomp);
-
-                                       if (kind == ICAL_VEVENT_COMPONENT ||
-                                           kind == ICAL_VJOURNAL_COMPONENT ||
-                                           kind == ICAL_VTODO_COMPONENT) {
-                                               e_cal_util_set_x_property (subcomp, E_CALDAV_X_ETAG, etag);
-                                       }
-                               }
+                       if (icalcomp) {
+                               ecb_caldav_store_component_etag (icalcomp, etag);
 
                                *out_component = icalcomp;
                        }
@@ -1217,11 +1252,11 @@ ecb_caldav_save_component_sync (ECalMetaBackend *meta_backend,
        }
 
        ical_string = icalcomponent_as_ical_string_r (vcalendar);
-       icalcomponent_free (vcalendar);
 
        webdav = ecb_caldav_ref_session (cbdav);
 
        if (uid && ical_string && (!overwrite_existing || (extra && *extra))) {
+               gchar *new_extra = NULL, *new_etag = NULL;
                gboolean force_write = FALSE;
 
                if (!extra || !*extra)
@@ -1242,16 +1277,43 @@ ecb_caldav_save_component_sync (ECalMetaBackend *meta_backend,
 
                success = e_webdav_session_put_data_sync (webdav, (extra && *extra) ? extra : href,
                        force_write ? "" : overwrite_existing ? etag : NULL, E_WEBDAV_CONTENT_TYPE_CALENDAR,
-                       ical_string, -1, out_new_extra, NULL, cancellable, &local_error);
+                       ical_string, -1, &new_extra, &new_etag, cancellable, &local_error);
 
-               /* To read the component back, because server can change it */
-               if (success)
+               if (success) {
+                       /* Only if both are returned and it's not a weak ETag */
+                       if (new_extra && *new_extra && new_etag && *new_etag &&
+                           g_ascii_strncasecmp (new_etag, "W/", 2) != 0) {
+                               gchar *tmp;
+
+                               ecb_caldav_store_component_etag (vcalendar, new_etag);
+
+                               g_free (ical_string);
+                               ical_string = icalcomponent_as_ical_string_r (vcalendar);
+
+                               /* Encodes the href and the component into one string, which
+                                  will be decoded in the load function */
+                               tmp = g_strconcat (new_extra, "\n", ical_string, NULL);
+                               g_free (new_extra);
+                               new_extra = tmp;
+                       }
+
+                       /* To read the component back, either from the new_extra
+                          or from the server, because the server could change it */
                        *out_new_uid = g_strdup (uid);
+
+                       if (out_new_extra)
+                               *out_new_extra = new_extra;
+                       else
+                               g_free (new_extra);
+               }
+
+               g_free (new_etag);
        } else {
                success = FALSE;
                g_propagate_error (error, EDC_ERROR (InvalidObject));
        }
 
+       icalcomponent_free (vcalendar);
        g_free (ical_string);
        g_free (href);
        g_free (etag);
diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c
index 6c798067f..4cb8e96ff 100644
--- a/src/libedataserver/e-webdav-session.c
+++ b/src/libedataserver/e-webdav-session.c
@@ -2446,6 +2446,8 @@ e_webdav_session_put_data_sync (EWebDAVSession *webdav,
        if (content_type && *content_type)
                soup_message_headers_replace (message->request_headers, "Content-Type", content_type);
 
+       soup_message_headers_replace (message->request_headers, "Prefer", "return=minimal");
+
        soup_message_set_request (message, content_type, SOUP_MEMORY_TEMPORARY, bytes, length);
 
        ret_bytes = e_soup_session_send_request_simple_sync (E_SOUP_SESSION (webdav), request, cancellable, 
error);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]