[shotwell/wip/libsoup3: 6/10] Initial port to libsoup3




commit 7575a6870eb48a70a7e1f693ddd387dfc2bd8657
Author: Jens Georg <mail jensge org>
Date:   Sun Jul 3 20:59:29 2022 +0200

    Initial port to libsoup3

 meson.build                                        |   6 +-
 .../shotwell/FlickrPublishingAuthenticator.vala    |  24 +-
 .../shotwell/GoogleAuthenticator.vala              |  31 +-
 plugins/common/OAuth1Support.vala                  |   6 +-
 plugins/common/RESTSupport.vala                    | 164 ++---
 .../RajcePublishing.vala                           |   4 +-
 .../YandexPublishing.vala                          | 662 ---------------------
 plugins/shotwell-publishing-extras/meson.build     |   1 -
 ....gnome.Shotwell.Publishing.Extras.gresource.xml |   1 -
 .../yandex_publish_model.ui                        | 182 ------
 plugins/shotwell-publishing/FlickrPublishing.vala  |   2 +-
 plugins/shotwell-publishing/PhotosUploader.vala    |  22 +-
 plugins/shotwell-publishing/PiwigoPublishing.vala  |  15 +-
 plugins/shotwell-publishing/TumblrPublishing.vala  |   6 +-
 plugins/shotwell-publishing/meson.build            |   4 +-
 .../shotwell-publishing/shotwell-publishing.vala   |   3 +-
 test/server.py                                     |  68 ++-
 17 files changed, 233 insertions(+), 968 deletions(-)
---
diff --git a/meson.build b/meson.build
index 0fcaf521..5e0d1177 100644
--- a/meson.build
+++ b/meson.build
@@ -49,8 +49,8 @@ gio = dependency('gio-2.0', version: '>= 2.40')
 gmodule = dependency('gmodule-2.0', version: '>= 2.40')
 gio_unix = dependency('gio-unix-2.0', version: '>= 2.40')
 gee = dependency('gee-0.8', version: '>= 0.8.5')
-webkit = dependency('webkit2gtk-4.0', version: '>= 2.26')
-soup = dependency('libsoup-2.4')
+webkit = dependency('webkit2gtk-4.1', version: '>= 2.26')
+soup = dependency('libsoup-3.0')
 json_glib = dependency('json-glib-1.0')
 xml = dependency('libxml-2.0')
 gdk = dependency('gdk-3.0', version : '>= 3.22')
@@ -110,7 +110,7 @@ if get_option('face_detection')
 endif
 
 json_glib = dependency('json-glib-1.0')
-gdata = dependency('libgdata')
+# gdata = dependency('libgdata')
 gcr = dependency('gcr-3')
 gcr_ui = dependency('gcr-ui-3')
 cairo = dependency('cairo')
diff --git a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala 
b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
index 22b19aa4..a6d84305 100644
--- a/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
+++ b/plugins/authenticator/shotwell/FlickrPublishingAuthenticator.vala
@@ -64,10 +64,16 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
                 return;
             }
 
