From a98affc88b03b18aa8e52ffccddf12a81141821a Mon Sep 17 00:00:00 2001
From: Johannes Rosenberger <johannes.rosenberger@tum.de>
Date: Tue, 4 Jun 2024 22:37:00 +0200
Subject: [PATCH] fix typo in CMakeLists.txt

---
 CMakeLists.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5dd70705..8613572e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -338,7 +338,7 @@ target_link_libraries (
   ${GTKMM3_LDFLAGS}
   ${GLIBMM2_LDFLAGS}
   ${GIOMM2_LDFLAGS}
-  ${GIOUINX_LDFLAGS}
+  ${GIOUNIX_LDFLAGS}
   ${LIBSOUP_LDFLAGS}
   ${GMIME_LDFLAGS}
   ${VTE2_LDFLAGS}
@@ -368,7 +368,7 @@ target_link_libraries (
   ${WEBKIT2GTK_LDFLAGS}
   ${GLIBMM2_LDFLAGS}
   ${GIOMM2_LDFLAGS}
-  ${GIOUINX_LDFLAGS}
+  ${GIOUNIX_LDFLAGS}
   ${CMAKE_THREAD_LIBS_INIT}
   ${PROTOBUF_LIBRARIES}
   ${GTKMM3_LDFLAGS}
From 4c76a4cf6226752f5270197ba8747c7c7550e35e Mon Sep 17 00:00:00 2001
From: Johannes Rosenberger <johannes.rosenberger@tum.de>
Date: Tue, 4 Jun 2024 22:43:06 +0200
Subject: [PATCH 1/2] improve mailto uri handling

1. This introduces consistent mailto uri handling, supporting passing a
   body and all headers that can be given as agruments to EditMessage,
   namely, To, From, Cc, Bcc and Subject.
   Other headers are not dropped but prepended to the email body with a note.

2. Positional (i.e., non-option) commandline arguments are now
   interpreted as --mailto options.

3. Multiple --mailto options on the commandline are now supported, and are
   merged into one mailto argument before being passed to EditMessage.

4. A consequence is that libsoup is not used any more, enabling
   compatibility with webkit2gtk-4.1 while maintaining compatibility with
   gio (glib) 2.16, and thus building on older and current distros.
---
 .github/workflows/ci-debian-build-test.yml |  1 -
 CMakeLists.txt                             |  6 +-
 src/astroid.cc                             | 91 ++++++++++------------
 src/astroid.hh                             |  1 +
 src/message_thread.cc                      |  3 +-
 src/modes/edit_message.cc                  | 89 +++++++++++++++++++--
 src/modes/edit_message.hh                  |  4 +-
 src/modes/thread_view/thread_view.cc       |  4 -
 8 files changed, 127 insertions(+), 72 deletions(-)

diff --git a/.github/workflows/ci-debian-build-test.yml b/.github/workflows/ci-debian-build-test.yml
index 3015b7f06..ddc33728f 100644
--- a/.github/workflows/ci-debian-build-test.yml
+++ b/.github/workflows/ci-debian-build-test.yml
@@ -59,7 +59,6 @@ jobs:
             libpeas-dev \
             libprotobuf-dev \
             libsass-dev \
-            libsoup2.4-dev \
             libvte-2.91-dev \
             libwebkit2gtk-${WEBKITGTK_VERSION}-dev \
             ninja-build \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8613572ec..498e4a260 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -98,8 +98,7 @@ pkg_check_modules (GLIBMM2    REQUIRED  glibmm-2.4)
 pkg_check_modules (WEBKIT2GTK REQUIRED  webkit2gtk-4.0>=2.22)
 pkg_check_modules (SASS       REQUIRED  libsass)
 pkg_check_modules (GIOMM2     REQUIRED  giomm-2.4)
-pkg_check_modules (GIOUNIX    REQUIRED  gio-unix-2.0)
-pkg_check_modules (LIBSOUP    REQUIRED  libsoup-2.4)
+pkg_check_modules (GIOUNIX    REQUIRED  gio-unix-2.0>=2.16)
 
 string (REGEX REPLACE "([0-9]+\.[0-9]+)\.[0-9]+" "\\1" GMIME_MAJOR_MINOR ${Notmuch_GMIME_VERSION})
 pkg_check_modules (GMIME      REQUIRED  gmime-${GMIME_MAJOR_MINOR}>=${Notmuch_GMIME_VERSION})
@@ -154,7 +153,6 @@ include_directories (
   ${GLIBMM2_INCLUDE_DIRS}
   ${GIOMM2_INCLUDE_DIRS}
   ${GIOUNIX_INCLUDE_DIRS}
-  ${LIBSOUP_INCLUDE_DIRS}
   ${GMIME_INCLUDE_DIRS}
   ${WEBKIT2GTK_INCLUDE_DIRS}
   ${VTE2_INCLUDE_DIRS}
@@ -169,7 +167,6 @@ add_compile_options (
   ${GLIBMM2_CFLAGS}
   ${GIOMM2_CFLAGS}
   ${GIOUNIX_CFLAGS}
-  ${LIBSOUP_CFLAGS}
   ${GMIME_CFLAGS}
   ${WEBKIT2GTK_CFLAGS}
   ${VTE2_CFLAGS}
@@ -339,7 +336,6 @@ target_link_libraries (
   ${GLIBMM2_LDFLAGS}
   ${GIOMM2_LDFLAGS}
   ${GIOUNIX_LDFLAGS}
-  ${LIBSOUP_LDFLAGS}
   ${GMIME_LDFLAGS}
   ${VTE2_LDFLAGS}
   ${SASS_LDFLAGS}
diff --git a/src/astroid.cc b/src/astroid.cc
index 3d5dc9f54..b23e8f6e6 100644
--- a/src/astroid.cc
+++ b/src/astroid.cc
@@ -53,8 +53,6 @@
 # include <gmime/gmime.h>
 # include <utils/gmime/gmime-compat.h>
 
-# include <libsoup/soup.h>
-
 using namespace std;
 using namespace boost::filesystem;
 
@@ -126,7 +124,7 @@ namespace Astroid {
 # ifdef DEBUG
       ( "test-config,t",  "use test config (same as used when tests are run), only makes sense from the source root")
 # endif
-      ( "mailto,m", po::value<ustring>(), "compose mail with mailto url or address")
+      ( "mailto,m", po::value< vector<ustring> >()->composing(), "compose mail with mailto url or address")
       ( "no-auto-poll",   "do not poll automatically")
       ( "disable-log",    "disable logging")
       ( "log-stdout",     "log to stdout regardless of configuration")
@@ -139,6 +137,9 @@ namespace Astroid {
 # else
       ;
 # endif
+
+      /* default option (without --<option> prefix) */
+    pdesc.add("mailto", -1);
   }
   // }}}
 
@@ -153,7 +154,11 @@ namespace Astroid {
     bool show_help = false;
 
     try {
-      po::store ( po::parse_command_line (argc, argv, desc), vm );
+      po::store ( po::command_line_parser(argc, argv).
+                      options(desc).
+                      positional(pdesc).
+                      run(),
+                  vm );
     } catch (po::unknown_option &ex) {
       LOG (error) << "unknown option" << endl;
       LOG (error) << ex.what() << endl;
@@ -438,7 +443,11 @@ namespace Astroid {
       po::variables_map vm;
 
       try {
-        po::store ( po::parse_command_line (argc, argv, desc), vm );
+        po::store ( po::command_line_parser(argc, argv).
+                        options(desc).
+                        positional(pdesc).
+                        run(),
+                    vm );
       } catch (po::unknown_option &ex) {
         LOG (error) << "unknown option" << endl;
         LOG (error) << ex.what() << endl;
@@ -446,8 +455,28 @@ namespace Astroid {
       }
 
       if (vm.count("mailto")) {
-        ustring mailtourl = vm["mailto"].as<ustring>();
-        send_mailto (mailtourl);
+        vector <ustring> mailto_list = vm["mailto"].as<vector <ustring>>();
+
+        // is the conversion from ustring to std::string really safe?
+        std::string mailto = "";
+        std::string next;
+
+        ustring::size_type sep;
+
+        for (std::vector<ustring>::size_type i = 0; i < mailto_list.size(); i++) {
+          next = mailto_list[i];
+          sep = next.find("?");
+          if (sep != next.npos)
+            next[sep] = '&';
+          if (next.substr(0, 7) == "mailto:")
+            next.erase(0,7);
+          mailto += "&to=" +  next;
+        }
+
+        mailto[0] = '?';
+        mailto.insert(0, "mailto:");
+
+        send_mailto (mailto);
         new_window = false;
       }
 
@@ -545,53 +574,11 @@ namespace Astroid {
     open_new_window ();
   }
 
-  void Astroid::send_mailto (ustring url) {
-    LOG (info) << "astroid: mailto: " << url;
+  void Astroid::send_mailto (ustring uri) {
+    LOG (info) << "astroid: mailto: " << uri;
 
     MainWindow * mw = (MainWindow*) get_windows ()[0];
-
-    SoupURI *uri = soup_uri_new(url.c_str());
-
-    if (SOUP_URI_IS_VALID(uri)) {
-      /* we got an mailto url */
-      ustring from, to, cc, bcc, subject, body;
-
-      to = soup_uri_decode (soup_uri_get_path (uri));
-
-      const char * soup_query = soup_uri_get_query (uri);
-      if (soup_query) {
-        std::istringstream query_string (soup_query);
-        std::string keyval;
-        while (std::getline(query_string, keyval, '&')) {
-          ustring::size_type pos = keyval.find ("=");
-
-          ustring key = keyval.substr (0, pos);
-          key = key.lowercase ();
-
-          ustring val = soup_uri_decode (keyval.substr (pos+1).c_str());
-
-          if (key == "from") {
-            from = ustring (val);
-          } else if (key == "cc") {
-            cc = ustring (val);
-          } else if (key == "bcc") {
-            bcc = ustring (val);
-          } else if (key == "subject" ) {
-            subject = ustring (val);
-          } else if (key == "body") {
-            body = ustring (val);
-          }
-        }
-      }
-
-      mw->add_mode (new EditMessage (mw, to, from, cc, bcc, subject, body));
-
-    } else {
-      /* we probably just got the address on the cmd line */
-      mw->add_mode (new EditMessage (mw, url));
-    }
-
-    soup_uri_free (uri);
+    mw->add_mode (new EditMessage (mw, uri));
   }
 
   int Astroid::hint_level () {
diff --git a/src/astroid.hh b/src/astroid.hh
index f04803716..06089281a 100644
--- a/src/astroid.hh
+++ b/src/astroid.hh
@@ -94,6 +94,7 @@ namespace Astroid {
 
       int _hint_level = 0;
       po::options_description desc;
+      po::positional_options_description pdesc;
   };
 
   /* globally available instance of our main Astroid-class */
diff --git a/src/message_thread.cc b/src/message_thread.cc
index e84e9e5a1..5a66a5695 100644
--- a/src/message_thread.cc
+++ b/src/message_thread.cc
@@ -721,8 +721,7 @@ namespace Astroid {
 
         ustring scheme = Glib::uri_parse_scheme (a);
         if (scheme == "mailto") {
-
-          a = a.substr (scheme.length ()+1, a.length () - scheme.length()-1);
+          a = a.substr (7);
           UstringUtils::trim (a);
           al += Address(a);
         }
diff --git a/src/modes/edit_message.cc b/src/modes/edit_message.cc
index 3c236ff27..e695e0ece 100644
--- a/src/modes/edit_message.cc
+++ b/src/modes/edit_message.cc
@@ -6,6 +6,7 @@
 # include <memory>
 
 # include <gtkmm.h>
+# include <glib.h>
 
 # include <boost/filesystem.hpp>
 
@@ -35,19 +36,88 @@ using namespace boost::filesystem;
 namespace Astroid {
   int EditMessage::edit_id = 0;
 
-  EditMessage::EditMessage (MainWindow * mw, ustring _to, ustring _from, ustring _cc, ustring _bcc, ustring _subject, ustring _body) :
+  EditMessage::EditMessage (MainWindow * mw, ustring mailto, ustring _from, ustring _cc, ustring _bcc, ustring _subject, ustring _body) :
     EditMessage (mw, false) { // {{{
-
     in_read = false;
-    to  = _to;
-    cc  = _cc;
-    bcc = _bcc;
-    subject = _subject;
-    body = _body;
+    to  = "";
+    cc  = "";
+    bcc = "";
+    subject = "";
+    body = "";
+
+    if (ustring (g_uri_parse_scheme(mailto.c_str())) != "mailto") {
+      // if the prefix 'mailto:' is missing, assume it is only the receipient's address
+      to = mailto;
+    } else {
+      mailto.erase(0,7);
+      ustring::size_type sep = mailto.find("?");
+
+      to = g_uri_unescape_string (mailto.substr(0, sep).c_str(), NULL);
+      try {
+        mailto.erase(0, sep+1);
+      } catch (const std::out_of_range& ex) {
+        mailto = "";
+      }
+
+      ustring key;
+      ustring val;
+
+      while (mailto.size() > 0) {
+        sep = mailto.find("=");
+        key = mailto.substr(0, sep);
+        try {
+          mailto.erase (0,sep+1);
+        } catch (const std::out_of_range& ex) {
+          // = not found, i.e. syntax error
+          break;
+        }
+        sep = mailto.find("&");
+        val = ustring (g_uri_unescape_string (mailto.substr(0, sep).c_str(), NULL));
+        try {
+          if (sep == mailto.npos) {
+            mailto = "";
+          } else {
+            mailto.erase (0, sep+1);
+          }
+        } catch (const std::out_of_range& ex) {
+          // no value
+          break;
+        }
+
+        key = key.lowercase ();
+        if (!val.empty ()) {
+          if (key == "to") {
+            append_address (&to, val);
+          } else if (key == "cc") {
+            append_address (&cc, val);
+          } else if (key == "bcc") {
+            append_address (&bcc, val);
+          } else if (key == "subject") {
+            subject += val;
+          } else if (key == "body") {
+            body += val;
+          } else if (key == "from") {
+            // we accept only one "From" address
+            set_from (Address (val));
+          } else {
+            body = ustring::compose ("Unknown header: %1: %2\n%3", key, val, body);
+          }
+        }
+      }
+    }
+
+    append_address (&cc, _cc);
+    append_address (&bcc, _bcc);
+    subject += _subject;
+    body += _body;
+
+
     if (!_from.empty ()) {
+      // we accept only one "From" address
       set_from (Address (_from));
     }
 
+
     /* reload message */
     prepare_message ();
     read_edited_message ();
@@ -1029,6 +1099,11 @@ namespace Astroid {
     }
   }
 
+  void EditMessage::append_address (ustring *s, ustring a) {
+    *s += (s->empty() ? a : "," + a);
+  }
+
+
   /* }}} */
 
   /* send message {{{ */
diff --git a/src/modes/edit_message.hh b/src/modes/edit_message.hh
index 4aa812ec7..cc30b07ac 100644
--- a/src/modes/edit_message.hh
+++ b/src/modes/edit_message.hh
@@ -31,7 +31,8 @@ namespace Astroid {
 
     public:
       EditMessage (MainWindow *, bool edit_when_ready = true);
-      EditMessage (MainWindow *, ustring to, ustring from = "", ustring cc = "", ustring bcc = "", ustring subject = "", ustring body = "");
+      // mailto can be mailto uri or simply To: addresses
+      EditMessage (MainWindow *, ustring mailto, ustring from = "", ustring cc = "", ustring bcc = "", ustring subject = "", ustring body = "");
       EditMessage (MainWindow *, refptr<Message> _msg);
       ~EditMessage ();
 
@@ -166,6 +167,7 @@ namespace Astroid {
       void on_from_combo_changed ();
       //bool on_from_combo_key_press (GdkEventKey *);
       void on_element_action (int id, ThreadView::ElementAction action);
+      void append_address (ustring *s, ustring a);
 
     public:
       void grab_modal () override;
diff --git a/src/modes/thread_view/thread_view.cc b/src/modes/thread_view/thread_view.cc
index c38efcc8d..3dca442ab 100644
--- a/src/modes/thread_view/thread_view.cc
+++ b/src/modes/thread_view/thread_view.cc
@@ -196,10 +196,6 @@ namespace Astroid {
             ustring scheme = Glib::uri_parse_scheme (uri);
 
             if (scheme == "mailto") {
-
-              uri = uri.substr (scheme.length ()+1, uri.length () - scheme.length()-1);
-              UstringUtils::trim(uri);
-
               main_window->add_mode (new EditMessage (main_window, uri));
 
             } else if (scheme == "id" || scheme == "mid" ) {

From 52641da9716c9b60ef8b04caddd2307359ff44ed Mon Sep 17 00:00:00 2001
From: oreo639 <oreo6391@gmail.com>
Date: Thu, 29 Feb 2024 14:46:27 -0800
Subject: [PATCH 2/2] use webkit2gtk-4.1 when available and 4.0 as fallback

This is derived from 7ac88057441f432dbe44ea478e23dbfda70d32ac by oreo639 <oreo6391@gmail.com>,
but only the part necessary after 4c76a4cf6226752f5270197ba8747c7c7550e35e.
---
 CMakeLists.txt | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 498e4a260..8991f07e9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -95,7 +95,10 @@ endif()
 
 pkg_check_modules (GTKMM3     REQUIRED  gtkmm-3.0>=3.10)
 pkg_check_modules (GLIBMM2    REQUIRED  glibmm-2.4)
-pkg_check_modules (WEBKIT2GTK REQUIRED  webkit2gtk-4.0>=2.22)
+pkg_check_modules (WEBKIT2GTK webkit2gtk-4.1)
+if (NOT WEBKIT2GTK_FOUND)
+  pkg_check_modules (WEBKIT2GTK REQUIRED webkit2gtk-4.0>=2.22)
+endif ()
 pkg_check_modules (SASS       REQUIRED  libsass)
 pkg_check_modules (GIOMM2     REQUIRED  giomm-2.4)
 pkg_check_modules (GIOUNIX    REQUIRED  gio-unix-2.0>=2.16)
