From 855a98c04501eede6dd3553b1acc5a8d036af743 Mon Sep 17 00:00:00 2001 From: mjk Date: Fri, 18 Feb 2022 21:41:28 +0000 Subject: [PATCH 01/15] OMEMO: Make QR code fixed-resolution and the quiet zone ISO-conformant --- main/data/theme.css | 6 +++- plugins/omemo/data/contact_details_dialog.ui | 1 - .../omemo/src/ui/contact_details_dialog.vala | 18 ++++-------- plugins/omemo/vapi/libqrencode.vapi | 29 ++++++++++++++----- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/main/data/theme.css b/main/data/theme.css index 3e076248..059585b7 100644 --- a/main/data/theme.css +++ b/main/data/theme.css @@ -369,4 +369,8 @@ box.dino-input-error label.input-status-highlight-once { .dino-call-window .own-video { box-shadow: 0 0 2px 0 rgba(0,0,0,0.5); -} \ No newline at end of file +} + +.qrcode-container { + background: white; /* Color of the quiet zone. MUST have the same "reflectance" as light modules of the QR code. */ +} diff --git a/plugins/omemo/data/contact_details_dialog.ui b/plugins/omemo/data/contact_details_dialog.ui index 188bf06e..62aded6b 100644 --- a/plugins/omemo/data/contact_details_dialog.ui +++ b/plugins/omemo/data/contact_details_dialog.ui @@ -278,7 +278,6 @@ True - 10 True diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index b268cc13..18884784 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -92,20 +92,14 @@ public class ContactDetailsDialog : Gtk.Dialog { copy_button.clicked.connect(() => {Clipboard.get_default(get_display()).set_text(fingerprint, fingerprint.length);}); int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; - Pixbuf qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2).to_pixbuf(); - qr_pixbuf = qr_pixbuf.scale_simple(150, 150, InterpType.NEAREST); - Pixbuf pixbuf = new Pixbuf( - qr_pixbuf.colorspace, - qr_pixbuf.has_alpha, - qr_pixbuf.bits_per_sample, - 170, - 170 - ); - pixbuf.fill(uint32.MAX); - qr_pixbuf.copy_area(0, 0, 150, 150, pixbuf, 10, 10); + const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 + const int MODULE_SIZE_PX = 4; // arbitrary + Pixbuf qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2).to_pixbuf(MODULE_SIZE_PX); + qrcode_image.set_from_pixbuf(qr_pixbuf); + qrcode_image.margin = QUIET_ZONE_MODULES*MODULE_SIZE_PX; + qrcode_popover.get_style_context().add_class("qrcode-container"); - qrcode_image.set_from_pixbuf(pixbuf); show_qrcode_button.clicked.connect(qrcode_popover.popup); } diff --git a/plugins/omemo/vapi/libqrencode.vapi b/plugins/omemo/vapi/libqrencode.vapi index fc77c855..253e239a 100644 --- a/plugins/omemo/vapi/libqrencode.vapi +++ b/plugins/omemo/vapi/libqrencode.vapi @@ -36,15 +36,28 @@ namespace Qrencode { [CCode (cname = "QRcode_encodeString")] public QRcode (string str, int version = 0, ECLevel level = ECLevel.L, EncodeMode hint = EncodeMode.EIGHT_BIT, bool casesensitive = true); - public Pixbuf to_pixbuf() { - uint8[] bitmap = new uint8[3*width*width]; - for (int i = 0; i < width*width; i++) { - uint8 color = (data[i] & 1) == 1 ? 0 : 255; - bitmap[i*3] = color; - bitmap[i*3+1] = color; - bitmap[i*3+2] = color; + public Pixbuf to_pixbuf(int module_size) { + GLib.assert(module_size > 0); + var src_w = width; + var src = data[0:width*width]; + var dst_w = src_w*module_size; + var dst = new uint8[dst_w*dst_w*3]; + for (int src_y = 0; src_y < src_w; src_y++) { + for (int repeat_y = 0; repeat_y < module_size; repeat_y++) { + var dst_y = src_y*module_size + repeat_y; + for (int src_x = 0; src_x < src_w; src_x++) { + uint8 color = (src[src_y*src_w + src_x] & 1) == 1 ? 0 : 255; + for (int repeat_x = 0; repeat_x < module_size; repeat_x++) { + var dst_x = src_x*module_size + repeat_x; + var px_idx = dst_y*dst_w + dst_x; + dst[px_idx*3+0] = color; + dst[px_idx*3+1] = color; + dst[px_idx*3+2] = color; + } + } + } } - return new Pixbuf.from_data(bitmap, Colorspace.RGB, false, 8, width, width, width*3); + return new Pixbuf.from_data(dst, Colorspace.RGB, false, 8, dst_w, dst_w, dst_w*3); } } } From d3ae541673b279d9ebeb84194d029625783aa724 Mon Sep 17 00:00:00 2001 From: mjk Date: Fri, 25 Feb 2022 22:43:02 +0000 Subject: [PATCH 02/15] Qrencode: Break out upsampling into a separate function --- plugins/omemo/vapi/libqrencode.vapi | 51 ++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/plugins/omemo/vapi/libqrencode.vapi b/plugins/omemo/vapi/libqrencode.vapi index 253e239a..79f98b62 100644 --- a/plugins/omemo/vapi/libqrencode.vapi +++ b/plugins/omemo/vapi/libqrencode.vapi @@ -38,26 +38,45 @@ namespace Qrencode { public Pixbuf to_pixbuf(int module_size) { GLib.assert(module_size > 0); - var src_w = width; - var src = data[0:width*width]; - var dst_w = src_w*module_size; - var dst = new uint8[dst_w*dst_w*3]; - for (int src_y = 0; src_y < src_w; src_y++) { - for (int repeat_y = 0; repeat_y < module_size; repeat_y++) { - var dst_y = src_y*module_size + repeat_y; - for (int src_x = 0; src_x < src_w; src_x++) { - uint8 color = (src[src_y*src_w + src_x] & 1) == 1 ? 0 : 255; - for (int repeat_x = 0; repeat_x < module_size; repeat_x++) { - var dst_x = src_x*module_size + repeat_x; - var px_idx = dst_y*dst_w + dst_x; - dst[px_idx*3+0] = color; - dst[px_idx*3+1] = color; - dst[px_idx*3+2] = color; + var dst_width = width*module_size; + var dst_data = new uint8[dst_width*dst_width*3]; + expand_and_upsample(data,width,width, dst_data,dst_width,dst_width); + return new Pixbuf.from_data(dst_data, + Colorspace.RGB, false, 8, dst_width, dst_width, dst_width*3); + } + + /** Does 2D nearest-neighbor upsampling of an array of single-byte + * samples, while expanding the least significant bit of each sample + * to three 0-or-255 bytes. + */ + private void expand_and_upsample( + uint8[] src, uint src_w, uint src_h, + uint8[] dst, uint dst_w, uint dst_h) { + GLib.assert(dst_w % src_w == 0); + GLib.assert(dst_h % src_h == 0); + var scale_x = dst_w/src_w, + scale_y = dst_h/src_h; + /* Doing the iteration in the order of destination samples for + * improved cache-friendliness (dst is 48 times larger than src in + * the typical case of scaling by 4x4). + * The choice of multiple nested loops over a single one is for + * avoiding a ton of divisions by non-constants. + */ + for (uint src_y = 0; src_y < src_h; ++src_y) { + for (uint repeat_y = 0; repeat_y < scale_y; ++repeat_y) { + var dst_y = src_y*scale_y + repeat_y; + for (uint src_x = 0; src_x < src_w; ++src_x) { + uint8 value = (src[src_y*src_w + src_x] & 1)==1 ? 0:255; + for (uint repeat_x = 0; repeat_x < scale_x; ++repeat_x){ + var dst_x = src_x*scale_x + repeat_x; + var dst_idx = dst_y*dst_w + dst_x; + dst[dst_idx*3+0] = value; + dst[dst_idx*3+1] = value; + dst[dst_idx*3+2] = value; } } } } - return new Pixbuf.from_data(dst, Colorspace.RGB, false, 8, dst_w, dst_w, dst_w*3); } } } From 98adfa332a4123cb0a0a77e28e655f22a3a39728 Mon Sep 17 00:00:00 2001 From: mjk Date: Fri, 25 Feb 2022 23:06:57 +0000 Subject: [PATCH 03/15] OMEMO: Make QR code "scalable" in accordance with GDK_SCALE The QR code is now generated at the required resolution instead of being stretched for display with linear filtering. --- plugins/omemo/src/ui/contact_details_dialog.vala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index 18884784..04694637 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -95,8 +95,10 @@ public class ContactDetailsDialog : Gtk.Dialog { const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 const int MODULE_SIZE_PX = 4; // arbitrary - Pixbuf qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2).to_pixbuf(MODULE_SIZE_PX); - qrcode_image.set_from_pixbuf(qr_pixbuf); + var qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2) + .to_pixbuf(MODULE_SIZE_PX * qrcode_image.scale_factor); + qrcode_image.set_from_surface( + Gdk.cairo_surface_create_from_pixbuf(qr_pixbuf,0,get_window())); qrcode_image.margin = QUIET_ZONE_MODULES*MODULE_SIZE_PX; qrcode_popover.get_style_context().add_class("qrcode-container"); From d1c82849642b3f44fb77e672d8cad484211777de Mon Sep 17 00:00:00 2001 From: mjk Date: Sat, 26 Feb 2022 00:08:00 +0000 Subject: [PATCH 04/15] OMEMO QR code: URI-escape the JID --- .../omemo/src/ui/contact_details_dialog.vala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index 04694637..df98d772 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -92,10 +92,18 @@ public class ContactDetailsDialog : Gtk.Dialog { copy_button.clicked.connect(() => {Clipboard.get_default(get_display()).set_text(fingerprint, fingerprint.length);}); int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; + var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; + #if 0 + // glib >=2.66 only; never compiled + string iri = GLib.Uri.join("xmpp", null, null, 0, jid, iri_query); + #else + var iri_path_seg = escape_for_iri_path_segment(jid.to_string()); + var iri = @"xmpp:$(iri_path_seg)?$(iri_query)"; + #endif const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 const int MODULE_SIZE_PX = 4; // arbitrary - var qr_pixbuf = new QRcode(@"xmpp:$(account.bare_jid)?omemo-sid-$(sid)=$(fingerprint)", 2) + var qr_pixbuf = new QRcode(iri, 2) .to_pixbuf(MODULE_SIZE_PX * qrcode_image.scale_factor); qrcode_image.set_from_surface( Gdk.cairo_surface_create_from_pixbuf(qr_pixbuf,0,get_window())); @@ -126,6 +134,14 @@ public class ContactDetailsDialog : Gtk.Dialog { fetch_unknown_bundles(); } + private static string escape_for_iri_path_segment(string s) { + // from RFC 3986, 2.2. Reserved Characters: + /*const*/ string SUB_DELIMS = "!$&'()*+,;="; + // from RFC 3986, 3.3. Path (pchar without unreserved and pct-encoded): + /*const*/ string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@"; + return GLib.Uri.escape_string(s, ALLOWED_RESERVED_CHARS, true); + } + private void fetch_unknown_bundles() { Dino.Application app = Application.get_default() as Dino.Application; XmppStream? stream = app.stream_interactor.get_stream(account); From 5f04a6eb099e714d76e6531051aa4677cc46ea78 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Tue, 8 Mar 2022 18:33:42 -0600 Subject: [PATCH 05/15] QR-code: Actually use Uri.join if glib version supports it --- CMakeLists.txt | 3 ++- plugins/omemo/src/ui/contact_details_dialog.vala | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8963937..c9525870 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,7 +198,8 @@ include(${VALA_USE_FILE}) include(MultiFind) include(GlibCompileResourcesSupport) -set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_GLOBAL_VERSION}") +find_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED) +set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_VERSION}") add_subdirectory(qlite) add_subdirectory(xmpp-vala) diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index df98d772..a40970d0 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -93,13 +93,13 @@ public class ContactDetailsDialog : Gtk.Dialog { int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; - #if 0 +#if GLIB_2_66 // glib >=2.66 only; never compiled - string iri = GLib.Uri.join("xmpp", null, null, 0, jid, iri_query); - #else + string iri = GLib.Uri.join(UriFlags.NONE, "xmpp", null, null, 0, jid.to_string(), iri_query, null); +#else var iri_path_seg = escape_for_iri_path_segment(jid.to_string()); var iri = @"xmpp:$(iri_path_seg)?$(iri_query)"; - #endif +#endif const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 const int MODULE_SIZE_PX = 4; // arbitrary @@ -136,9 +136,9 @@ public class ContactDetailsDialog : Gtk.Dialog { private static string escape_for_iri_path_segment(string s) { // from RFC 3986, 2.2. Reserved Characters: - /*const*/ string SUB_DELIMS = "!$&'()*+,;="; + string SUB_DELIMS = "!$&'()*+,;="; // from RFC 3986, 3.3. Path (pchar without unreserved and pct-encoded): - /*const*/ string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@"; + string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@"; return GLib.Uri.escape_string(s, ALLOWED_RESERVED_CHARS, true); } From ee4fbf160d9abca47faa7eb1cc4d12b8199f4194 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 21 Feb 2022 12:13:40 +0100 Subject: [PATCH 06/15] Add XEP implementation status to DOAP --- dino.doap | 42 ++++++++++++++++++++++++++++++++++++++++-- dino.doap.in | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/dino.doap b/dino.doap index ba5814e3..819777b0 100644 --- a/dino.doap +++ b/dino.doap @@ -250,11 +250,15 @@ + complete + For use with XEP-0261 + deprecated + Migrating to XEP-0402 if supported by server @@ -273,6 +277,8 @@ + partial + For use with XEP-0313 @@ -284,6 +290,7 @@ + partial For use with XEP-0260 @@ -291,6 +298,7 @@ complete + For file transfers using XEP-0363 @@ -326,27 +334,32 @@ + deprecated + Only to fetch Avatars from other users - partial + complete + partial + partial + partial @@ -358,7 +371,7 @@ - partial + complete @@ -382,21 +395,25 @@ + partial + complete + complete + partial @@ -409,11 +426,14 @@ + partial + No support for sending + complete @@ -431,21 +451,26 @@ + partial + partial + complete + For use with XEP-0280 + complete 1.2.0 0.2 @@ -460,6 +485,8 @@ + complete + 1.0.0 @@ -471,16 +498,20 @@ + partial + complete + 0.3.1 + complete @@ -506,11 +537,13 @@ complete + 0.3.0 + partial @@ -522,6 +555,7 @@ + complete @@ -533,16 +567,20 @@ + complete + complete + partial + No support for embedded thumbnails diff --git a/dino.doap.in b/dino.doap.in index 941fd11b..563de1d4 100644 --- a/dino.doap.in +++ b/dino.doap.in @@ -70,11 +70,15 @@ + complete + For use with XEP-0261 + deprecated + Migrating to XEP-0402 if supported by server @@ -93,6 +97,8 @@ + partial + For use with XEP-0313 @@ -104,6 +110,7 @@ + partial For use with XEP-0260 @@ -111,6 +118,7 @@ complete + For file transfers using XEP-0363 @@ -146,27 +154,32 @@ + deprecated + Only to fetch Avatars from other users - partial + complete + partial + partial + partial @@ -178,7 +191,7 @@ - partial + complete @@ -202,21 +215,25 @@ + partial + complete + complete + partial @@ -229,11 +246,14 @@ + partial + No support for sending + complete @@ -251,21 +271,26 @@ + partial + partial + complete + For use with XEP-0280 + complete 1.2.0 0.2 @@ -280,6 +305,8 @@ + complete + 1.0.0 @@ -291,16 +318,20 @@ + partial + complete + 0.3.1 + complete @@ -326,11 +357,13 @@ complete + 0.3.0 + partial @@ -342,6 +375,7 @@ + complete @@ -353,16 +387,20 @@ + complete + complete + partial + No support for embedded thumbnails From f0ed11ec49b6c5b3a52bc666e31ff9791dee77cf Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 16 Mar 2022 08:33:13 -0600 Subject: [PATCH 07/15] Fix build on Vala < 0.50 and pre-release Vala versions --- CMakeLists.txt | 5 ++++- plugins/omemo/src/ui/contact_details_dialog.vala | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9525870..b5734c45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,7 +199,10 @@ include(MultiFind) include(GlibCompileResourcesSupport) find_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED) -set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_VERSION}") +string(REGEX REPLACE "^([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" GLib_MAJOR_VERSION "${GLib_VERSION}") +string(REGEX REPLACE "^[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" GLib_MINOR_VERSION "${GLib_VERSION}") +math(EXPR GLib_LAST_RELEASE_MINOR_VERSION "${GLib_MINOR_VERSION} / 2 * 2") +set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_MAJOR_VERSION}.${GLib_LAST_RELEASE_MINOR_VERSION}") add_subdirectory(qlite) add_subdirectory(xmpp-vala) diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala index a40970d0..b4d6d8f0 100644 --- a/plugins/omemo/src/ui/contact_details_dialog.vala +++ b/plugins/omemo/src/ui/contact_details_dialog.vala @@ -93,8 +93,7 @@ public class ContactDetailsDialog : Gtk.Dialog { int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; -#if GLIB_2_66 - // glib >=2.66 only; never compiled +#if GLIB_2_66 && VALA_0_50 string iri = GLib.Uri.join(UriFlags.NONE, "xmpp", null, null, 0, jid.to_string(), iri_query, null); #else var iri_path_seg = escape_for_iri_path_segment(jid.to_string()); From c5ed719b665938a59011913e39ce94df63690b40 Mon Sep 17 00:00:00 2001 From: Nkwuda Sunday Cletus <49409497+sunny775@users.noreply.github.com> Date: Wed, 23 Mar 2022 14:42:43 -0500 Subject: [PATCH 08/15] set a generic MIME type for encrypted file (#1213) --- plugins/omemo/src/file_transfer/file_encryptor.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/omemo/src/file_transfer/file_encryptor.vala b/plugins/omemo/src/file_transfer/file_encryptor.vala index 7e79abdc..39af8292 100644 --- a/plugins/omemo/src/file_transfer/file_encryptor.vala +++ b/plugins/omemo/src/file_transfer/file_encryptor.vala @@ -38,7 +38,7 @@ public class OmemoFileEncryptor : Dino.FileEncryptor, Object { omemo_http_file_meta.iv = iv; omemo_http_file_meta.key = key; omemo_http_file_meta.size = file_transfer.size + 16; - omemo_http_file_meta.mime_type = "omemo"; + omemo_http_file_meta.mime_type = "application/octet-stream"; file_transfer.input_stream = new ConverterInputStream(file_transfer.input_stream, new SymmetricCipherEncrypter((owned) cipher, 16)); } catch (Crypto.Error error) { throw new FileSendError.ENCRYPTION_FAILED("OMEMO file encryption error: %s".printf(error.message)); From 339d1d8f557e88ef6bb5812980b8988dc8fdf10d Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 30 Mar 2022 10:36:07 -0600 Subject: [PATCH 09/15] Fix Version handling, add function to retrieve short version without git commit --- libdino/CMakeLists.txt | 2 +- libdino/src/application.vala | 6 ++++++ main/CMakeLists.txt | 2 +- main/src/ui/application.vala | 4 ++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libdino/CMakeLists.txt b/libdino/CMakeLists.txt index ce836f62..20f5ffee 100644 --- a/libdino/CMakeLists.txt +++ b/libdino/CMakeLists.txt @@ -89,7 +89,7 @@ DEPENDS ${CMAKE_BINARY_DIR}/exports/dino_i18n.h ) -add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino") +add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino" -DDINO_VERSION=\"${PROJECT_VERSION}\") add_library(libdino SHARED ${LIBDINO_VALA_C} ${CMAKE_BINARY_DIR}/exports/dino_i18n.h) add_dependencies(libdino dino-vapi) target_link_libraries(libdino xmpp-vala qlite ${LIBDINO_PACKAGES} m) diff --git a/libdino/src/application.vala b/libdino/src/application.vala index f381c21d..9b36dd79 100644 --- a/libdino/src/application.vala +++ b/libdino/src/application.vala @@ -1,7 +1,13 @@ using Dino.Entities; namespace Dino { + extern const string VERSION; +public string get_version() { return VERSION; } +public string get_short_version() { + if (!VERSION.contains("~")) return VERSION; + return VERSION.split("~")[0] + "+"; +} public interface Application : GLib.Application { diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index e052785a..2326253c 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -212,7 +212,7 @@ OPTIONS ${MAIN_EXTRA_OPTIONS} ) -add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DDINO_VERSION=\"${PROJECT_VERSION}\") +add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\") add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET} src/emojichooser.c) add_dependencies(dino ${GETTEXT_PACKAGE}-translations) target_include_directories(dino PRIVATE src) diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala index 2fd5320e..6c02087f 100644 --- a/main/src/ui/application.vala +++ b/main/src/ui/application.vala @@ -42,7 +42,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { startup.connect(() => { if (print_version) { - print(@"Dino $(Dino.VERSION)\n"); + print(@"Dino $(Dino.get_version())\n"); Process.exit(0); } @@ -270,7 +270,7 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application { } private void show_about_window() { - string? version = Dino.VERSION.strip().length == 0 ? null : Dino.VERSION; + string? version = Dino.get_version().strip().length == 0 ? null : Dino.get_version(); if (version != null && !version.contains("git")) { switch (version.substring(0, 3)) { case "0.2": version = @"$version - Mexican Caribbean Coral Reefs"; break; From 193bf38a790b2a124493c3b7ad591f826e0f773d Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 30 Mar 2022 10:36:52 -0600 Subject: [PATCH 10/15] Allow cancellation of file transfers --- libdino/src/entity/file_transfer.vala | 1 + libdino/src/service/file_manager.vala | 11 +++-- .../file_default_widget.vala | 16 +++++- .../file_widget.vala | 5 ++ plugins/http-files/src/file_provider.vala | 49 +++++++++++++++++-- plugins/http-files/src/file_sender.vala | 3 +- 6 files changed, 73 insertions(+), 12 deletions(-) diff --git a/libdino/src/entity/file_transfer.vala b/libdino/src/entity/file_transfer.vala index 1823478f..20bc1a7a 100644 --- a/libdino/src/entity/file_transfer.vala +++ b/libdino/src/entity/file_transfer.vala @@ -70,6 +70,7 @@ public class FileTransfer : Object { public State state { get; set; default=State.NOT_STARTED; } public int provider { get; set; } public string info { get; set; } + public Cancellable cancellable { get; default=new Cancellable(); } private Database? db; private string storage_dir; diff --git a/libdino/src/service/file_manager.vala b/libdino/src/service/file_manager.vala index a478695c..b82e0afb 100644 --- a/libdino/src/service/file_manager.vala +++ b/libdino/src/service/file_manager.vala @@ -246,7 +246,7 @@ public class FileManager : StreamInteractionModule, Object { File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); - yield os.splice_async(input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET); + yield os.splice_async(input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET, Priority.LOW, file_transfer.cancellable); file_transfer.path = file.get_basename(); file_transfer.input_stream = yield file.read_async(); @@ -292,14 +292,15 @@ public class FileManager : StreamInteractionModule, Object { if (is_sender_trustworthy(file_transfer, conversation)) { try { yield get_file_meta(file_provider, file_transfer, conversation, receive_data); - - if (file_transfer.size >= 0 && file_transfer.size < 5000000) { - yield download_file_internal(file_provider, file_transfer, conversation); - } } catch (Error e) { warning("Error downloading file: %s", e.message); file_transfer.state = FileTransfer.State.FAILED; } + if (file_transfer.size >= 0 && file_transfer.size < 5000000) { + download_file_internal.begin(file_provider, file_transfer, conversation, (_, res) => { + download_file_internal.end(res); + }); + } } conversation.last_active = file_transfer.time; diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala index 28b7d477..638dab15 100644 --- a/main/src/ui/conversation_content_view/file_default_widget.vala +++ b/main/src/ui/conversation_content_view/file_default_widget.vala @@ -19,6 +19,7 @@ public class FileDefaultWidget : EventBox { public ModelButton file_open_button; public ModelButton file_save_button; + public ModelButton cancel_button; private FileTransfer.State state; @@ -27,6 +28,7 @@ public class FileDefaultWidget : EventBox { this.leave_notify_event.connect(on_pointer_left_event); file_open_button = new ModelButton() { text=_("Open"), visible=true }; file_save_button = new ModelButton() { text=_("Save as…"), visible=true }; + cancel_button = new ModelButton() { text=_("Cancel"), visible=true }; } public void update_file_info(string? mime_type, FileTransfer.State state, long size) { @@ -59,6 +61,18 @@ public class FileDefaultWidget : EventBox { mime_label.label = _("Downloading %s…").printf(get_size_string(size)); spinner.active = true; image_stack.set_visible_child_name("spinner"); + + // Create a menu + Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu(); + Box file_menu_box = new Box(Orientation.VERTICAL, 0) { margin=10, visible=true }; + file_menu_box.add(cancel_button); + popover_menu.add(file_menu_box); + file_menu.popover = popover_menu; + file_menu.button_release_event.connect(() => { + popover_menu.visible = true; + return true; + }); + popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.NOT_STARTED: if (mime_description != null) { @@ -84,7 +98,7 @@ public class FileDefaultWidget : EventBox { if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("download_image"); } - if (state == FileTransfer.State.COMPLETE) { + if (state == FileTransfer.State.COMPLETE || state == FileTransfer.State.IN_PROGRESS) { file_menu.opacity = 1; } return false; diff --git a/main/src/ui/conversation_content_view/file_widget.vala b/main/src/ui/conversation_content_view/file_widget.vala index 0040db3c..b63195dc 100644 --- a/main/src/ui/conversation_content_view/file_widget.vala +++ b/main/src/ui/conversation_content_view/file_widget.vala @@ -131,6 +131,7 @@ public class FileDefaultWidgetController : Object { widget.button_release_event.connect(on_clicked); widget.file_open_button.clicked.connect(open_file); widget.file_save_button.clicked.connect(save_file); + widget.cancel_button.clicked.connect(cancel_download); } public void set_file_transfer(FileTransfer file_transfer, StreamInteractor stream_interactor) { @@ -186,6 +187,10 @@ public class FileDefaultWidgetController : Object { } } + private void cancel_download() { + file_transfer.cancellable.cancel(); + } + private bool on_clicked(EventButton event_button) { switch (state) { case FileTransfer.State.COMPLETE: diff --git a/plugins/http-files/src/file_provider.vala b/plugins/http-files/src/file_provider.vala index e3382439..11885721 100644 --- a/plugins/http-files/src/file_provider.vala +++ b/plugins/http-files/src/file_provider.vala @@ -46,6 +46,38 @@ public class FileProvider : Dino.FileProvider, Object { } } + private class LimitInputStream : InputStream { + InputStream inner; + int64 remaining_size; + + public LimitInputStream(InputStream inner, int64 max_size) { + this.inner = inner; + this.remaining_size = max_size; + } + + private ssize_t check_limit(ssize_t read) throws IOError { + this.remaining_size -= read; + if (remaining_size < 0) throw new IOError.FAILED("Stream length exceeded limit"); + return read; + } + + public override ssize_t read(uint8[] buffer, Cancellable? cancellable = null) throws IOError { + return check_limit(inner.read(buffer, cancellable)); + } + + public override async ssize_t read_async(uint8[]? buffer, int io_priority = GLib.Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { + return check_limit(yield inner.read_async(buffer, io_priority, cancellable)); + } + + public override bool close(Cancellable? cancellable = null) throws IOError { + return inner.close(cancellable); + } + + public override async bool close_async(int io_priority = GLib.Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { + return yield inner.close_async(io_priority, cancellable); + } + } + private void on_file_message(Entities.Message message, Conversation conversation) { var additional_info = message.id.to_string(); @@ -64,9 +96,11 @@ public class FileProvider : Dino.FileProvider, Object { if (http_receive_data == null) return file_meta; var session = new Soup.Session(); + session.user_agent = @"Dino/$(Dino.get_short_version()) "; var head_message = new Soup.Message("HEAD", http_receive_data.url); if (head_message != null) { + head_message.request_headers.append("Accept-Encoding", "identity"); try { yield session.send_async(head_message, null); } catch (Error e) { @@ -75,12 +109,12 @@ public class FileProvider : Dino.FileProvider, Object { string? content_type = null, content_length = null; head_message.response_headers.foreach((name, val) => { - if (name == "Content-Type") content_type = val; - if (name == "Content-Length") content_length = val; + if (name.down() == "content-type") content_type = val; + if (name.down() == "content-length") content_length = val; }); file_meta.mime_type = content_type; if (content_length != null) { - file_meta.size = int.parse(content_length); + file_meta.size = int64.parse(content_length); } } @@ -97,9 +131,14 @@ public class FileProvider : Dino.FileProvider, Object { try { var session = new Soup.Session(); + session.user_agent = @"Dino/$(Dino.get_short_version()) "; Soup.Request request = session.request(http_receive_data.url); - - return yield request.send_async(null); + InputStream stream = yield request.send_async(file_transfer.cancellable); + if (file_meta.size != -1) { + return new LimitInputStream(stream, file_meta.size); + } else { + return stream; + } } catch (Error e) { throw new FileReceiveError.DOWNLOAD_FAILED("Downloading file error: %s".printf(e.message)); } diff --git a/plugins/http-files/src/file_sender.vala b/plugins/http-files/src/file_sender.vala index e005b8c5..8a22ffe1 100644 --- a/plugins/http-files/src/file_sender.vala +++ b/plugins/http-files/src/file_sender.vala @@ -98,8 +98,9 @@ public class HttpFileSender : FileSender, Object { message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, message.request_body)); message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, message.request_body)); Soup.Session session = new Soup.Session(); + session.user_agent = @"Dino/$(Dino.get_short_version()) "; try { - yield session.send_async(message); + yield session.send_async(message, file_transfer.cancellable); if (message.status_code < 200 || message.status_code >= 300) { throw new FileSendError.UPLOAD_FAILED("HTTP status code %s".printf(message.status_code.to_string())); } From a0eac798cd1a1abf20772db9f89c54e945ded3ea Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 8 Apr 2022 22:03:31 +0200 Subject: [PATCH 11/15] Fix build of tests --- libdino/tests/testcase.vala | 2 +- plugins/signal-protocol/tests/testcase.vala | 4 ++-- xmpp-vala/tests/testcase.vala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libdino/tests/testcase.vala b/libdino/tests/testcase.vala index 87147604..59fcf193 100644 --- a/libdino/tests/testcase.vala +++ b/libdino/tests/testcase.vala @@ -48,7 +48,7 @@ public abstract class Gee.TestCase : Object { } public GLib.TestSuite get_suite () { - return this.suite; + return (owned) this.suite; } private class Adaptor { diff --git a/plugins/signal-protocol/tests/testcase.vala b/plugins/signal-protocol/tests/testcase.vala index 061ccc63..59fcf193 100644 --- a/plugins/signal-protocol/tests/testcase.vala +++ b/plugins/signal-protocol/tests/testcase.vala @@ -48,7 +48,7 @@ public abstract class Gee.TestCase : Object { } public GLib.TestSuite get_suite () { - return this.suite; + return (owned) this.suite; } private class Adaptor { @@ -77,4 +77,4 @@ public abstract class Gee.TestCase : Object { this.test_case.tear_down (); } } -} \ No newline at end of file +} diff --git a/xmpp-vala/tests/testcase.vala b/xmpp-vala/tests/testcase.vala index 9bdf5f6b..178b6353 100644 --- a/xmpp-vala/tests/testcase.vala +++ b/xmpp-vala/tests/testcase.vala @@ -56,7 +56,7 @@ public abstract class Gee.TestCase : Object { } public GLib.TestSuite get_suite () { - return this.suite; + return (owned) this.suite; } } From baa4a6a1eb4e98558acfef6e2a24c2c13d42db5c Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 9 Apr 2022 00:28:42 +0200 Subject: [PATCH 12/15] Prepare http-files plugin for libsoup-3 support Note: ice plugin still depends on libsoup-2.4 and one can't have both in the same process, so this remains disabled by default --- cmake/FindSoup.cmake | 31 --------------- cmake/FindSoup2.cmake | 31 +++++++++++++++ cmake/FindSoup3.cmake | 31 +++++++++++++++ plugins/http-files/CMakeLists.txt | 12 +++++- plugins/http-files/src/file_provider.vala | 47 +++++++++++++---------- plugins/http-files/src/file_sender.vala | 36 ++++++++++------- 6 files changed, 123 insertions(+), 65 deletions(-) delete mode 100644 cmake/FindSoup.cmake create mode 100644 cmake/FindSoup2.cmake create mode 100644 cmake/FindSoup3.cmake diff --git a/cmake/FindSoup.cmake b/cmake/FindSoup.cmake deleted file mode 100644 index d5afab48..00000000 --- a/cmake/FindSoup.cmake +++ /dev/null @@ -1,31 +0,0 @@ -include(PkgConfigWithFallback) -find_pkg_config_with_fallback(Soup - PKG_CONFIG_NAME libsoup-2.4 - LIB_NAMES soup-2.4 - INCLUDE_NAMES libsoup/soup.h - INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include - DEPENDS GIO -) - -if(Soup_FOUND AND NOT Soup_VERSION) - find_file(Soup_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup_INCLUDE_DIRS}) - mark_as_advanced(Soup_VERSION_HEADER) - - if(Soup_VERSION_HEADER) - file(STRINGS "${Soup_VERSION_HEADER}" Soup_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$") - string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MAJOR_VERSION "${Soup_MAJOR_VERSION}") - file(STRINGS "${Soup_VERSION_HEADER}" Soup_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$") - string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MINOR_VERSION "${Soup_MINOR_VERSION}") - file(STRINGS "${Soup_VERSION_HEADER}" Soup_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$") - string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MICRO_VERSION "${Soup_MICRO_VERSION}") - set(Soup_VERSION "${Soup_MAJOR_VERSION}.${Soup_MINOR_VERSION}.${Soup_MICRO_VERSION}") - unset(Soup_MAJOR_VERSION) - unset(Soup_MINOR_VERSION) - unset(Soup_MICRO_VERSION) - endif() -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Soup - REQUIRED_VARS Soup_LIBRARY - VERSION_VAR Soup_VERSION) diff --git a/cmake/FindSoup2.cmake b/cmake/FindSoup2.cmake new file mode 100644 index 00000000..07ffed14 --- /dev/null +++ b/cmake/FindSoup2.cmake @@ -0,0 +1,31 @@ +include(PkgConfigWithFallback) +find_pkg_config_with_fallback(Soup2 + PKG_CONFIG_NAME libsoup-2.4 + LIB_NAMES soup-2.4 + INCLUDE_NAMES libsoup/soup.h + INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include + DEPENDS GIO +) + +if(Soup2_FOUND AND NOT Soup2_VERSION) + find_file(Soup2_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup_INCLUDE_DIRS}) + mark_as_advanced(Soup2_VERSION_HEADER) + + if(Soup_VERSION_HEADER) + file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$") + string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MAJOR_VERSION "${Soup2_MAJOR_VERSION}") + file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$") + string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MINOR_VERSION "${Soup2_MINOR_VERSION}") + file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$") + string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MICRO_VERSION "${Soup2_MICRO_VERSION}") + set(Soup_VERSION "${Soup2_MAJOR_VERSION}.${Soup2_MINOR_VERSION}.${Soup2_MICRO_VERSION}") + unset(Soup2_MAJOR_VERSION) + unset(Soup2_MINOR_VERSION) + unset(Soup2_MICRO_VERSION) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Soup2 + REQUIRED_VARS Soup2_LIBRARY + VERSION_VAR Soup2_VERSION) diff --git a/cmake/FindSoup3.cmake b/cmake/FindSoup3.cmake new file mode 100644 index 00000000..07b4893a --- /dev/null +++ b/cmake/FindSoup3.cmake @@ -0,0 +1,31 @@ +include(PkgConfigWithFallback) +find_pkg_config_with_fallback(Soup3 + PKG_CONFIG_NAME libsoup-3.0 + LIB_NAMES soup-3.0 + INCLUDE_NAMES libsoup/soup.h + INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include + DEPENDS GIO +) + +if(Soup3_FOUND AND NOT Soup3_VERSION) + find_file(Soup3_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup3_INCLUDE_DIRS}) + mark_as_advanced(Soup3_VERSION_HEADER) + + if(Soup3_VERSION_HEADER) + file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$") + string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MAJOR_VERSION "${Soup3_MAJOR_VERSION}") + file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$") + string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MINOR_VERSION "${Soup3_MINOR_VERSION}") + file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$") + string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MICRO_VERSION "${Soup3_MICRO_VERSION}") + set(Soup3_VERSION "${Soup3_MAJOR_VERSION}.${Soup3_MINOR_VERSION}.${Soup3_MICRO_VERSION}") + unset(Soup3_MAJOR_VERSION) + unset(Soup3_MINOR_VERSION) + unset(Soup3_MICRO_VERSION) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Soup3 + REQUIRED_VARS Soup3_LIBRARY + VERSION_VAR Soup3_VERSION) diff --git a/plugins/http-files/CMakeLists.txt b/plugins/http-files/CMakeLists.txt index 77579fdb..2da46731 100644 --- a/plugins/http-files/CMakeLists.txt +++ b/plugins/http-files/CMakeLists.txt @@ -1,10 +1,18 @@ +set(HTTP_FILES_DEFINITIONS) +if(USE_SOUP3) + set(Soup Soup3) + set(HTTP_FILES_DEFINITIONS ${HTTP_FILES_DEFINITIONS} SOUP_3) +else() + set(Soup Soup2) +endif() + find_packages(HTTP_FILES_PACKAGES REQUIRED Gee GLib GModule GObject GTK3 - Soup + ${Soup} ) vala_precompile(HTTP_FILES_VALA_C @@ -19,6 +27,8 @@ CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/qlite.vapi PACKAGES ${HTTP_FILES_PACKAGES} +DEFINITIONS + ${HTTP_FILES_DEFINITIONS} ) add_definitions(${VALA_CFLAGS}) diff --git a/plugins/http-files/src/file_provider.vala b/plugins/http-files/src/file_provider.vala index 11885721..3a3aeb94 100644 --- a/plugins/http-files/src/file_provider.vala +++ b/plugins/http-files/src/file_provider.vala @@ -98,24 +98,26 @@ public class FileProvider : Dino.FileProvider, Object { var session = new Soup.Session(); session.user_agent = @"Dino/$(Dino.get_short_version()) "; var head_message = new Soup.Message("HEAD", http_receive_data.url); + head_message.request_headers.append("Accept-Encoding", "identity"); - if (head_message != null) { - head_message.request_headers.append("Accept-Encoding", "identity"); - try { - yield session.send_async(head_message, null); - } catch (Error e) { - throw new FileReceiveError.GET_METADATA_FAILED("HEAD request failed"); - } + try { +#if SOUP_3 + yield session.send_async(head_message, GLib.Priority.LOW, null); +#else + yield session.send_async(head_message, null); +#endif + } catch (Error e) { + throw new FileReceiveError.GET_METADATA_FAILED("HEAD request failed"); + } - string? content_type = null, content_length = null; - head_message.response_headers.foreach((name, val) => { - if (name.down() == "content-type") content_type = val; - if (name.down() == "content-length") content_length = val; - }); - file_meta.mime_type = content_type; - if (content_length != null) { - file_meta.size = int64.parse(content_length); - } + string? content_type = null, content_length = null; + head_message.response_headers.foreach((name, val) => { + if (name.down() == "content-type") content_type = val; + if (name.down() == "content-length") content_length = val; + }); + file_meta.mime_type = content_type; + if (content_length != null) { + file_meta.size = int64.parse(content_length); } return file_meta; @@ -129,11 +131,16 @@ public class FileProvider : Dino.FileProvider, Object { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) assert(false); + var session = new Soup.Session(); + session.user_agent = @"Dino/$(Dino.get_short_version()) "; + var get_message = new Soup.Message("GET", http_receive_data.url); + try { - var session = new Soup.Session(); - session.user_agent = @"Dino/$(Dino.get_short_version()) "; - Soup.Request request = session.request(http_receive_data.url); - InputStream stream = yield request.send_async(file_transfer.cancellable); +#if SOUP_3 + InputStream stream = yield session.send_async(get_message, GLib.Priority.LOW, file_transfer.cancellable); +#else + InputStream stream = yield session.send_async(get_message, file_transfer.cancellable); +#endif if (file_meta.size != -1) { return new LimitInputStream(stream, file_meta.size); } else { diff --git a/plugins/http-files/src/file_sender.vala b/plugins/http-files/src/file_sender.vala index 8a22ffe1..ab81ffce 100644 --- a/plugins/http-files/src/file_sender.vala +++ b/plugins/http-files/src/file_sender.vala @@ -73,6 +73,7 @@ public class HttpFileSender : FileSender, Object { } } +#if !SOUP_3 private static void transfer_more_bytes(InputStream stream, Soup.MessageBody body) { uint8[] bytes = new uint8[4096]; ssize_t read = stream.read(bytes); @@ -83,26 +84,35 @@ public class HttpFileSender : FileSender, Object { bytes.length = (int)read; body.append_buffer(new Soup.Buffer.take(bytes)); } +#endif private async void upload(FileTransfer file_transfer, HttpFileSendData file_send_data, FileMeta file_meta) throws FileSendError { Xmpp.XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) return; - Soup.Message message = new Soup.Message("PUT", file_send_data.url_up); - message.request_headers.set_content_type(file_meta.mime_type, null); - message.request_headers.set_content_length(file_meta.size); - foreach (var entry in file_send_data.headers.entries) { - message.request_headers.append(entry.key, entry.value); - } - message.request_body.set_accumulate(false); - message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, message.request_body)); - message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, message.request_body)); - Soup.Session session = new Soup.Session(); + var session = new Soup.Session(); session.user_agent = @"Dino/$(Dino.get_short_version()) "; + var put_message = new Soup.Message("PUT", file_send_data.url_up); +#if SOUP_3 + put_message.set_request_body(file_meta.mime_type, file_transfer.input_stream, (ssize_t) file_meta.size); +#else + put_message.request_headers.set_content_type(file_meta.mime_type, null); + put_message.request_headers.set_content_length(file_meta.size); + put_message.request_body.set_accumulate(false); + put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); + put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); +#endif + foreach (var entry in file_send_data.headers.entries) { + put_message.request_headers.append(entry.key, entry.value); + } try { - yield session.send_async(message, file_transfer.cancellable); - if (message.status_code < 200 || message.status_code >= 300) { - throw new FileSendError.UPLOAD_FAILED("HTTP status code %s".printf(message.status_code.to_string())); +#if SOUP_3 + yield session.send_async(put_message, GLib.Priority.LOW, file_transfer.cancellable); +#else + yield session.send_async(put_message, file_transfer.cancellable); +#endif + if (put_message.status_code < 200 || put_message.status_code >= 300) { + throw new FileSendError.UPLOAD_FAILED("HTTP status code %s".printf(put_message.status_code.to_string())); } } catch (Error e) { throw new FileSendError.UPLOAD_FAILED("HTTP upload error: %s".printf(e.message)); From 99c076254abc6e2b03b784732a76a389e5a4f801 Mon Sep 17 00:00:00 2001 From: Xavi92 Date: Wed, 11 May 2022 09:12:30 +0000 Subject: [PATCH 13/15] Inhibit idle and suspend during calls (#1233) --- .../src/ui/call_window/call_window_controller.vala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/main/src/ui/call_window/call_window_controller.vala b/main/src/ui/call_window/call_window_controller.vala index 65e5ebd6..276fff98 100644 --- a/main/src/ui/call_window/call_window_controller.vala +++ b/main/src/ui/call_window/call_window_controller.vala @@ -22,6 +22,7 @@ public class Dino.Ui.CallWindowController : Object { private bool window_size_changed = false; private ulong[] call_window_handler_ids = new ulong[0]; private ulong[] bottom_bar_handler_ids = new ulong[0]; + private uint inhibit_cookie; public CallWindowController(CallWindow call_window, CallState call_state, StreamInteractor stream_interactor) { this.call_window = call_window; @@ -118,6 +119,19 @@ public class Dino.Ui.CallWindowController : Object { update_audio_device_choices(); update_video_device_choices(); + + var app = GLib.Application.get_default() as Application; + inhibit_cookie = app.inhibit(call_window, IDLE | SUSPEND, "Ongoing call"); + + if (inhibit_cookie == 0) { + warning("suspend inhibit request failed or unsupported"); + } + + call_window.destroy.connect(() => { + if (inhibit_cookie != 0) { + app.uninhibit(inhibit_cookie); + } + }); } private void invite_button_clicked() { From 186361fd8a381ef3c3334683dfb9cc4de1417596 Mon Sep 17 00:00:00 2001 From: foucault Date: Tue, 17 May 2022 14:02:12 +0200 Subject: [PATCH 14/15] Fix calculation of best camera framerate When the algorithm iterates over all the available v4l2 capabilities it tries to determine the best framerate for each one of YUYV (video/x-raw) modes presented by the hardware (best_fraction, line 357 in device.vala). Regardless of what's determined to be the "best" YUYV mode from within the conditional right after (line 385) the best fractional framerate will always point to the last iterated framerate, which might be an extremely low one, like 7 or 5 FPS. When the framerate is then stored in the Gst.Structure (line 394) the fractional framerate will always be that last value which might be different than the correct one as calculated by best_fps (line 386). This workaround solves this issue by only updating best_fraction when the conditional in line 385 is satisfied. from issue #1195 --- plugins/rtp/src/device.vala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/rtp/src/device.vala b/plugins/rtp/src/device.vala index d4eca09a..1db8c996 100644 --- a/plugins/rtp/src/device.vala +++ b/plugins/rtp/src/device.vala @@ -354,6 +354,7 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object { int best_height = 0; for (int i = 0; i < device.caps.get_size(); i++) { unowned Gst.Structure? that = device.caps.get_structure(i); + Value? best_fraction_now = null; if (!that.has_name("video/x-raw")) continue; int num = 0, den = 0, width = 0, height = 0; if (!that.has_field("framerate")) continue; @@ -369,7 +370,7 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object { int fps = den > 0 ? (num/den) : 0; int in_fps = in_den > 0 ? (in_num/in_den) : 0; if (in_fps > fps) { - best_fraction = fraction; + best_fraction_now = fraction; num = in_num; den = in_den; } @@ -386,6 +387,7 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object { best_width = width; best_height = height; best_index = i; + best_fraction = best_fraction_now; } } Gst.Caps res = caps_copy_nth(device.caps, best_index); From f25bfb00969a7e09996da2d5500e6718f4cc0148 Mon Sep 17 00:00:00 2001 From: Matthew Egeler Date: Tue, 17 May 2022 08:12:32 -0400 Subject: [PATCH 15/15] Support devices with multiple framerate options in get_max_fps (#1224) --- plugins/rtp/src/plugin.vala | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/rtp/src/plugin.vala b/plugins/rtp/src/plugin.vala index 3a4f6ce1..dc446530 100644 --- a/plugins/rtp/src/plugin.vala +++ b/plugins/rtp/src/plugin.vala @@ -382,9 +382,27 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object { int fps = 0; for (int i = 0; i < device.device.caps.get_size(); i++) { unowned Gst.Structure structure = device.device.caps.get_structure(i); - int num = 0, den = 0; - if (structure.has_field("framerate") && structure.get_fraction("framerate", out num, out den)) fps = int.max(fps, num / den); + + if (structure.has_field("framerate")) { + Value framerate = structure.get_value("framerate"); + if (framerate.type() == typeof(Gst.Fraction)) { + int num = Gst.Value.get_fraction_numerator(framerate); + int den = Gst.Value.get_fraction_denominator(framerate); + fps = int.max(fps, num / den); + } else if (framerate.type() == typeof(Gst.ValueList)) { + for(uint j = 0; j < Gst.ValueList.get_size(framerate); j++) { + Value fraction = Gst.ValueList.get_value(framerate, j); + int num = Gst.Value.get_fraction_numerator(fraction); + int den = Gst.Value.get_fraction_denominator(fraction); + fps = int.max(fps, num / den); + } + } else { + debug("Unknown type for framerate %s on device %s", framerate.type_name(), device.display_name); + } + } } + + debug("Max framerate for device %s: %d", device.display_name, fps); return fps; }