-            var uri = new Soup.URI(get_view().get_uri());
-            if (uri.scheme == "shotwell-auth" && this.auth_code == null) {
-                var form_data = Soup.Form.decode (uri.query);
-                this.auth_code = form_data.lookup("oauth_verifier");
+            try {
+                var uri = GLib.Uri.parse(get_view().get_uri(), GLib.UriFlags.NONE);
+                if (uri.get_scheme() == "shotwell-auth" && this.auth_code == null) {
+                    var form_data = Soup.Form.decode (uri.get_query());
+                    this.auth_code = form_data.lookup("oauth_verifier");
+                }
+            } catch (Error err) {
+                this.error();
+
+                return;
             }
 
             if (this.auth_code != null) {
@@ -76,9 +82,13 @@ namespace Publishing.Authenticator.Shotwell.Flickr {
         }
 
         private void on_shotwell_auth_request_cb(WebKit.URISchemeRequest request) {
-            var uri = new Soup.URI(request.get_uri());
-            var form_data = Soup.Form.decode (uri.query);
-            this.auth_code = form_data.lookup("oauth_verifier");
+            try {
+                var uri = GLib.Uri.parse(request.get_uri(), GLib.UriFlags.NONE);
+                var form_data = Soup.Form.decode (uri.get_query());
+                this.auth_code = form_data.lookup("oauth_verifier");
+            } catch (Error err) {
+                debug ("Failed to parse URI %s: %s", request.get_uri(), err.message);
+            }
 
             var response = "";
             var mins = new MemoryInputStream.from_data(response.data, null);
diff --git a/plugins/authenticator/shotwell/GoogleAuthenticator.vala 
b/plugins/authenticator/shotwell/GoogleAuthenticator.vala
index a0a0bf99..61e763cc 100644
--- a/plugins/authenticator/shotwell/GoogleAuthenticator.vala
+++ b/plugins/authenticator/shotwell/GoogleAuthenticator.vala
@@ -30,10 +30,15 @@ namespace Publishing.Authenticator.Shotwell.Google {
                 return;
             }
 
-            var uri = new Soup.URI(get_view().get_uri());
-            if (uri.scheme == REVERSE_CLIENT_ID && this.auth_code == null) {
-                var form_data = Soup.Form.decode (uri.query);
-                this.auth_code = form_data.lookup("code");
+            try {
+                var uri = GLib.Uri.parse(get_view().get_uri(), UriFlags.NONE);
+                if (uri.get_scheme() == REVERSE_CLIENT_ID && this.auth_code == null) {
+                    var form_data = Soup.Form.decode (uri.get_query());
+                    this.auth_code = form_data.lookup("code");
+                }
+            } catch (Error err) {
+                debug ("Failed to parse auth code from URI %s: %s", get_view().get_uri(),
+                    err.message);
             }
 
             if (this.auth_code != null) {
@@ -42,10 +47,14 @@ namespace Publishing.Authenticator.Shotwell.Google {
         }
 
         private void on_shotwell_auth_request_cb(WebKit.URISchemeRequest request) {
-            var uri = new Soup.URI(request.get_uri());
-            debug("URI: %s", request.get_uri());
-            var form_data = Soup.Form.decode (uri.query);
-            this.auth_code = form_data.lookup("code");
+            try {
+                var uri = GLib.Uri.parse(request.get_uri(), GLib.UriFlags.NONE);
+                debug("URI: %s", request.get_uri());
+                var form_data = Soup.Form.decode (uri.get_query());
+                this.auth_code = form_data.lookup("code");
+            } catch (Error err) {
+                debug("Failed to parse request URI: %s", err.message);
+            }
 
             var response = "";
             var mins = new MemoryInputStream.from_data(response.data, null);
@@ -200,9 +209,9 @@ namespace Publishing.Authenticator.Shotwell.Google {
             string user_authorization_url = "https://accounts.google.com/o/oauth2/auth?"; +
                 "response_type=code&" +
                 "client_id=" + OAUTH_CLIENT_ID + "&" +
-                "redirect_uri=" + Soup.URI.encode(OAUTH_CALLBACK_URI, null) + "&" +
-                "scope=" + Soup.URI.encode(this.scope, null) + "+" +
-                Soup.URI.encode("https://www.googleapis.com/auth/userinfo.profile";, null) + "&" +
+                "redirect_uri=" + GLib.Uri.escape_string(OAUTH_CALLBACK_URI, null) + "&" +
+                "scope=" + GLib.Uri.escape_string(this.scope, null) + "+" +
+                GLib.Uri.escape_string("https://www.googleapis.com/auth/userinfo.profile";, null) + "&" +
                 "state=connect&" +
                 "access_type=offline&" +
                 "approval_prompt=force";
diff --git a/plugins/common/OAuth1Support.vala b/plugins/common/OAuth1Support.vala
index e94f5fa3..195d14ba 100644
--- a/plugins/common/OAuth1Support.vala
+++ b/plugins/common/OAuth1Support.vala
@@ -80,9 +80,9 @@ namespace Publishing.RESTSupport.OAuth1 {
                 signing_key = consumer_secret + "&";
             }
 
-            string signature_base_string = http_method + "&" + Soup.URI.encode(
+            string signature_base_string = http_method + "&" + GLib.Uri.escape_string(
                     txn.get_endpoint_url(), ENCODE_RFC_3986_EXTRA) + "&" +
-                Soup.URI.encode(arguments_string, ENCODE_RFC_3986_EXTRA);
+                GLib.Uri.escape_string (arguments_string, ENCODE_RFC_3986_EXTRA);
 
             debug("signature base string = '%s'", signature_base_string);
 
@@ -90,7 +90,7 @@ namespace Publishing.RESTSupport.OAuth1 {
 
             // compute the signature
             string signature = RESTSupport.hmac_sha1(signing_key, signature_base_string);
-            signature = Soup.URI.encode(signature, ENCODE_RFC_3986_EXTRA);
+            signature = GLib.Uri.escape_string(signature, ENCODE_RFC_3986_EXTRA);
 
             debug("signature = '%s'", signature);
 
diff --git a/plugins/common/RESTSupport.vala b/plugins/common/RESTSupport.vala
index cb050189..1d97078f 100644
--- a/plugins/common/RESTSupport.vala
+++ b/plugins/common/RESTSupport.vala
@@ -26,6 +26,9 @@ public abstract class Session {
     private string? endpoint_url = null;
     private Soup.Session soup_session = null;
     private bool transactions_stopped = false;
+    private Bytes? body = null;
+    private Error? transport_error= null;
+    private bool insecure = false;
     
     public signal void wire_message_unqueued(Soup.Message message);
     public signal void authenticated();
@@ -35,9 +38,8 @@ public abstract class Session {
         this.endpoint_url = endpoint_url;
         soup_session = new Soup.Session ();
         if (Environment.get_variable("SHOTWELL_SOUP_LOG") != null) {
-            soup_session.add_feature (new Soup.Logger (Soup.LoggerLogLevel.BODY, -1));
+            soup_session.add_feature (new Soup.Logger (Soup.LoggerLogLevel.BODY));
         }
-        this.soup_session.ssl_use_system_ca_file = true;
     }
     
     protected void notify_wire_message_unqueued(Soup.Message message) {
@@ -71,15 +73,33 @@ public abstract class Session {
         if (are_transactions_stopped())
             return;
 
-        soup_session.request_unqueued.connect(notify_wire_message_unqueued);
-        soup_session.send_message(message);
-        
-        soup_session.request_unqueued.disconnect(notify_wire_message_unqueued);
+        this.body = null;
+        this.transport_error = null;
+        try {
+            debug ("===================================== Send and read...");
+            this.body = soup_session.send_and_read(message, null);
+            debug ("====================================== This.body: %p", this.body);
+        } catch (Error error) {
+            debug ("Failed to send_and_read: %s", error.message);
+            this.transport_error = error;
+        }
+        notify_wire_message_unqueued(message);
     }
 
     public void set_insecure () {
-        this.soup_session.ssl_use_system_ca_file = false;
-        this.soup_session.ssl_strict = false;
+        this.insecure = true;
+    }
+
+    public bool get_is_insecure() {
+        return this.insecure;
+    }
+
+    public Error? get_transport_error() {
+        return this.transport_error;
+    }
+
+    public Bytes? get_body() {
+        return this.body;
     }
 }
 
@@ -163,7 +183,7 @@ public class Argument {
 
     public string to_string (bool escape = false, bool encode = false) {
         return "%s=%s%s%s".printf (this.key, escape ? "\"" : "",
-            encode ? Soup.URI.encode(this.value, OAuth1.ENCODE_RFC_3986_EXTRA) : this.value,
+            encode ? GLib.Uri.escape_string(this.value, OAuth1.ENCODE_RFC_3986_EXTRA) : this.value,
             escape ? "\"" : "");
     }
 }
@@ -173,12 +193,13 @@ public class Transaction {
     private bool is_executed = false;
     private weak Session parent_session = null;
     private Soup.Message message = null;
-    private int bytes_written = 0;
+    private uint bytes_written = 0;
+    private ulong request_length;
     private Spit.Publishing.PublishingError? err = null;
     private string? endpoint_url = null;
     private bool use_custom_payload;
     
-    public signal void chunk_transmitted(int bytes_written_so_far, int total_bytes);
+    public signal void chunk_transmitted(uint bytes_written_so_far, uint total_bytes);
     public signal void network_error(Spit.Publishing.PublishingError err);
     public signal void completed();
 
@@ -201,12 +222,12 @@ public class Transaction {
         message = new Soup.Message(method.to_string(), endpoint_url);
     }
 
-    private void on_wrote_body_data(Soup.Buffer written_data) {
-        bytes_written += (int) written_data.length;
+    private void on_wrote_body_data(Soup.Message message, uint chunk_size) {
+        bytes_written += chunk_size;
         while (Gtk.events_pending()) {
             Gtk.main_iteration();
         }
-        chunk_transmitted(bytes_written, (int) message.request_body.length);
+        chunk_transmitted(bytes_written, (uint)request_length);
     }
 
     private void on_message_unqueued(Soup.Message message) {
@@ -225,7 +246,8 @@ public class Transaction {
     /* Texts copied from epiphany */
     public string detailed_error_from_tls_flags (out TlsCertificate cert) {
         TlsCertificateFlags tls_errors;
-        this.message.get_https_status (out cert, out tls_errors);
+        cert = this.message.get_tls_peer_certificate();
+        tls_errors = this.message.get_tls_peer_certificate_errors();
 
         var list = new Gee.ArrayList<string> ();
         if (TlsCertificateFlags.BAD_IDENTITY in tls_errors) {
@@ -276,38 +298,38 @@ public class Transaction {
   }
 
     protected void check_response(Soup.Message message) throws Spit.Publishing.PublishingError {
-        switch (message.status_code) {
-            case Soup.Status.OK:
-            case Soup.Status.CREATED: // HTTP code 201 (CREATED) signals that a new
-                                               // resource was created in response to a PUT or POST
-            break;
-            
-            case Soup.Status.CANT_RESOLVE:
-            case Soup.Status.CANT_RESOLVE_PROXY:
+        var transport_error = parent_session.get_transport_error();
+        if (transport_error != null) {
+            if (transport_error is GLib.ResolverError) {
                 throw new Spit.Publishing.PublishingError.NO_ANSWER("Unable to resolve %s (error code %u)",
-                    get_endpoint_url(), message.status_code);
-            
-            case Soup.Status.CANT_CONNECT:
-            case Soup.Status.CANT_CONNECT_PROXY:
+                get_endpoint_url(), message.status_code);
+            }
+            if (transport_error is GLib.IOError) {
                 throw new Spit.Publishing.PublishingError.NO_ANSWER("Unable to connect to %s (error code 
%u)",
                     get_endpoint_url(), message.status_code);
-            case Soup.Status.SSL_FAILED:
+            }
+            if (transport_error is GLib.TlsError) {
                 throw new Spit.Publishing.PublishingError.SSL_FAILED ("Unable to connect to %s: Secure 
connection failed",
                     get_endpoint_url ());
+            }
+
+            throw new Spit.Publishing.PublishingError.NO_ANSWER("Failure communicating with %s (error code 
%u)",
+            get_endpoint_url(), message.status_code);
+}
+        switch (message.status_code) {
+            case Soup.Status.OK:
+            case Soup.Status.CREATED: // HTTP code 201 (CREATED) signals that a new
+                                               // resource was created in response to a PUT or POST
+            break;
             
             default:
-                // status codes below 100 are used by Soup, 100 and above are defined HTTP codes
-                if (message.status_code >= 100) {
-                    throw new Spit.Publishing.PublishingError.NO_ANSWER("Service %s returned HTTP status 
code %u %s",
-                        get_endpoint_url(), message.status_code, message.reason_phrase);
-                } else {
-                    throw new Spit.Publishing.PublishingError.NO_ANSWER("Failure communicating with %s 
(error code %u)",
-                        get_endpoint_url(), message.status_code);
-                }
+                throw new Spit.Publishing.PublishingError.NO_ANSWER("Service %s returned HTTP status code %u 
%s",
+                    get_endpoint_url(), message.status_code, message.reason_phrase);            
         }
         
         // All valid communication involves body data in the response
-        if (message.response_body.data == null || message.response_body.data.length == 0)
+        var body = parent_session.get_body();
+        if (body == null || body.get_size() == 0)
             throw new Spit.Publishing.PublishingError.MALFORMED_RESPONSE("No response data from %s",
                 get_endpoint_url());
     }
@@ -324,13 +346,20 @@ public class Transaction {
         this.is_executed = is_executed;
     }
 
+    private bool on_accecpt_certificate(Soup.Message message, TlsCertificate cert, TlsCertificateFlags 
errors) {
+        debug ("HTTPS connect error. Will ignore? %s", this.parent_session.get_is_insecure().to_string());
+        return this.parent_session.get_is_insecure();
+    }
+
     protected void send() throws Spit.Publishing.PublishingError {
         parent_session.wire_message_unqueued.connect(on_message_unqueued);
         message.wrote_body_data.connect(on_wrote_body_data);
+        message.accept_certificate.connect(on_accecpt_certificate);
         parent_session.send_wire_message(message);
         
         parent_session.wire_message_unqueued.disconnect(on_message_unqueued);
         message.wrote_body_data.disconnect(on_wrote_body_data);
+        message.accept_certificate.disconnect(on_accecpt_certificate);
         
         if (err != null)
             network_error(err);
@@ -367,7 +396,8 @@ public class Transaction {
         }
         
         ulong length = (payload_length > 0) ? payload_length : custom_payload.length;
-        message.set_request(payload_content_type, Soup.MemoryUse.COPY, custom_payload.data[0:length]);
+        message.set_request_body_from_bytes(payload_content_type, new Bytes (custom_payload.data[0:length]));
+        this.request_length = length;
 
         use_custom_payload = true;
     }
@@ -377,8 +407,9 @@ public class Transaction {
     // alone and let the Transaction class manage it for you. You should only need
     // to install a new message if your subclass has radically different behavior from
     // normal Transactions -- like multipart encoding.
-    protected void set_message(Soup.Message message) {
+    protected void set_message(Soup.Message message, ulong request_length) {
         this.message = message;
+        this.request_length = request_length;
     }
     
     public bool get_is_executed() {
@@ -406,42 +437,45 @@ public class Transaction {
             assert(arguments.length > 0);
 
         // concatenate the REST arguments array into an HTTP formdata string
-        string formdata_string = "";
+        var formdata_string = new StringBuilder("");
         for (int i = 0; i < arguments.length; i++) {
-            formdata_string += arguments[i].to_string ();
+            formdata_string.append(arguments[i].to_string());
             if (i < arguments.length - 1)
-                formdata_string += "&";
+                formdata_string.append("&");
         }
         
         // for GET requests with arguments, append the formdata string to the endpoint url after a
         // query divider ('?') -- but make sure to save the old (caller-specified) endpoint URL
         // and restore it after the GET so that the underlying Soup message remains consistent
-        string old_url = null;
+        GLib.Uri? old_url = null;
         string url_with_query = null;
         if (get_method() == HttpMethod.GET && arguments.length > 0) {
-            old_url = message.get_uri().to_string(false);
-            url_with_query = get_endpoint_url() + "?" + formdata_string;
-            message.set_uri(new Soup.URI(url_with_query));
+            old_url = message.get_uri();
+            url_with_query = get_endpoint_url() + "?" + formdata_string.str;
+            try {
+                message.set_uri(GLib.Uri.parse(url_with_query, GLib.UriFlags.NONE));
+            } catch (Error err) {
+                error ("Invalid uri for service: %s", err.message);
+            }
         } else {
-            message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY,
-                formdata_string.data);
+            message.set_request_body_from_bytes("application/x-www-form-urlencoded", 
StringBuilder.free_to_bytes((owned)formdata_string));
         }
 
         is_executed = true;
 
         try {
-            debug("sending message to URI = '%s'", message.get_uri().to_string(false));
+            debug("sending message to URI = '%s'", message.get_uri().to_string());
             send();
         } finally {
             // if old_url is non-null, then restore it
             if (old_url != null)
-                message.set_uri(new Soup.URI(old_url));
+                message.set_uri(old_url);
         }
     }
 
     public string get_response() {
         assert(get_is_executed());
-        return (string) message.response_body.data;
+        return parent_session.get_body() == null ? "" : (string) parent_session.get_body().get_data();
     }
     
     public unowned Soup.MessageHeaders get_response_headers() {
@@ -523,7 +557,7 @@ public class UploadTransaction : Transaction {
         GLib.HashTable<string, string> result =
             new GLib.HashTable<string, string>(GLib.str_hash, GLib.str_equal);
 
-        result.insert("filename", Soup.URI.encode(publishable.get_serialized_file().get_basename(),
+        result.insert("filename", GLib.Uri.escape_string(publishable.get_serialized_file().get_basename(),
             null));
 
         return result;
@@ -542,37 +576,33 @@ public class UploadTransaction : Transaction {
         foreach (Argument arg in request_arguments)
             message_parts.append_form_string(arg.key, arg.value);
 
-        string payload;
-        size_t payload_length;
+        MappedFile? mapped_file = null;
         try {
-            FileUtils.get_contents(publishable.get_serialized_file().get_path(), out payload,
-                out payload_length);
-        } catch (FileError e) {
+            mapped_file = new MappedFile(publishable.get_serialized_file().get_path(), false);
+        } catch (Error e) {
             throw new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(
                 _("A temporary file needed for publishing is unavailable"));
         }
 
-        int payload_part_num = message_parts.get_length();
-
-        var bindable_data = new Soup.Buffer.take(payload.data[0:payload_length]);
         message_parts.append_form_file("", publishable.get_serialized_file().get_path(), mime_type,
-            bindable_data);
+            mapped_file.get_bytes());
 
         unowned Soup.MessageHeaders image_part_header;
-        unowned Soup.Buffer image_part_body;
+        unowned Bytes image_part_body;
+        int payload_part_num = message_parts.get_length() - 1;
         message_parts.get_part(payload_part_num, out image_part_header, out image_part_body);
+        debug ("Image part header %p", image_part_header);
         image_part_header.set_content_disposition("form-data", binary_disposition_table);
 
-        Soup.Message outbound_message =
-            Soup.Form.request_new_from_multipart(get_endpoint_url(), message_parts);
-        // TODO: there must be a better way to iterate over a map
+        var outbound_message = new Soup.Message.from_multipart(get_endpoint_url(), message_parts);
+
         Gee.MapIterator<string, string> i = message_headers.map_iterator();
         bool cont = i.next();
         while(cont) {
             outbound_message.request_headers.append(i.get_key(), i.get_value());
             cont = i.next();
         }
-        set_message(outbound_message);
+        set_message(outbound_message, mapped_file.get_length());
         
         set_is_executed(true);
         send();
@@ -742,7 +772,7 @@ public abstract class BatchUploader {
             upload_complete(current_file);
     }
     
-    private void on_chunk_transmitted(int bytes_written_so_far, int total_bytes) {
+    private void on_chunk_transmitted(uint bytes_written_so_far, uint total_bytes) {
         double file_span = 1.0 / publishables.length;
         double this_file_fraction_complete = ((double) bytes_written_so_far) / total_bytes;
         double fraction_complete = (current_file * file_span) + (this_file_fraction_complete *
diff --git a/plugins/shotwell-publishing-extras/RajcePublishing.vala 
b/plugins/shotwell-publishing-extras/RajcePublishing.vala
index 4b733f5e..32dc6045 100644
--- a/plugins/shotwell-publishing-extras/RajcePublishing.vala
+++ b/plugins/shotwell-publishing-extras/RajcePublishing.vala
@@ -1363,7 +1363,7 @@ internal class LiveApiRequest
         delete doc;
                if( urlencode )
                {
-               return Soup.URI.encode( xmlstr, "&;" );
+               return GLib.Uri.escape_string( xmlstr, "&;" );
                }
                return xmlstr;
     }
@@ -1586,7 +1586,7 @@ private class AddPhotoTransaction : Publishing.RESTSupport.UploadTransaction
                
         GLib.HashTable<string, string> disposition_table = new GLib.HashTable<string, string>(GLib.str_hash, 
GLib.str_equal);
         disposition_table.insert("name", "photo");
-        disposition_table.insert("filename", Soup.URI.encode( basename, null ) );
+        disposition_table.insert("filename", GLib.Uri.escape_string( basename, null ) );
         set_binary_disposition_table( disposition_table );
     }
 
diff --git a/plugins/shotwell-publishing-extras/meson.build b/plugins/shotwell-publishing-extras/meson.build
index 3f7845a6..d0339083 100644
--- a/plugins/shotwell-publishing-extras/meson.build
+++ b/plugins/shotwell-publishing-extras/meson.build
@@ -2,7 +2,6 @@ shotwell_publishing_extra_sources = [
     'GalleryConnector.vala',
     'RajcePublishing.vala',
     'shotwell-publishing-extras.vala',
-    'YandexPublishing.vala'
     ]
 
 shotwell_publishing_extra_resources = gnome.compile_resources('publishing-extra-resource',
diff --git a/plugins/shotwell-publishing-extras/org.gnome.Shotwell.Publishing.Extras.gresource.xml 
b/plugins/shotwell-publishing-extras/org.gnome.Shotwell.Publishing.Extras.gresource.xml
index 5916f823..39436232 100644
--- a/plugins/shotwell-publishing-extras/org.gnome.Shotwell.Publishing.Extras.gresource.xml
+++ b/plugins/shotwell-publishing-extras/org.gnome.Shotwell.Publishing.Extras.gresource.xml
@@ -7,6 +7,5 @@
         <file>gallery3_publishing_options_pane.ui</file>
         <file>rajce_authentication_pane.ui</file>
         <file>rajce_publishing_options_pane.ui</file>
-        <file>yandex_publish_model.ui</file>
     </gresource>
 </gresources>
diff --git a/plugins/shotwell-publishing/FlickrPublishing.vala 
b/plugins/shotwell-publishing/FlickrPublishing.vala
index bfa1c690..782cfacc 100644
--- a/plugins/shotwell-publishing/FlickrPublishing.vala
+++ b/plugins/shotwell-publishing/FlickrPublishing.vala
@@ -534,7 +534,7 @@ private class UploadTransaction : Publishing.RESTSupport.OAuth1.UploadTransactio
 
         /// TODO: This may need to be revisited to send the title separately; please see
         /// http://www.flickr.com/services/api/upload.api.html for more details.
-        disposition_table.insert("filename",  Soup.URI.encode(
+        disposition_table.insert("filename",  GLib.Uri.escape_string(
             publishable.get_param_string(Spit.Publishing.Publishable.PARAM_STRING_BASENAME), null));
 
         disposition_table.insert("name", "photo");
diff --git a/plugins/shotwell-publishing/PhotosUploader.vala b/plugins/shotwell-publishing/PhotosUploader.vala
index 5c73a10e..ed3b8830 100644
--- a/plugins/shotwell-publishing/PhotosUploader.vala
+++ b/plugins/shotwell-publishing/PhotosUploader.vala
@@ -11,7 +11,7 @@ internal class UploadTransaction : Publishing.RESTSupport.GooglePublisher.Authen
     private PublishingParameters parameters;
     private Publishing.RESTSupport.GoogleSession session;
     private Spit.Publishing.Publishable publishable;
-    private MappedFile mapped_file;
+    private InputStream mapped_file;
 
     public UploadTransaction(Publishing.RESTSupport.GoogleSession session,
         PublishingParameters parameters, Spit.Publishing.Publishable publishable) {
@@ -30,11 +30,14 @@ internal class UploadTransaction : Publishing.RESTSupport.GooglePublisher.Authen
 
     public override void execute() throws Spit.Publishing.PublishingError {
         var basename = publishable.get_param_string(Spit.Publishing.Publishable.PARAM_STRING_BASENAME);
+        int64 mapped_file_size = -1;
 
         // attempt to map the binary image data from disk into memory
         try {
-            mapped_file = new MappedFile(publishable.get_serialized_file().get_path(), false);
-        } catch (FileError e) {
+            mapped_file = publishable.get_serialized_file().read(null);
+            var info = ((FileInputStream)mapped_file).query_info("standard::size", null);
+            mapped_file_size = info.get_size();
+        } catch (Error e) {
             string msg = "Google Photos: couldn't read data from %s: %s".printf(
                 publishable.get_serialized_file().get_path(), e.message);
             warning("%s", msg);
@@ -42,15 +45,6 @@ internal class UploadTransaction : Publishing.RESTSupport.GooglePublisher.Authen
             throw new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(msg);
         }
 
-        unowned uint8[] photo_data = (uint8[]) mapped_file.get_contents();
-        photo_data.length = (int) mapped_file.get_length();
-
-        // bind the binary image data read from disk into a Soup.Buffer object so that we
-        // can attach it to the multipart request, then actaully append the buffer
-        // to the multipart request. Then, set the MIME type for this part.
-        // FIXME: Passing no free function here only works because we are sync
-        Soup.Buffer bindable_data = new Soup.Buffer.with_owner(photo_data, mapped_file, null);
-
         // create a message that can be sent over the wire whose payload is the multipart container
         // that we've been building up
         var outbound_message = new Soup.Message ("POST", get_endpoint_url());
@@ -59,8 +53,8 @@ internal class UploadTransaction : Publishing.RESTSupport.GooglePublisher.Authen
         outbound_message.request_headers.append("X-Goog-Upload-File-Name", basename);
         outbound_message.request_headers.append("X-Goog-Upload-Protocol", "raw");
         outbound_message.request_headers.set_content_type("application/octet-stream", null);
-        outbound_message.request_body.append_buffer (bindable_data);
-        set_message(outbound_message);
+        outbound_message.set_request_body(null, mapped_file, (ssize_t)mapped_file_size);
+        set_message(outbound_message, (ulong)mapped_file_size);
 
         // send the message and get its response
         set_is_executed(true);
diff --git a/plugins/shotwell-publishing/PiwigoPublishing.vala 
b/plugins/shotwell-publishing/PiwigoPublishing.vala
index f39032ab..acfb3f3f 100644
--- a/plugins/shotwell-publishing/PiwigoPublishing.vala
+++ b/plugins/shotwell-publishing/PiwigoPublishing.vala
@@ -339,9 +339,14 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
 
     private void do_show_ssl_downgrade_pane (SessionLoginTransaction trans,
                                              string url) {
-        var uri = new Soup.URI (url);
+        string host_name = "";
+        try {
+            host_name = GLib.Uri.parse (url, GLib.UriFlags.NONE).get_host();
+        } catch (Error err) {
+            debug("Failed to parse URL: %s", err.message);
+        }
         host.set_service_locked (false);
-        var ssl_pane = new SSLErrorPane (trans, uri.get_host ());
+        var ssl_pane = new SSLErrorPane (trans, host_name);
         ssl_pane.proceed.connect (() => {
             debug ("SSL: User wants us to retry with broken certificate");
             this.session = new Session ();
@@ -1080,7 +1085,7 @@ internal class SSLErrorPane : Shotwell.Plugins.Common.BuilderPane {
         base.constructed ();
 
         var label = this.get_builder ().get_object ("main_text") as Gtk.Label;
-        var bold_host = "<b>%s</b".printf(host);
+        var bold_host = "<b>%s</b>".printf(host);
         // %s is the host name that we tried to connect to
         label.set_text (_("This does not look like the real %s. Attackers might be trying to steal or alter 
information going to or from this site (for example, private messages, credit card information, or 
passwords).").printf(bold_host));
         label.use_markup = true;
@@ -1847,7 +1852,7 @@ private class ImagesAddTransaction : Publishing.RESTSupport.UploadTransaction {
             !basename.down().has_suffix(".jpg")) {
             basename += ".jpg";
         }
-        disposition_table.insert("filename",  Soup.URI.encode(basename, null));
+        disposition_table.insert("filename", GLib.Uri.escape_string(basename, null));
         disposition_table.insert("name", "image");
 
         set_binary_disposition_table(disposition_table);
@@ -1882,7 +1887,7 @@ private class ImagesAddRating : Publishing.RESTSupport.UploadTransaction {
         try {
             base.execute();
         } catch (Spit.Publishing.PublishingError err) {
-            debug("Rating upload error");
+            debug("Rating upload error. Ignored.");
         }
     }
 }
diff --git a/plugins/shotwell-publishing/TumblrPublishing.vala 
b/plugins/shotwell-publishing/TumblrPublishing.vala
index f80da0af..3525d227 100644
--- a/plugins/shotwell-publishing/TumblrPublishing.vala
+++ b/plugins/shotwell-publishing/TumblrPublishing.vala
@@ -612,7 +612,9 @@ namespace Publishing.Tumblr {
                 }
                 assert(form.size() > 0);
 
-                var outbound_message = Soup.Form.request_new_from_hash ("POST", get_endpoint_url(), form);
+                var outbound_message = new Soup.Message ("POST", get_endpoint_url());
+                var body = new Bytes(Soup.Form.encode_hash(form).data);
+                outbound_message.set_request_body_from_bytes(Soup.FORM_MIME_TYPE_URLENCODED, body);
 
                 // TODO: there must be a better way to iterate over a map
                 Gee.MapIterator<string, string> i = base.message_headers.map_iterator();
@@ -621,7 +623,7 @@ namespace Publishing.Tumblr {
                     outbound_message.request_headers.append(i.get_key(), i.get_value());
                     cont = i.next();
                 }
-                set_message(outbound_message);
+                set_message(outbound_message, body.length);
 
                 set_is_executed(true);
 
diff --git a/plugins/shotwell-publishing/meson.build b/plugins/shotwell-publishing/meson.build
index 18c101aa..4d495438 100644
--- a/plugins/shotwell-publishing/meson.build
+++ b/plugins/shotwell-publishing/meson.build
@@ -2,7 +2,7 @@ shotwell_publishing_sources = [
     'shotwell-publishing.vala',
     'FlickrPublishing.vala',
     'TumblrPublishing.vala',
-    'YouTubePublishing.vala',
+#    'YouTubePublishing.vala',
     'PiwigoPublishing.vala',
     'PhotosPublisher.vala',
     'PhotosService.vala',
@@ -17,7 +17,7 @@ shotwell_publishing_resources = gnome.compile_resources('publishing-resource',
 shared_module('shotwell-publishing',
               shotwell_publishing_sources + shotwell_publishing_resources,
               dependencies : [gtk, soup, gexiv2, gee, sw_plugin, json_glib,
-                              webkit, sw_plugin_common_dep, xml, gdata, gcr,
+                              webkit, sw_plugin_common_dep, xml, gcr,
                               gcr_ui, authenticator_dep, secret],
               c_args : ['-DPLUGIN_RESOURCE_PATH="/org/gnome/Shotwell/Publishing"',
                         '-DGCR_API_SUBJECT_TO_CHANGE'],
diff --git a/plugins/shotwell-publishing/shotwell-publishing.vala 
b/plugins/shotwell-publishing/shotwell-publishing.vala
index 5cebf0e3..da6c87d5 100644
--- a/plugins/shotwell-publishing/shotwell-publishing.vala
+++ b/plugins/shotwell-publishing/shotwell-publishing.vala
@@ -34,7 +34,8 @@ private class ShotwellPublishingCoreServices : Object, Spit.Module {
         }
 #endif
 
-#if HAVE_YOUTUBE
+#if 0
+//HAVE_YOUTUBE
         if (authenicators.contains("youtube")) {
             pluggables += new YouTubeService(resource_directory);
         }
diff --git a/test/server.py b/test/server.py
index 92ce04aa..72c7acfd 100755
--- a/test/server.py
+++ b/test/server.py
@@ -25,6 +25,49 @@ import http.server
 import cgi
 import urllib.parse
 import time
+import argparse
+import ssl
+
+from OpenSSL import crypto, SSL
+
+def cert_gen(
+    emailAddress="emailAddress",
+    commonName="commonName",
+    countryName="NT",
+    localityName="localityName",
+    stateOrProvinceName="stateOrProvinceName",
+    organizationName="organizationName",
+    organizationUnitName="organizationUnitName",
+    serialNumber=0,
+    validityStartInSeconds=0,
+    validityEndInSeconds=10*365*24*60*60,
+    KEY_FILE = "key.pem",
+    CERT_FILE="cert.pem"):
+    #can look at generated file using openssl:
+    #openssl x509 -inform pem -in selfsigned.crt -noout -text
+    # create a key pair
+    k = crypto.PKey()
+    k.generate_key(crypto.TYPE_RSA, 4096)
+    # create a self-signed cert
+    cert = crypto.X509()
+    cert.get_subject().C = countryName
+    cert.get_subject().ST = stateOrProvinceName
+    cert.get_subject().L = localityName
+    cert.get_subject().O = organizationName
+    cert.get_subject().OU = organizationUnitName
+    cert.get_subject().CN = commonName
+    cert.get_subject().emailAddress = emailAddress
+    cert.set_serial_number(serialNumber)
+    cert.gmtime_adj_notBefore(0)
+    cert.gmtime_adj_notAfter(validityEndInSeconds)
+    cert.set_issuer(cert.get_subject())
+    cert.set_pubkey(k)
+    cert.sign(k, 'sha512')
+    with open(CERT_FILE, "wt") as f:
+        f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode("utf-8"))
+    with open(KEY_FILE, "wt") as f:
+        f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode("utf-8"))
+
 
 # This is a simple implementation of the Piwigo protocol to run locally
 # for testing publishing in offline-situations
@@ -61,7 +104,6 @@ class SimpleRequestHandler(http.server.BaseHTTPRequestHandler):
 
         if self.path == '/ws.php':
             try:
-
                 if method == 'pwg.session.login':
                     self.send_response(200)
                     self.send_header('Content-type', 'text/xml')
@@ -95,15 +137,33 @@ class SimpleRequestHandler(http.server.BaseHTTPRequestHandler):
                     self.end_headers()
                     self.wfile.write(b'<?xml version="1.0"?><piwigo 
stat="ok"><image_id>2387</image_id></piwigo>')
                     return
+                elif method == 'pwg.images.rate':
+                    self.send_response(200)
+                    self.send_header('Set-Cookie', 'pwg_id="12345"')
+                    self.end_headers()
+                    self.wfile.write(b'<?xml version="1.0"?><piwigo 
stat="ok"><image_id>2387</image_id></piwigo>')
+                    return
             except:
                 self.log_error('Unknown method {0}'.format(postvars[b'method']))
                 pass
 
         self.send_response(500)
 
-def run(server_class = http.server.HTTPServer, handler_class = SimpleRequestHandler):
-    server_address = ('127.0.0.1', 8080)
+def run(server_class = http.server.HTTPServer, handler_class = SimpleRequestHandler, port=8080, 
do_ssl=False):
+    server_address = ('127.0.0.1', port)
     httpd = server_class(server_address, handler_class)
+    if do_ssl:
+        cert_gen()
+        context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+        context.load_cert_chain("cert.pem", "key.pem")
+        httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
+
     httpd.serve_forever()
 
-run()
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description = "Piwigo test server")
+    parser.add_argument('--port', type=int, default=8080)
+    parser.add_argument('--ssl', action='store_true')
+    args = parser.parse_args()
+
+    run(port=args.port, do_ssl = args.ssl)


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