From ee35c57ab0d1f0fa54a4fbfd1aad2f07ee5e1277 Mon Sep 17 00:00:00 2001
From: Raven <raven@sysadmins.ws>
Date: Sat, 15 Mar 2025 12:56:01 +0600
Subject: [PATCH] update of rx-* packages

---
 base/rx/rx-cairo/cairo.spec                   |    5 +-
 base/rx/rx-freerdp/freerdp.spec               |   10 +-
 .../freetype-2.10.1-debughook.patch           |   13 -
 ...type-2.10.4-avoid-invalid-face-index.patch |   43 -
 .../rx-freetype/freetype-2.10.4-covscan.patch |   45 -
 .../freetype-2.10.4-guard-face-size.patch     |   27 -
 .../freetype-2.10.4-png-memory-leak.patch     |   43 -
 ...ype-2.10.4-properly-guard-face_index.patch |   46 -
 .../freetype-2.13.2-SAST-findings.patch       |   75 +
 .../freetype-2.5.2-more-demos.patch           |    2 +-
 base/rx/rx-freetype/freetype.spec             |  116 +-
 .../fribidi-drop-bundled-gnulib.patch         | 1873 +++++++++
 base/rx/rx-fribidi/fribidi.spec               |   60 +
 base/rx/rx-glib2/1965.patch                   |  222 ++
 base/rx/rx-glib2/1968.patch                   | 1052 ++++++
 base/rx/rx-glib2/2194.patch                   |  920 +++++
 base/rx/rx-glib2/2222.patch                   |  739 ++++
 base/rx/rx-glib2/2244.patch                   |   49 +
 base/rx/rx-glib2/2291.patch                   |  129 +
 base/rx/rx-glib2/2408.patch                   |  391 ++
 base/rx/rx-glib2/2435.patch                   |  132 +
 base/rx/rx-glib2/2826.patch                   |  278 ++
 base/rx/rx-glib2/3126.patch                   | 3021 +++++++++++++++
 base/rx/rx-glib2/3136.patch                   |   65 +
 base/rx/rx-glib2/3163.patch                   |  199 +
 base/rx/rx-glib2/3272.patch                   |  141 +
 base/rx/rx-glib2/3353.patch                   | 1078 ++++++
 base/rx/rx-glib2/3845.patch                   |  195 +
 base/rx/rx-glib2/4038.patch                   | 3359 +++++++++++++++++
 base/rx/rx-glib2/glib-change-version.patch    | 2133 +++++++++++
 base/rx/rx-glib2/glib2.spec                   | 1012 +++++
 base/rx/rx-glib2/gnutls-hmac.patch            | 1086 ++++++
 ...on-t-add-extra-libraries-for-linking.patch |   47 +
 .../0001-fix-stupid-ax_python_devel.patch     |  112 +
 .../rx-gpgme/0002-setup_py_extra_opts.patch   |   12 +
 ...-qt-skip-test-remarks-for-gnupg2-2.4.patch |   58 +
 base/rx/rx-gpgme/changelog                    |  380 ++
 base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch  |   24 +
 ...gpgme-downgrade-libgpg-error-version.patch |   24 +
 ...exclude-extra-libraries-from-linking.patch |   31 +
 base/rx/rx-gpgme/gpgme-multilib.h             |   20 +
 base/rx/rx-gpgme/gpgme.spec                   |  412 ++
 base/rx/rx-harfbuzz/harfbuzz.spec             |    5 +-
 .../libgpg-error-1.29-multilib.patch          |  122 +
 base/rx/rx-libgpg-error/libgpg-error.spec     |  361 ++
 .../set-proper-version-suffix.patch           |   11 +
 .../rx-libjpeg-turbo/libjpeg-turbo-CET.patch  | 1248 ++++++
 .../libjpeg-turbo-cmake.patch                 |   33 +
 base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec   |  508 +++
 ...5226c870449522240ccff26f0b006037c520.patch |   26 +
 .../rx-libwebp/fix-cmake-files-location.patch |   11 +
 base/rx/rx-libwebp/libwebp-cmakedir.patch     |   12 +
 base/rx/rx-libwebp/libwebp-freeglut.patch     |   35 +
 .../rx-libwebp/libwebp-mingw-libsuffix.patch  |   13 +
 base/rx/rx-libwebp/libwebp-rpath.patch        |   20 +
 base/rx/rx-libwebp/libwebp.spec               |  365 ++
 base/rx/rx-libwebp/libwebp_jni_example.java   |   27 +
 base/rx/rx-poppler/poppler.spec               |    8 +-
 58 files changed, 22211 insertions(+), 273 deletions(-)
 delete mode 100644 base/rx/rx-freetype/freetype-2.10.1-debughook.patch
 delete mode 100644 base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch
 delete mode 100644 base/rx/rx-freetype/freetype-2.10.4-covscan.patch
 delete mode 100644 base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch
 delete mode 100644 base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch
 delete mode 100644 base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch
 create mode 100644 base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch
 create mode 100644 base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch
 create mode 100644 base/rx/rx-fribidi/fribidi.spec
 create mode 100644 base/rx/rx-glib2/1965.patch
 create mode 100644 base/rx/rx-glib2/1968.patch
 create mode 100644 base/rx/rx-glib2/2194.patch
 create mode 100644 base/rx/rx-glib2/2222.patch
 create mode 100644 base/rx/rx-glib2/2244.patch
 create mode 100644 base/rx/rx-glib2/2291.patch
 create mode 100644 base/rx/rx-glib2/2408.patch
 create mode 100644 base/rx/rx-glib2/2435.patch
 create mode 100644 base/rx/rx-glib2/2826.patch
 create mode 100644 base/rx/rx-glib2/3126.patch
 create mode 100644 base/rx/rx-glib2/3136.patch
 create mode 100644 base/rx/rx-glib2/3163.patch
 create mode 100644 base/rx/rx-glib2/3272.patch
 create mode 100644 base/rx/rx-glib2/3353.patch
 create mode 100644 base/rx/rx-glib2/3845.patch
 create mode 100644 base/rx/rx-glib2/4038.patch
 create mode 100644 base/rx/rx-glib2/glib-change-version.patch
 create mode 100644 base/rx/rx-glib2/glib2.spec
 create mode 100644 base/rx/rx-glib2/gnutls-hmac.patch
 create mode 100644 base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch
 create mode 100644 base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch
 create mode 100644 base/rx/rx-gpgme/0002-setup_py_extra_opts.patch
 create mode 100644 base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch
 create mode 100644 base/rx/rx-gpgme/changelog
 create mode 100644 base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch
 create mode 100644 base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch
 create mode 100644 base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch
 create mode 100644 base/rx/rx-gpgme/gpgme-multilib.h
 create mode 100644 base/rx/rx-gpgme/gpgme.spec
 create mode 100644 base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch
 create mode 100644 base/rx/rx-libgpg-error/libgpg-error.spec
 create mode 100644 base/rx/rx-libgpg-error/set-proper-version-suffix.patch
 create mode 100644 base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch
 create mode 100644 base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch
 create mode 100644 base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec
 create mode 100644 base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch
 create mode 100644 base/rx/rx-libwebp/fix-cmake-files-location.patch
 create mode 100644 base/rx/rx-libwebp/libwebp-cmakedir.patch
 create mode 100644 base/rx/rx-libwebp/libwebp-freeglut.patch
 create mode 100644 base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch
 create mode 100644 base/rx/rx-libwebp/libwebp-rpath.patch
 create mode 100644 base/rx/rx-libwebp/libwebp.spec
 create mode 100644 base/rx/rx-libwebp/libwebp_jni_example.java

diff --git a/base/rx/rx-cairo/cairo.spec b/base/rx/rx-cairo/cairo.spec
index bb423b3..0caa4db 100644
--- a/base/rx/rx-cairo/cairo.spec
+++ b/base/rx/rx-cairo/cairo.spec
@@ -14,7 +14,7 @@
 
 Name:		rx-cairo
 Version:	1.17.4
-Release:	3%{?dist}
+Release:	5%{?dist}
 Summary:	A 2D graphics library
 
 License:	LGPLv2 or MPLv1.1
@@ -187,6 +187,9 @@ find $RPM_BUILD_ROOT -name '*.la' -delete
 %{_libdir}/cairo/
 
 %changelog
+* Tue Oct 29 2024 Raven <raven@sysadmins.ws> - 1.17.4-4
+- rebuilt with new rx-freetype
+
 * Mon Jan 30 2023 Stewart Smith <trawets@amazon.com> - 1.17.4-3.amzn2023.0.2
 - Mass rebuild for AL2023
 
diff --git a/base/rx/rx-freerdp/freerdp.spec b/base/rx/rx-freerdp/freerdp.spec
index fc6c175..b036324 100644
--- a/base/rx/rx-freerdp/freerdp.spec
+++ b/base/rx/rx-freerdp/freerdp.spec
@@ -4,7 +4,7 @@
 # "--with=ffmpeg", or "--with=openh264" to mock/rpmbuild; or by globally
 # setting these variables:
 # https://bugzilla.redhat.com/show_bug.cgi?id=2242028
-%global _with_ffmpeg 1
+#global _with_ffmpeg 1
 %global _with_openh264 1
 
 # Can be rebuilt with OpenCL support enabled by passing # "--with=opencl"
@@ -28,8 +28,8 @@
 %endif
 
 Name:           rx-freerdp
-Version:        2.11.2
-Release:        3%{?dist}
+Version:        2.11.7
+Release:        1%{?dist}
 Epoch:          2
 Summary:        Free implementation of the Remote Desktop Protocol (RDP)
 License:        ASL 2.0
@@ -100,7 +100,6 @@ Obsoletes:      %{name}-plugins < 1:1.1.0
 Provides:       %{name}-plugins = %{?epoch}:%{version}-%{release}
 Requires:       (libwayland-client >= 1.22 or rx-libwayland-client)
 Requires:       (libwayland-cursor >= 1.22 or rx-libwayland-cursor)
-#Requires:       (libxkbcommon_ng or libxkbcommon >= 1.0)
 %description    libs
 libfreerdp-core can be embedded in applications.
 
@@ -302,6 +301,9 @@ find %{buildroot} -name "*.a" -delete
 %{_libdir}/pkgconfig/winpr-tools2.pc
 
 %changelog
+* Tue Oct 29 2024 Raven <raven@sysadmins.ws> - 2:2.11.7-1
+- Update to 2.11.7
+
 * Fri Sep 13 2024 Raven <raven@sysadmins.ws> - 2:2.11.2-3
 - rebuilt with new OpenSSL
 
diff --git a/base/rx/rx-freetype/freetype-2.10.1-debughook.patch b/base/rx/rx-freetype/freetype-2.10.1-debughook.patch
deleted file mode 100644
index 4421401..0000000
--- a/base/rx/rx-freetype/freetype-2.10.1-debughook.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-diff --git a/include/freetype/ftmodapi.h b/include/freetype/ftmodapi.h
-index 8d039c4f3..88488bfe8 100644
---- a/include/freetype/ftmodapi.h
-+++ b/include/freetype/ftmodapi.h
-@@ -623,7 +623,7 @@ FT_BEGIN_HEADER
-    *     it is bytecode interpreter's execution context, `TT_ExecContext`,
-    *     which is declared in FreeType's internal header file `tttypes.h`.
-    */
--  typedef FT_Error
-+  typedef void
-   (*FT_DebugHook_Func)( void*  arg );
- 
- 
diff --git a/base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch b/base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch
deleted file mode 100644
index 55c09bd..0000000
--- a/base/rx/rx-freetype/freetype-2.10.4-avoid-invalid-face-index.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 53dfdcd8198d2b3201a23c4bad9190519ba918db Mon Sep 17 00:00:00 2001
-From: Werner Lemberg <wl@gnu.org>
-Date: Thu, 17 Mar 2022 19:24:16 +0100
-Subject: [PATCH] [sfnt] Avoid invalid face index.
-
-Fixes #1138.
-
-* src/sfnt/sfobjs.c (sfnt_init_face), src/sfnt/sfwoff2.c (woff2_open_font):
-Check `face_index` before decrementing.
----
- src/sfnt/sfobjs.c  | 2 +-
- src/sfnt/sfwoff2.c | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
-index f9d4d3858..9771c35df 100644
---- a/src/sfnt/sfobjs.c
-+++ b/src/sfnt/sfobjs.c
-@@ -566,7 +566,7 @@
-     face_index = FT_ABS( face_instance_index ) & 0xFFFF;
- 
-     /* value -(N+1) requests information on index N */
--    if ( face_instance_index < 0 )
-+    if ( face_instance_index < 0 && face_index > 0 )
-       face_index--;
- 
-     if ( face_index >= face->ttc_header.count )
-diff --git a/src/sfnt/sfwoff2.c b/src/sfnt/sfwoff2.c
-index cb1e0664a..165b875e5 100644
---- a/src/sfnt/sfwoff2.c
-+++ b/src/sfnt/sfwoff2.c
-@@ -2085,7 +2085,7 @@
-     /* Validate requested face index. */
-     *num_faces = woff2.num_fonts;
-     /* value -(N+1) requests information on index N */
--    if ( *face_instance_index < 0 )
-+    if ( *face_instance_index < 0 && face_index > 0 )
-       face_index--;
- 
-     if ( face_index >= woff2.num_fonts )
--- 
-2.35.1
-
diff --git a/base/rx/rx-freetype/freetype-2.10.4-covscan.patch b/base/rx/rx-freetype/freetype-2.10.4-covscan.patch
deleted file mode 100644
index d42ca75..0000000
--- a/base/rx/rx-freetype/freetype-2.10.4-covscan.patch
+++ /dev/null
@@ -1,45 +0,0 @@
---- freetype-2.10.4/builds/unix/freetype-config.in
-+++ freetype-2.10.4/builds/unix/freetype-config.in
-@@ -32,9 +32,6 @@ cflags=`%PKG_CONFIG% --cflags freetype2`
- dynamic_libs=`pkgconf --libs freetype2`
- static_libs=`pkgconf --static --libs freetype2`
- 
--orig_prefix=$prefix
--orig_exec_prefix=$exec_prefix
--
- orig_includedir=$includedir
- orig_libdir=$libdir
- 
---- freetype-2.10.4/ft2demos-2.10.4/src/ftbench.c
-+++ freetype-2.10.4/ft2demos-2.10.4/src/ftbench.c
-@@ -749,6 +749,7 @@
-         {
-           fprintf( stderr,
-                    "couldn't allocate memory to pre-load font file\n" );
-+          fclose( file );
- 
-           return 1;
-         }
-@@ -758,9 +759,12 @@
-           fprintf( stderr, "read error\n" );
-           free( memory_file );
-           memory_file = NULL;
-+          fclose( file );
- 
-           return 1;
-         }
-+
-+        fclose( file );
-       }
- 
-       error = FT_New_Memory_Face( lib,
---- freetype-2.10.4/ft2demos-2.10.4/src/ftgrid.c
-+++ freetype-2.10.4/ft2demos-2.10.4/src/ftgrid.c
-@@ -662,6 +662,7 @@
-         break;
- 
-       default:
-+        free( t );
-         return;
-     }
- 
diff --git a/base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch b/base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch
deleted file mode 100644
index 8a447ae..0000000
--- a/base/rx/rx-freetype/freetype-2.10.4-guard-face-size.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 0c2bdb01a2e1d24a3e592377a6d0822856e10df2 Mon Sep 17 00:00:00 2001
-From: Werner Lemberg <wl@gnu.org>
-Date: Sat, 19 Mar 2022 09:37:28 +0100
-Subject: [PATCH] * src/base/ftobjs.c (FT_Request_Size): Guard `face->size`.
-
-Fixes #1140.
----
- src/base/ftobjs.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
-index 6492a1517..282c9121a 100644
---- a/src/base/ftobjs.c
-+++ b/src/base/ftobjs.c
-@@ -3409,6 +3409,9 @@
-     if ( !face )
-       return FT_THROW( Invalid_Face_Handle );
- 
-+    if ( !face->size )
-+      return FT_THROW( Invalid_Size_Handle );
-+
-     if ( !req || req->width < 0 || req->height < 0 ||
-          req->type >= FT_SIZE_REQUEST_TYPE_MAX )
-       return FT_THROW( Invalid_Argument );
--- 
-2.35.1
-
diff --git a/base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch b/base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch
deleted file mode 100644
index 52df99a..0000000
--- a/base/rx/rx-freetype/freetype-2.10.4-png-memory-leak.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-From 007c109b4594c5e63948bd08b4d5011ad76ffb10 Mon Sep 17 00:00:00 2001
-From: Ben Wagner <bungeman@google.com>
-Date: Fri, 23 Oct 2020 08:29:14 +0200
-Subject: [PATCH] * src/sfnt/pngshim.c (Load_SBit_Png): Fix memory leak
- (#59322).
-
-The issue is that `rows` is allocated but will not be freed in the
-event that the call to `png_read_image` fails and calls `longjmp`.
----
- ChangeLog          | 7 +++++++
- src/sfnt/pngshim.c | 1 +
- 2 files changed, 8 insertions(+)
-
-diff --git a/ChangeLog b/ChangeLog
-index 42f7c34ba..ff048b8ab 100644
---- a/ChangeLog
-+++ b/ChangeLog
-@@ -1,3 +1,10 @@
-+2020-10-23  Ben Wagner  <bungeman@google.com>
-+
-+	* src/sfnt/pngshim.c (Load_SBit_Png): Fix memory leak (#59322).
-+
-+	The issue is that `rows` is allocated but will not be freed in the
-+	event that the call to `png_read_image` fails and calls `longjmp`.
-+
- 2020-10-20  Werner Lemberg  <wl@gnu.org>
- 
- 	* Version 2.10.4 released.
-diff --git a/src/sfnt/pngshim.c b/src/sfnt/pngshim.c
-index f55016122..d4e43a9f4 100644
---- a/src/sfnt/pngshim.c
-+++ b/src/sfnt/pngshim.c
-@@ -443,6 +443,7 @@
-     png_read_end( png, info );
- 
-   DestroyExit:
-+    FT_FREE( rows );
-     png_destroy_read_struct( &png, &info, NULL );
-     FT_Stream_Close( &stream );
- 
--- 
-2.26.2
-
diff --git a/base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch b/base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch
deleted file mode 100644
index 92ae817..0000000
--- a/base/rx/rx-freetype/freetype-2.10.4-properly-guard-face_index.patch
+++ /dev/null
@@ -1,46 +0,0 @@
-From 22a0cccb4d9d002f33c1ba7a4b36812c7d4f46b5 Mon Sep 17 00:00:00 2001
-From: Werner Lemberg <wl@gnu.org>
-Date: Sat, 19 Mar 2022 06:40:17 +0100
-Subject: [PATCH] * src/base/ftobjs.c (ft_open_face_internal): Properly guard
- `face_index`.
-
-We must ensure that the cast to `FT_Int` doesn't change the sign.
-
-Fixes #1139.
----
- src/base/ftobjs.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-From d014387ad4a5dd04d8e7f99587c7dacb70261924 Mon Sep 17 00:00:00 2001
-From: Werner Lemberg <wl@gnu.org>
-Date: Sat, 19 Mar 2022 09:30:45 +0100
-Subject: [PATCH 2/2] * src/base/ftobjs.c (ft_open_face_internal): Thinko.
-
----
- src/base/ftobjs.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
-index 2c0f0e6c9..10952a6c6 100644
---- a/src/base/ftobjs.c
-+++ b/src/base/ftobjs.c
-@@ -2527,6 +2527,16 @@
- #endif
- 
- 
-+    /* only use lower 31 bits together with sign bit */
-+    if ( face_index > 0 )
-+      face_index &= 0x7FFFFFFFL;
-+    else
-+    {
-+      face_index  = -face_index;
-+      face_index &= 0x7FFFFFFFL;
-+      face_index  = -face_index;
-+    }
-+
- #ifdef FT_DEBUG_LEVEL_TRACE
-     FT_TRACE3(( "FT_Open_Face: " ));
-     if ( face_index < 0 )
--- 
-2.35.1
-
diff --git a/base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch b/base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch
new file mode 100644
index 0000000..81d28f8
--- /dev/null
+++ b/base/rx/rx-freetype/freetype-2.13.2-SAST-findings.patch
@@ -0,0 +1,75 @@
+diff --git a/src/autofit/afglobal.c b/src/autofit/afglobal.c
+index b7403fa65..1fd5a0be3 100644
+--- a/src/autofit/afglobal.c
++++ b/src/autofit/afglobal.c
+@@ -245,6 +245,12 @@
+         af_shaper_get_coverage( globals, style_class, gstyles, 0 );
+     }
+ 
++    if ( dflt >= sizeof (af_style_classes) / sizeof (AF_StyleClass) )
++    {
++      error = FT_THROW( Invalid_Offset );
++      goto Exit;
++    }
++
+     /* ... and finally the default OpenType features of the default script */
+     af_shaper_get_coverage( globals, af_style_classes[dflt], gstyles, 1 );
+ 
+diff --git a/src/tools/apinames.c b/src/tools/apinames.c
+index 5a49b0649..feefb4ee7 100644
+--- a/src/tools/apinames.c
++++ b/src/tools/apinames.c
+@@ -182,6 +182,7 @@ names_dump( FILE*         out,
+   case OUTPUT_WATCOM_LBC:
+     {
+       const char*  dot;
++      char  temp[512];
+ 
+ 
+       if ( !dll_name )
+@@ -195,7 +196,6 @@ names_dump( FILE*         out,
+       dot = strchr( dll_name, '.' );
+       if ( dot )
+       {
+-        char  temp[512];
+         int   len = dot - dll_name;
+ 
+ 
+diff --git a/src/ftbench.c b/src/ftbench.c
+index ec5c46c..7d96f60 100644
+--- a/ft2demos-2.13.2/src/ftbench.c
++++ b/ft2demos-2.13.2/src/ftbench.c
+@@ -907,6 +907,7 @@
+         {
+           fprintf( stderr,
+                    "couldn't allocate memory to pre-load font file\n" );
++          fclose( file );
+ 
+           return 1;
+         }
+@@ -916,9 +917,11 @@
+           fprintf( stderr, "read error\n" );
+           free( memory_file );
+           memory_file = NULL;
++          fclose( file );
+ 
+           return 1;
+         }
++        fclose( file );
+       }
+ 
+       error = FT_New_Memory_Face( lib,
+diff --git a/src/ftgrid.c b/src/ftgrid.c
+index bae4826..1a8f421 100644
+--- a/ft2demos-2.13.2/src/ftgrid.c
++++ b/ft2demos-2.13.2/src/ftgrid.c
+@@ -420,6 +420,9 @@
+     if ( !line )
+       return;
+ 
++    if (bit->mode == gr_pixel_mode_mono)
++      memset( line, 0, (size_t)( pitch * bit->rows * scale * scale ));
++
+     switch( bit->mode )
+     {
+       case gr_pixel_mode_mono:
diff --git a/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch b/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch
index e737cb7..7e58be7 100644
--- a/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch
+++ b/base/rx/rx-freetype/freetype-2.5.2-more-demos.patch
@@ -14,4 +14,4 @@
 +  EXES += fttimer
    # EXES += testname
  
-   exes: $(EXES:%=$(BIN_DIR_2)/%$E)
+   # Not all demo programs have a man page; we thus check for existence in a
diff --git a/base/rx/rx-freetype/freetype.spec b/base/rx/rx-freetype/freetype.spec
index 448bdfe..1d14a95 100644
--- a/base/rx/rx-freetype/freetype.spec
+++ b/base/rx/rx-freetype/freetype.spec
@@ -5,9 +5,9 @@
 
 Summary: A free and portable font rendering engine
 Name: rx-freetype
-Version: 2.10.4
-Release: 9%{?dist}
-License: (FTL or GPLv2+) and BSD and MIT and Public Domain and zlib with acknowledgement
+Version: 2.13.2
+Release: 7%{?dist}
+License: (FTL OR GPL-2.0-or-later) AND BSD-3-Clause AND MIT AND MIT-Modern-Variant AND LicenseRef-Fedora-Public-Domain AND Zlib
 URL: http://www.freetype.org
 Source:  http://download.savannah.gnu.org/releases/freetype/freetype-%{version}.tar.xz
 Source1: http://download.savannah.gnu.org/releases/freetype/freetype-doc-%{version}.tar.xz
@@ -26,22 +26,8 @@ Patch3:  freetype-2.6.5-libtool.patch
 Patch4:  freetype-2.8-multilib.patch
 
 Patch5:  freetype-2.10.0-internal-outline.patch
-# Revert ABI/API change
-Patch6:  freetype-2.10.1-debughook.patch
 
-Patch7:  freetype-2.10.4-png-memory-leak.patch
-
-# https://bugzilla.redhat.com/show_bug.cgi?id=1964066
-Patch8:  freetype-2.10.4-covscan.patch
-
-# https://bugzilla.redhat.com/show_bug.cgi?id=2077989
-Patch9:  freetype-2.10.4-avoid-invalid-face-index.patch
-
-# https://bugzilla.redhat.com/show_bug.cgi?id=2077991
-Patch10: freetype-2.10.4-properly-guard-face_index.patch
-
-# https://bugzilla.redhat.com/show_bug.cgi?id=2077985
-Patch11: freetype-2.10.4-guard-face-size.patch
+Patch6:  freetype-2.13.2-SAST-findings.patch
 
 BuildRequires:  gcc
 BuildRequires: libX11-devel
@@ -52,14 +38,13 @@ BuildRequires: brotli-devel
 BuildRequires: make
 %if %{without bootstrap}
 BuildRequires: rx-harfbuzz-devel
+
+Requires: rx-harfbuzz
 %endif
 
-
-Provides: pkgconfig(freetype2) = %{version}-%{release}
 Provides: %{name}-bytecode
 Provides: %{name}-subpixel
 Obsoletes: freetype-freeworld < 2.9.1-2
-Requires: rx-harfbuzz
 
 %description
 The FreeType engine is a free and portable font rendering
@@ -85,8 +70,6 @@ small utilities showing various capabilities of the FreeType library.
 Summary: FreeType development libraries and header files
 Requires: %{name} = %{version}-%{release}
 Requires: pkgconf%{?_isa}
-#Conflicts: freetype-devel
-
 
 %description devel
 The freetype-devel package includes the static libraries and header files
@@ -109,14 +92,11 @@ popd
 %patch3 -p1 -b .libtool
 %patch4 -p1 -b .multilib
 %patch5 -p1 -b .internal-outline
-%patch6 -p1 -b .debughook
-%patch7 -p1 -b .png-memory-leak
-%patch8 -p1 -b .covscan
-%patch9 -p1 -b .avoid-invalid-face-index
-%patch10 -p1 -b .properly-guard-face_index
-%patch11 -p1 -b .guard-face-size
+%patch6 -p1 -b .SAST-findings
 
 %build
+
+export PATH=%{_bindir}:$PATH
 export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} ${LDFLAGS}"
 export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
 
@@ -210,7 +190,7 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.{a,la}
 
 %files
 %{!?_licensedir:%global license %%doc}
-%license docs/LICENSE.TXT docs/FTL.TXT docs/GPLv2.TXT
+%license LICENSE.TXT docs/FTL.TXT docs/GPLv2.TXT
 %{_libdir}/libfreetype.so.*
 %doc README
 
@@ -258,28 +238,70 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.{a,la}
 %{_mandir}/man1/*
 
 %changelog
-* Tue May 31 2022 Marek Kasik <mkasik@redhat.com> - 2.10.4-9
-- Guard face->size
-- Resolves: #2079280
+* Mon Sep 30 2024 Marek Kasik <mkasik@redhat.com> - 2.13.2-7
+- Fix SAST Automation findings
+- Resolves: RHEL-44737
 
-* Mon May 30 2022 Marek Kasik <mkasik@redhat.com> - 2.10.4-8
-- Properly guard "face_index"
-- Resolves: #2079262
+* Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 2.13.2-6
+- Bump release for June 2024 mass rebuild
 
-* Thu May 26 2022 Marek Kasik <mkasik@redhat.com> - 2.10.4-7
-- Avoid invalid face index
-- Resolves: #2079271
+* Tue Feb  6 2024 Marek Kasik <mkasik@redhat.com> - 2.13.2-5
+- Migrated to SPDX license
 
-* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 2.10.4-6
-- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
-  Related: rhbz#1991688
+* Tue Jan 30 2024 Marek Kasik <mkasik@redhat.com> - 2.13.2-4
+- Remove a patch which causes FTBFS
+- Resolves: #2261113
 
-* Wed May 26 2021 Marek Kasik <mkasik@redhat.com> - 2.10.4-5
-- Backport fixes for issues found by Coverity scan
-- Resolves: #1964066
+* Wed Jan 24 2024 Fedora Release Engineering <releng@fedoraproject.org> - 2.13.2-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
 
-* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 2.10.4-4
-- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
+* Fri Jan 19 2024 Fedora Release Engineering <releng@fedoraproject.org> - 2.13.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
+
+* Tue Jan 16 2024 Marek Kasik <mkasik@redhat.com> - 2.13.2-1
+- Update to 2.13.2
+- Resolves: #2217137
+
+* Wed Jul 19 2023 Fedora Release Engineering <releng@fedoraproject.org> - 2.13.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild
+
+* Tue Jun 27 2023 Marek Kasik <mkasik@redhat.com> - 2.13.1-1
+- Update to 2.13.1
+- Resolves: #2217137
+
+* Sat Feb 25 2023 Marek Kasik <mkasik@redhat.com> - 2.13.0-1
+- Update to 2.13.0
+- Resolves: #2168496
+
+* Thu Jan 19 2023 Fedora Release Engineering <releng@fedoraproject.org> - 2.12.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
+
+* Thu Jul 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 2.12.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
+
+* Fri Jul  8 2022 Marek Kasik <mkasik@redhat.com> - 2.12.1-2
+- Clear correct flags for doc ownership
+- Resolves: #2104570
+
+* Mon May  2 2022 Marek Kasik <mkasik@redhat.com> - 2.12.1-1
+- Update to 2.12.1
+- Resolves: #2080714
+
+* Mon Apr 25 2022 Marek Kasik <mkasik@redhat.com> - 2.12.0-1
+- Update to 2.12.0
+- Resolves: #2070686
+
+* Thu Jan 20 2022 Fedora Release Engineering <releng@fedoraproject.org> - 2.11.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
+
+* Sat Dec 4 2021 Diego Herrera <dherrera@redhat.com> - 2.11.1-1
+- Update to 2.11.1
+
+* Thu Jul 22 2021 Marek Kasik <mkasik@redhat.com> - 2.11.0-1
+- Update to 2.11.0
+
+* Wed Jul 21 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.10.4-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
 
 * Fri Feb 5 2021 Akira TAGOH <tagoh@redhat.com> - 2.10.4-3
 - Enable HarfBuzz support
diff --git a/base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch b/base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch
new file mode 100644
index 0000000..1315922
--- /dev/null
+++ b/base/rx/rx-fribidi/fribidi-drop-bundled-gnulib.patch
@@ -0,0 +1,1873 @@
+diff -pruN fribidi-1.0.14.orig/bin/Makefile.am fribidi-1.0.14/bin/Makefile.am
+--- fribidi-1.0.14.orig/bin/Makefile.am	2020-07-06 04:17:23.000000000 +0900
++++ fribidi-1.0.14/bin/Makefile.am	2024-05-07 21:40:04.500166714 +0900
+@@ -2,11 +2,9 @@ bin_PROGRAMS = fribidi
+ 
+ noinst_PROGRAMS = fribidi-benchmark fribidi-bidi-types fribidi-caprtl2utf8
+ 
+-getopt_SOURCES = getopt.c getopt1.c getopt_int.h getopt.h gettext.h
++fribidi_SOURCES = fribidi-main.c
+ 
+-fribidi_SOURCES = fribidi-main.c $(getopt_SOURCES)
+-
+-fribidi_benchmark_SOURCES = fribidi-benchmark.c $(getopt_SOURCES)
++fribidi_benchmark_SOURCES = fribidi-benchmark.c
+ 
+ AM_CPPFLAGS = \
+ 		@FRIBIDI_CPPFLAGS@ \
+diff -pruN fribidi-1.0.14.orig/bin/getopt.c fribidi-1.0.14/bin/getopt.c
+--- fribidi-1.0.14.orig/bin/getopt.c	2015-08-05 03:49:07.000000000 +0900
++++ fribidi-1.0.14/bin/getopt.c	1970-01-01 09:00:00.000000000 +0900
+@@ -1,1268 +0,0 @@
+-/* Getopt for GNU.
+-   NOTE: getopt is now part of the C library, so if you don't know what
+-   "Keep this file name-space clean" means, talk to drepper@gnu.org
+-   before changing it!
+-   Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004
+-   	Free Software Foundation, Inc.
+-   This file is part of the GNU C Library.
+-
+-   This program is free software; you can redistribute it and/or modify
+-   it under the terms of the GNU General Public License as published by
+-   the Free Software Foundation; either version 2, or (at your option)
+-   any later version.
+-
+-   This program is distributed in the hope that it will be useful,
+-   but WITHOUT ANY WARRANTY; without even the implied warranty of
+-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-   GNU General Public License for more details.
+-
+-   You should have received a copy of the GNU General Public License along
+-   with this program; if not, write to the Free Software Foundation,
+-   Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.  */
+-
+-/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+-   Ditto for AIX 3.2 and <stdlib.h>.  */
+-#ifndef _NO_PROTO
+-# define _NO_PROTO
+-#endif
+-
+-#ifdef HAVE_CONFIG_H
+-# include <config.h>
+-#endif
+-
+-#include <stdio.h>
+-
+-/* Comment out all this code if we are using the GNU C Library, and are not
+-   actually compiling the library itself.  This code is part of the GNU C
+-   Library, but also included in many other GNU distributions.  Compiling
+-   and linking in this code is a waste when using the GNU C library
+-   (especially if it is a shared library).  Rather than having every GNU
+-   program understand `configure --with-gnu-libc' and omit the object files,
+-   it is simpler to just do this in the source for each such file.  */
+-
+-#define GETOPT_INTERFACE_VERSION 2
+-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+-# include <gnu-versions.h>
+-# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+-#  define ELIDE_CODE
+-# endif
+-#endif
+-
+-#ifndef ELIDE_CODE
+-
+-
+-/* This needs to come after some library #include
+-   to get __GNU_LIBRARY__ defined.  */
+-#ifdef	__GNU_LIBRARY__
+-/* Don't include stdlib.h for non-GNU C libraries because some of them
+-   contain conflicting prototypes for getopt.  */
+-# include <stdlib.h>
+-# include <unistd.h>
+-#endif /* GNU C library.  */
+-
+-#include <string.h>
+-
+-#ifdef VMS
+-# include <unixlib.h>
+-#endif
+-
+-#ifdef _LIBC
+-# include <libintl.h>
+-#else
+-# include "gettext.h"
+-# define _(msgid) gettext (msgid)
+-#endif
+-
+-#if defined _LIBC && defined USE_IN_LIBIO
+-# include <wchar.h>
+-#endif
+-
+-#ifndef attribute_hidden
+-# define attribute_hidden
+-#endif
+-
+-/* This version of `getopt' appears to the caller like standard Unix `getopt'
+-   but it behaves differently for the user, since it allows the user
+-   to intersperse the options with the other arguments.
+-
+-   As `getopt' works, it permutes the elements of ARGV so that,
+-   when it is done, all the options precede everything else.  Thus
+-   all application programs are extended to handle flexible argument order.
+-
+-   Setting the environment variable POSIXLY_CORRECT disables permutation.
+-   Then the behavior is completely standard.
+-
+-   GNU application programs can use a third alternative mode in which
+-   they can distinguish the relative order of options and other arguments.  */
+-
+-#include "getopt.h"
+-#include "getopt_int.h"
+-
+-/* For communication from `getopt' to the caller.
+-   When `getopt' finds an option that takes an argument,
+-   the argument value is returned here.
+-   Also, when `ordering' is RETURN_IN_ORDER,
+-   each non-option ARGV-element is returned here.  */
+-
+-char *optarg;
+-
+-/* Index in ARGV of the next element to be scanned.
+-   This is used for communication to and from the caller
+-   and for communication between successive calls to `getopt'.
+-
+-   On entry to `getopt', zero means this is the first call; initialize.
+-
+-   When `getopt' returns -1, this is the index of the first of the
+-   non-option elements that the caller should itself scan.
+-
+-   Otherwise, `optind' communicates from one call to the next
+-   how much of ARGV has been scanned so far.  */
+-
+-/* 1003.2 says this must be 1 before any call.  */
+-int optind = 1;
+-
+-/* Callers store zero here to inhibit the error message
+-   for unrecognized options.  */
+-
+-int opterr = 1;
+-
+-/* Set to an option character which was unrecognized.
+-   This must be initialized on some systems to avoid linking in the
+-   system's own getopt implementation.  */
+-
+-int optopt = '?';
+-
+-/* Keep a global copy of all internal members of getopt_data.  */
+-
+-static struct _getopt_data getopt_data;
+-
+-
+-#ifndef __GNU_LIBRARY__
+-
+-/* Avoid depending on library functions or files
+-   whose names are inconsistent.  */
+-
+-#ifndef getenv
+-extern char *getenv (
+-);
+-#endif
+-
+-#endif /* not __GNU_LIBRARY__ */
+-
+-#ifdef _LIBC
+-/* Stored original parameters.
+-   XXX This is no good solution.  We should rather copy the args so
+-   that we can compare them later.  But we must not use malloc(3).  */
+-extern int __libc_argc;
+-extern char **__libc_argv;
+-
+-/* Bash 2.0 gives us an environment variable containing flags
+-   indicating ARGV elements that should not be considered arguments.  */
+-
+-# ifdef USE_NONOPTION_FLAGS
+-/* Defined in getopt_init.c  */
+-extern char *__getopt_nonoption_flags;
+-# endif
+-
+-# ifdef USE_NONOPTION_FLAGS
+-#  define SWAP_FLAGS(ch1, ch2) \
+-  if (d->__nonoption_flags_len > 0)					      \
+-    {									      \
+-      char __tmp = __getopt_nonoption_flags[ch1];			      \
+-      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];	      \
+-      __getopt_nonoption_flags[ch2] = __tmp;				      \
+-    }
+-# else
+-#  define SWAP_FLAGS(ch1, ch2)
+-# endif
+-#else /* !_LIBC */
+-# define SWAP_FLAGS(ch1, ch2)
+-#endif /* _LIBC */
+-
+-/* Exchange two adjacent subsequences of ARGV.
+-   One subsequence is elements [first_nonopt,last_nonopt)
+-   which contains all the non-options that have been skipped so far.
+-   The other is elements [last_nonopt,optind), which contains all
+-   the options processed since those non-options were skipped.
+-
+-   `first_nonopt' and `last_nonopt' are relocated so that they describe
+-   the new indices of the non-options in ARGV after they are moved.  */
+-
+-static void
+-exchange (
+-  char **argv,
+-  struct _getopt_data *d
+-)
+-{
+-  int bottom = d->__first_nonopt;
+-  int middle = d->__last_nonopt;
+-  int top = d->optind;
+-  char *tem;
+-
+-  /* Exchange the shorter segment with the far end of the longer segment.
+-     That puts the shorter segment into the right place.
+-     It leaves the longer segment in the right place overall,
+-     but it consists of two parts that need to be swapped next.  */
+-
+-#if defined _LIBC && defined USE_NONOPTION_FLAGS
+-  /* First make sure the handling of the `__getopt_nonoption_flags'
+-     string can work normally.  Our top argument must be in the range
+-     of the string.  */
+-  if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len)
+-    {
+-      /* We must extend the array.  The user plays games with us and
+-         presents new arguments.  */
+-      char *new_str = malloc (top + 1);
+-      if (new_str == NULL)
+-	d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0;
+-      else
+-	{
+-	  memset (__mempcpy (new_str, __getopt_nonoption_flags,
+-			     d->__nonoption_flags_max_len),
+-		  '\0', top + 1 - d->__nonoption_flags_max_len);
+-	  d->__nonoption_flags_max_len = top + 1;
+-	  __getopt_nonoption_flags = new_str;
+-	}
+-    }
+-#endif
+-
+-  while (top > middle && middle > bottom)
+-    {
+-      if (top - middle > middle - bottom)
+-	{
+-	  /* Bottom segment is the short one.  */
+-	  int len = middle - bottom;
+-	  register int i;
+-
+-	  /* Swap it with the top part of the top segment.  */
+-	  for (i = 0; i < len; i++)
+-	    {
+-	      tem = argv[bottom + i];
+-	      argv[bottom + i] = argv[top - (middle - bottom) + i];
+-	      argv[top - (middle - bottom) + i] = tem;
+-	      SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+-	    }
+-	  /* Exclude the moved bottom segment from further swapping.  */
+-	  top -= len;
+-	}
+-      else
+-	{
+-	  /* Top segment is the short one.  */
+-	  int len = top - middle;
+-	  register int i;
+-
+-	  /* Swap it with the bottom part of the bottom segment.  */
+-	  for (i = 0; i < len; i++)
+-	    {
+-	      tem = argv[bottom + i];
+-	      argv[bottom + i] = argv[middle + i];
+-	      argv[middle + i] = tem;
+-	      SWAP_FLAGS (bottom + i, middle + i);
+-	    }
+-	  /* Exclude the moved top segment from further swapping.  */
+-	  bottom += len;
+-	}
+-    }
+-
+-  /* Update records for the slots the non-options now occupy.  */
+-
+-  d->__first_nonopt += (d->optind - d->__last_nonopt);
+-  d->__last_nonopt = d->optind;
+-}
+-
+-/* Initialize the internal data when the first call is made.  */
+-
+-static const char *
+-_getopt_initialize (
+-  int argc,
+-  char *const *argv,
+-  const char *optstring,
+-  struct _getopt_data *d
+-)
+-{
+-  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+-     is the program name); the sequence of previously skipped
+-     non-option ARGV-elements is empty.  */
+-
+-  d->__first_nonopt = d->__last_nonopt = d->optind;
+-
+-  d->__nextchar = NULL;
+-
+-  d->__posixly_correct = !!getenv ("POSIXLY_CORRECT");
+-
+-  /* Determine how to handle the ordering of options and nonoptions.  */
+-
+-  if (optstring[0] == '-')
+-    {
+-      d->__ordering = RETURN_IN_ORDER;
+-      ++optstring;
+-    }
+-  else if (optstring[0] == '+')
+-    {
+-      d->__ordering = REQUIRE_ORDER;
+-      ++optstring;
+-    }
+-  else if (d->__posixly_correct)
+-    d->__ordering = REQUIRE_ORDER;
+-  else
+-    d->__ordering = PERMUTE;
+-
+-#if defined _LIBC && defined USE_NONOPTION_FLAGS
+-  if (!d->__posixly_correct && argc == __libc_argc && argv == __libc_argv)
+-    {
+-      if (d->__nonoption_flags_max_len == 0)
+-	{
+-	  if (__getopt_nonoption_flags == NULL
+-	      || __getopt_nonoption_flags[0] == '\0')
+-	    d->__nonoption_flags_max_len = -1;
+-	  else
+-	    {
+-	      const char *orig_str = __getopt_nonoption_flags;
+-	      int len = d->__nonoption_flags_max_len = strlen (orig_str);
+-	      if (d->__nonoption_flags_max_len < argc)
+-		d->__nonoption_flags_max_len = argc;
+-	      __getopt_nonoption_flags =
+-		(char *) malloc (d->__nonoption_flags_max_len);
+-	      if (__getopt_nonoption_flags == NULL)
+-		d->__nonoption_flags_max_len = -1;
+-	      else
+-		memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+-			'\0', d->__nonoption_flags_max_len - len);
+-	    }
+-	}
+-      d->__nonoption_flags_len = d->__nonoption_flags_max_len;
+-    }
+-  else
+-    d->__nonoption_flags_len = 0;
+-#endif
+-
+-  return optstring;
+-}
+-
+-/* Scan elements of ARGV (whose length is ARGC) for option characters
+-   given in OPTSTRING.
+-
+-   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+-   then it is an option element.  The characters of this element
+-   (aside from the initial '-') are option characters.  If `getopt'
+-   is called repeatedly, it returns successively each of the option characters
+-   from each of the option elements.
+-
+-   If `getopt' finds another option character, it returns that character,
+-   updating `optind' and `nextchar' so that the next call to `getopt' can
+-   resume the scan with the following option character or ARGV-element.
+-
+-   If there are no more option characters, `getopt' returns -1.
+-   Then `optind' is the index in ARGV of the first ARGV-element
+-   that is not an option.  (The ARGV-elements have been permuted
+-   so that those that are not options now come last.)
+-
+-   OPTSTRING is a string containing the legitimate option characters.
+-   If an option character is seen that is not listed in OPTSTRING,
+-   return '?' after printing an error message.  If you set `opterr' to
+-   zero, the error message is suppressed but we still return '?'.
+-
+-   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+-   so the following text in the same ARGV-element, or the text of the following
+-   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+-   wants an optional arg; if there is text in the current ARGV-element,
+-   it is returned in `optarg', otherwise `optarg' is set to zero.
+-
+-   If OPTSTRING starts with `-' or `+', it requests different methods of
+-   handling the non-option ARGV-elements.
+-   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+-
+-   Long-named options begin with `--' instead of `-'.
+-   Their names may be abbreviated as long as the abbreviation is unique
+-   or is an exact match for some defined option.  If they have an
+-   argument, it follows the option name in the same ARGV-element, separated
+-   from the option name by a `=', or else the in next ARGV-element.
+-   When `getopt' finds a long-named option, it returns 0 if that option's
+-   `flag' field is nonzero, the value of the option's `val' field
+-   if the `flag' field is zero.
+-
+-   The elements of ARGV aren't really const, because we permute them.
+-   But we pretend they're const in the prototype to be compatible
+-   with other systems.
+-
+-   LONGOPTS is a vector of `struct option' terminated by an
+-   element containing a name which is zero.
+-
+-   LONGIND returns the index in LONGOPT of the long-named option found.
+-   It is only valid when a long-named option has been found by the most
+-   recent call.
+-
+-   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+-   long-named options.  */
+-
+-int
+-_getopt_internal_r (
+-  int argc,
+-  char *const *argv,
+-  const char *optstring,
+-  const struct option *longopts,
+-  int *longind,
+-  int long_only,
+-  struct _getopt_data *d
+-)
+-{
+-  int print_errors = d->opterr;
+-  if (optstring[0] == ':')
+-    print_errors = 0;
+-
+-  if (argc < 1)
+-    return -1;
+-
+-  d->optarg = NULL;
+-
+-  if (d->optind == 0 || !d->__initialized)
+-    {
+-      if (d->optind == 0)
+-	d->optind = 1;		/* Don't scan ARGV[0], the program name.  */
+-      optstring = _getopt_initialize (argc, argv, optstring, d);
+-      d->__initialized = 1;
+-    }
+-
+-  /* Test whether ARGV[optind] points to a non-option argument.
+-     Either it does not have option syntax, or there is an environment flag
+-     from the shell indicating it is not an option.  The later information
+-     is only used when the used in the GNU libc.  */
+-#if defined _LIBC && defined USE_NONOPTION_FLAGS
+-# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \
+-		      || (d->optind < d->__nonoption_flags_len		      \
+-			  && __getopt_nonoption_flags[d->optind] == '1'))
+-#else
+-# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')
+-#endif
+-
+-  if (d->__nextchar == NULL || *d->__nextchar == '\0')
+-    {
+-      /* Advance to the next ARGV-element.  */
+-
+-      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+-         moved back by the user (who may also have changed the arguments).  */
+-      if (d->__last_nonopt > d->optind)
+-	d->__last_nonopt = d->optind;
+-      if (d->__first_nonopt > d->optind)
+-	d->__first_nonopt = d->optind;
+-
+-      if (d->__ordering == PERMUTE)
+-	{
+-	  /* If we have just processed some options following some non-options,
+-	     exchange them so that the options come first.  */
+-
+-	  if (d->__first_nonopt != d->__last_nonopt
+-	      && d->__last_nonopt != d->optind)
+-	    exchange ((char **) argv, d);
+-	  else if (d->__last_nonopt != d->optind)
+-	    d->__first_nonopt = d->optind;
+-
+-	  /* Skip any additional non-options
+-	     and extend the range of non-options previously skipped.  */
+-
+-	  while (d->optind < argc && NONOPTION_P)
+-	    d->optind++;
+-	  d->__last_nonopt = d->optind;
+-	}
+-
+-      /* The special ARGV-element `--' means premature end of options.
+-         Skip it like a null option,
+-         then exchange with previous non-options as if it were an option,
+-         then skip everything else like a non-option.  */
+-
+-      if (d->optind != argc && !strcmp (argv[d->optind], "--"))
+-	{
+-	  d->optind++;
+-
+-	  if (d->__first_nonopt != d->__last_nonopt
+-	      && d->__last_nonopt != d->optind)
+-	    exchange ((char **) argv, d);
+-	  else if (d->__first_nonopt == d->__last_nonopt)
+-	    d->__first_nonopt = d->optind;
+-	  d->__last_nonopt = argc;
+-
+-	  d->optind = argc;
+-	}
+-
+-      /* If we have done all the ARGV-elements, stop the scan
+-         and back over any non-options that we skipped and permuted.  */
+-
+-      if (d->optind == argc)
+-	{
+-	  /* Set the next-arg-index to point at the non-options
+-	     that we previously skipped, so the caller will digest them.  */
+-	  if (d->__first_nonopt != d->__last_nonopt)
+-	    d->optind = d->__first_nonopt;
+-	  return -1;
+-	}
+-
+-      /* If we have come to a non-option and did not permute it,
+-         either stop the scan or describe it to the caller and pass it by.  */
+-
+-      if (NONOPTION_P)
+-	{
+-	  if (d->__ordering == REQUIRE_ORDER)
+-	    return -1;
+-	  d->optarg = argv[d->optind++];
+-	  return 1;
+-	}
+-
+-      /* We have found another option-ARGV-element.
+-         Skip the initial punctuation.  */
+-
+-      d->__nextchar = (argv[d->optind] + 1
+-		       + (longopts != NULL && argv[d->optind][1] == '-'));
+-    }
+-
+-  /* Decode the current option-ARGV-element.  */
+-
+-  /* Check whether the ARGV-element is a long option.
+-
+-     If long_only and the ARGV-element has the form "-f", where f is
+-     a valid short option, don't consider it an abbreviated form of
+-     a long option that starts with f.  Otherwise there would be no
+-     way to give the -f short option.
+-
+-     On the other hand, if there's a long option "fubar" and
+-     the ARGV-element is "-fu", do consider that an abbreviation of
+-     the long option, just like "--fu", and not "-f" with arg "u".
+-
+-     This distinction seems to be the most useful approach.  */
+-
+-  if (longopts != NULL
+-      && (argv[d->optind][1] == '-'
+-	  || (long_only && (argv[d->optind][2]
+-			    || !strchr (optstring, argv[d->optind][1])))))
+-    {
+-      char *nameend;
+-      const struct option *p;
+-      const struct option *pfound = NULL;
+-      int exact = 0;
+-      int ambig = 0;
+-      int indfound = -1;
+-      int option_index;
+-
+-      for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
+-	/* Do nothing.  */ ;
+-
+-      /* Test all long options for either exact match
+-         or abbreviated matches.  */
+-      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+-	if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+-	  {
+-	    if ((unsigned int) (nameend - d->__nextchar)
+-		== (unsigned int) strlen (p->name))
+-	      {
+-		/* Exact match found.  */
+-		pfound = p;
+-		indfound = option_index;
+-		exact = 1;
+-		break;
+-	      }
+-	    else if (pfound == NULL)
+-	      {
+-		/* First nonexact match found.  */
+-		pfound = p;
+-		indfound = option_index;
+-	      }
+-	    else if (long_only
+-		     || pfound->has_arg != p->has_arg
+-		     || pfound->flag != p->flag || pfound->val != p->val)
+-	      /* Second or later nonexact match found.  */
+-	      ambig = 1;
+-	  }
+-
+-      if (ambig && !exact)
+-	{
+-	  if (print_errors)
+-	    {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-	      char *buf;
+-
+-	      if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
+-			      argv[0], argv[d->optind]) >= 0)
+-		{
+-		  _IO_flockfile (stderr);
+-
+-		  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-		  ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+-
+-		  if (_IO_fwide (stderr, 0) > 0)
+-		    __fwprintf (stderr, L"%s", buf);
+-		  else
+-		    fputs (buf, stderr);
+-
+-		  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-		  _IO_funlockfile (stderr);
+-
+-		  free (buf);
+-		}
+-#else
+-	      fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+-		       argv[0], argv[d->optind]);
+-#endif
+-	    }
+-	  d->__nextchar += strlen (d->__nextchar);
+-	  d->optind++;
+-	  d->optopt = 0;
+-	  return '?';
+-	}
+-
+-      if (pfound != NULL)
+-	{
+-	  option_index = indfound;
+-	  d->optind++;
+-	  if (*nameend)
+-	    {
+-	      /* Don't test has_arg with >, because some C compilers don't
+-	         allow it to be used on enums.  */
+-	      if (pfound->has_arg)
+-		d->optarg = nameend + 1;
+-	      else
+-		{
+-		  if (print_errors)
+-		    {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		      char *buf;
+-		      int n;
+-#endif
+-
+-		      if (argv[d->optind - 1][1] == '-')
+-			{
+-			  /* --option */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-			  n = __asprintf (&buf, _("\
+-%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name);
+-#else
+-			  fprintf (stderr, _("\
+-%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name);
+-#endif
+-			}
+-		      else
+-			{
+-			  /* +option or -option */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-			  n = __asprintf (&buf, _("\
+-%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[d->optind - 1][0], pfound->name);
+-#else
+-			  fprintf (stderr, _("\
+-%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[d->optind - 1][0], pfound->name);
+-#endif
+-			}
+-
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		      if (n >= 0)
+-			{
+-			  _IO_flockfile (stderr);
+-
+-			  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-			  ((_IO_FILE *) stderr)->_flags2
+-			    |= _IO_FLAGS2_NOTCANCEL;
+-
+-			  if (_IO_fwide (stderr, 0) > 0)
+-			    __fwprintf (stderr, L"%s", buf);
+-			  else
+-			    fputs (buf, stderr);
+-
+-			  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-			  _IO_funlockfile (stderr);
+-
+-			  free (buf);
+-			}
+-#endif
+-		    }
+-
+-		  d->__nextchar += strlen (d->__nextchar);
+-
+-		  d->optopt = pfound->val;
+-		  return '?';
+-		}
+-	    }
+-	  else if (pfound->has_arg == 1)
+-	    {
+-	      if (d->optind < argc)
+-		d->optarg = argv[d->optind++];
+-	      else
+-		{
+-		  if (print_errors)
+-		    {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		      char *buf;
+-
+-		      if (__asprintf (&buf, _("\
+-%s: option `%s' requires an argument\n"), argv[0], argv[d->optind - 1]) >= 0)
+-			{
+-			  _IO_flockfile (stderr);
+-
+-			  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-			  ((_IO_FILE *) stderr)->_flags2
+-			    |= _IO_FLAGS2_NOTCANCEL;
+-
+-			  if (_IO_fwide (stderr, 0) > 0)
+-			    __fwprintf (stderr, L"%s", buf);
+-			  else
+-			    fputs (buf, stderr);
+-
+-			  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-			  _IO_funlockfile (stderr);
+-
+-			  free (buf);
+-			}
+-#else
+-		      fprintf (stderr,
+-			       _("%s: option `%s' requires an argument\n"),
+-			       argv[0], argv[d->optind - 1]);
+-#endif
+-		    }
+-		  d->__nextchar += strlen (d->__nextchar);
+-		  d->optopt = pfound->val;
+-		  return optstring[0] == ':' ? ':' : '?';
+-		}
+-	    }
+-	  d->__nextchar += strlen (d->__nextchar);
+-	  if (longind != NULL)
+-	    *longind = option_index;
+-	  if (pfound->flag)
+-	    {
+-	      *(pfound->flag) = pfound->val;
+-	      return 0;
+-	    }
+-	  return pfound->val;
+-	}
+-
+-      /* Can't find it as a long option.  If this is not getopt_long_only,
+-         or the option starts with '--' or is not a valid short
+-         option, then it's an error.
+-         Otherwise interpret it as a short option.  */
+-      if (!long_only || argv[d->optind][1] == '-'
+-	  || strchr (optstring, *d->__nextchar) == NULL)
+-	{
+-	  if (print_errors)
+-	    {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-	      char *buf;
+-	      int n;
+-#endif
+-
+-	      if (argv[d->optind][1] == '-')
+-		{
+-		  /* --option */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		  n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
+-				  argv[0], d->__nextchar);
+-#else
+-		  fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+-			   argv[0], d->__nextchar);
+-#endif
+-		}
+-	      else
+-		{
+-		  /* +option or -option */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		  n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
+-				  argv[0], argv[d->optind][0], d->__nextchar);
+-#else
+-		  fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+-			   argv[0], argv[d->optind][0], d->__nextchar);
+-#endif
+-		}
+-
+-#if defined _LIBC && defined USE_IN_LIBIO
+-	      if (n >= 0)
+-		{
+-		  _IO_flockfile (stderr);
+-
+-		  int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-		  ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+-
+-		  if (_IO_fwide (stderr, 0) > 0)
+-		    __fwprintf (stderr, L"%s", buf);
+-		  else
+-		    fputs (buf, stderr);
+-
+-		  ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-		  _IO_funlockfile (stderr);
+-
+-		  free (buf);
+-		}
+-#endif
+-	    }
+-	  d->__nextchar = (char *) "";
+-	  d->optind++;
+-	  d->optopt = 0;
+-	  return '?';
+-	}
+-    }
+-
+-  /* Look at and handle the next short option-character.  */
+-
+-  {
+-    char c = *d->__nextchar++;
+-    char *temp = strchr (optstring, c);
+-
+-    /* Increment `optind' when we start to process its last character.  */
+-    if (*d->__nextchar == '\0')
+-      ++d->optind;
+-
+-    if (temp == NULL || c == ':')
+-      {
+-	if (print_errors)
+-	  {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-	    char *buf;
+-	    int n;
+-#endif
+-
+-	    if (d->__posixly_correct)
+-	      {
+-		/* 1003.2 specifies the format of this message.  */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
+-				argv[0], c);
+-#else
+-		fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+-#endif
+-	      }
+-	    else
+-	      {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
+-				argv[0], c);
+-#else
+-		fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+-#endif
+-	      }
+-
+-#if defined _LIBC && defined USE_IN_LIBIO
+-	    if (n >= 0)
+-	      {
+-		_IO_flockfile (stderr);
+-
+-		int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-		((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+-
+-		if (_IO_fwide (stderr, 0) > 0)
+-		  __fwprintf (stderr, L"%s", buf);
+-		else
+-		  fputs (buf, stderr);
+-
+-		((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-		_IO_funlockfile (stderr);
+-
+-		free (buf);
+-	      }
+-#endif
+-	  }
+-	d->optopt = c;
+-	return '?';
+-      }
+-    /* Convenience. Treat POSIX -W foo same as long option --foo */
+-    if (temp[0] == 'W' && temp[1] == ';')
+-      {
+-	char *nameend;
+-	const struct option *p;
+-	const struct option *pfound = NULL;
+-	int exact = 0;
+-	int ambig = 0;
+-	int indfound = 0;
+-	int option_index;
+-
+-	/* This is an option that requires an argument.  */
+-	if (*d->__nextchar != '\0')
+-	  {
+-	    d->optarg = d->__nextchar;
+-	    /* If we end this ARGV-element by taking the rest as an arg,
+-	       we must advance to the next element now.  */
+-	    d->optind++;
+-	  }
+-	else if (d->optind == argc)
+-	  {
+-	    if (print_errors)
+-	      {
+-		/* 1003.2 specifies the format of this message.  */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		char *buf;
+-
+-		if (__asprintf (&buf,
+-				_("%s: option requires an argument -- %c\n"),
+-				argv[0], c) >= 0)
+-		  {
+-		    _IO_flockfile (stderr);
+-
+-		    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-		    ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+-
+-		    if (_IO_fwide (stderr, 0) > 0)
+-		      __fwprintf (stderr, L"%s", buf);
+-		    else
+-		      fputs (buf, stderr);
+-
+-		    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-		    _IO_funlockfile (stderr);
+-
+-		    free (buf);
+-		  }
+-#else
+-		fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+-			 argv[0], c);
+-#endif
+-	      }
+-	    d->optopt = c;
+-	    if (optstring[0] == ':')
+-	      c = ':';
+-	    else
+-	      c = '?';
+-	    return c;
+-	  }
+-	else
+-	  /* We already incremented `d->optind' once;
+-	     increment it again when taking next ARGV-elt as argument.  */
+-	  d->optarg = argv[d->optind++];
+-
+-	/* optarg is now the argument, see if it's in the
+-	   table of longopts.  */
+-
+-	for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '=';
+-	     nameend++)
+-	  /* Do nothing.  */ ;
+-
+-	/* Test all long options for either exact match
+-	   or abbreviated matches.  */
+-	for (p = longopts, option_index = 0; p->name; p++, option_index++)
+-	  if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+-	    {
+-	      if ((unsigned int) (nameend - d->__nextchar) ==
+-		  strlen (p->name))
+-		{
+-		  /* Exact match found.  */
+-		  pfound = p;
+-		  indfound = option_index;
+-		  exact = 1;
+-		  break;
+-		}
+-	      else if (pfound == NULL)
+-		{
+-		  /* First nonexact match found.  */
+-		  pfound = p;
+-		  indfound = option_index;
+-		}
+-	      else
+-		/* Second or later nonexact match found.  */
+-		ambig = 1;
+-	    }
+-	if (ambig && !exact)
+-	  {
+-	    if (print_errors)
+-	      {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		char *buf;
+-
+-		if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
+-				argv[0], argv[d->optind]) >= 0)
+-		  {
+-		    _IO_flockfile (stderr);
+-
+-		    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-		    ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+-
+-		    if (_IO_fwide (stderr, 0) > 0)
+-		      __fwprintf (stderr, L"%s", buf);
+-		    else
+-		      fputs (buf, stderr);
+-
+-		    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-		    _IO_funlockfile (stderr);
+-
+-		    free (buf);
+-		  }
+-#else
+-		fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+-			 argv[0], argv[d->optind]);
+-#endif
+-	      }
+-	    d->__nextchar += strlen (d->__nextchar);
+-	    d->optind++;
+-	    return '?';
+-	  }
+-	if (pfound != NULL)
+-	  {
+-	    option_index = indfound;
+-	    if (*nameend)
+-	      {
+-		/* Don't test has_arg with >, because some C compilers don't
+-		   allow it to be used on enums.  */
+-		if (pfound->has_arg)
+-		  d->optarg = nameend + 1;
+-		else
+-		  {
+-		    if (print_errors)
+-		      {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-			char *buf;
+-
+-			if (__asprintf (&buf, _("\
+-%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name) >= 0)
+-			  {
+-			    _IO_flockfile (stderr);
+-
+-			    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-			    ((_IO_FILE *) stderr)->_flags2
+-			      |= _IO_FLAGS2_NOTCANCEL;
+-
+-			    if (_IO_fwide (stderr, 0) > 0)
+-			      __fwprintf (stderr, L"%s", buf);
+-			    else
+-			      fputs (buf, stderr);
+-
+-			    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-			    _IO_funlockfile (stderr);
+-
+-			    free (buf);
+-			  }
+-#else
+-			fprintf (stderr, _("\
+-%s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name);
+-#endif
+-		      }
+-
+-		    d->__nextchar += strlen (d->__nextchar);
+-		    return '?';
+-		  }
+-	      }
+-	    else if (pfound->has_arg == 1)
+-	      {
+-		if (d->optind < argc)
+-		  d->optarg = argv[d->optind++];
+-		else
+-		  {
+-		    if (print_errors)
+-		      {
+-#if defined _LIBC && defined USE_IN_LIBIO
+-			char *buf;
+-
+-			if (__asprintf (&buf, _("\
+-%s: option `%s' requires an argument\n"), argv[0], argv[d->optind - 1]) >= 0)
+-			  {
+-			    _IO_flockfile (stderr);
+-
+-			    int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-			    ((_IO_FILE *) stderr)->_flags2
+-			      |= _IO_FLAGS2_NOTCANCEL;
+-
+-			    if (_IO_fwide (stderr, 0) > 0)
+-			      __fwprintf (stderr, L"%s", buf);
+-			    else
+-			      fputs (buf, stderr);
+-
+-			    ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-			    _IO_funlockfile (stderr);
+-
+-			    free (buf);
+-			  }
+-#else
+-			fprintf (stderr,
+-				 _("%s: option `%s' requires an argument\n"),
+-				 argv[0], argv[d->optind - 1]);
+-#endif
+-		      }
+-		    d->__nextchar += strlen (d->__nextchar);
+-		    return optstring[0] == ':' ? ':' : '?';
+-		  }
+-	      }
+-	    d->__nextchar += strlen (d->__nextchar);
+-	    if (longind != NULL)
+-	      *longind = option_index;
+-	    if (pfound->flag)
+-	      {
+-		*(pfound->flag) = pfound->val;
+-		return 0;
+-	      }
+-	    return pfound->val;
+-	  }
+-	d->__nextchar = NULL;
+-	return 'W';		/* Let the application handle it.   */
+-      }
+-    if (temp[1] == ':')
+-      {
+-	if (temp[2] == ':')
+-	  {
+-	    /* This is an option that accepts an argument optionally.  */
+-	    if (*d->__nextchar != '\0')
+-	      {
+-		d->optarg = d->__nextchar;
+-		d->optind++;
+-	      }
+-	    else
+-	      d->optarg = NULL;
+-	    d->__nextchar = NULL;
+-	  }
+-	else
+-	  {
+-	    /* This is an option that requires an argument.  */
+-	    if (*d->__nextchar != '\0')
+-	      {
+-		d->optarg = d->__nextchar;
+-		/* If we end this ARGV-element by taking the rest as an arg,
+-		   we must advance to the next element now.  */
+-		d->optind++;
+-	      }
+-	    else if (d->optind == argc)
+-	      {
+-		if (print_errors)
+-		  {
+-		    /* 1003.2 specifies the format of this message.  */
+-#if defined _LIBC && defined USE_IN_LIBIO
+-		    char *buf;
+-
+-		    if (__asprintf (&buf, _("\
+-%s: option requires an argument -- %c\n"), argv[0], c) >= 0)
+-		      {
+-			_IO_flockfile (stderr);
+-
+-			int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+-			((_IO_FILE *) stderr)->_flags2 |=
+-			  _IO_FLAGS2_NOTCANCEL;
+-
+-			if (_IO_fwide (stderr, 0) > 0)
+-			  __fwprintf (stderr, L"%s", buf);
+-			else
+-			  fputs (buf, stderr);
+-
+-			((_IO_FILE *) stderr)->_flags2 = old_flags2;
+-			_IO_funlockfile (stderr);
+-
+-			free (buf);
+-		      }
+-#else
+-		    fprintf (stderr,
+-			     _("%s: option requires an argument -- %c\n"),
+-			     argv[0], c);
+-#endif
+-		  }
+-		d->optopt = c;
+-		if (optstring[0] == ':')
+-		  c = ':';
+-		else
+-		  c = '?';
+-	      }
+-	    else
+-	      /* We already incremented `optind' once;
+-	         increment it again when taking next ARGV-elt as argument.  */
+-	      d->optarg = argv[d->optind++];
+-	    d->__nextchar = NULL;
+-	  }
+-      }
+-    return c;
+-  }
+-}
+-
+-int
+-_getopt_internal (
+-  int argc,
+-  char *const *argv,
+-  const char *optstring,
+-  const struct option *longopts,
+-  int *longind,
+-  int long_only
+-)
+-{
+-  int result;
+-
+-  getopt_data.optind = optind;
+-  getopt_data.opterr = opterr;
+-
+-  result = _getopt_internal_r (argc, argv, optstring, longopts,
+-			       longind, long_only, &getopt_data);
+-
+-  optind = getopt_data.optind;
+-  optarg = getopt_data.optarg;
+-  optopt = getopt_data.optopt;
+-
+-  return result;
+-}
+-
+-int
+-getopt (
+-  int argc,
+-  char *const *argv,
+-  const char *optstring
+-)
+-{
+-  return _getopt_internal (argc, argv, optstring,
+-			   (const struct option *) 0, (int *) 0, 0);
+-}
+-
+-#endif /* Not ELIDE_CODE.  */
+-
+-#ifdef TEST
+-
+-/* Compile with -DTEST to make an executable for use in testing
+-   the above definition of `getopt'.  */
+-
+-int
+-main (
+-  int argc,
+-  char **argv
+-)
+-{
+-  int c;
+-  int digit_optind = 0;
+-
+-  while (1)
+-    {
+-      int this_option_optind = optind ? optind : 1;
+-
+-      c = getopt (argc, argv, "abc:d:0123456789");
+-      if (c == -1)
+-	break;
+-
+-      switch (c)
+-	{
+-	case '0':
+-	case '1':
+-	case '2':
+-	case '3':
+-	case '4':
+-	case '5':
+-	case '6':
+-	case '7':
+-	case '8':
+-	case '9':
+-	  if (digit_optind != 0 && digit_optind != this_option_optind)
+-	    printf ("digits occur in two different argv-elements.\n");
+-	  digit_optind = this_option_optind;
+-	  printf ("option %c\n", c);
+-	  break;
+-
+-	case 'a':
+-	  printf ("option a\n");
+-	  break;
+-
+-	case 'b':
+-	  printf ("option b\n");
+-	  break;
+-
+-	case 'c':
+-	  printf ("option c with value `%s'\n", optarg);
+-	  break;
+-
+-	case '?':
+-	  break;
+-
+-	default:
+-	  printf ("?? getopt returned character code 0%o ??\n", c);
+-	}
+-    }
+-
+-  if (optind < argc)
+-    {
+-      printf ("non-option ARGV-elements: ");
+-      while (optind < argc)
+-	printf ("%s ", argv[optind++]);
+-      printf ("\n");
+-    }
+-
+-  exit (0);
+-}
+-
+-#endif /* TEST */
+diff -pruN fribidi-1.0.14.orig/bin/getopt.h fribidi-1.0.14/bin/getopt.h
+--- fribidi-1.0.14.orig/bin/getopt.h	2015-08-05 03:49:07.000000000 +0900
++++ fribidi-1.0.14/bin/getopt.h	1970-01-01 09:00:00.000000000 +0900
+@@ -1,187 +0,0 @@
+-/* Declarations for getopt.
+-   Copyright (C) 1989-1994,1996-1999,2001,2003,2004
+-   Free Software Foundation, Inc.
+-   This file is part of the GNU C Library.
+-
+-   This program is free software; you can redistribute it and/or modify
+-   it under the terms of the GNU General Public License as published by
+-   the Free Software Foundation; either version 2, or (at your option)
+-   any later version.
+-
+-   This program is distributed in the hope that it will be useful,
+-   but WITHOUT ANY WARRANTY; without even the implied warranty of
+-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-   GNU General Public License for more details.
+-
+-   You should have received a copy of the GNU General Public License along
+-   with this program; if not, write to the Free Software Foundation,
+-   Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.  */
+-
+-#ifndef _GETOPT_H
+-
+-#ifndef __need_getopt
+-# define _GETOPT_H 1
+-#endif
+-
+-/* If __GNU_LIBRARY__ is not already defined, either we are being used
+-   standalone, or this is the first header included in the source file.
+-   If we are being used with glibc, we need to include <features.h>, but
+-   that does not exist if we are standalone.  So: if __GNU_LIBRARY__ is
+-   not defined, include <ctype.h>, which will pull in <features.h> for us
+-   if it's from glibc.  (Why ctype.h?  It's guaranteed to exist and it
+-   doesn't flood the namespace with stuff the way some other headers do.)  */
+-#if !defined __GNU_LIBRARY__
+-# include <ctype.h>
+-#endif
+-
+-#ifndef __THROW
+-# ifndef __GNUC_PREREQ
+-#  define __GNUC_PREREQ(maj, min) (0)
+-# endif
+-# if defined __cplusplus && __GNUC_PREREQ (2,8)
+-#  define __THROW	throw ()
+-# else
+-#  define __THROW
+-# endif
+-#endif
+-
+-#ifdef	__cplusplus
+-extern "C"
+-{
+-#endif
+-
+-/* For communication from `getopt' to the caller.
+-   When `getopt' finds an option that takes an argument,
+-   the argument value is returned here.
+-   Also, when `ordering' is RETURN_IN_ORDER,
+-   each non-option ARGV-element is returned here.  */
+-
+-  extern char *optarg;
+-
+-/* Index in ARGV of the next element to be scanned.
+-   This is used for communication to and from the caller
+-   and for communication between successive calls to `getopt'.
+-
+-   On entry to `getopt', zero means this is the first call; initialize.
+-
+-   When `getopt' returns -1, this is the index of the first of the
+-   non-option elements that the caller should itself scan.
+-
+-   Otherwise, `optind' communicates from one call to the next
+-   how much of ARGV has been scanned so far.  */
+-
+-  extern int optind;
+-
+-/* Callers store zero here to inhibit the error message `getopt' prints
+-   for unrecognized options.  */
+-
+-  extern int opterr;
+-
+-/* Set to an option character which was unrecognized.  */
+-
+-  extern int optopt;
+-
+-#ifndef __need_getopt
+-/* Describe the long-named options requested by the application.
+-   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+-   of `struct option' terminated by an element containing a name which is
+-   zero.
+-
+-   The field `has_arg' is:
+-   no_argument		(or 0) if the option does not take an argument,
+-   required_argument	(or 1) if the option requires an argument,
+-   optional_argument 	(or 2) if the option takes an optional argument.
+-
+-   If the field `flag' is not NULL, it points to a variable that is set
+-   to the value given in the field `val' when the option is found, but
+-   left unchanged if the option is not found.
+-
+-   To have a long-named option do something other than set an `int' to
+-   a compiled-in constant, such as set a value from `optarg', set the
+-   option's `flag' field to zero and its `val' field to a nonzero
+-   value (the equivalent single-letter option character, if there is
+-   one).  For long options that have a zero `flag' field, `getopt'
+-   returns the contents of the `val' field.  */
+-
+-  struct option
+-  {
+-    const char *name;
+-    /* has_arg can't be an enum because some compilers complain about
+-       type mismatches in all the code that assumes it is an int.  */
+-    int has_arg;
+-    int *flag;
+-    int val;
+-  };
+-
+-/* Names for the values of the `has_arg' field of `struct option'.  */
+-
+-# define no_argument		0
+-# define required_argument	1
+-# define optional_argument	2
+-#endif				/* need getopt */
+-
+-
+-/* Get definitions and prototypes for functions to process the
+-   arguments in ARGV (ARGC of them, minus the program name) for
+-   options given in OPTS.
+-
+-   Return the option character from OPTS just read.  Return -1 when
+-   there are no more options.  For unrecognized options, or options
+-   missing arguments, `optopt' is set to the option letter, and '?' is
+-   returned.
+-
+-   The OPTS string is a list of characters which are recognized option
+-   letters, optionally followed by colons, specifying that that letter
+-   takes an argument, to be placed in `optarg'.
+-
+-   If a letter in OPTS is followed by two colons, its argument is
+-   optional.  This behavior is specific to the GNU `getopt'.
+-
+-   The argument `--' causes premature termination of argument
+-   scanning, explicitly telling `getopt' that there are no more
+-   options.
+-
+-   If OPTS begins with `--', then non-option arguments are treated as
+-   arguments to the option '\0'.  This behavior is specific to the GNU
+-   `getopt'.  */
+-
+-#ifdef __GNU_LIBRARY__
+-/* Many other libraries have conflicting prototypes for getopt, with
+-   differences in the consts, in stdlib.h.  To avoid compilation
+-   errors, only prototype getopt for the GNU C library.  */
+-  extern int getopt (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts
+-  ) __THROW;
+-#else				/* not __GNU_LIBRARY__ */
+-  extern int getopt (
+-  );
+-#endif				/* __GNU_LIBRARY__ */
+-
+-#ifndef __need_getopt
+-  extern int getopt_long (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts,
+-  const struct option *__longopts,
+-  int *__longind
+-  ) __THROW;
+-  extern int getopt_long_only (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts,
+-  const struct option *__longopts,
+-  int *__longind
+-  ) __THROW;
+-
+-#endif
+-
+-#ifdef	__cplusplus
+-}
+-#endif
+-
+-/* Make sure we later can get all the definitions and declarations.  */
+-#undef __need_getopt
+-
+-#endif				/* getopt.h */
+diff -pruN fribidi-1.0.14.orig/bin/getopt1.c fribidi-1.0.14/bin/getopt1.c
+--- fribidi-1.0.14.orig/bin/getopt1.c	2015-08-05 03:49:07.000000000 +0900
++++ fribidi-1.0.14/bin/getopt1.c	1970-01-01 09:00:00.000000000 +0900
+@@ -1,213 +0,0 @@
+-/* getopt_long and getopt_long_only entry points for GNU getopt.
+-   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004
+-     Free Software Foundation, Inc.
+-   This file is part of the GNU C Library.
+-
+-   This program is free software; you can redistribute it and/or modify
+-   it under the terms of the GNU General Public License as published by
+-   the Free Software Foundation; either version 2, or (at your option)
+-   any later version.
+-
+-   This program is distributed in the hope that it will be useful,
+-   but WITHOUT ANY WARRANTY; without even the implied warranty of
+-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-   GNU General Public License for more details.
+-
+-   You should have received a copy of the GNU General Public License along
+-   with this program; if not, write to the Free Software Foundation,
+-   Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.  */
+-
+-#ifdef HAVE_CONFIG_H
+-#include <config.h>
+-#endif
+-
+-#ifdef _LIBC
+-# include <getopt.h>
+-#else
+-# include "getopt.h"
+-#endif
+-#include "getopt_int.h"
+-
+-#include <stdio.h>
+-
+-/* Comment out all this code if we are using the GNU C Library, and are not
+-   actually compiling the library itself.  This code is part of the GNU C
+-   Library, but also included in many other GNU distributions.  Compiling
+-   and linking in this code is a waste when using the GNU C library
+-   (especially if it is a shared library).  Rather than having every GNU
+-   program understand `configure --with-gnu-libc' and omit the object files,
+-   it is simpler to just do this in the source for each such file.  */
+-
+-#define GETOPT_INTERFACE_VERSION 2
+-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+-#include <gnu-versions.h>
+-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+-#define ELIDE_CODE
+-#endif
+-#endif
+-
+-#ifndef ELIDE_CODE
+-
+-
+-/* This needs to come after some library #include
+-   to get __GNU_LIBRARY__ defined.  */
+-#ifdef __GNU_LIBRARY__
+-#include <stdlib.h>
+-#endif
+-
+-#ifndef	NULL
+-#define NULL 0
+-#endif
+-
+-int
+-getopt_long (
+-  int argc,
+-  char *const *argv,
+-  const char *options,
+-  const struct option *long_options,
+-  int *opt_index
+-)
+-{
+-  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+-}
+-
+-int
+-_getopt_long_r (
+-  int argc,
+-  char *const *argv,
+-  const char *options,
+-  const struct option *long_options,
+-  int *opt_index,
+-  struct _getopt_data *d
+-)
+-{
+-  return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+-			     0, d);
+-}
+-
+-/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+-   If an option that starts with '-' (not '--') doesn't match a long option,
+-   but does match a short option, it is parsed as a short option
+-   instead.  */
+-
+-int
+-getopt_long_only (
+-  int argc,
+-  char *const *argv,
+-  const char *options,
+-  const struct option *long_options,
+-  int *opt_index
+-)
+-{
+-  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+-}
+-
+-int
+-_getopt_long_only_r (
+-  int argc,
+-  char *const *argv,
+-  const char *options,
+-  const struct option *long_options,
+-  int *opt_index,
+-  struct _getopt_data *d
+-)
+-{
+-  return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+-			     1, d);
+-}
+-
+-#endif /* Not ELIDE_CODE.  */
+-
+-#ifdef TEST
+-
+-#include <stdio.h>
+-
+-int
+-main (
+-  int argc,
+-  char **argv
+-)
+-{
+-  int c;
+-  int digit_optind = 0;
+-
+-  while (1)
+-    {
+-      int this_option_optind = optind ? optind : 1;
+-      int option_index = 0;
+-      static struct option long_options[] = {
+-	{"add", 1, 0, 0},
+-	{"append", 0, 0, 0},
+-	{"delete", 1, 0, 0},
+-	{"verbose", 0, 0, 0},
+-	{"create", 0, 0, 0},
+-	{"file", 1, 0, 0},
+-	{0, 0, 0, 0}
+-      };
+-
+-      c = getopt_long (argc, argv, "abc:d:0123456789",
+-		       long_options, &option_index);
+-      if (c == -1)
+-	break;
+-
+-      switch (c)
+-	{
+-	case 0:
+-	  printf ("option %s", long_options[option_index].name);
+-	  if (optarg)
+-	    printf (" with arg %s", optarg);
+-	  printf ("\n");
+-	  break;
+-
+-	case '0':
+-	case '1':
+-	case '2':
+-	case '3':
+-	case '4':
+-	case '5':
+-	case '6':
+-	case '7':
+-	case '8':
+-	case '9':
+-	  if (digit_optind != 0 && digit_optind != this_option_optind)
+-	    printf ("digits occur in two different argv-elements.\n");
+-	  digit_optind = this_option_optind;
+-	  printf ("option %c\n", c);
+-	  break;
+-
+-	case 'a':
+-	  printf ("option a\n");
+-	  break;
+-
+-	case 'b':
+-	  printf ("option b\n");
+-	  break;
+-
+-	case 'c':
+-	  printf ("option c with value `%s'\n", optarg);
+-	  break;
+-
+-	case 'd':
+-	  printf ("option d with value `%s'\n", optarg);
+-	  break;
+-
+-	case '?':
+-	  break;
+-
+-	default:
+-	  printf ("?? getopt returned character code 0%o ??\n", c);
+-	}
+-    }
+-
+-  if (optind < argc)
+-    {
+-      printf ("non-option ARGV-elements: ");
+-      while (optind < argc)
+-	printf ("%s ", argv[optind++]);
+-      printf ("\n");
+-    }
+-
+-  exit (0);
+-}
+-
+-#endif /* TEST */
+diff -pruN fribidi-1.0.14.orig/bin/getopt_int.h fribidi-1.0.14/bin/getopt_int.h
+--- fribidi-1.0.14.orig/bin/getopt_int.h	2015-08-05 03:49:07.000000000 +0900
++++ fribidi-1.0.14/bin/getopt_int.h	1970-01-01 09:00:00.000000000 +0900
+@@ -1,145 +0,0 @@
+-/* Internal declarations for getopt.
+-   Copyright (C) 1989-1994,1996-1999,2001,2003,2004
+-   Free Software Foundation, Inc.
+-   This file is part of the GNU C Library.
+-
+-   This program is free software; you can redistribute it and/or modify
+-   it under the terms of the GNU General Public License as published by
+-   the Free Software Foundation; either version 2, or (at your option)
+-   any later version.
+-
+-   This program is distributed in the hope that it will be useful,
+-   but WITHOUT ANY WARRANTY; without even the implied warranty of
+-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-   GNU General Public License for more details.
+-
+-   You should have received a copy of the GNU General Public License along
+-   with this program; if not, write to the Free Software Foundation,
+-   Inc., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.  */
+-
+-#ifndef _GETOPT_INT_H
+-#define _GETOPT_INT_H	1
+-
+-extern int _getopt_internal (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts,
+-  const struct option *__longopts,
+-  int *__longind,
+-  int __long_only
+-);
+-
+-
+-/* Reentrant versions which can handle parsing multiple argument
+-   vectors at the same time.  */
+-
+-/* Data type for reentrant functions.  */
+-struct _getopt_data
+-{
+-  /* These have exactly the same meaning as the corresponding global
+-     variables, except that they are used for the reentrant
+-     versions of getopt.  */
+-  int optind;
+-  int opterr;
+-  int optopt;
+-  char *optarg;
+-
+-  /* Internal members.  */
+-
+-  /* True if the internal members have been initialized.  */
+-  int __initialized;
+-
+-  /* The next char to be scanned in the option-element
+-     in which the last option character we returned was found.
+-     This allows us to pick up the scan where we left off.
+-
+-     If this is zero, or a null string, it means resume the scan
+-     by advancing to the next ARGV-element.  */
+-  char *__nextchar;
+-
+-  /* Describe how to deal with options that follow non-option ARGV-elements.
+-
+-     If the caller did not specify anything,
+-     the default is REQUIRE_ORDER if the environment variable
+-     POSIXLY_CORRECT is defined, PERMUTE otherwise.
+-
+-     REQUIRE_ORDER means don't recognize them as options;
+-     stop option processing when the first non-option is seen.
+-     This is what Unix does.
+-     This mode of operation is selected by either setting the environment
+-     variable POSIXLY_CORRECT, or using `+' as the first character
+-     of the list of option characters.
+-
+-     PERMUTE is the default.  We permute the contents of ARGV as we
+-     scan, so that eventually all the non-options are at the end.
+-     This allows options to be given in any order, even with programs
+-     that were not written to expect this.
+-
+-     RETURN_IN_ORDER is an option available to programs that were
+-     written to expect options and other ARGV-elements in any order
+-     and that care about the ordering of the two.  We describe each
+-     non-option ARGV-element as if it were the argument of an option
+-     with character code 1.  Using `-' as the first character of the
+-     list of option characters selects this mode of operation.
+-
+-     The special argument `--' forces an end of option-scanning regardless
+-     of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+-     `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+-
+-  enum
+-  {
+-    REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+-  } __ordering;
+-
+-  /* If the POSIXLY_CORRECT environment variable is set.  */
+-  int __posixly_correct;
+-
+-
+-  /* Handle permutation of arguments.  */
+-
+-  /* Describe the part of ARGV that contains non-options that have
+-     been skipped.  `first_nonopt' is the index in ARGV of the first
+-     of them; `last_nonopt' is the index after the last of them.  */
+-
+-  int __first_nonopt;
+-  int __last_nonopt;
+-
+-#if defined _LIBC && defined USE_NONOPTION_FLAGS
+-  int __nonoption_flags_max_len;
+-  int __nonoption_flags_len;
+-# endif
+-};
+-
+-/* The initializer is necessary to set OPTIND and OPTERR to their
+-   default values and to clear the initialization flag.  */
+-#define _GETOPT_DATA_INITIALIZER	{ 1, 1 }
+-
+-extern int _getopt_internal_r (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts,
+-  const struct option *__longopts,
+-  int *__longind,
+-  int __long_only,
+-  struct _getopt_data *__data
+-);
+-
+-extern int _getopt_long_r (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts,
+-  const struct option *__longopts,
+-  int *__longind,
+-  struct _getopt_data *__data
+-);
+-
+-extern int _getopt_long_only_r (
+-  int ___argc,
+-  char *const *___argv,
+-  const char *__shortopts,
+-  const struct option *__longopts,
+-  int *__longind,
+-  struct _getopt_data *__data
+-);
+-
+-#endif /* getopt_int.h */
+diff -pruN fribidi-1.0.14.orig/bin/gettext.h fribidi-1.0.14/bin/gettext.h
+--- fribidi-1.0.14.orig/bin/gettext.h	2015-08-05 03:49:07.000000000 +0900
++++ fribidi-1.0.14/bin/gettext.h	1970-01-01 09:00:00.000000000 +0900
+@@ -1,2 +0,0 @@
+-#undef gettext
+-#define gettext
+diff -pruN fribidi-1.0.14.orig/bin/meson.build fribidi-1.0.14/bin/meson.build
+--- fribidi-1.0.14.orig/bin/meson.build	2024-03-18 03:10:09.000000000 +0900
++++ fribidi-1.0.14/bin/meson.build	2024-05-07 21:40:55.573086578 +0900
+@@ -1,7 +1,7 @@
+ # The fribidi binary is used by the test setup, so if bin=false we still
+ # need to build it for internal usage, we just won't install it.
+ fribidi = executable('fribidi',
+-  'fribidi-main.c', 'getopt.c', 'getopt1.c', fribidi_unicode_version_h,
++  'fribidi-main.c', fribidi_unicode_version_h,
+   c_args: ['-DHAVE_CONFIG_H'] + fribidi_static_cargs,
+   include_directories: incs,
+   link_with: libfribidi,
+@@ -12,7 +12,7 @@ if not get_option('bin')
+ endif
+ 
+ executable('fribidi-benchmark',
+-  'fribidi-benchmark.c', 'getopt.c', 'getopt1.c', fribidi_unicode_version_h,
++  'fribidi-benchmark.c', fribidi_unicode_version_h,
+   c_args: ['-DHAVE_CONFIG_H'] + fribidi_static_cargs,
+   include_directories: incs,
+   link_with: libfribidi,
diff --git a/base/rx/rx-fribidi/fribidi.spec b/base/rx/rx-fribidi/fribidi.spec
new file mode 100644
index 0000000..d384381
--- /dev/null
+++ b/base/rx/rx-fribidi/fribidi.spec
@@ -0,0 +1,60 @@
+%global realname fribidi
+%global _prefix /opt/rx
+
+Summary: Library implementing the Unicode Bidirectional Algorithm
+Name: rx-fribidi
+Version: 1.0.14
+Release: 1%{?dist}
+URL: https://github.com/fribidi/fribidi/
+Source: https://github.com/%{realname}/%{realname}/releases/download/v%{version}/%{realname}-%{version}.tar.xz
+License: LGPL-2.1-or-later AND Unicode-DFS-2016
+BuildRequires: gcc
+BuildRequires: meson
+BuildRequires: make
+Patch0: fribidi-drop-bundled-gnulib.patch
+
+%description
+A library to handle bidirectional scripts (for example Hebrew, Arabic),
+so that the display is done in the proper way; while the text data itself
+is always written in logical order.
+
+%package devel
+Summary: Libraries and include files for FriBidi
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description devel
+Include files and libraries needed for developing applications which use
+FriBidi.
+
+%prep
+%autosetup -p1 -n %{realname}-%{version}
+
+%build
+export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
+export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} $LDFLAGS"
+
+%meson -Ddocs=false
+%meson_build
+
+%check
+%meson_test
+
+
+%install
+%meson_install
+
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+
+%ldconfig_scriptlets
+
+%files
+%doc README AUTHORS ChangeLog THANKS NEWS TODO
+%license COPYING
+%{_bindir}/fribidi
+%{_libdir}/libfribidi.so.0*
+
+%files devel
+%{_includedir}/fribidi
+%{_libdir}/libfribidi.so
+%{_libdir}/pkgconfig/*.pc
+#%%{_mandir}/man3/*.gz
\ No newline at end of file
diff --git a/base/rx/rx-glib2/1965.patch b/base/rx/rx-glib2/1965.patch
new file mode 100644
index 0000000..59d7cb8
--- /dev/null
+++ b/base/rx/rx-glib2/1965.patch
@@ -0,0 +1,222 @@
+From 1248b642ad32b0bdf296211c1a0a8817bebf1c66 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Thu, 25 Feb 2021 10:35:36 +0000
+Subject: [PATCH 1/2] gversionmacros: Add version macros for GLib 2.70
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ docs/reference/gio/gio-docs.xml         |  4 +++
+ docs/reference/glib/glib-docs.xml       |  4 +++
+ docs/reference/glib/glib-sections.txt   | 14 ++++++++
+ docs/reference/gobject/gobject-docs.xml |  4 +++
+ docs/reference/meson.build              |  2 +-
+ glib/gversionmacros.h                   | 44 +++++++++++++++++++++++++
+ 6 files changed, 71 insertions(+), 1 deletion(-)
+
+diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
+index 9cd3d0e39..a09d6d31d 100644
+--- a/docs/reference/gio/gio-docs.xml
++++ b/docs/reference/gio/gio-docs.xml
+@@ -389,6 +389,10 @@
+     <title>Index of new symbols in 2.68</title>
+     <xi:include href="xml/api-index-2.68.xml"><xi:fallback /></xi:include>
+   </index>
++  <index id="api-index-2-70" role="2.70">
++    <title>Index of new symbols in 2.70</title>
++    <xi:include href="xml/api-index-2.70.xml"><xi:fallback /></xi:include>
++  </index>
+ 
+   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+ 
+diff --git a/docs/reference/glib/glib-docs.xml b/docs/reference/glib/glib-docs.xml
+index e464fb792..2f5de9e31 100644
+--- a/docs/reference/glib/glib-docs.xml
++++ b/docs/reference/glib/glib-docs.xml
+@@ -288,6 +288,10 @@
+     <title>Index of new symbols in 2.68</title>
+     <xi:include href="xml/api-index-2.68.xml"><xi:fallback /></xi:include>
+   </index>
++  <index id="api-index-2-70" role="2.70">
++    <title>Index of new symbols in 2.70</title>
++    <xi:include href="xml/api-index-2.70.xml"><xi:fallback /></xi:include>
++  </index>
+ 
+   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+ 
+diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
+index 460a299bf..75994e889 100644
+--- a/docs/reference/glib/glib-sections.txt
++++ b/docs/reference/glib/glib-sections.txt
+@@ -138,6 +138,7 @@ GLIB_VERSION_2_62
+ GLIB_VERSION_2_64
+ GLIB_VERSION_2_66
+ GLIB_VERSION_2_68
++GLIB_VERSION_2_70
+ GLIB_VERSION_CUR_STABLE
+ GLIB_VERSION_PREV_STABLE
+ GLIB_VERSION_MIN_REQUIRED
+@@ -168,6 +169,7 @@ GLIB_AVAILABLE_ENUMERATOR_IN_2_62
+ GLIB_AVAILABLE_ENUMERATOR_IN_2_64
+ GLIB_AVAILABLE_ENUMERATOR_IN_2_66
+ GLIB_AVAILABLE_ENUMERATOR_IN_2_68
++GLIB_AVAILABLE_ENUMERATOR_IN_2_70
+ GLIB_AVAILABLE_IN_ALL
+ GLIB_AVAILABLE_IN_2_26
+ GLIB_AVAILABLE_IN_2_28
+@@ -191,6 +193,7 @@ GLIB_AVAILABLE_IN_2_62
+ GLIB_AVAILABLE_IN_2_64
+ GLIB_AVAILABLE_IN_2_66
+ GLIB_AVAILABLE_IN_2_68
++GLIB_AVAILABLE_IN_2_70
+ GLIB_AVAILABLE_MACRO_IN_2_26
+ GLIB_AVAILABLE_MACRO_IN_2_28
+ GLIB_AVAILABLE_MACRO_IN_2_30
+@@ -213,12 +216,14 @@ GLIB_AVAILABLE_MACRO_IN_2_62
+ GLIB_AVAILABLE_MACRO_IN_2_64
+ GLIB_AVAILABLE_MACRO_IN_2_66
+ GLIB_AVAILABLE_MACRO_IN_2_68
++GLIB_AVAILABLE_MACRO_IN_2_70
+ GLIB_AVAILABLE_STATIC_INLINE_IN_2_44
+ GLIB_AVAILABLE_STATIC_INLINE_IN_2_60
+ GLIB_AVAILABLE_STATIC_INLINE_IN_2_62
+ GLIB_AVAILABLE_STATIC_INLINE_IN_2_64
+ GLIB_AVAILABLE_STATIC_INLINE_IN_2_66
+ GLIB_AVAILABLE_STATIC_INLINE_IN_2_68
++GLIB_AVAILABLE_STATIC_INLINE_IN_2_70
+ GLIB_AVAILABLE_TYPE_IN_2_26
+ GLIB_AVAILABLE_TYPE_IN_2_28
+ GLIB_AVAILABLE_TYPE_IN_2_30
+@@ -241,6 +246,7 @@ GLIB_AVAILABLE_TYPE_IN_2_62
+ GLIB_AVAILABLE_TYPE_IN_2_64
+ GLIB_AVAILABLE_TYPE_IN_2_66
+ GLIB_AVAILABLE_TYPE_IN_2_68
++GLIB_AVAILABLE_TYPE_IN_2_70
+ GLIB_DEPRECATED_ENUMERATOR
+ GLIB_DEPRECATED_ENUMERATOR_FOR
+ GLIB_DEPRECATED_ENUMERATOR_IN_2_26
+@@ -287,6 +293,8 @@ GLIB_DEPRECATED_ENUMERATOR_IN_2_66
+ GLIB_DEPRECATED_ENUMERATOR_IN_2_66_FOR
+ GLIB_DEPRECATED_ENUMERATOR_IN_2_68
+ GLIB_DEPRECATED_ENUMERATOR_IN_2_68_FOR
++GLIB_DEPRECATED_ENUMERATOR_IN_2_70
++GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR
+ GLIB_DEPRECATED_IN_2_26
+ GLIB_DEPRECATED_IN_2_26_FOR
+ GLIB_DEPRECATED_IN_2_28
+@@ -331,6 +339,8 @@ GLIB_DEPRECATED_IN_2_66
+ GLIB_DEPRECATED_IN_2_66_FOR
+ GLIB_DEPRECATED_IN_2_68
+ GLIB_DEPRECATED_IN_2_68_FOR
++GLIB_DEPRECATED_IN_2_70
++GLIB_DEPRECATED_IN_2_70_FOR
+ GLIB_DEPRECATED_MACRO
+ GLIB_DEPRECATED_MACRO_FOR
+ GLIB_DEPRECATED_MACRO_IN_2_26
+@@ -377,6 +387,8 @@ GLIB_DEPRECATED_MACRO_IN_2_66
+ GLIB_DEPRECATED_MACRO_IN_2_66_FOR
+ GLIB_DEPRECATED_MACRO_IN_2_68
+ GLIB_DEPRECATED_MACRO_IN_2_68_FOR
++GLIB_DEPRECATED_MACRO_IN_2_70
++GLIB_DEPRECATED_MACRO_IN_2_70_FOR
+ GLIB_DEPRECATED_TYPE
+ GLIB_DEPRECATED_TYPE_FOR
+ GLIB_DEPRECATED_TYPE_IN_2_26
+@@ -423,6 +435,8 @@ GLIB_DEPRECATED_TYPE_IN_2_66
+ GLIB_DEPRECATED_TYPE_IN_2_66_FOR
+ GLIB_DEPRECATED_TYPE_IN_2_68
+ GLIB_DEPRECATED_TYPE_IN_2_68_FOR
++GLIB_DEPRECATED_TYPE_IN_2_70
++GLIB_DEPRECATED_TYPE_IN_2_70_FOR
+ GLIB_VERSION_CUR_STABLE
+ GLIB_VERSION_PREV_STABLE
+ </SECTION>
+diff --git a/docs/reference/gobject/gobject-docs.xml b/docs/reference/gobject/gobject-docs.xml
+index ddbc9f274..e8e7c76d9 100644
+--- a/docs/reference/gobject/gobject-docs.xml
++++ b/docs/reference/gobject/gobject-docs.xml
+@@ -208,6 +208,10 @@
+     <title>Index of new symbols in 2.68</title>
+     <xi:include href="xml/api-index-2.68.xml"><xi:fallback /></xi:include>
+   </index>
++  <index id="api-index-2-70" role="2.70">
++    <title>Index of new symbols in 2.70</title>
++    <xi:include href="xml/api-index-2.70.xml"><xi:fallback /></xi:include>
++  </index>
+ 
+   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+ 
+diff --git a/docs/reference/meson.build b/docs/reference/meson.build
+index 3f09be555..53ca12ff8 100644
+--- a/docs/reference/meson.build
++++ b/docs/reference/meson.build
+@@ -7,7 +7,7 @@
+ stable_2_series_versions = [
+   '26', '28', '30', '32', '34', '36', '38',
+   '40', '42', '44', '46', '48', '50', '52', '54', '56', '58',
+-  '60', '62', '64', '66', '68',
++  '60', '62', '64', '66', '68', '70',
+ ]
+ 
+ ignore_decorators = [
+diff --git a/glib/gversionmacros.h b/glib/gversionmacros.h
+index 77486eafb..d052709cf 100644
+--- a/glib/gversionmacros.h
++++ b/glib/gversionmacros.h
+@@ -255,6 +255,16 @@
+  */
+ #define GLIB_VERSION_2_68       (G_ENCODE_VERSION (2, 68))
+ 
++/**
++ * GLIB_VERSION_2_70:
++ *
++ * A macro that evaluates to the 2.70 version of GLib, in a format
++ * that can be used by the C pre-processor.
++ *
++ * Since: 2.70
++ */
++#define GLIB_VERSION_2_70       (G_ENCODE_VERSION (2, 70))
++
+ /**
+  * GLIB_VERSION_CUR_STABLE:
+  *
+@@ -1076,4 +1086,38 @@
+ # define GLIB_AVAILABLE_TYPE_IN_2_68
+ #endif
+ 
++#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_70
++# define GLIB_DEPRECATED_IN_2_70                GLIB_DEPRECATED
++# define GLIB_DEPRECATED_IN_2_70_FOR(f)         GLIB_DEPRECATED_FOR(f)
++# define GLIB_DEPRECATED_MACRO_IN_2_70          GLIB_DEPRECATED_MACRO
++# define GLIB_DEPRECATED_MACRO_IN_2_70_FOR(f)   GLIB_DEPRECATED_MACRO_FOR(f)
++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70          GLIB_DEPRECATED_ENUMERATOR
++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR(f)   GLIB_DEPRECATED_ENUMERATOR_FOR(f)
++# define GLIB_DEPRECATED_TYPE_IN_2_70           GLIB_DEPRECATED_TYPE
++# define GLIB_DEPRECATED_TYPE_IN_2_70_FOR(f)    GLIB_DEPRECATED_TYPE_FOR(f)
++#else
++# define GLIB_DEPRECATED_IN_2_70                _GLIB_EXTERN
++# define GLIB_DEPRECATED_IN_2_70_FOR(f)         _GLIB_EXTERN
++# define GLIB_DEPRECATED_MACRO_IN_2_70
++# define GLIB_DEPRECATED_MACRO_IN_2_70_FOR(f)
++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70
++# define GLIB_DEPRECATED_ENUMERATOR_IN_2_70_FOR(f)
++# define GLIB_DEPRECATED_TYPE_IN_2_70
++# define GLIB_DEPRECATED_TYPE_IN_2_70_FOR(f)
++#endif
++
++#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_70
++# define GLIB_AVAILABLE_IN_2_70                 GLIB_UNAVAILABLE(2, 70)
++# define GLIB_AVAILABLE_STATIC_INLINE_IN_2_70   GLIB_UNAVAILABLE_STATIC_INLINE(2, 70)
++# define GLIB_AVAILABLE_MACRO_IN_2_70           GLIB_UNAVAILABLE_MACRO(2, 70)
++# define GLIB_AVAILABLE_ENUMERATOR_IN_2_70      GLIB_UNAVAILABLE_ENUMERATOR(2, 70)
++# define GLIB_AVAILABLE_TYPE_IN_2_70            GLIB_UNAVAILABLE_TYPE(2, 70)
++#else
++# define GLIB_AVAILABLE_IN_2_70                 _GLIB_EXTERN
++# define GLIB_AVAILABLE_STATIC_INLINE_IN_2_70
++# define GLIB_AVAILABLE_MACRO_IN_2_70
++# define GLIB_AVAILABLE_ENUMERATOR_IN_2_70
++# define GLIB_AVAILABLE_TYPE_IN_2_70
++#endif
++
+ #endif /*  __G_VERSION_MACROS_H__ */
+-- 
+GitLab
diff --git a/base/rx/rx-glib2/1968.patch b/base/rx/rx-glib2/1968.patch
new file mode 100644
index 0000000..1809214
--- /dev/null
+++ b/base/rx/rx-glib2/1968.patch
@@ -0,0 +1,1052 @@
+From 9e69f8b280afe8eccd9188cc53b8117e1b238db7 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Tue, 12 Oct 2021 15:52:18 -0500
+Subject: [PATCH 01/10] gspawn: use close_and_invalidate more
+
+---
+ glib/gspawn.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index a15fb1ca1..5d8422869 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1710,7 +1710,7 @@ do_exec (gint                  child_err_report_fd,
+                 child_err_report_fd = safe_dup (child_err_report_fd);
+ 
+               safe_dup2 (source_fds[i], target_fds[i]);
+-              (void) close (source_fds[i]);
++              close_and_invalidate (&source_fds[i]);
+             }
+         }
+     }
+-- 
+2.33.1
+
+From fe2148fd5dd4f2e5c413c5cc0bb56c4a19304887 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Thu, 14 Oct 2021 10:43:52 -0500
+Subject: [PATCH 02/10] gspawn: Improve error message when dup fails
+
+This error message is no longer accurate now that we allow arbitrary fd
+remapping.
+---
+ glib/gspawn.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index 5d8422869..e214a3998 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -2363,7 +2363,7 @@ fork_exec (gboolean              intermediate_child,
+               g_set_error (error,
+                            G_SPAWN_ERROR,
+                            G_SPAWN_ERROR_FAILED,
+-                           _("Failed to redirect output or input of child process (%s)"),
++                           _("Failed to duplicate file descriptor for child process (%s)"),
+                            g_strerror (buf[1]));
+ 
+               break;
+-- 
+2.33.1
+
+From 566eccdb0a2594b4d3ec13c7443028d968b41af8 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Tue, 12 Oct 2021 15:33:59 -0500
+Subject: [PATCH 03/10] gspawn: fix hangs when duping child_err_report_fd
+
+In case child_err_report_fd conflicts with one of the target_fds, the
+code here is careful to dup child_err_report_fd in order to avoid
+conflating the two. It was a good idea, but evidently was not tested,
+because the newly-created fd is not created with CLOEXEC set. This means
+it stays open in the child process, causing the parent to hang forever
+waiting to read from the other end of the pipe. Oops!
+
+The fix is simple: just set CLOEXEC. This removes our only usage of the
+safe_dup() function, so it can be dropped.
+
+Fixes #2506
+---
+ glib/gspawn.c | 16 +---------------
+ 1 file changed, 1 insertion(+), 15 deletions(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index e214a3998..8bbe573f7 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1500,20 +1500,6 @@ safe_closefrom (int lowfd)
+ #endif
+ }
+ 
+-/* This function is called between fork() and exec() and hence must be
+- * async-signal-safe (see signal-safety(7)). */
+-static gint
+-safe_dup (gint fd)
+-{
+-  gint ret;
+-
+-  do
+-    ret = dup (fd);
+-  while (ret < 0 && (errno == EINTR || errno == EBUSY));
+-
+-  return ret;
+-}
+-
+ /* This function is called between fork() and exec() and hence must be
+  * async-signal-safe (see signal-safety(7)). */
+ static gint
+@@ -1707,7 +1693,7 @@ do_exec (gint                  child_err_report_fd,
+           else
+             {
+               if (target_fds[i] == child_err_report_fd)
+-                child_err_report_fd = safe_dup (child_err_report_fd);
++                child_err_report_fd = dupfd_cloexec (child_err_report_fd);
+ 
+               safe_dup2 (source_fds[i], target_fds[i]);
+               close_and_invalidate (&source_fds[i]);
+-- 
+2.33.1
+
+From b703fa8b760ac9272c5a0ed3e3763b2f71ecf574 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Thu, 14 Oct 2021 10:44:57 -0500
+Subject: [PATCH 04/10] gspawn: fix fd remapping conflation issue
+
+We currently dup all source fds to avoid possible conflation with the
+target fds, but fail to consider that the result of a dup might itself
+conflict with one of the target fds. Solve this the easy way by duping
+all source_fds to values that are greater than the largest fd in
+target_fds.
+
+Fixes #2503
+---
+ glib/gspawn.c | 43 +++++++++++++++++++++++++------------------
+ 1 file changed, 25 insertions(+), 18 deletions(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index 8bbe573f7..2b48b5600 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1258,13 +1258,13 @@ unset_cloexec (int fd)
+ /* This function is called between fork() and exec() and hence must be
+  * async-signal-safe (see signal-safety(7)). */
+ static int
+-dupfd_cloexec (int parent_fd)
++dupfd_cloexec (int old_fd, int new_fd_min)
+ {
+   int fd, errsv;
+ #ifdef F_DUPFD_CLOEXEC
+   do
+     {
+-      fd = fcntl (parent_fd, F_DUPFD_CLOEXEC, 3);
++      fd = fcntl (old_fd, F_DUPFD_CLOEXEC, new_fd_min);
+       errsv = errno;
+     }
+   while (fd == -1 && errsv == EINTR);
+@@ -1275,7 +1275,7 @@ dupfd_cloexec (int parent_fd)
+   int result, flags;
+   do
+     {
+-      fd = fcntl (parent_fd, F_DUPFD, 3);
++      fd = fcntl (old_fd, F_DUPFD, new_fd_min);
+       errsv = errno;
+     }
+   while (fd == -1 && errsv == EINTR);
+@@ -1563,6 +1563,7 @@ do_exec (gint                  child_err_report_fd,
+          gpointer              user_data)
+ {
+   gsize i;
++  gint max_target_fd = 0;
+ 
+   if (working_directory && chdir (working_directory) < 0)
+     write_err_and_exit (child_err_report_fd,
+@@ -1661,39 +1662,45 @@ do_exec (gint                  child_err_report_fd,
+   /*
+    * Work through the @source_fds and @target_fds mapping.
+    *
+-   * Based on code derived from
++   * Based on code originally derived from
+    * gnome-terminal:src/terminal-screen.c:terminal_screen_child_setup(),
+-   * used under the LGPLv2+ with permission from author.
++   * used under the LGPLv2+ with permission from author. (The code has
++   * since migrated to vte:src/spawn.cc:SpawnContext::exec and is no longer
++   * terribly similar to what we have here.)
+    */
+ 
+-  /* Basic fd assignments (where source == target) we can just unset FD_CLOEXEC
+-   *
+-   * If we're doing remapping fd assignments, we need to handle
+-   * the case where the user has specified e.g.:
+-   * 5 -> 4, 4 -> 6
+-   *
+-   * We do this by duping the source fds temporarily in a first pass.
+-   *
+-   * If any of the @target_fds conflict with @child_err_report_fd, dup the
+-   * latter so it doesn’t get conflated.
+-   */
+   if (n_fds > 0)
+     {
++      for (i = 0; i < n_fds; i++)
++        max_target_fd = MAX (max_target_fd, target_fds[i]);
++
++      /* If we're doing remapping fd assignments, we need to handle
++       * the case where the user has specified e.g. 5 -> 4, 4 -> 6.
++       * We do this by duping all source fds, taking care to ensure the new
++       * fds are larger than any target fd to avoid introducing new conflicts.
++       */
+       for (i = 0; i < n_fds; i++)
+         {
+           if (source_fds[i] != target_fds[i])
+-            source_fds[i] = dupfd_cloexec (source_fds[i]);
++            source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1);
+         }
++
+       for (i = 0; i < n_fds; i++)
+         {
++          /* For basic fd assignments (where source == target), we can just
++           * unset FD_CLOEXEC.
++           */
+           if (source_fds[i] == target_fds[i])
+             {
+               unset_cloexec (source_fds[i]);
+             }
+           else
+             {
++              /* If any of the @target_fds conflict with @child_err_report_fd,
++               * dup it so it doesn’t get conflated.
++               */
+               if (target_fds[i] == child_err_report_fd)
+-                child_err_report_fd = dupfd_cloexec (child_err_report_fd);
++                child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1);
+ 
+               safe_dup2 (source_fds[i], target_fds[i]);
+               close_and_invalidate (&source_fds[i]);
+-- 
+2.33.1
+
+From ecc3538a942760e8b403c319d359711c8e166778 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@gnome.org>
+Date: Thu, 25 Feb 2021 12:20:39 -0600
+Subject: [PATCH 05/10] gspawn: Implement fd remapping for posix_spawn codepath
+
+This means that GSubprocess will (sometimes) be able to use the
+optimized posix_spawn codepath instead of having to fall back to
+fork/exec.
+---
+ glib/gspawn.c | 65 +++++++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 58 insertions(+), 7 deletions(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index 2b48b5600..9ef78dbe1 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1786,9 +1786,14 @@ do_posix_spawn (const gchar * const *argv,
+                 gint       *child_close_fds,
+                 gint        stdin_fd,
+                 gint        stdout_fd,
+-                gint        stderr_fd)
++                gint        stderr_fd,
++                const gint *source_fds,
++                const gint *target_fds,
++                gsize       n_fds)
+ {
+   pid_t pid;
++  gint *duped_source_fds = NULL;
++  gint max_target_fd = 0;
+   const gchar * const *argv_pass;
+   posix_spawnattr_t attr;
+   posix_spawn_file_actions_t file_actions;
+@@ -1797,7 +1802,8 @@ do_posix_spawn (const gchar * const *argv,
+   GSList *child_close = NULL;
+   GSList *elem;
+   sigset_t mask;
+-  int i, r;
++  size_t i;
++  int r;
+ 
+   if (*argv[0] == '\0')
+     {
+@@ -1911,6 +1917,43 @@ do_posix_spawn (const gchar * const *argv,
+         goto out_close_fds;
+     }
+ 
++  /* If source_fds[i] != target_fds[i], we need to handle the case
++   * where the user has specified, e.g., 5 -> 4, 4 -> 6. We do this
++   * by duping the source fds, taking care to ensure the new fds are
++   * larger than any target fd to avoid introducing new conflicts.
++   *
++   * If source_fds[i] == target_fds[i], then we just need to leak
++   * the fd into the child process, which we *could* do by temporarily
++   * unsetting CLOEXEC and then setting it again after we spawn if
++   * it was originally set. POSIX requires that the addup2 action unset
++   * CLOEXEC if source and target are identical, so you'd think doing it
++   * manually wouldn't be needed, but unfortunately as of 2021 many
++   * libcs still don't do so. Example nonconforming libcs:
++   *  Bionic: https://android.googlesource.com/platform/bionic/+/f6e5b582604715729b09db3e36a7aeb8c24b36a4/libc/bionic/spawn.cpp#71
++   *  uclibc-ng: https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/librt/spawn.c?id=7c36bcae09d66bbaa35cbb02253ae0556f42677e#n88
++   *
++   * Anyway, unsetting CLOEXEC ourselves would open a small race window
++   * where the fd could be inherited into a child process if another
++   * thread spawns something at the same time, because we have not
++   * called fork() and are multithreaded here. This race is avoidable by
++   * using dupfd_cloexec, which we already have to do to handle the
++   * source_fds[i] != target_fds[i] case. So let's always do it!
++   */
++
++  for (i = 0; i < n_fds; i++)
++    max_target_fd = MAX (max_target_fd, target_fds[i]);
++
++  duped_source_fds = g_new (gint, n_fds);
++  for (i = 0; i < n_fds; i++)
++    duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1);
++
++  for (i = 0; i < n_fds; i++)
++    {
++      r = posix_spawn_file_actions_adddup2 (&file_actions, duped_source_fds[i], target_fds[i]);
++      if (r != 0)
++        goto out_close_fds;
++    }
++
+   /* Intentionally close the fds in the child as the last file action,
+    * having been careful not to add the same fd to this list twice.
+    *
+@@ -1940,9 +1983,16 @@ do_posix_spawn (const gchar * const *argv,
+     *child_pid = pid;
+ 
+ out_close_fds:
+-  for (i = 0; i < num_parent_close_fds; i++)
++  for (i = 0; i < (size_t) num_parent_close_fds; i++)
+     close_and_invalidate (&parent_close_fds [i]);
+ 
++  if (duped_source_fds != NULL)
++    {
++      for (i = 0; i < n_fds; i++)
++        close_and_invalidate (&duped_source_fds[i]);
++      g_free (duped_source_fds);
++    }
++
+   posix_spawn_file_actions_destroy (&file_actions);
+ out_free_spawnattr:
+   posix_spawnattr_destroy (&attr);
+@@ -2030,10 +2080,8 @@ fork_exec (gboolean              intermediate_child,
+   child_close_fds[n_child_close_fds++] = -1;
+ 
+ #ifdef POSIX_SPAWN_AVAILABLE
+-  /* FIXME: Handle @source_fds and @target_fds in do_posix_spawn() using the
+-   * file actions API. */
+   if (!intermediate_child && working_directory == NULL && !close_descriptors &&
+-      !search_path_from_envp && child_setup == NULL && n_fds == 0)
++      !search_path_from_envp && child_setup == NULL)
+     {
+       g_trace_mark (G_TRACE_CURRENT_TIME, 0,
+                     "GLib", "posix_spawn",
+@@ -2050,7 +2098,10 @@ fork_exec (gboolean              intermediate_child,
+                                child_close_fds,
+                                stdin_fd,
+                                stdout_fd,
+-                               stderr_fd);
++                               stderr_fd,
++                               source_fds,
++                               target_fds,
++                               n_fds);
+       if (status == 0)
+         goto success;
+ 
+-- 
+2.33.1
+
+From 731d6c32105dc97f2b777ef9a34c6b76d1489c04 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@gnome.org>
+Date: Thu, 25 Feb 2021 12:21:38 -0600
+Subject: [PATCH 06/10] gsubprocess: ensure we test fd remapping on the
+ posix_spawn() codepath
+
+We should run test_pass_fd twice, once using gspawn's fork/exec codepath
+and once attempting to use its posix_spawn() codepath. There's no
+guarantee we'll actually get the posix_spawn() codepath, but it works
+for now on Linux.
+
+For good measure, run it a third time with no flags at all.
+
+This causes the test to fail if I separately break the fd remapping
+implementation. Without this, we fail to test fd remapping on the
+posix_spawn() codepath.
+---
+ gio/tests/gsubprocess.c | 44 ++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 41 insertions(+), 3 deletions(-)
+
+diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
+index 7e22678ec..ba49c1c43 100644
+--- a/gio/tests/gsubprocess.c
++++ b/gio/tests/gsubprocess.c
+@@ -1697,7 +1697,8 @@ test_child_setup (void)
+ }
+ 
+ static void
+-test_pass_fd (void)
++do_test_pass_fd (GSubprocessFlags     flags,
++                 GSpawnChildSetupFunc child_setup)
+ {
+   GError *local_error = NULL;
+   GError **error = &local_error;
+@@ -1722,9 +1723,11 @@ test_pass_fd (void)
+   needdup_fd_str = g_strdup_printf ("%d", needdup_pipefds[1] + 1);
+ 
+   args = get_test_subprocess_args ("write-to-fds", basic_fd_str, needdup_fd_str, NULL);
+-  launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
++  launcher = g_subprocess_launcher_new (flags);
+   g_subprocess_launcher_take_fd (launcher, basic_pipefds[1], basic_pipefds[1]);
+   g_subprocess_launcher_take_fd (launcher, needdup_pipefds[1], needdup_pipefds[1] + 1);
++  if (child_setup != NULL)
++    g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL);
+   proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error);
+   g_ptr_array_free (args, TRUE);
+   g_assert_no_error (local_error);
+@@ -1754,6 +1757,39 @@ test_pass_fd (void)
+   g_object_unref (proc);
+ }
+ 
++static void
++test_pass_fd (void)
++{
++  do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, NULL);
++}
++
++static void
++empty_child_setup (gpointer user_data)
++{
++}
++
++static void
++test_pass_fd_empty_child_setup (void)
++{
++  /* Using a child setup function forces gspawn to use fork/exec
++   * rather than posix_spawn.
++   */
++  do_test_pass_fd (G_SUBPROCESS_FLAGS_NONE, empty_child_setup);
++}
++
++static void
++test_pass_fd_inherit_fds (void)
++{
++  /* Try to test the optimized posix_spawn codepath instead of
++   * fork/exec. Currently this requires using INHERIT_FDS since gspawn's
++   * posix_spawn codepath does not currently handle closing
++   * non-inherited fds. Note that using INHERIT_FDS means our testing of
++   * g_subprocess_launcher_take_fd() is less-comprehensive than when
++   * using G_SUBPROCESS_FLAGS_NONE.
++   */
++  do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL);
++}
++
+ #endif
+ 
+ static void
+@@ -1891,7 +1927,9 @@ main (int argc, char **argv)
+   g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file);
+   g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd);
+   g_test_add_func ("/gsubprocess/child-setup", test_child_setup);
+-  g_test_add_func ("/gsubprocess/pass-fd", test_pass_fd);
++  g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd);
++  g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup);
++  g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds);
+ #endif
+   g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment);
+ 
+-- 
+2.33.1
+
+From 4608940466a04a32d4e6e71dbe872cfecb136118 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Thu, 14 Oct 2021 11:01:33 -0500
+Subject: [PATCH 07/10] gspawn: Check from errors from safe_dup2() and
+ dupfd_cloexec()
+
+Although unlikely, these functions can fail, e.g. if we run out of file
+descriptors. Check for errors to improve robustness. This is especially
+important now that I changed our use of dupfd_cloexec() to avoid
+returning fds smaller than the largest fd in target_fds. An application
+that attempts to remap to the highest-allowed fd value deserves at least
+some sort of attempt at error reporting, not silent failure.
+---
+ glib/gspawn.c | 40 +++++++++++++++++++++++++++++-----------
+ 1 file changed, 29 insertions(+), 11 deletions(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index 9ef78dbe1..7ef678047 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1572,7 +1572,6 @@ do_exec (gint                  child_err_report_fd,
+   /* Redirect pipes as required */
+   if (stdin_fd >= 0)
+     {
+-      /* dup2 can't actually fail here I don't think */
+       if (safe_dup2 (stdin_fd, 0) < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+@@ -1588,13 +1587,14 @@ do_exec (gint                  child_err_report_fd,
+       if (read_null < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+-      safe_dup2 (read_null, 0);
++      if (safe_dup2 (read_null, 0) < 0)
++        write_err_and_exit (child_err_report_fd,
++                            CHILD_DUP2_FAILED);
+       close_and_invalidate (&read_null);
+     }
+ 
+   if (stdout_fd >= 0)
+     {
+-      /* dup2 can't actually fail here I don't think */
+       if (safe_dup2 (stdout_fd, 1) < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+@@ -1609,13 +1609,14 @@ do_exec (gint                  child_err_report_fd,
+       if (write_null < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+-      safe_dup2 (write_null, 1);
++      if (safe_dup2 (write_null, 1) < 0)
++        write_err_and_exit (child_err_report_fd,
++                            CHILD_DUP2_FAILED);
+       close_and_invalidate (&write_null);
+     }
+ 
+   if (stderr_fd >= 0)
+     {
+-      /* dup2 can't actually fail here I don't think */
+       if (safe_dup2 (stderr_fd, 2) < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+@@ -1630,7 +1631,9 @@ do_exec (gint                  child_err_report_fd,
+       if (write_null < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+-      safe_dup2 (write_null, 2);
++      if (safe_dup2 (write_null, 2) < 0)
++        write_err_and_exit (child_err_report_fd,
++                            CHILD_DUP2_FAILED);
+       close_and_invalidate (&write_null);
+     }
+ 
+@@ -1643,7 +1646,8 @@ do_exec (gint                  child_err_report_fd,
+     {
+       if (child_setup == NULL && n_fds == 0)
+         {
+-          safe_dup2 (child_err_report_fd, 3);
++          if (safe_dup2 (child_err_report_fd, 3) < 0)
++            write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED);
+           set_cloexec (GINT_TO_POINTER (0), 3);
+           safe_closefrom (4);
+           child_err_report_fd = 3;
+@@ -1682,7 +1686,11 @@ do_exec (gint                  child_err_report_fd,
+       for (i = 0; i < n_fds; i++)
+         {
+           if (source_fds[i] != target_fds[i])
+-            source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1);
++            {
++              source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1);
++              if (source_fds[i] < 0)
++                write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED);
++            }
+         }
+ 
+       for (i = 0; i < n_fds; i++)
+@@ -1700,9 +1708,15 @@ do_exec (gint                  child_err_report_fd,
+                * dup it so it doesn’t get conflated.
+                */
+               if (target_fds[i] == child_err_report_fd)
+-                child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1);
++                {
++                  child_err_report_fd = dupfd_cloexec (child_err_report_fd, max_target_fd + 1);
++                  if (child_err_report_fd < 0)
++                    write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED);
++                }
++
++              if (safe_dup2 (source_fds[i], target_fds[i]) < 0)
++                write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED);
+ 
+-              safe_dup2 (source_fds[i], target_fds[i]);
+               close_and_invalidate (&source_fds[i]);
+             }
+         }
+@@ -1945,7 +1959,11 @@ do_posix_spawn (const gchar * const *argv,
+ 
+   duped_source_fds = g_new (gint, n_fds);
+   for (i = 0; i < n_fds; i++)
+-    duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1);
++    {
++      duped_source_fds[i] = dupfd_cloexec (source_fds[i], max_target_fd + 1);
++      if (duped_source_fds[i] < 0)
++        goto out_close_fds;
++    }
+ 
+   for (i = 0; i < n_fds; i++)
+     {
+-- 
+2.33.1
+
+From 0198b6a1c8c215f524d7c6ed2d240fb1b31d9865 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Wed, 20 Oct 2021 16:51:44 -0500
+Subject: [PATCH 08/10] gspawn: add new error message for open() failures
+
+Reporting these as dup2() failures is bogus.
+---
+ glib/gspawn.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index 7ef678047..c2fe306dc 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1532,6 +1532,7 @@ enum
+ {
+   CHILD_CHDIR_FAILED,
+   CHILD_EXEC_FAILED,
++  CHILD_OPEN_FAILED,
+   CHILD_DUP2_FAILED,
+   CHILD_FORK_FAILED
+ };
+@@ -1586,7 +1587,7 @@ do_exec (gint                  child_err_report_fd,
+       gint read_null = safe_open ("/dev/null", O_RDONLY);
+       if (read_null < 0)
+         write_err_and_exit (child_err_report_fd,
+-                            CHILD_DUP2_FAILED);
++                            CHILD_OPEN_FAILED);
+       if (safe_dup2 (read_null, 0) < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+@@ -1608,7 +1609,7 @@ do_exec (gint                  child_err_report_fd,
+       gint write_null = safe_open ("/dev/null", O_WRONLY);
+       if (write_null < 0)
+         write_err_and_exit (child_err_report_fd,
+-                            CHILD_DUP2_FAILED);
++                            CHILD_OPEN_FAILED);
+       if (safe_dup2 (write_null, 1) < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+@@ -1630,7 +1631,7 @@ do_exec (gint                  child_err_report_fd,
+       gint write_null = safe_open ("/dev/null", O_WRONLY);
+       if (write_null < 0)
+         write_err_and_exit (child_err_report_fd,
+-                            CHILD_DUP2_FAILED);
++                            CHILD_OPEN_FAILED);
+       if (safe_dup2 (write_null, 2) < 0)
+         write_err_and_exit (child_err_report_fd,
+                             CHILD_DUP2_FAILED);
+@@ -2420,7 +2421,15 @@ fork_exec (gboolean              intermediate_child,
+                            g_strerror (buf[1]));
+ 
+               break;
+-              
++
++            case CHILD_OPEN_FAILED:
++              g_set_error (error,
++                           G_SPAWN_ERROR,
++                           G_SPAWN_ERROR_FAILED,
++                           _("Failed to open file to remap file descriptor (%s)"),
++                           g_strerror (buf[1]));
++              break;
++
+             case CHILD_DUP2_FAILED:
+               g_set_error (error,
+                            G_SPAWN_ERROR,
+-- 
+2.33.1
+
+From e4abb5f3db85b2f730e192e6398f26934e41ba21 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Tue, 26 Oct 2021 21:27:15 -0500
+Subject: [PATCH 09/10] Add tests for GSubprocess fd conflation issues
+
+This tests for #2503. It's fragile, but there is no non-fragile way to
+test this. If the test breaks in the future, it will pass without
+successfully testing the bug, not fail spuriously, so I think this is
+OK.
+---
+ gio/tests/gsubprocess-testprog.c |  53 +++++++++++-
+ gio/tests/gsubprocess.c          | 144 +++++++++++++++++++++++++++++++
+ 2 files changed, 195 insertions(+), 2 deletions(-)
+
+diff --git a/gio/tests/gsubprocess-testprog.c b/gio/tests/gsubprocess-testprog.c
+index c9b06c2a2..58cb1c71d 100644
+--- a/gio/tests/gsubprocess-testprog.c
++++ b/gio/tests/gsubprocess-testprog.c
+@@ -5,8 +5,6 @@
+ #include <errno.h>
+ #ifdef G_OS_UNIX
+ #include <unistd.h>
+-#include <gio/gunixinputstream.h>
+-#include <gio/gunixoutputstream.h>
+ #else
+ #include <io.h>
+ #endif
+@@ -150,6 +148,55 @@ write_to_fds (int argc, char **argv)
+   return 0;
+ }
+ 
++static int
++read_from_fd (int argc, char **argv)
++{
++  int fd;
++  const char expectedResult[] = "Yay success!";
++  guint8 buf[sizeof (expectedResult) + 1];
++  gsize bytes_read;
++  FILE *f;
++
++  if (argc != 3)
++    {
++      g_print ("Usage: %s read-from-fd FD\n", argv[0]);
++      return 1;
++    }
++
++  fd = atoi (argv[2]);
++  if (fd == 0)
++    {
++      g_warning ("Argument \"%s\" does not look like a valid nonzero file descriptor", argv[2]);
++      return 1;
++    }
++
++  f = fdopen (fd, "r");
++  if (f == NULL)
++    {
++      g_warning ("Failed to open fd %d: %s", fd, g_strerror (errno));
++      return 1;
++    }
++
++  bytes_read = fread (buf, 1, sizeof (buf), f);
++  if (bytes_read != sizeof (expectedResult))
++    {
++      g_warning ("Read %zu bytes, but expected %zu", bytes_read, sizeof (expectedResult));
++      return 1;
++    }
++
++  if (memcmp (expectedResult, buf, sizeof (expectedResult)) != 0)
++    {
++      buf[sizeof (expectedResult)] = '\0';
++      g_warning ("Expected \"%s\" but read  \"%s\"", expectedResult, (char *)buf);
++      return 1;
++    }
++
++  if (fclose (f) == -1)
++    g_assert_not_reached ();
++
++  return 0;
++}
++
+ static int
+ env_mode (int argc, char **argv)
+ {
+@@ -242,6 +289,8 @@ main (int argc, char **argv)
+     return sleep_forever_mode (argc, argv);
+   else if (strcmp (mode, "write-to-fds") == 0)
+     return write_to_fds (argc, argv);
++  else if (strcmp (mode, "read-from-fd") == 0)
++    return read_from_fd (argc, argv);
+   else if (strcmp (mode, "env") == 0)
+     return env_mode (argc, argv);
+   else if (strcmp (mode, "cwd") == 0)
+diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
+index ba49c1c43..a6e24c2e8 100644
+--- a/gio/tests/gsubprocess.c
++++ b/gio/tests/gsubprocess.c
+@@ -5,6 +5,7 @@
+ #include <sys/wait.h>
+ #include <glib-unix.h>
+ #include <gio/gunixinputstream.h>
++#include <gio/gunixoutputstream.h>
+ #include <gio/gfiledescriptorbased.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+@@ -1790,6 +1791,146 @@ test_pass_fd_inherit_fds (void)
+   do_test_pass_fd (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL);
+ }
+ 
++static void
++do_test_fd_conflation (GSubprocessFlags     flags,
++                       GSpawnChildSetupFunc child_setup)
++{
++  char success_message[] = "Yay success!";
++  GError *error = NULL;
++  GOutputStream *output_stream;
++  GSubprocessLauncher *launcher;
++  GSubprocess *proc;
++  GPtrArray *args;
++  int unused_pipefds[2];
++  int pipefds[2];
++  gsize bytes_written;
++  gboolean success;
++  char *fd_str;
++
++  /* This test must run in a new process because it is extremely sensitive to
++   * order of opened fds.
++   */
++  if (!g_test_subprocess ())
++    {
++      g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_INHERIT_STDOUT | G_TEST_SUBPROCESS_INHERIT_STDERR);
++      g_test_trap_assert_passed ();
++      return;
++    }
++
++  g_unix_open_pipe (unused_pipefds, FD_CLOEXEC, &error);
++  g_assert_no_error (error);
++
++  g_unix_open_pipe (pipefds, FD_CLOEXEC, &error);
++  g_assert_no_error (error);
++
++  /* The fds should be sequential since we are in a new process. */
++  g_assert_cmpint (unused_pipefds[0] /* 3 */, ==, unused_pipefds[1] - 1);
++  g_assert_cmpint (unused_pipefds[1] /* 4 */, ==, pipefds[0] - 1);
++  g_assert_cmpint (pipefds[0] /* 5 */, ==, pipefds[1] /* 6 */ - 1);
++
++  /* Because GSubprocess allows arbitrary remapping of fds, it has to be careful
++   * to avoid fd conflation issues, e.g. it should properly handle 5 -> 4 and
++   * 4 -> 5 at the same time. GIO previously attempted to handle this by naively
++   * dup'ing the source fds, but this was not good enough because it was
++   * possible that the dup'ed result could still conflict with one of the target
++   * fds. For example:
++   *
++   * source_fd 5 -> target_fd 9, source_fd 3 -> target_fd 7
++   *
++   * dup(5) -> dup returns 8
++   * dup(3) -> dup returns 9
++   *
++   * After dup'ing, we wind up with: 8 -> 9, 9 -> 7. That means that after we
++   * dup2(8, 9), we have clobbered fd 9 before we dup2(9, 7). The end result is
++   * we have remapped 5 -> 9 as expected, but then remapped 5 -> 7 instead of
++   * 3 -> 7 as the application intended.
++   *
++   * This issue has been fixed in the simplest way possible, by passing a
++   * minimum fd value when using F_DUPFD_CLOEXEC that is higher than any of the
++   * target fds, to guarantee all source fds are different than all target fds,
++   * eliminating any possibility of conflation.
++   *
++   * Anyway, that is why we have the unused_pipefds here. We need to open fds in
++   * a certain order in order to trick older GSubprocess into conflating the
++   * fds. The primary goal of this test is to ensure this particular conflation
++   * issue is not reintroduced. See glib#2503.
++   *
++   * Be aware this test is necessarily extremely fragile. To reproduce these
++   * bugs, it relies on internals of gspawn and gmain that will likely change
++   * in the future, eventually causing this test to no longer test the the bugs
++   * it was originally designed to test. That is OK! If the test fails, at
++   * least you know *something* is wrong.
++   */
++  launcher = g_subprocess_launcher_new (flags);
++  g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, pipefds[1] + 3 /* 9 */);
++  g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */);
++  if (child_setup != NULL)
++    g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL);
++  fd_str = g_strdup_printf ("%d", pipefds[1] + 3);
++  args = get_test_subprocess_args ("read-from-fd", fd_str, NULL);
++  proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error);
++  g_assert_no_error (error);
++  g_assert_nonnull (proc);
++  g_ptr_array_free (args, TRUE);
++  g_object_unref (launcher);
++  g_free (fd_str);
++
++  /* Close the read ends of the pipes. */
++  close (unused_pipefds[0]);
++  close (pipefds[0]);
++
++  /* Also close the write end of the unused pipe. */
++  close (unused_pipefds[1]);
++
++  /* So now pipefds[0] should be inherited into the subprocess as
++   * pipefds[1] + 2, and unused_pipefds[0] should be inherited as
++   * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify
++   * that it reads the expected data. But older broken GIO will accidentally
++   * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess
++   * to hang trying to read from the wrong pipe.
++   */
++  output_stream = g_unix_output_stream_new (pipefds[1], TRUE);
++  success = g_output_stream_write_all (output_stream,
++                                       success_message, sizeof (success_message),
++                                       &bytes_written,
++                                       NULL,
++                                       &error);
++  g_assert_no_error (error);
++  g_assert_cmpint (bytes_written, ==, sizeof (success_message));
++  g_assert_true (success);
++  g_object_unref (output_stream);
++
++  success = g_subprocess_wait_check (proc, NULL, &error);
++  g_assert_no_error (error);
++  g_object_unref (proc);
++}
++
++static void
++test_fd_conflation (void)
++{
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL);
++}
++
++static void
++test_fd_conflation_empty_child_setup (void)
++{
++  /* Using a child setup function forces gspawn to use fork/exec
++   * rather than posix_spawn.
++   */
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup);
++}
++
++static void
++test_fd_conflation_inherit_fds (void)
++{
++  /* Try to test the optimized posix_spawn codepath instead of
++   * fork/exec. Currently this requires using INHERIT_FDS since gspawn's
++   * posix_spawn codepath does not currently handle closing
++   * non-inherited fds.
++   */
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL);
++}
++
+ #endif
+ 
+ static void
+@@ -1930,6 +2071,9 @@ main (int argc, char **argv)
+   g_test_add_func ("/gsubprocess/pass-fd/basic", test_pass_fd);
+   g_test_add_func ("/gsubprocess/pass-fd/empty-child-setup", test_pass_fd_empty_child_setup);
+   g_test_add_func ("/gsubprocess/pass-fd/inherit-fds", test_pass_fd_inherit_fds);
++  g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation);
++  g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup);
++  g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds);
+ #endif
+   g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment);
+ 
+-- 
+2.33.1
+
+From 5542612c805857a244561ec160e904dd302ae799 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Wed, 27 Oct 2021 18:30:47 -0500
+Subject: [PATCH 10/10] Add test for child_err_report_fd conflation with target
+ fds
+
+This tests for glib#2506.
+---
+ gio/tests/gsubprocess.c | 42 ++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 35 insertions(+), 7 deletions(-)
+
+diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
+index a6e24c2e8..4629cdea7 100644
+--- a/gio/tests/gsubprocess.c
++++ b/gio/tests/gsubprocess.c
+@@ -1793,7 +1793,8 @@ test_pass_fd_inherit_fds (void)
+ 
+ static void
+ do_test_fd_conflation (GSubprocessFlags     flags,
+-                       GSpawnChildSetupFunc child_setup)
++                       GSpawnChildSetupFunc child_setup,
++                       gboolean             test_child_err_report_fd)
+ {
+   char success_message[] = "Yay success!";
+   GError *error = NULL;
+@@ -1803,6 +1804,7 @@ do_test_fd_conflation (GSubprocessFlags     flags,
+   GPtrArray *args;
+   int unused_pipefds[2];
+   int pipefds[2];
++  int fd_to_pass_to_child;
+   gsize bytes_written;
+   gboolean success;
+   char *fd_str;
+@@ -1855,18 +1857,26 @@ do_test_fd_conflation (GSubprocessFlags     flags,
+    * fds. The primary goal of this test is to ensure this particular conflation
+    * issue is not reintroduced. See glib#2503.
+    *
++   * This test also has an alternate mode of operation where it instead tests
++   * for conflation with gspawn's child_err_report_fd, glib#2506.
++   *
+    * Be aware this test is necessarily extremely fragile. To reproduce these
+    * bugs, it relies on internals of gspawn and gmain that will likely change
+    * in the future, eventually causing this test to no longer test the the bugs
+    * it was originally designed to test. That is OK! If the test fails, at
+    * least you know *something* is wrong.
+    */
++  if (test_child_err_report_fd)
++    fd_to_pass_to_child = pipefds[1] + 2 /* 8 */;
++  else
++    fd_to_pass_to_child = pipefds[1] + 3 /* 9 */;
++
+   launcher = g_subprocess_launcher_new (flags);
+-  g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, pipefds[1] + 3 /* 9 */);
++  g_subprocess_launcher_take_fd (launcher, pipefds[0] /* 5 */, fd_to_pass_to_child);
+   g_subprocess_launcher_take_fd (launcher, unused_pipefds[0] /* 3 */, pipefds[1] + 1 /* 7 */);
+   if (child_setup != NULL)
+     g_subprocess_launcher_set_child_setup (launcher, child_setup, NULL, NULL);
+-  fd_str = g_strdup_printf ("%d", pipefds[1] + 3);
++  fd_str = g_strdup_printf ("%d", fd_to_pass_to_child);
+   args = get_test_subprocess_args ("read-from-fd", fd_str, NULL);
+   proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, &error);
+   g_assert_no_error (error);
+@@ -1882,12 +1892,20 @@ do_test_fd_conflation (GSubprocessFlags     flags,
+   /* Also close the write end of the unused pipe. */
+   close (unused_pipefds[1]);
+ 
+-  /* So now pipefds[0] should be inherited into the subprocess as
++  /* If doing our normal test:
++   *
++   * So now pipefds[0] should be inherited into the subprocess as
+    * pipefds[1] + 2, and unused_pipefds[0] should be inherited as
+    * pipefds[1] + 1. We will write to pipefds[1] and the subprocess will verify
+    * that it reads the expected data. But older broken GIO will accidentally
+    * clobber pipefds[1] + 2 with pipefds[1] + 1! This will cause the subprocess
+    * to hang trying to read from the wrong pipe.
++   *
++   * If testing conflation with child_err_report_fd:
++   *
++   * We are actually already done. The real test succeeded if we made it this
++   * far without hanging while spawning the child. But let's continue with our
++   * write and read anyway, to ensure things are good.
+    */
+   output_stream = g_unix_output_stream_new (pipefds[1], TRUE);
+   success = g_output_stream_write_all (output_stream,
+@@ -1908,7 +1926,7 @@ do_test_fd_conflation (GSubprocessFlags     flags,
+ static void
+ test_fd_conflation (void)
+ {
+-  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL);
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, NULL, FALSE);
+ }
+ 
+ static void
+@@ -1917,7 +1935,7 @@ test_fd_conflation_empty_child_setup (void)
+   /* Using a child setup function forces gspawn to use fork/exec
+    * rather than posix_spawn.
+    */
+-  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup);
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, FALSE);
+ }
+ 
+ static void
+@@ -1928,7 +1946,16 @@ test_fd_conflation_inherit_fds (void)
+    * posix_spawn codepath does not currently handle closing
+    * non-inherited fds.
+    */
+-  do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL);
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_INHERIT_FDS, NULL, FALSE);
++}
++
++static void
++test_fd_conflation_child_err_report_fd (void)
++{
++  /* Using a child setup function forces gspawn to use fork/exec
++   * rather than posix_spawn.
++   */
++  do_test_fd_conflation (G_SUBPROCESS_FLAGS_NONE, empty_child_setup, TRUE);
+ }
+ 
+ #endif
+@@ -2074,6 +2101,7 @@ main (int argc, char **argv)
+   g_test_add_func ("/gsubprocess/fd-conflation/basic", test_fd_conflation);
+   g_test_add_func ("/gsubprocess/fd-conflation/empty-child-setup", test_fd_conflation_empty_child_setup);
+   g_test_add_func ("/gsubprocess/fd-conflation/inherit-fds", test_fd_conflation_inherit_fds);
++  g_test_add_func ("/gsubprocess/fd-conflation/child-err-report-fd", test_fd_conflation_child_err_report_fd);
+ #endif
+   g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment);
+ 
+-- 
+2.33.1
+
diff --git a/base/rx/rx-glib2/2194.patch b/base/rx/rx-glib2/2194.patch
new file mode 100644
index 0000000..5c9aaf2
--- /dev/null
+++ b/base/rx/rx-glib2/2194.patch
@@ -0,0 +1,920 @@
+From 2e500304e304e45042a59855319ff0379b1978b3 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Tue, 27 Jul 2021 17:24:17 +0200
+Subject: [PATCH 1/4] tests: Remove unused constant in GMemoryMonitor test
+
+---
+ gio/tests/memory-monitor-dbus.py.in | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in
+index cd16cf4e3..7823e7309 100755
+--- a/gio/tests/memory-monitor-dbus.py.in
++++ b/gio/tests/memory-monitor-dbus.py.in
+@@ -31,9 +31,6 @@ try:
+ 
+     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ 
+-    # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal")
+-    XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal"
+-
+     class TestLowMemoryMonitor(dbusmock.DBusTestCase):
+         '''Test GMemoryMonitorDBus'''
+ 
+-- 
+GitLab
+
+
+From a7000cd989438b01e599b2cfa8b6d5a360bfd102 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Wed, 28 Jul 2021 15:10:16 +0200
+Subject: [PATCH 2/4] gio: g_clear_signal_handler() can handle NULL args
+
+---
+ gio/gmemorymonitordbus.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/gio/gmemorymonitordbus.c b/gio/gmemorymonitordbus.c
+index a34a58d3b..08dc53df1 100644
+--- a/gio/gmemorymonitordbus.c
++++ b/gio/gmemorymonitordbus.c
+@@ -115,8 +115,7 @@ lmm_vanished_cb (GDBusConnection *connection,
+ {
+   GMemoryMonitorDBus *dbus = user_data;
+ 
+-  if (dbus->proxy != NULL)
+-    g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
++  g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
+   g_clear_object (&dbus->proxy);
+ }
+ 
+@@ -143,8 +142,7 @@ g_memory_monitor_dbus_finalize (GObject *object)
+ {
+   GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object);
+ 
+-  if (dbus->proxy != NULL)
+-    g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
++  g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
+   g_clear_object (&dbus->proxy);
+   g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name);
+ 
+-- 
+GitLab
+
+
+From 92399e7114e590f0371b1a5d71f478f840cb4074 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Wed, 28 Jul 2021 15:30:15 +0200
+Subject: [PATCH 3/4] gio: Do not block when low-memory-monitor daemon appears
+
+---
+ gio/gmemorymonitordbus.c | 42 +++++++++++++++++++++++++++-------------
+ 1 file changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/gio/gmemorymonitordbus.c b/gio/gmemorymonitordbus.c
+index 08dc53df1..739b83214 100644
+--- a/gio/gmemorymonitordbus.c
++++ b/gio/gmemorymonitordbus.c
+@@ -25,6 +25,7 @@
+ #include "giomodule-priv.h"
+ #include "glibintl.h"
+ #include "glib/gstdio.h"
++#include "gcancellable.h"
+ #include "gdbusproxy.h"
+ #include "gdbusnamewatching.h"
+ 
+@@ -38,6 +39,7 @@ struct _GMemoryMonitorDBus
+   GObject parent_instance;
+ 
+   guint watch_id;
++  GCancellable *cancellable;
+   GDBusProxy *proxy;
+   gulong signal_id;
+ };
+@@ -77,24 +79,15 @@ proxy_signal_cb (GDBusProxy         *proxy,
+ }
+ 
+ static void
+-lmm_appeared_cb (GDBusConnection *connection,
+-                 const gchar     *name,
+-                 const gchar     *name_owner,
+-                 gpointer         user_data)
++lmm_proxy_cb (GObject      *source_object,
++              GAsyncResult *res,
++              gpointer      user_data)
+ {
+   GMemoryMonitorDBus *dbus = user_data;
+   GDBusProxy *proxy;
+   GError *error = NULL;
+ 
+-  proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+-                                         G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+-                                         NULL,
+-                                         "org.freedesktop.LowMemoryMonitor",
+-                                         "/org/freedesktop/LowMemoryMonitor",
+-                                         "org.freedesktop.LowMemoryMonitor",
+-                                         NULL,
+-                                         &error);
+-
++  proxy = g_dbus_proxy_new_finish (res, &error);
+   if (!proxy)
+     {
+       g_debug ("Failed to create LowMemoryMonitor D-Bus proxy: %s",
+@@ -106,6 +99,26 @@ lmm_appeared_cb (GDBusConnection *connection,
+   dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal",
+                                       G_CALLBACK (proxy_signal_cb), dbus);
+   dbus->proxy = proxy;
++
++}
++
++static void
++lmm_appeared_cb (GDBusConnection *connection,
++                 const gchar     *name,
++                 const gchar     *name_owner,
++                 gpointer         user_data)
++{
++  GMemoryMonitorDBus *dbus = user_data;
++
++  g_dbus_proxy_new (connection,
++                    G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
++                    NULL,
++                    "org.freedesktop.LowMemoryMonitor",
++                    "/org/freedesktop/LowMemoryMonitor",
++                    "org.freedesktop.LowMemoryMonitor",
++                    dbus->cancellable,
++                    lmm_proxy_cb,
++                    dbus);
+ }
+ 
+ static void
+@@ -126,6 +139,7 @@ g_memory_monitor_dbus_initable_init (GInitable     *initable,
+ {
+   GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (initable);
+ 
++  dbus->cancellable = g_cancellable_new ();
+   dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
+                                      "org.freedesktop.LowMemoryMonitor",
+                                      G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+@@ -142,6 +156,8 @@ g_memory_monitor_dbus_finalize (GObject *object)
+ {
+   GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object);
+ 
++  g_cancellable_cancel (dbus->cancellable);
++  g_clear_object (&dbus->cancellable);
+   g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
+   g_clear_object (&dbus->proxy);
+   g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name);
+-- 
+GitLab
+
+
+From 889bdb994fed44344a84ad01aa5633a1b1b62b19 Mon Sep 17 00:00:00 2001
+From: Patrick Griffis <pgriffis@igalia.com>
+Date: Tue, 20 Jul 2021 16:04:31 -0500
+Subject: [PATCH 4/4] Add GPowerProfileMonitor
+
+---
+ docs/reference/gio/gio-docs.xml            |   1 +
+ docs/reference/gio/gio-sections-common.txt |  18 ++
+ docs/reference/gio/meson.build             |   1 +
+ gio/gio.h                                  |   1 +
+ gio/giomodule.c                            |   7 +
+ gio/gpowerprofilemonitor.c                 | 141 ++++++++++++
+ gio/gpowerprofilemonitor.h                 |  63 ++++++
+ gio/gpowerprofilemonitordbus.c             | 240 +++++++++++++++++++++
+ gio/gpowerprofilemonitordbus.h             |  32 +++
+ gio/meson.build                            |   3 +
+ gio/tests/meson.build                      |   1 +
+ gio/tests/power-profile-monitor.c          |  79 +++++++
+ 12 files changed, 587 insertions(+)
+ create mode 100644 gio/gpowerprofilemonitor.c
+ create mode 100644 gio/gpowerprofilemonitor.h
+ create mode 100644 gio/gpowerprofilemonitordbus.c
+ create mode 100644 gio/gpowerprofilemonitordbus.h
+ create mode 100644 gio/tests/power-profile-monitor.c
+
+diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
+index a09d6d31d..b01133900 100644
+--- a/docs/reference/gio/gio-docs.xml
++++ b/docs/reference/gio/gio-docs.xml
+@@ -238,6 +238,7 @@
+         <xi:include href="xml/gmenuexporter.xml"/>
+         <xi:include href="xml/gdbusmenumodel.xml"/>
+         <xi:include href="xml/gnotification.xml"/>
++        <xi:include href="xml/gpowerprofilemonitor.xml"/>
+     </chapter>
+     <chapter id="extending">
+         <title>Extending GIO</title>
+diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt
+index 250491a42..a7addedc2 100644
+--- a/docs/reference/gio/gio-sections-common.txt
++++ b/docs/reference/gio/gio-sections-common.txt
+@@ -4247,6 +4247,24 @@ G_NETWORK_MONITOR_GET_INTERFACE
+ g_network_connectivity_get_type
+ </SECTION>
+ 
++<SECTION>
++<FILE>gpowerprofilemonitor</FILE>
++<TITLE>GPowerProfileMonitor</TITLE>
++GPowerProfileMonitor
++GPowerProfileMonitorInterface
++G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME
++g_power_profile_monitor_dup_default
++g_power_profile_monitor_get_power_saver_enabled
++<SUBSECTION Standard>
++g_power_profile_monitor_get_type
++G_TYPE_POWER_PROFILE_MONITOR
++G_POWER_PROFILE_MONITOR
++G_IS_POWER_PROFILE_MONITOR
++G_POWER_PROFILE_MONITOR_GET_INTERFACE
++G_TYPE_POWER_PROFILE_LEVEL
++g_power_profile_level_get_type
++</SECTION>
++
+ <SECTION>
+ <FILE>gmenuexporter</FILE>
+ g_dbus_connection_export_menu_model
+diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build
+index 4d0364819..fbabd25ca 100644
+--- a/docs/reference/gio/meson.build
++++ b/docs/reference/gio/meson.build
+@@ -65,6 +65,7 @@ if get_option('gtk_doc')
+     'gopenuriportal.h',
+     'gpollfilemonitor.h',
+     'gportalsupport.h',
++    'gpowerprofilemonitordbus.h',
+     'gproxyresolverportal.h',
+     'gregistrysettingsbackend.h',
+     'gresourcefile.h',
+diff --git a/gio/gio.h b/gio/gio.h
+index f5d2dd5a3..e9afab666 100644
+--- a/gio/gio.h
++++ b/gio/gio.h
+@@ -120,6 +120,7 @@
+ #include <gio/gpollableinputstream.h>
+ #include <gio/gpollableoutputstream.h>
+ #include <gio/gpollableutils.h>
++#include <gio/gpowerprofilemonitor.h>
+ #include <gio/gpropertyaction.h>
+ #include <gio/gproxy.h>
+ #include <gio/gproxyaddress.h>
+diff --git a/gio/giomodule.c b/gio/giomodule.c
+index c1d451b5c..dfd895717 100644
+--- a/gio/giomodule.c
++++ b/gio/giomodule.c
+@@ -48,6 +48,8 @@
+ #include "gmemorymonitor.h"
+ #include "gmemorymonitorportal.h"
+ #include "gmemorymonitordbus.h"
++#include "gpowerprofilemonitor.h"
++#include "gpowerprofilemonitordbus.h"
+ #ifdef G_OS_WIN32
+ #include "gregistrysettingsbackend.h"
+ #include "giowin32-priv.h"
+@@ -1077,6 +1079,7 @@ extern GType _g_network_monitor_nm_get_type (void);
+ 
+ extern GType g_memory_monitor_dbus_get_type (void);
+ extern GType g_memory_monitor_portal_get_type (void);
++extern GType g_power_profile_monitor_dbus_get_type (void);
+ 
+ #ifdef G_OS_UNIX
+ extern GType g_fdo_notification_backend_get_type (void);
+@@ -1187,6 +1190,9 @@ _g_io_modules_ensure_extension_points_registered (void)
+ 
+       ep = g_io_extension_point_register (G_MEMORY_MONITOR_EXTENSION_POINT_NAME);
+       g_io_extension_point_set_required_type (ep, G_TYPE_MEMORY_MONITOR);
++
++      ep = g_io_extension_point_register (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME);
++      g_io_extension_point_set_required_type (ep, G_TYPE_POWER_PROFILE_MONITOR);
+     }
+   
+   G_UNLOCK (registered_extensions);
+@@ -1272,6 +1278,7 @@ _g_io_modules_ensure_loaded (void)
+       g_type_ensure (g_null_settings_backend_get_type ());
+       g_type_ensure (g_memory_settings_backend_get_type ());
+       g_type_ensure (g_keyfile_settings_backend_get_type ());
++      g_type_ensure (g_power_profile_monitor_dbus_get_type ());
+ #if defined(HAVE_INOTIFY_INIT1)
+       g_type_ensure (g_inotify_file_monitor_get_type ());
+ #endif
+diff --git a/gio/gpowerprofilemonitor.c b/gio/gpowerprofilemonitor.c
+new file mode 100644
+index 000000000..f5028b3e8
+--- /dev/null
++++ b/gio/gpowerprofilemonitor.c
+@@ -0,0 +1,141 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2019 Red Hat, Inc
++ * Copyright 2021 Igalia S.L.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "config.h"
++#include "glib.h"
++#include "glibintl.h"
++
++#include "gpowerprofilemonitor.h"
++#include "ginetaddress.h"
++#include "ginetsocketaddress.h"
++#include "ginitable.h"
++#include "gioenumtypes.h"
++#include "giomodule-priv.h"
++#include "gtask.h"
++
++/**
++ * SECTION:gpowerprofilemonitor
++ * @title: GPowerProfileMonitor
++ * @short_description: Power profile monitor
++ * @include: gio/gio.h
++ *
++ * #GPowerProfileMonitor makes it possible for applications as well as OS components
++ * to monitor system power profiles and act upon them. It currently only exports
++ * whether the system is in “Power Saver” mode (known as “Low Power” mode on
++ * some systems).
++ *
++ * When in “Low Power” mode, it is recommended that applications:
++ * - disabling automatic downloads
++ * - reduce the rate of refresh from online sources such as calendar or
++ *   email synchronisation
++ * - if the application has expensive visual effects, reduce them
++ *
++ * It is also likely that OS components providing services to applications will
++ * lower their own background activity, for the sake of the system.
++ *
++ * There are a variety of tools that exist for power consumption analysis, but those
++ * usually depend on the OS and hardware used. On Linux, one could use `upower` to
++ * monitor the battery discharge rate, `powertop` to check on the background activity
++ * or activity at all), `sysprof` to inspect CPU usage, and `intel_gpu_time` to
++ * profile GPU usage.
++ *
++ * Don't forget to disconnect the #GPowerProfileMonitor::notify::power-saver-enabled
++ * signal, and unref the #GPowerProfileMonitor itself when exiting.
++ *
++ * Since: 2.70
++ */
++
++/**
++ * GPowerProfileMonitor:
++ *
++ * #GPowerProfileMonitor monitors system power profile and notifies on
++ * changes.
++ *
++ * Since: 2.70
++ */
++
++/**
++ * GPowerProfileMonitorInterface:
++ * @g_iface: The parent interface.
++ *
++ * The virtual function table for #GPowerProfileMonitor.
++ *
++ * Since: 2.70
++ */
++
++G_DEFINE_INTERFACE_WITH_CODE (GPowerProfileMonitor, g_power_profile_monitor, G_TYPE_OBJECT,
++                              g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_INITABLE))
++
++
++/**
++ * g_power_profile_monitor_dup_default:
++ *
++ * Gets a reference to the default #GPowerProfileMonitor for the system.
++ *
++ * Returns: (not nullable) (transfer full): a new reference to the default #GPowerProfileMonitor
++ *
++ * Since: 2.70
++ */
++GPowerProfileMonitor *
++g_power_profile_monitor_dup_default (void)
++{
++  return g_object_ref (_g_io_module_get_default (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME,
++                                                 "GIO_USE_POWER_PROFILE_MONITOR",
++                                                 NULL));
++}
++
++/**
++ * g_power_profile_monitor_get_power_saver_enabled:
++ * @monitor: a #GPowerProfileMonitor
++ *
++ * Gets whether the system is in “Power Saver” mode.
++ *
++ * You are expected to listen to the
++ * #GPowerProfileMonitor::notify::power-saver-enabled signal to know when the profile has
++ * changed.
++ *
++ * Returns: Whether the system is in “Power Saver” mode.
++ *
++ * Since: 2.70
++ */
++gboolean
++g_power_profile_monitor_get_power_saver_enabled (GPowerProfileMonitor *monitor)
++{
++  gboolean enabled;
++  g_object_get (monitor, "power-saver-enabled", &enabled, NULL);
++  return enabled;
++}
++
++static void
++g_power_profile_monitor_default_init (GPowerProfileMonitorInterface *iface)
++{
++  /**
++   * GPowerProfileMonitor:power-saver-enabled:
++   *
++   * Whether “Power Saver” mode is enabled on the system.
++   *
++   * Since: 2.70
++   */
++  g_object_interface_install_property (iface,
++                                       g_param_spec_boolean ("power-saver-enabled",
++                                                             "power-saver-enabled",
++                                                             "Power Saver Enabled",
++                                                             FALSE,
++                                                             G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
++}
+diff --git a/gio/gpowerprofilemonitor.h b/gio/gpowerprofilemonitor.h
+new file mode 100644
+index 000000000..0891fc3dc
+--- /dev/null
++++ b/gio/gpowerprofilemonitor.h
+@@ -0,0 +1,63 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2019 Red Hat, Inc.
++ * Copyright 2021 Igalia S.L.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef __G_POWER_PROFILE_MONITOR_H__
++#define __G_POWER_PROFILE_MONITOR_H__
++
++#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
++#error "Only <gio/gio.h> can be included directly."
++#endif
++
++#include <gio/giotypes.h>
++
++G_BEGIN_DECLS
++
++/**
++ * G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME:
++ *
++ * Extension point for power profile usage monitoring functionality.
++ * See [Extending GIO][extending-gio].
++ *
++ * Since: 2.70
++ */
++#define G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME "gio-power-profile-monitor"
++
++#define G_TYPE_POWER_PROFILE_MONITOR             (g_power_profile_monitor_get_type ())
++GLIB_AVAILABLE_IN_2_70
++G_DECLARE_INTERFACE (GPowerProfileMonitor, g_power_profile_monitor, g, power_profile_monitor, GObject)
++
++#define G_POWER_PROFILE_MONITOR(o)               (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_POWER_PROFILE_MONITOR, GPowerProfileMonitor))
++#define G_IS_POWER_PROFILE_MONITOR(o)            (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_POWER_PROFILE_MONITOR))
++#define G_POWER_PROFILE_MONITOR_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_POWER_PROFILE_MONITOR, GPowerProfileMonitorInterface))
++
++struct _GPowerProfileMonitorInterface
++{
++  /*< private >*/
++  GTypeInterface g_iface;
++};
++
++GLIB_AVAILABLE_IN_2_70
++GPowerProfileMonitor      *g_power_profile_monitor_dup_default              (void);
++
++GLIB_AVAILABLE_IN_2_70
++gboolean                   g_power_profile_monitor_get_power_saver_enabled  (GPowerProfileMonitor *monitor);
++
++G_END_DECLS
++
++#endif /* __G_POWER_PROFILE_MONITOR_H__ */
+diff --git a/gio/gpowerprofilemonitordbus.c b/gio/gpowerprofilemonitordbus.c
+new file mode 100644
+index 000000000..8bbfe3acc
+--- /dev/null
++++ b/gio/gpowerprofilemonitordbus.c
+@@ -0,0 +1,240 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2019 Red Hat, Inc.
++ * Copyrgith 2021 Igalia S.L.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "config.h"
++
++#include "gpowerprofilemonitor.h"
++#include "gpowerprofilemonitordbus.h"
++#include "gioerror.h"
++#include "ginitable.h"
++#include "giomodule-priv.h"
++#include "glibintl.h"
++#include "glib/gstdio.h"
++#include "gcancellable.h"
++#include "gdbusproxy.h"
++#include "gdbusnamewatching.h"
++
++#define G_POWER_PROFILE_MONITOR_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
++
++static void g_power_profile_monitor_dbus_iface_init (GPowerProfileMonitorInterface *iface);
++static void g_power_profile_monitor_dbus_initable_iface_init (GInitableIface *iface);
++
++struct _GPowerProfileMonitorDBus
++{
++  GObject parent_instance;
++
++  guint watch_id;
++  GCancellable *cancellable;
++  GDBusProxy *proxy;
++  gulong signal_id;
++
++  gboolean power_saver_enabled;
++};
++
++typedef enum
++{
++  PROP_POWER_SAVER_ENABLED = 1,
++} GPowerProfileMonitorDBusProperty;
++
++#define POWERPROFILES_DBUS_NAME "net.hadess.PowerProfiles"
++#define POWERPROFILES_DBUS_IFACE "net.hadess.PowerProfiles"
++#define POWERPROFILES_DBUS_PATH "/net/hadess/PowerProfiles"
++
++G_DEFINE_TYPE_WITH_CODE (GPowerProfileMonitorDBus, g_power_profile_monitor_dbus, G_TYPE_OBJECT,
++                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
++                                                g_power_profile_monitor_dbus_initable_iface_init)
++                         G_IMPLEMENT_INTERFACE (G_TYPE_POWER_PROFILE_MONITOR,
++                                                g_power_profile_monitor_dbus_iface_init)
++                         _g_io_modules_ensure_extension_points_registered ();
++                         g_io_extension_point_implement (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME,
++                                                         g_define_type_id,
++                                                         "dbus",
++                                                         30))
++
++static void
++g_power_profile_monitor_dbus_init (GPowerProfileMonitorDBus *dbus)
++{
++  dbus->power_saver_enabled = FALSE;
++}
++
++static void
++ppd_properties_changed_cb (GDBusProxy *proxy,
++                           GVariant   *changed_properties,
++                           GStrv      *invalidated_properties,
++                           gpointer    user_data)
++{
++  GPowerProfileMonitorDBus *dbus = user_data;
++  const char *active_profile;
++  gboolean enabled;
++
++  if (!g_variant_lookup (changed_properties, "ActiveProfile", "&s", &active_profile))
++    return;
++
++  enabled = g_strcmp0 (active_profile, "power-saver") == 0;
++  if (enabled == dbus->power_saver_enabled)
++    return;
++
++  dbus->power_saver_enabled = enabled;
++  g_object_notify (G_OBJECT (dbus), "power-saver-enabled");
++}
++
++static void
++ppd_proxy_cb (GObject      *source_object,
++              GAsyncResult *res,
++              gpointer      user_data)
++{
++  GPowerProfileMonitorDBus *dbus = user_data;
++  GVariant *active_profile_variant;
++  GDBusProxy *proxy;
++  GError *error = NULL;
++  const char *active_profile;
++  gboolean power_saver_enabled;
++
++  proxy = g_dbus_proxy_new_finish (res, &error);
++  if (!proxy)
++    {
++      g_debug ("GPowerProfileMonitorDBus: Failed to create PowerProfiles D-Bus proxy: %s",
++               error->message);
++      g_error_free (error);
++      return;
++    }
++
++  active_profile_variant = g_dbus_proxy_get_cached_property (proxy, "ActiveProfile");
++  if (g_variant_is_of_type (active_profile_variant, G_VARIANT_TYPE_STRING))
++    {
++      active_profile = g_variant_get_string (active_profile_variant, NULL);
++      power_saver_enabled = g_strcmp0 (active_profile, "power-saver") == 0;
++      if (power_saver_enabled != dbus->power_saver_enabled)
++        {
++          dbus->power_saver_enabled = power_saver_enabled;
++          g_object_notify (G_OBJECT (dbus), "power-saver-enabled");
++        }
++    }
++
++  dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed",
++                                      G_CALLBACK (ppd_properties_changed_cb), dbus);
++  dbus->proxy = g_steal_pointer (&proxy);
++}
++
++static void
++ppd_appeared_cb (GDBusConnection *connection,
++                 const gchar     *name,
++                 const gchar     *name_owner,
++                 gpointer         user_data)
++{
++  GPowerProfileMonitorDBus *dbus = user_data;
++
++  g_dbus_proxy_new (connection,
++                    G_DBUS_PROXY_FLAGS_NONE,
++                    NULL,
++                    POWERPROFILES_DBUS_NAME,
++                    POWERPROFILES_DBUS_PATH,
++                    POWERPROFILES_DBUS_IFACE,
++                    dbus->cancellable,
++                    ppd_proxy_cb,
++                    dbus);
++}
++
++static void
++ppd_vanished_cb (GDBusConnection *connection,
++                 const gchar     *name,
++                 gpointer         user_data)
++{
++  GPowerProfileMonitorDBus *dbus = user_data;
++
++  g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
++  g_clear_object (&dbus->proxy);
++
++  dbus->power_saver_enabled = FALSE;
++  g_object_notify (G_OBJECT (dbus), "power-saver-enabled");
++}
++
++static void
++g_power_profile_monitor_dbus_get_property (GObject    *object,
++                                           guint       prop_id,
++                                           GValue     *value,
++                                           GParamSpec *pspec)
++{
++  GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (object);
++
++  switch ((GPowerProfileMonitorDBusProperty) prop_id)
++    {
++    case PROP_POWER_SAVER_ENABLED:
++      g_value_set_boolean (value, dbus->power_saver_enabled);
++      break;
++
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++    }
++}
++
++static gboolean
++g_power_profile_monitor_dbus_initable_init (GInitable     *initable,
++                                            GCancellable  *cancellable,
++                                            GError       **error)
++{
++  GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (initable);
++
++  dbus->cancellable = g_cancellable_new ();
++  dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
++                                     POWERPROFILES_DBUS_NAME,
++                                     G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
++                                     ppd_appeared_cb,
++                                     ppd_vanished_cb,
++                                     dbus,
++                                     NULL);
++
++  return TRUE;
++}
++
++static void
++g_power_profile_monitor_dbus_finalize (GObject *object)
++{
++  GPowerProfileMonitorDBus *dbus = G_POWER_PROFILE_MONITOR_DBUS (object);
++
++  g_cancellable_cancel (dbus->cancellable);
++  g_clear_object (&dbus->cancellable);
++  g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
++  g_clear_object (&dbus->proxy);
++  g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name);
++
++  G_OBJECT_CLASS (g_power_profile_monitor_dbus_parent_class)->finalize (object);
++}
++
++static void
++g_power_profile_monitor_dbus_class_init (GPowerProfileMonitorDBusClass *nl_class)
++{
++  GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
++
++  gobject_class->get_property = g_power_profile_monitor_dbus_get_property;
++  gobject_class->finalize = g_power_profile_monitor_dbus_finalize;
++
++  g_object_class_override_property (gobject_class, PROP_POWER_SAVER_ENABLED, "power-saver-enabled");
++}
++
++static void
++g_power_profile_monitor_dbus_iface_init (GPowerProfileMonitorInterface *monitor_iface)
++{
++}
++
++static void
++g_power_profile_monitor_dbus_initable_iface_init (GInitableIface *iface)
++{
++  iface->init = g_power_profile_monitor_dbus_initable_init;
++}
+diff --git a/gio/gpowerprofilemonitordbus.h b/gio/gpowerprofilemonitordbus.h
+new file mode 100644
+index 000000000..ecf7246d1
+--- /dev/null
++++ b/gio/gpowerprofilemonitordbus.h
+@@ -0,0 +1,32 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2019 Red Hat, Inc.
++ * Copyright 2021 Igalia S.L.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef __G_POWER_PROFILE_MONITOR_DBUS_H__
++#define __G_POWER_PROFILE_MONITOR_DBUS_H__
++
++#include <glib-object.h>
++
++G_BEGIN_DECLS
++
++#define G_TYPE_POWER_PROFILE_MONITOR_DBUS         (g_power_profile_monitor_dbus_get_type ())
++G_DECLARE_FINAL_TYPE (GPowerProfileMonitorDBus, g_power_profile_monitor_dbus, G, POWER_PROFILE_MONITOR_DBUS, GObject)
++
++G_END_DECLS
++
++#endif /* __G_POWER_PROFILE_MONITOR_DBUS_H__ */
+diff --git a/gio/meson.build b/gio/meson.build
+index 49a37a7bd..d5838ed8a 100644
+--- a/gio/meson.build
++++ b/gio/meson.build
+@@ -533,6 +533,8 @@ gio_sources = files(
+   'gpollableoutputstream.c',
+   'gpollableutils.c',
+   'gpollfilemonitor.c',
++  'gpowerprofilemonitor.c',
++  'gpowerprofilemonitordbus.c',
+   'gproxy.c',
+   'gproxyaddress.c',
+   'gproxyaddressenumerator.c',
+@@ -673,6 +675,7 @@ gio_headers = files(
+   'gpollableinputstream.h',
+   'gpollableoutputstream.h',
+   'gpollableutils.h',
++  'gpowerprofilemonitor.h',
+   'gproxy.h',
+   'gproxyaddress.h',
+   'gproxyaddressenumerator.h',
+diff --git a/gio/tests/meson.build b/gio/tests/meson.build
+index 98d1401d0..fc2055101 100644
+--- a/gio/tests/meson.build
++++ b/gio/tests/meson.build
+@@ -75,6 +75,7 @@ gio_tests = {
+   'network-monitor-race' : {},
+   'permission' : {},
+   'pollable' : {'dependencies' : [libdl_dep]},
++  'power-profile-monitor' : {},
+   'proxy-test' : {},
+   'readwrite' : {},
+   'simple-async-result' : {},
+diff --git a/gio/tests/power-profile-monitor.c b/gio/tests/power-profile-monitor.c
+new file mode 100644
+index 000000000..bb32f181f
+--- /dev/null
++++ b/gio/tests/power-profile-monitor.c
+@@ -0,0 +1,79 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2021 Igalia S.L.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <gio/gio.h>
++
++static void
++test_dup_default (void)
++{
++  GPowerProfileMonitor *monitor;
++
++  monitor = g_power_profile_monitor_dup_default ();
++  g_assert_nonnull (monitor);
++  g_object_unref (monitor);
++}
++
++static void
++power_saver_enabled_cb (GPowerProfileMonitor *monitor,
++                        GParamSpec           *pspec,
++                        gpointer              user_data)
++{
++  gboolean enabled;
++
++  enabled = g_power_profile_monitor_get_power_saver_enabled (monitor);
++  g_debug ("Power Saver %s (%d)", enabled ? "enabled" : "disabled", enabled);
++}
++
++static void
++do_watch_power_profile (void)
++{
++  GPowerProfileMonitor *monitor;
++  GMainLoop *loop;
++  gulong signal_id;
++
++  monitor = g_power_profile_monitor_dup_default ();
++  signal_id = g_signal_connect (G_OBJECT (monitor), "notify::power-saver-enabled",
++		                G_CALLBACK (power_saver_enabled_cb), NULL);
++
++  loop = g_main_loop_new (NULL, TRUE);
++  g_main_loop_run (loop);
++
++  g_signal_handler_disconnect (monitor, signal_id);
++  g_object_unref (monitor);
++  g_main_loop_unref (loop);
++}
++
++int
++main (int argc, char **argv)
++{
++  int ret;
++
++  if (argc == 2 && !strcmp (argv[1], "--watch"))
++    {
++      do_watch_power_profile ();
++      return 0;
++    }
++
++  g_test_init (&argc, &argv, NULL);
++
++  g_test_add_func ("/power-profile-monitor/default", test_dup_default);
++
++  ret = g_test_run ();
++
++  return ret;
++}
+-- 
+GitLab
+
diff --git a/base/rx/rx-glib2/2222.patch b/base/rx/rx-glib2/2222.patch
new file mode 100644
index 0000000..0b6106f
--- /dev/null
+++ b/base/rx/rx-glib2/2222.patch
@@ -0,0 +1,739 @@
+From 9645cbffa8ba1a08b73fdae50b31125d11aa5684 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Mon, 9 Aug 2021 23:19:17 +0200
+Subject: [PATCH 1/4] gio: Add portal version of GPowerProfileMonitor
+
+---
+ docs/reference/gio/meson.build   |   1 +
+ gio/giomodule.c                  |   2 +
+ gio/gpowerprofilemonitorportal.c | 182 +++++++++++++++++++++++++++++++
+ gio/gpowerprofilemonitorportal.h |  31 ++++++
+ gio/meson.build                  |   1 +
+ 5 files changed, 217 insertions(+)
+ create mode 100644 gio/gpowerprofilemonitorportal.c
+ create mode 100644 gio/gpowerprofilemonitorportal.h
+
+diff --git a/docs/reference/gio/meson.build b/docs/reference/gio/meson.build
+index fbabd25ca..9aaafeed5 100644
+--- a/docs/reference/gio/meson.build
++++ b/docs/reference/gio/meson.build
+@@ -66,6 +66,7 @@ if get_option('gtk_doc')
+     'gpollfilemonitor.h',
+     'gportalsupport.h',
+     'gpowerprofilemonitordbus.h',
++    'gpowerprofilemonitorportal.h',
+     'gproxyresolverportal.h',
+     'gregistrysettingsbackend.h',
+     'gresourcefile.h',
+diff --git a/gio/giomodule.c b/gio/giomodule.c
+index dfd895717..d34037a45 100644
+--- a/gio/giomodule.c
++++ b/gio/giomodule.c
+@@ -50,6 +50,7 @@
+ #include "gmemorymonitordbus.h"
+ #include "gpowerprofilemonitor.h"
+ #include "gpowerprofilemonitordbus.h"
++#include "gpowerprofilemonitorportal.h"
+ #ifdef G_OS_WIN32
+ #include "gregistrysettingsbackend.h"
+ #include "giowin32-priv.h"
+@@ -1305,6 +1306,7 @@ _g_io_modules_ensure_loaded (void)
+       g_type_ensure (g_memory_monitor_dbus_get_type ());
+       g_type_ensure (g_memory_monitor_portal_get_type ());
+       g_type_ensure (g_network_monitor_portal_get_type ());
++      g_type_ensure (g_power_profile_monitor_portal_get_type ());
+       g_type_ensure (g_proxy_resolver_portal_get_type ());
+ #endif
+ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
+diff --git a/gio/gpowerprofilemonitorportal.c b/gio/gpowerprofilemonitorportal.c
+new file mode 100644
+index 000000000..bb1b4fd15
+--- /dev/null
++++ b/gio/gpowerprofilemonitorportal.c
+@@ -0,0 +1,182 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2021 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "config.h"
++
++#include "gpowerprofilemonitor.h"
++#include "gpowerprofilemonitorportal.h"
++#include "gdbuserror.h"
++#include "gdbusproxy.h"
++#include "ginitable.h"
++#include "gioerror.h"
++#include "giomodule-priv.h"
++#include "gportalsupport.h"
++
++#define G_POWER_PROFILE_MONITOR_PORTAL_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
++
++static void g_power_profile_monitor_portal_iface_init (GPowerProfileMonitorInterface *iface);
++static void g_power_profile_monitor_portal_initable_iface_init (GInitableIface *iface);
++
++typedef enum
++{
++  PROP_POWER_SAVER_ENABLED = 1,
++} GPowerProfileMonitorPortalProperty;
++
++struct _GPowerProfileMonitorPortal
++{
++  GObject parent_instance;
++
++  GDBusProxy *proxy;
++  gulong signal_id;
++  gboolean power_saver_enabled;
++};
++
++G_DEFINE_TYPE_WITH_CODE (GPowerProfileMonitorPortal, g_power_profile_monitor_portal, G_TYPE_OBJECT,
++                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
++                                                g_power_profile_monitor_portal_initable_iface_init)
++                         G_IMPLEMENT_INTERFACE (G_TYPE_POWER_PROFILE_MONITOR,
++                                                g_power_profile_monitor_portal_iface_init)
++                         _g_io_modules_ensure_extension_points_registered ();
++                         g_io_extension_point_implement (G_POWER_PROFILE_MONITOR_EXTENSION_POINT_NAME,
++                                                         g_define_type_id,
++                                                         "portal",
++                                                         40))
++
++static void
++g_power_profile_monitor_portal_init (GPowerProfileMonitorPortal *portal)
++{
++}
++
++static void
++proxy_properties_changed (GDBusProxy *proxy,
++                          GVariant   *changed_properties,
++                          GStrv       invalidated_properties,
++                          gpointer    user_data)
++{
++  GPowerProfileMonitorPortal *ppm = user_data;
++  gboolean power_saver_enabled;
++
++  if (!g_variant_lookup (changed_properties, "power-saver-enabled", "b", &power_saver_enabled))
++    return;
++
++  if (power_saver_enabled == ppm->power_saver_enabled)
++    return;
++
++  ppm->power_saver_enabled = power_saver_enabled;
++  g_object_notify (G_OBJECT (ppm), "power-saver-enabled");
++}
++
++static void
++g_power_profile_monitor_portal_get_property (GObject    *object,
++                                             guint       prop_id,
++                                             GValue     *value,
++                                             GParamSpec *pspec)
++{
++  GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (object);
++
++  switch ((GPowerProfileMonitorPortalProperty) prop_id)
++    {
++    case PROP_POWER_SAVER_ENABLED:
++      g_value_set_boolean (value, ppm->power_saver_enabled);
++      break;
++
++    default:
++      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
++    }
++}
++
++static gboolean
++g_power_profile_monitor_portal_initable_init (GInitable     *initable,
++                                              GCancellable  *cancellable,
++                                              GError       **error)
++{
++  GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (initable);
++  GDBusProxy *proxy;
++  gchar *name_owner;
++
++  if (!glib_should_use_portal ())
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Not using portals");
++      return FALSE;
++    }
++
++  proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
++                                         G_DBUS_PROXY_FLAGS_NONE,
++                                         NULL,
++                                         "org.freedesktop.portal.Desktop",
++                                         "/org/freedesktop/portal/desktop",
++                                         "org.freedesktop.portal.PowerProfileMonitor",
++                                         cancellable,
++                                         error);
++  if (!proxy)
++    return FALSE;
++
++  name_owner = g_dbus_proxy_get_name_owner (proxy);
++
++  if (name_owner == NULL)
++    {
++      g_object_unref (proxy);
++      g_set_error (error,
++                   G_DBUS_ERROR,
++                   G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++                   "Desktop portal not found");
++      return FALSE;
++    }
++
++  g_free (name_owner);
++
++  ppm->signal_id = g_signal_connect (proxy, "g-properties-changed",
++                                     G_CALLBACK (proxy_properties_changed), ppm);
++
++  ppm->proxy = g_steal_pointer (&proxy);
++
++  return TRUE;
++}
++
++static void
++g_power_profile_monitor_portal_finalize (GObject *object)
++{
++  GPowerProfileMonitorPortal *ppm = G_POWER_PROFILE_MONITOR_PORTAL (object);
++
++  g_clear_signal_handler (&ppm->signal_id, ppm->proxy);
++  g_clear_object (&ppm->proxy);
++
++  G_OBJECT_CLASS (g_power_profile_monitor_portal_parent_class)->finalize (object);
++}
++
++static void
++g_power_profile_monitor_portal_class_init (GPowerProfileMonitorPortalClass *nl_class)
++{
++  GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
++
++  gobject_class->get_property = g_power_profile_monitor_portal_get_property;
++  gobject_class->finalize  = g_power_profile_monitor_portal_finalize;
++
++  g_object_class_override_property (gobject_class, PROP_POWER_SAVER_ENABLED, "power-saver-enabled");
++}
++
++static void
++g_power_profile_monitor_portal_iface_init (GPowerProfileMonitorInterface *monitor_iface)
++{
++}
++
++static void
++g_power_profile_monitor_portal_initable_iface_init (GInitableIface *iface)
++{
++  iface->init = g_power_profile_monitor_portal_initable_init;
++}
+diff --git a/gio/gpowerprofilemonitorportal.h b/gio/gpowerprofilemonitorportal.h
+new file mode 100644
+index 000000000..b91a14610
+--- /dev/null
++++ b/gio/gpowerprofilemonitorportal.h
+@@ -0,0 +1,31 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright 2021 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef __G_POWER_PROFILE_MONITOR_PORTAL_H__
++#define __G_POWER_PROFILE_MONITOR_PORTAL_H__
++
++#include <glib-object.h>
++
++G_BEGIN_DECLS
++
++#define G_TYPE_POWER_PROFILE_MONITOR_PORTAL         (g_power_profile_monitor_portal_get_type ())
++G_DECLARE_FINAL_TYPE (GPowerProfileMonitorPortal, g_power_profile_monitor_portal, G, POWER_PROFILE_MONITOR_PORTAL, GObject)
++
++G_END_DECLS
++
++#endif /* __G_POWER_PROFILE_MONITOR_PORTAL_H__ */
+diff --git a/gio/meson.build b/gio/meson.build
+index d5838ed8a..ac3373f2b 100644
+--- a/gio/meson.build
++++ b/gio/meson.build
+@@ -383,6 +383,7 @@ if host_system != 'windows'
+     'gopenuriportal.c',
+     'gmemorymonitorportal.c',
+     'gnetworkmonitorportal.c',
++    'gpowerprofilemonitorportal.c',
+     'gproxyresolverportal.c',
+     'gtrashportal.c',
+     'gportalsupport.c',
+-- 
+GitLab
+
+
+From 18eb29897d80bf662d58bd11a89617ddd7ebfeed Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Tue, 10 Aug 2021 10:58:53 +0200
+Subject: [PATCH 2/4] gio: Add GPowerProfileMonitor tests
+
+Tests both the portal and direct D-Bus variants.
+---
+ gio/tests/meson.build                        |  14 ++-
+ gio/tests/power-profile-monitor-dbus.py.in   | 107 ++++++++++++++++
+ gio/tests/power-profile-monitor-portal.py.in | 126 +++++++++++++++++++
+ 3 files changed, 241 insertions(+), 6 deletions(-)
+ create mode 100755 gio/tests/power-profile-monitor-dbus.py.in
+ create mode 100755 gio/tests/power-profile-monitor-portal.py.in
+
+diff --git a/gio/tests/meson.build b/gio/tests/meson.build
+index fc2055101..5dbfb8e60 100644
+--- a/gio/tests/meson.build
++++ b/gio/tests/meson.build
+@@ -541,27 +541,29 @@ if installed_tests_enabled
+   install_subdir('static-link', install_dir : installed_tests_execdir)
+   install_data('static-link.py', install_dir : installed_tests_execdir)
+ 
+-  memory_monitor_tests = [
++  monitor_tests = [
+     'memory-monitor-dbus',
+     'memory-monitor-portal',
++    'power-profile-monitor-dbus',
++    'power-profile-monitor-portal'
+   ]
+ 
+-  foreach memory_monitor_test : memory_monitor_tests
++  foreach monitor_test : monitor_tests
+     cdata = configuration_data()
+     cdata.set('installed_tests_dir', installed_tests_execdir)
+-    cdata.set('program', memory_monitor_test + '.py')
++    cdata.set('program', monitor_test + '.py')
+     cdata.set('env', '')
+     configure_file(
+       input: installed_tests_template_tap,
+-      output: memory_monitor_test + '.test',
++      output: monitor_test + '.test',
+       install_dir: installed_tests_metadir,
+       configuration: cdata
+     )
+     cdata = configuration_data()
+     cdata.set('libexecdir', join_paths(glib_prefix, get_option('libexecdir')))
+     configure_file(
+-      input: memory_monitor_test + '.py.in',
+-      output: memory_monitor_test + '.py',
++      input: monitor_test + '.py.in',
++      output: monitor_test + '.py',
+       install_dir : installed_tests_execdir,
+       configuration: cdata,
+     )
+diff --git a/gio/tests/power-profile-monitor-dbus.py.in b/gio/tests/power-profile-monitor-dbus.py.in
+new file mode 100755
+index 000000000..06e594f4a
+--- /dev/null
++++ b/gio/tests/power-profile-monitor-dbus.py.in
+@@ -0,0 +1,107 @@
++#!/usr/bin/python3
++
++# This program is free software; you can redistribute it and/or modify it under
++# the terms of the GNU Lesser General Public License as published by the Free
++# Software Foundation; either version 3 of the License, or (at your option) any
++# later version.  See http://www.gnu.org/copyleft/lgpl.html for the full text
++# of the license.
++
++__author__ = 'Bastien Nocera'
++__email__ = 'hadess@hadess.net'
++__copyright__ = '(c) 2019, 2021 Red Hat Inc.'
++__license__ = 'LGPL 3+'
++
++import unittest
++import sys
++import subprocess
++import fcntl
++import os
++import time
++
++import taptestrunner
++
++try:
++    # Do all non-standard imports here so we can skip the tests if any
++    # needed packages are not available.
++    import dbus
++    import dbus.mainloop.glib
++    import dbusmock
++    from gi.repository import GLib
++    from gi.repository import Gio
++
++    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
++
++    class TestPowerProfileMonitor(dbusmock.DBusTestCase):
++        '''Test GPowerProfileMonitorDBus'''
++
++        @classmethod
++        def setUpClass(klass):
++            klass.start_system_bus()
++            klass.dbus_con = klass.get_dbus(True)
++
++        def setUp(self):
++            try:
++                Gio.PowerProfileMonitor
++            except AttributeError:
++                raise unittest.SkipTest('Power Profile Monitor not in '
++                                        'introspection data. Requires '
++                                        'GObject-Introspection ≥ 1.63.2') # FIXME version
++            try:
++                (self.p_mock, self.obj_ppd) = self.spawn_server_template(
++                    'power_profiles_daemon', {}, stdout=subprocess.PIPE)
++            except ModuleNotFoundError:
++                raise unittest.SkipTest("power-profiles-daemon dbusmock template not "
++                                        "found. Requires dbusmock > 0.23.1.") # FIXME version
++            # set log to nonblocking
++            flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
++            fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
++            self.power_saver_enabled = False
++            self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE)
++            self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default()
++            self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb)
++            self.mainloop = GLib.MainLoop()
++            self.main_context = self.mainloop.get_context()
++
++        def tearDown(self):
++            self.p_mock.terminate()
++            self.p_mock.wait()
++
++        def assertEventually(self, condition, message=None, timeout=50):
++            '''Assert that condition function eventually returns True.
++
++            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
++            printed on failure.
++            '''
++            while timeout >= 0:
++                context = GLib.MainContext.default()
++                while context.iteration(False):
++                    pass
++                if condition():
++                    break
++                timeout -= 1
++                time.sleep(0.1)
++            else:
++                self.fail(message or 'timed out waiting for ' + str(condition))
++
++        def power_saver_enabled_cb(self, spec, data):
++            self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled()
++            self.main_context.wakeup()
++
++        def test_power_profile_power_saver_enabled(self):
++            '''power-saver-enabled property'''
++
++            self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False)
++            self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1))
++            self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10)
++
++            self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1))
++            self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10)
++
++except ImportError as e:
++    @unittest.skip("Cannot import %s" % e.name)
++    class TestPowerProfileMonitor(unittest.TestCase):
++        def test_power_profile_power_saver_enabled(self):
++            pass
++
++if __name__ == '__main__':
++    unittest.main(testRunner=taptestrunner.TAPTestRunner())
+diff --git a/gio/tests/power-profile-monitor-portal.py.in b/gio/tests/power-profile-monitor-portal.py.in
+new file mode 100755
+index 000000000..960a62232
+--- /dev/null
++++ b/gio/tests/power-profile-monitor-portal.py.in
+@@ -0,0 +1,126 @@
++#!/usr/bin/python3
++
++# This program is free software; you can redistribute it and/or modify it under
++# the terms of the GNU Lesser General Public License as published by the Free
++# Software Foundation; either version 3 of the License, or (at your option) any
++# later version.  See http://www.gnu.org/copyleft/lgpl.html for the full text
++# of the license.
++
++__author__ = 'Bastien Nocera'
++__email__ = 'hadess@hadess.net'
++__copyright__ = '(c) 2021 Red Hat Inc.'
++__license__ = 'LGPL 3+'
++
++import unittest
++import sys
++import subprocess
++import fcntl
++import os
++import time
++
++import taptestrunner
++
++try:
++    # Do all non-standard imports here so we can skip the tests if any
++    # needed packages are not available.
++    import dbus
++    import dbus.mainloop.glib
++    import dbusmock
++    from gi.repository import GLib
++    from gi.repository import Gio
++
++    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
++
++    # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal")
++    XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal"
++
++    class TestPowerProfileMonitorPortal(dbusmock.DBusTestCase):
++        '''Test GPowerProfileMonitorPortal'''
++
++        @classmethod
++        def setUpClass(klass):
++            klass.start_system_bus()
++            klass.dbus_con = klass.get_dbus(True)
++            # Start session bus so that xdg-desktop-portal can run on it
++            klass.start_session_bus()
++
++        def setUp(self):
++            try:
++                Gio.PowerProfileMonitor
++            except AttributeError:
++                raise unittest.SkipTest('Power Profile Monitor not in '
++                                        'introspection data. Requires '
++                                        'GObject-Introspection > 1.69.0')
++            try:
++                (self.p_mock, self.obj_ppd) = self.spawn_server_template(
++                    'power_profiles_daemon', {}, stdout=subprocess.PIPE)
++            except ModuleNotFoundError:
++                raise unittest.SkipTest("power-profiles-daemon dbusmock template not "
++                                        "found. Requires dbusmock > 0.23.1.")
++            # set log to nonblocking
++            flags = fcntl.fcntl(self.p_mock.stdout, fcntl.F_GETFL)
++            fcntl.fcntl(self.p_mock.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
++            self.power_saver_enabled = False
++            self.dbus_props = dbus.Interface(self.obj_ppd, dbus.PROPERTIES_IFACE)
++            try:
++                self.xdp = subprocess.Popen([XDG_DESKTOP_PORTAL_PATH])
++            except FileNotFoundError:
++                raise unittest.SkipTest("xdg-desktop-portal not available")
++
++            try:
++                self.wait_for_bus_object('org.freedesktop.portal.Desktop',
++                                        '/org/freedesktop/portal/desktop')
++            except:
++                raise
++            # subprocess.Popen(['gdbus', 'monitor', '--session', '--dest', 'org.freedesktop.portal.Desktop'])
++
++            os.environ['GTK_USE_PORTAL'] = "1"
++            self.power_profile_monitor = Gio.PowerProfileMonitor.dup_default()
++            assert("GPowerProfileMonitorPortal" in str(self.power_profile_monitor))
++            self.power_profile_monitor.connect("notify::power-saver-enabled", self.power_saver_enabled_cb)
++            self.mainloop = GLib.MainLoop()
++            self.main_context = self.mainloop.get_context()
++
++        def tearDown(self):
++            self.p_mock.terminate()
++            self.p_mock.wait()
++
++        def assertEventually(self, condition, message=None, timeout=50):
++            '''Assert that condition function eventually returns True.
++
++            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
++            printed on failure.
++            '''
++            while timeout >= 0:
++                context = GLib.MainContext.default()
++                while context.iteration(False):
++                    pass
++                if condition():
++                    break
++                timeout -= 1
++                time.sleep(0.1)
++            else:
++                self.fail(message or 'timed out waiting for ' + str(condition))
++
++        def power_saver_enabled_cb(self, spec, data):
++            self.power_saver_enabled = self.power_profile_monitor.get_power_saver_enabled()
++            self.main_context.wakeup()
++
++        def test_power_profile_power_saver_enabled_portal(self):
++            '''power-saver-enabled property'''
++
++            self.assertEqual(self.power_profile_monitor.get_power_saver_enabled(), False)
++            self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('power-saver', variant_level=1))
++            self.assertEventually(lambda: self.power_saver_enabled == True, "power-saver didn't become enabled", 10)
++
++            self.dbus_props.Set('net.hadess.PowerProfiles', 'ActiveProfile', dbus.String('balanced', variant_level=1))
++            self.assertEventually(lambda: self.power_saver_enabled == False, "power-saver didn't become disabled", 10)
++
++except ImportError as e:
++    @unittest.skip("Cannot import %s" % e.name)
++    class TestPowerProfileMonitorPortal(unittest.TestCase):
++        def test_power_profile_power_saver_enabled_portal(self):
++            pass
++
++if __name__ == '__main__':
++    unittest.main(testRunner=taptestrunner.TAPTestRunner())
+-- 
+GitLab
+
+
+From 66acea8418eb3d8e46bb6f93dc0c3f13a1f7822b Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Wed, 11 Aug 2021 15:37:40 +0200
+Subject: [PATCH 3/4] gio: Remove left-over debug statement from memory monitor
+ portal test
+
+---
+ gio/tests/memory-monitor-portal.py.in | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in
+index cb4a960eb..f5fd2283f 100755
+--- a/gio/tests/memory-monitor-portal.py.in
++++ b/gio/tests/memory-monitor-portal.py.in
+@@ -31,7 +31,6 @@ try:
+ 
+     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ 
+-    # XDG_DESKTOP_PORTAL_PATH = os.path.expanduser("~/.cache/jhbuild/build/xdg-desktop-portal/xdg-desktop-portal")
+     XDG_DESKTOP_PORTAL_PATH = "@libexecdir@/xdg-desktop-portal"
+ 
+     class TestLowMemoryMonitorPortal(dbusmock.DBusTestCase):
+-- 
+GitLab
+
+
+From 2e9842cafc73a7fb94cfde7937e125e1a91f35f8 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Wed, 11 Aug 2021 15:38:12 +0200
+Subject: [PATCH 4/4] gio: Simplify memory monitor tests by using
+ assertEventually() helper
+
+assertEventually is a helper used in a number of projects that use
+dbusmock.
+
+See https://github.com/martinpitt/python-dbusmock/issues/82
+---
+ gio/tests/memory-monitor-dbus.py.in   | 31 ++++++++++++++++-----------
+ gio/tests/memory-monitor-portal.py.in | 31 ++++++++++++++++-----------
+ 2 files changed, 38 insertions(+), 24 deletions(-)
+
+diff --git a/gio/tests/memory-monitor-dbus.py.in b/gio/tests/memory-monitor-dbus.py.in
+index 7823e7309..e8ac28faf 100755
+--- a/gio/tests/memory-monitor-dbus.py.in
++++ b/gio/tests/memory-monitor-dbus.py.in
+@@ -66,6 +66,23 @@ try:
+             self.p_mock.terminate()
+             self.p_mock.wait()
+ 
++        def assertEventually(self, condition, message=None, timeout=50):
++            '''Assert that condition function eventually returns True.
++
++            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
++            printed on failure.
++            '''
++            while timeout >= 0:
++                context = GLib.MainContext.default()
++                while context.iteration(False):
++                    pass
++                if condition():
++                    break
++                timeout -= 1
++                time.sleep(0.1)
++            else:
++                self.fail(message or 'timed out waiting for ' + str(condition))
++
+         def memory_warning_cb(self, monitor, level):
+             self.last_warning = level
+             self.main_context.wakeup()
+@@ -82,21 +99,11 @@ try:
+ 
+             self.dbusmock.EmitWarning(100)
+             # Wait 2 seconds or until warning
+-            timeout = 2
+-            while timeout > 0 and self.last_warning != 100:
+-                time.sleep(0.5)
+-                timeout -= 0.5
+-                self.main_context.iteration(False)
+-            self.assertEqual(self.last_warning, 100)
++            self.assertEventually(self.last_warning == 100, "'100' low-memory warning not received", 20)
+ 
+             self.dbusmock.EmitWarning(255)
+             # Wait 2 seconds or until warning
+-            timeout = 2
+-            while timeout > 0 and self.last_warning != 255:
+-                time.sleep(0.5)
+-                timeout -= 0.5
+-                self.main_context.iteration(False)
+-            self.assertEqual(self.last_warning, 255)
++            self.assertEventually(self.last_warning == 255, "'255' low-memory warning not received", 20)
+ 
+ except ImportError as e:
+     @unittest.skip("Cannot import %s" % e.name)
+diff --git a/gio/tests/memory-monitor-portal.py.in b/gio/tests/memory-monitor-portal.py.in
+index f5fd2283f..36d5094d3 100755
+--- a/gio/tests/memory-monitor-portal.py.in
++++ b/gio/tests/memory-monitor-portal.py.in
+@@ -84,6 +84,23 @@ try:
+             self.p_mock.terminate()
+             self.p_mock.wait()
+ 
++        def assertEventually(self, condition, message=None, timeout=50):
++            '''Assert that condition function eventually returns True.
++
++            Timeout is in deciseconds, defaulting to 50 (5 seconds). message is
++            printed on failure.
++            '''
++            while timeout >= 0:
++                context = GLib.MainContext.default()
++                while context.iteration(False):
++                    pass
++                if condition():
++                    break
++                timeout -= 1
++                time.sleep(0.1)
++            else:
++                self.fail(message or 'timed out waiting for ' + str(condition))
++
+         def portal_memory_warning_cb(self, monitor, level):
+             self.last_warning = level
+             self.main_context.wakeup()
+@@ -100,21 +117,11 @@ try:
+ 
+             self.dbusmock.EmitWarning(100)
+             # Wait 2 seconds or until warning
+-            timeout = 2
+-            while timeout > 0 and self.last_warning != 100:
+-                time.sleep(0.5)
+-                timeout -= 0.5
+-                self.main_context.iteration(False)
+-            self.assertEqual(self.last_warning, 100)
++            self.assertEventually(self.last_warning == 100, "'100' low-memory warning not received", 20)
+ 
+             self.dbusmock.EmitWarning(255)
+             # Wait 2 seconds or until warning
+-            timeout = 2
+-            while timeout > 0 and self.last_warning != 255:
+-                time.sleep(0.5)
+-                timeout -= 0.5
+-                self.main_context.iteration(False)
+-            self.assertEqual(self.last_warning, 255)
++            self.assertEventually(self.last_warning == 255, "'255' low-memory warning not received", 20)
+ 
+ except ImportError as e:
+     @unittest.skip("Cannot import %s" % e.name)
+-- 
+GitLab
+
diff --git a/base/rx/rx-glib2/2244.patch b/base/rx/rx-glib2/2244.patch
new file mode 100644
index 0000000..cb55031
--- /dev/null
+++ b/base/rx/rx-glib2/2244.patch
@@ -0,0 +1,49 @@
+From b6036e23b0477be147211b4e21a6b49cd4d6c9a0 Mon Sep 17 00:00:00 2001
+From: Jamie Bainbridge <jamie.bainbridge@gmail.com>
+Date: Wed, 8 Sep 2021 12:08:17 +1000
+Subject: [PATCH] gutils: Avoid segfault in g_get_user_database_entry
+
+g_get_user_database_entry() uses variable pwd to store the contents of
+the call to getpwnam_r(), then capitalises the first letter of pw_name
+with g_ascii_toupper (pw->pw_name[0]).
+
+However, as per the getpwnam manpage, the result of that call "may point
+to a static area". When this happens, GLib is trying to edit static
+memory which belongs to a shared library, so segfaults.
+
+Instead, copy pw_name off to a temporary variable, set uppercase on
+that variable, and use the variable to join into the desired string.
+Free the new variable after it is no longer needed.
+
+Signed-off-by: Jamie Bainbridge <jamie.bainbridge@gmail.com>
+---
+ glib/gutils.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/glib/gutils.c b/glib/gutils.c
+index b7a2113d4..4bccd7229 100644
+--- a/glib/gutils.c
++++ b/glib/gutils.c
+@@ -692,14 +692,17 @@ g_get_user_database_entry (void)
+               {
+                 gchar **gecos_fields;
+                 gchar **name_parts;
++                gchar *uppercase_pw_name;
+ 
+                 /* split the gecos field and substitute '&' */
+                 gecos_fields = g_strsplit (pw->pw_gecos, ",", 0);
+                 name_parts = g_strsplit (gecos_fields[0], "&", 0);
+-                pw->pw_name[0] = g_ascii_toupper (pw->pw_name[0]);
+-                e.real_name = g_strjoinv (pw->pw_name, name_parts);
++                uppercase_pw_name = g_strdup (pw->pw_name);
++                uppercase_pw_name[0] = g_ascii_toupper (uppercase_pw_name[0]);
++                e.real_name = g_strjoinv (uppercase_pw_name, name_parts);
+                 g_strfreev (gecos_fields);
+                 g_strfreev (name_parts);
++                g_free (uppercase_pw_name);
+               }
+ #endif
+ 
+-- 
+GitLab
+
diff --git a/base/rx/rx-glib2/2291.patch b/base/rx/rx-glib2/2291.patch
new file mode 100644
index 0000000..2e4e44d
--- /dev/null
+++ b/base/rx/rx-glib2/2291.patch
@@ -0,0 +1,129 @@
+From f419966808475cb6c0f0ba2f63967876218ffdaf Mon Sep 17 00:00:00 2001
+From: Julian Andres Klode <julian.klode@canonical.com>
+Date: Tue, 12 Oct 2021 12:01:50 +0200
+Subject: [PATCH 1/2] gnetworkmonitornm: Stop using removed PropertiesChanged
+ signal
+
+Use the org.freedesktop.DBus.Properties interface to listen
+to PropertiesChanged signals on /org/freedesktop/NetworkManager.
+
+NetworkManager used to provide its own legacy PropertiesChanged
+signal, but that was dropped in
+https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/853
+
+This requires NetworkManager >= 1.2 (2016)
+
+Fixes: #2505
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1946196
+---
+ gio/gnetworkmonitornm.c | 29 +++++++----------------------
+ 1 file changed, 7 insertions(+), 22 deletions(-)
+
+diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c
+index 5a36a0ba1..6a6d1d666 100644
+--- a/gio/gnetworkmonitornm.c
++++ b/gio/gnetworkmonitornm.c
+@@ -267,29 +267,14 @@ update_cached_property (GDBusProxy   *proxy,
+ }
+ 
+ static void
+-proxy_signal_cb (GDBusProxy        *proxy,
+-                 const gchar       *sender_name,
+-                 const gchar       *signal_name,
+-                 GVariant          *parameters,
+-                 GNetworkMonitorNM *nm)
++proxy_properties_changed_cb (GDBusProxy        *proxy,
++                             GVariant          *changed_properties,
++                             GStrv              invalidated_properties,
++                             GNetworkMonitorNM *nm)
+ {
+-  GVariant *asv;
+   GVariantDict *dict;
+ 
+-  if (g_strcmp0 (signal_name, "PropertiesChanged") != 0)
+-    return;
+-
+-  g_variant_get (parameters, "(@a{sv})", &asv);
+-  if (!asv)
+-    return;
+-
+-  dict = g_variant_dict_new (asv);
+-  g_variant_unref (asv);
+-  if (!dict)
+-    {
+-      g_warning ("Failed to handle PropertiesChanged signal from NetworkManager");
+-      return;
+-    }
++  dict = g_variant_dict_new (changed_properties);
+ 
+   update_cached_property (nm->priv->proxy, "Connectivity", dict);
+ 
+@@ -361,8 +346,8 @@ g_network_monitor_nm_initable_init (GInitable     *initable,
+       return FALSE;
+     }
+ 
+-  nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal",
+-                                          G_CALLBACK (proxy_signal_cb), nm);
++  nm->priv->signal_id = g_signal_connect (G_OBJECT (proxy), "g-properties-changed",
++                                          G_CALLBACK (proxy_properties_changed_cb), nm);
+   nm->priv->proxy = proxy;
+   sync_properties (nm, FALSE);
+ 
+-- 
+GitLab
+
+
+From 643fc7ea49e818310f6b3f6e4ebe621c7a4d6bd7 Mon Sep 17 00:00:00 2001
+From: Julian Andres Klode <julian.klode@canonical.com>
+Date: Tue, 12 Oct 2021 17:31:42 +0200
+Subject: [PATCH 2/2] gnetworkmonitornm: Do not re-update cached property
+
+GDBusProxy already takes care of updating the cached property
+before emitting the signal, so there is no need to do this
+a second time ourselves.
+---
+ gio/gnetworkmonitornm.c | 22 ----------------------
+ 1 file changed, 22 deletions(-)
+
+diff --git a/gio/gnetworkmonitornm.c b/gio/gnetworkmonitornm.c
+index 6a6d1d666..a8040fb36 100644
+--- a/gio/gnetworkmonitornm.c
++++ b/gio/gnetworkmonitornm.c
+@@ -252,34 +252,12 @@ sync_properties (GNetworkMonitorNM *nm,
+     }
+ }
+ 
+-static void
+-update_cached_property (GDBusProxy   *proxy,
+-                        const char   *property_name,
+-                        GVariantDict *dict)
+-{
+-  GVariant *v;
+-
+-  v = g_variant_dict_lookup_value (dict, property_name, NULL);
+-  if (!v)
+-    return;
+-  g_dbus_proxy_set_cached_property (proxy, property_name, v);
+-  g_variant_unref (v);
+-}
+-
+ static void
+ proxy_properties_changed_cb (GDBusProxy        *proxy,
+                              GVariant          *changed_properties,
+                              GStrv              invalidated_properties,
+                              GNetworkMonitorNM *nm)
+ {
+-  GVariantDict *dict;
+-
+-  dict = g_variant_dict_new (changed_properties);
+-
+-  update_cached_property (nm->priv->proxy, "Connectivity", dict);
+-
+-  g_variant_dict_unref (dict);
+-
+   sync_properties (nm, TRUE);
+ }
+ 
+-- 
+GitLab
+
diff --git a/base/rx/rx-glib2/2408.patch b/base/rx/rx-glib2/2408.patch
new file mode 100644
index 0000000..90d8953
--- /dev/null
+++ b/base/rx/rx-glib2/2408.patch
@@ -0,0 +1,391 @@
+From 0bbd63bf1945c6f3e1c88232521e1618c21d44f2 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Thu, 23 Dec 2021 17:45:51 +0000
+Subject: [PATCH 1/4] gmain: Use waitid() on pidfds rather than a global
+ SIGCHLD handler
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When the system supports it (as all Linux kernels ≥ 5.3 should), it’s
+preferable to use `pidfd_open()` and `waitid()` to be notified of
+child processes exiting or being signalled, rather than installing a
+default `SIGCHLD` handler.
+
+A default `SIGCHLD` handler is global, and can never interact well with
+other code (from the application or other libraries) which also wants to
+install a `SIGCHLD` handler.
+
+This use of `pidfd_open()` is racy (the PID may be reused between
+`g_child_watch_source_new()` being called and `pidfd_open()` being
+called), so it doesn’t improve behaviour there. For that, we’d need
+continuous use of pidfds throughout GLib, from fork/spawn time until
+here. See #1866 for that.
+
+The use of `waitid()` to get the process exit status could be expanded
+in future to also work for stopped or continued processes (as per #175)
+by adding `WSTOPPED | WCONTINUED` into the flags. That’s a behaviour
+change which is outside the strict scope of adding pidfd support,
+though.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #1866
+Fixes: #2216
+---
+ glib/gmain.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++---
+ meson.build  |  14 ++++++
+ 2 files changed, 125 insertions(+), 6 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 15581ee7a..e9965f7f6 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -67,6 +67,12 @@
+ #include <errno.h>
+ #include <string.h>
+ 
++#ifdef HAVE_PIDFD
++#include <sys/syscall.h>
++#include <sys/wait.h>
++#include <linux/wait.h>  /* P_PIDFD */
++#endif  /* HAVE_PIDFD */
++
+ #ifdef G_OS_WIN32
+ #define STRICT
+ #include <windows.h>
+@@ -329,10 +335,11 @@ struct _GChildWatchSource
+   GSource     source;
+   GPid        pid;
+   gint        child_status;
+-#ifdef G_OS_WIN32
++  /* @poll is always used on Windows, and used on Unix iff @using_pidfd is set: */
+   GPollFD     poll;
+-#else /* G_OS_WIN32 */
+-  gboolean    child_exited; /* (atomic) */
++#ifndef G_OS_WIN32
++  gboolean    child_exited; /* (atomic); not used iff @using_pidfd is set */
++  gboolean    using_pidfd;
+ #endif /* G_OS_WIN32 */
+ };
+ 
+@@ -5325,7 +5332,8 @@ dispatch_unix_signals_unlocked (void)
+         {
+           GChildWatchSource *source = node->data;
+ 
+-          if (!g_atomic_int_get (&source->child_exited))
++          if (!source->using_pidfd &&
++              !g_atomic_int_get (&source->child_exited))
+             {
+               pid_t pid;
+               do
+@@ -5384,6 +5392,38 @@ g_child_watch_prepare (GSource *source,
+   return g_atomic_int_get (&child_watch_source->child_exited);
+ }
+ 
++#ifdef HAVE_PIDFD
++static int
++siginfo_t_to_wait_status (const siginfo_t *info)
++{
++  /* Each of these returns is essentially the inverse of WIFEXITED(),
++   * WIFSIGNALED(), etc. */
++  switch (info->si_code)
++    {
++    case CLD_EXITED:
++      return W_EXITCODE (info->si_status, 0);
++    case CLD_KILLED:
++      return W_EXITCODE (0, info->si_status);
++    case CLD_DUMPED:
++#ifdef WCOREFLAG
++      return W_EXITCODE (0, info->si_status | WCOREFLAG);
++#else
++      g_assert_not_reached ();
++#endif
++    case CLD_CONTINUED:
++#ifdef __W_CONTINUED
++      return __W_CONTINUED;
++#else
++      g_assert_not_reached ();
++#endif
++    case CLD_STOPPED:
++    case CLD_TRAPPED:
++    default:
++      return W_STOPCODE (info->si_status);
++    }
++}
++#endif  /* HAVE_PIDFD */
++
+ static gboolean
+ g_child_watch_check (GSource *source)
+ {
+@@ -5391,6 +5431,34 @@ g_child_watch_check (GSource *source)
+ 
+   child_watch_source = (GChildWatchSource *) source;
+ 
++#ifdef HAVE_PIDFD
++  if (child_watch_source->using_pidfd)
++    {
++      gboolean child_exited = child_watch_source->poll.revents & G_IO_IN;
++
++      if (child_exited)
++        {
++          siginfo_t child_info = { 0, };
++
++          /* Get the exit status */
++          if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 &&
++              child_info.si_pid != 0)
++            {
++              /* waitid() helpfully provides the wait status in a decomposed
++               * form which is quite useful. Unfortunately we have to report it
++               * to the #GChildWatchFunc as a waitpid()-style platform-specific
++               * wait status, so that the user code in #GChildWatchFunc can then
++               * call WIFEXITED() (etc.) on it. That means re-composing the
++               * status information. */
++              child_watch_source->child_status = siginfo_t_to_wait_status (&child_info);
++              child_watch_source->child_exited = TRUE;
++            }
++        }
++
++      return child_exited;
++    }
++#endif  /* HAVE_PIDFD */
++
+   return g_atomic_int_get (&child_watch_source->child_exited);
+ }
+ 
+@@ -5575,6 +5643,11 @@ g_unix_signal_watch_finalize (GSource    *source)
+ static void
+ g_child_watch_finalize (GSource *source)
+ {
++  GChildWatchSource *child_watch_source = (GChildWatchSource *) source;
++
++  if (child_watch_source->using_pidfd)
++    return;
++
+   G_LOCK (unix_signal_lock);
+   unix_child_watches = g_slist_remove (unix_child_watches, source);
+   unref_unix_signal_handler_unlocked (SIGCHLD);
+@@ -5676,6 +5749,9 @@ g_child_watch_source_new (GPid pid)
+ {
+   GSource *source;
+   GChildWatchSource *child_watch_source;
++#ifdef HAVE_PIDFD
++  int errsv;
++#endif
+ 
+ #ifndef G_OS_WIN32
+   g_return_val_if_fail (pid > 0, NULL);
+@@ -5694,14 +5770,43 @@ g_child_watch_source_new (GPid pid)
+   child_watch_source->poll.events = G_IO_IN;
+ 
+   g_source_add_poll (source, &child_watch_source->poll);
+-#else /* G_OS_WIN32 */
++#else /* !G_OS_WIN32 */
++
++#ifdef HAVE_PIDFD
++  /* Use a pidfd, if possible, to avoid having to install a global SIGCHLD
++   * handler and potentially competing with any other library/code which wants
++   * to install one.
++   *
++   * Unfortunately this use of pidfd isn’t race-free (the PID could be recycled
++   * between the caller calling g_child_watch_source_new() and here), but it’s
++   * better than SIGCHLD.
++   */
++  child_watch_source->poll.fd = (int) syscall (SYS_pidfd_open, pid, 0);
++  errsv = errno;
++
++  if (child_watch_source->poll.fd >= 0)
++    {
++      child_watch_source->using_pidfd = TRUE;
++      child_watch_source->poll.events = G_IO_IN;
++      g_source_add_poll (source, &child_watch_source->poll);
++
++      return source;
++    }
++  else
++    {
++      g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s",
++               pid, g_strerror (errsv));
++      /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */
++    }
++#endif  /* HAVE_PIDFD */
++
+   G_LOCK (unix_signal_lock);
+   ref_unix_signal_handler_unlocked (SIGCHLD);
+   unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source);
+   if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0)
+     child_watch_source->child_exited = TRUE;
+   G_UNLOCK (unix_signal_lock);
+-#endif /* G_OS_WIN32 */
++#endif /* !G_OS_WIN32 */
+ 
+   return source;
+ }
+diff --git a/meson.build b/meson.build
+index a0223ce5b..1e1bd602c 100644
+--- a/meson.build
++++ b/meson.build
+@@ -810,6 +810,20 @@ if cc.links('''#include <sys/eventfd.h>
+   glib_conf.set('HAVE_EVENTFD', 1)
+ endif
+ 
++# Check for pidfd_open(2)
++if cc.links('''#include <sys/syscall.h>
++               #include <sys/wait.h>
++               #include <linux/wait.h>
++               #include <unistd.h>
++               int main (int argc, char ** argv) {
++                 siginfo_t child_info = { 0, };
++                 syscall (SYS_pidfd_open, 0, 0);
++                 waitid (P_PIDFD, 0, &child_info, WEXITED | WNOHANG);
++                 return 0;
++               }''', name : 'pidfd_open(2) system call')
++  glib_conf.set('HAVE_PIDFD', 1)
++endif
++
+ # Check for __uint128_t (gcc) by checking for 128-bit division
+ uint128_t_src = '''int main() {
+ static __uint128_t v1 = 100;
+-- 
+2.41.0
+
+
+From 13c62bc181c6da9f287b737f7a3238e0269b40b3 Mon Sep 17 00:00:00 2001
+From: Christian Hergert <chergert@redhat.com>
+Date: Tue, 2 Aug 2022 12:35:40 -0700
+Subject: [PATCH 2/4] gmain: close pidfd when finalizing GChildWatchSource
+
+A file-descriptor was created with the introduction of pidfd_getfd() but
+nothing is closing it when the source finalizes. The GChildWatchSource is
+the creator and consumer of this FD and therefore responsible for closing
+it on finalization.
+
+The pidfd leak was introduced in !2408.
+
+This fixes issues with Builder where anon_inode:[pidfd] exhaust the
+available FD limit for the process.
+
+Fixes #2708
+---
+ glib/gmain.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index e9965f7f6..3ceec61ee 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -5646,7 +5646,11 @@ g_child_watch_finalize (GSource *source)
+   GChildWatchSource *child_watch_source = (GChildWatchSource *) source;
+ 
+   if (child_watch_source->using_pidfd)
+-    return;
++    {
++      if (child_watch_source->poll.fd >= 0)
++        close (child_watch_source->poll.fd);
++      return;
++    }
+ 
+   G_LOCK (unix_signal_lock);
+   unix_child_watches = g_slist_remove (unix_child_watches, source);
+-- 
+2.41.0
+
+
+From 378c72cbe12767b8f6aedc19c7ca46c07aa1ca73 Mon Sep 17 00:00:00 2001
+From: Owen Rafferty <owen@owenrafferty.com>
+Date: Tue, 12 Jul 2022 20:03:56 -0500
+Subject: [PATCH 3/4] gmain: define non-posix symbols
+
+---
+ glib/gmain.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 3ceec61ee..a2d7bb3ba 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -71,6 +71,12 @@
+ #include <sys/syscall.h>
+ #include <sys/wait.h>
+ #include <linux/wait.h>  /* P_PIDFD */
++#ifndef W_EXITCODE
++#define W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
++#endif
++#ifndef W_STOPCODE
++#define W_STOPCODE(sig)      ((sig) << 8 | 0x7f)
++#endif
+ #endif  /* HAVE_PIDFD */
+ 
+ #ifdef G_OS_WIN32
+-- 
+2.41.0
+
+
+From aac37188ce26366bd86626700d49cee0cb121472 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Wed, 21 Dec 2022 12:11:46 +0000
+Subject: [PATCH 4/4] gmain: Define fallback values for siginfo_t constants for
+ musl
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+musl doesn’t define them itself, presumably because they’re not defined
+in POSIX. glibc does define them. Thankfully, the values used in glibc
+match the values used internally in other musl macros.
+
+Define the values as a fallback. As a result of this, we can get rid of
+the `g_assert_if_reached()` checks in `siginfo_t_to_wait_status()`.
+
+This should fix catching signals from a subprocess when built against
+musl.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2852
+---
+ glib/gmain.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index a2d7bb3ba..f0cf700c0 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -77,6 +77,16 @@
+ #ifndef W_STOPCODE
+ #define W_STOPCODE(sig)      ((sig) << 8 | 0x7f)
+ #endif
++#ifndef WCOREFLAG
++/* musl doesn’t define WCOREFLAG while glibc does. Unfortunately, there’s no way
++ * to detect we’re building against musl, so just define it and hope.
++ * See https://git.musl-libc.org/cgit/musl/tree/include/sys/wait.h#n51 */
++#define WCOREFLAG 0x80
++#endif
++#ifndef __W_CONTINUED
++/* Same as above, for musl */
++#define __W_CONTINUED 0xffff
++#endif
+ #endif  /* HAVE_PIDFD */
+ 
+ #ifdef G_OS_WIN32
+@@ -5411,17 +5421,9 @@ siginfo_t_to_wait_status (const siginfo_t *info)
+     case CLD_KILLED:
+       return W_EXITCODE (0, info->si_status);
+     case CLD_DUMPED:
+-#ifdef WCOREFLAG
+       return W_EXITCODE (0, info->si_status | WCOREFLAG);
+-#else
+-      g_assert_not_reached ();
+-#endif
+     case CLD_CONTINUED:
+-#ifdef __W_CONTINUED
+       return __W_CONTINUED;
+-#else
+-      g_assert_not_reached ();
+-#endif
+     case CLD_STOPPED:
+     case CLD_TRAPPED:
+     default:
+-- 
+2.41.0
+
diff --git a/base/rx/rx-glib2/2435.patch b/base/rx/rx-glib2/2435.patch
new file mode 100644
index 0000000..862503b
--- /dev/null
+++ b/base/rx/rx-glib2/2435.patch
@@ -0,0 +1,132 @@
+From a879d08e912a4421786b44af479f94f7b4503f5a Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Mon, 17 Jan 2022 15:27:24 +0000
+Subject: [PATCH] gspawn: Report errors with closing file descriptors between
+ fork/exec
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If a seccomp policy is set up incorrectly so that it returns `EPERM` for
+`close_range()` rather than `ENOSYS` due to it not being recognised, no
+error would previously be reported from GLib, but some file descriptors
+wouldn’t be closed, and that would cause a hung zombie process. The
+zombie process would be waiting for one half of a socket to be closed.
+
+Fix that by correctly propagating errors from `close_range()` back to the
+parent process so they can be reported correctly.
+
+Distributions which aren’t yet carrying the Docker fix to correctly
+return `ENOSYS` from unrecognised syscalls may want to temporarily carry
+an additional patch to fall back to `safe_fdwalk()` if `close_range()`
+fails with `EPERM`. This change will not be accepted upstream as `EPERM`
+is not the right error for `close_range()` to be returning.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2580
+---
+ glib/gspawn.c | 35 ++++++++++++++++++++++++++---------
+ 1 file changed, 26 insertions(+), 9 deletions(-)
+
+diff --git a/glib/gspawn.c b/glib/gspawn.c
+index c2fe306dc..9c2f7ba7b 100644
+--- a/glib/gspawn.c
++++ b/glib/gspawn.c
+@@ -1457,8 +1457,10 @@ safe_fdwalk (int (*cb)(void *data, int fd), void *data)
+ }
+ 
+ /* This function is called between fork() and exec() and hence must be
+- * async-signal-safe (see signal-safety(7)). */
+-static void
++ * async-signal-safe (see signal-safety(7)).
++ *
++ * On failure, `-1` will be returned and errno will be set. */
++static int
+ safe_closefrom (int lowfd)
+ {
+ #if defined(__FreeBSD__) || defined(__OpenBSD__)
+@@ -1472,6 +1474,7 @@ safe_closefrom (int lowfd)
+    * should be safe to use.
+    */
+   (void) closefrom (lowfd);
++  return 0;
+ #elif defined(__DragonFly__)
+   /* It is unclear whether closefrom function included in DragonFlyBSD libc_r
+    * is safe to use because it calls a lot of library functions. It is also
+@@ -1479,12 +1482,13 @@ safe_closefrom (int lowfd)
+    * direct system call here ourselves to avoid possible issues.
+    */
+   (void) syscall (SYS_closefrom, lowfd);
++  return 0;
+ #elif defined(F_CLOSEM)
+   /* NetBSD and AIX have a special fcntl command which does the same thing as
+    * closefrom. NetBSD also includes closefrom function, which seems to be a
+    * simple wrapper of the fcntl command.
+    */
+-  (void) fcntl (lowfd, F_CLOSEM);
++  return fcntl (lowfd, F_CLOSEM);
+ #else
+ 
+ #if defined(HAVE_CLOSE_RANGE)
+@@ -1494,9 +1498,11 @@ safe_closefrom (int lowfd)
+    *
+    * Handle ENOSYS in case it’s supported in libc but not the kernel; if so,
+    * fall back to safe_fdwalk(). */
+-  if (close_range (lowfd, G_MAXUINT, 0) != 0 && errno == ENOSYS)
++  int ret = close_range (lowfd, G_MAXUINT, 0);
++  if (ret == 0 || errno != ENOSYS)
++    return ret;
+ #endif  /* HAVE_CLOSE_RANGE */
+-  (void) safe_fdwalk (close_func, GINT_TO_POINTER (lowfd));
++  return safe_fdwalk (close_func, GINT_TO_POINTER (lowfd));
+ #endif
+ }
+ 
+@@ -1534,7 +1540,8 @@ enum
+   CHILD_EXEC_FAILED,
+   CHILD_OPEN_FAILED,
+   CHILD_DUP2_FAILED,
+-  CHILD_FORK_FAILED
++  CHILD_FORK_FAILED,
++  CHILD_CLOSE_FAILED,
+ };
+ 
+ /* This function is called between fork() and exec() and hence must be
+@@ -1650,12 +1657,14 @@ do_exec (gint                  child_err_report_fd,
+           if (safe_dup2 (child_err_report_fd, 3) < 0)
+             write_err_and_exit (child_err_report_fd, CHILD_DUP2_FAILED);
+           set_cloexec (GINT_TO_POINTER (0), 3);
+-          safe_closefrom (4);
++          if (safe_closefrom (4) < 0)
++            write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED);
+           child_err_report_fd = 3;
+         }
+       else
+         {
+-          safe_fdwalk (set_cloexec, GINT_TO_POINTER (3));
++          if (safe_fdwalk (set_cloexec, GINT_TO_POINTER (3)) < 0)
++            write_err_and_exit (child_err_report_fd, CHILD_CLOSE_FAILED);
+         }
+     }
+   else
+@@ -2446,7 +2455,15 @@ fork_exec (gboolean              intermediate_child,
+                            _("Failed to fork child process (%s)"),
+                            g_strerror (buf[1]));
+               break;
+-              
++
++            case CHILD_CLOSE_FAILED:
++              g_set_error (error,
++                           G_SPAWN_ERROR,
++                           G_SPAWN_ERROR_FAILED,
++                           _("Failed to close file descriptor for child process (%s)"),
++                           g_strerror (buf[1]));
++              break;
++
+             default:
+               g_set_error (error,
+                            G_SPAWN_ERROR,
+-- 
+2.34.1
+
diff --git a/base/rx/rx-glib2/2826.patch b/base/rx/rx-glib2/2826.patch
new file mode 100644
index 0000000..83d527a
--- /dev/null
+++ b/base/rx/rx-glib2/2826.patch
@@ -0,0 +1,278 @@
+From 764f071909df70622e79ee71323973c18c055c8c Mon Sep 17 00:00:00 2001
+From: Giuseppe Scrivano <giuseppe@scrivano.org>
+Date: Mon, 14 Sep 2020 16:28:10 +0200
+Subject: [PATCH 1/5] gdbusauth: empty DATA does not need a trailing space
+
+This is an interoperability fix. If the line is exactly "DATA\r\n",
+the reference implementation of D-Bus treats this as equivalent to
+"DATA \r\n", meaning the data block consists of zero hex-encoded bytes.
+In practice, D-Bus clients send empty data blocks as "DATA\r\n", and
+in fact sd-bus only accepts that, rejecting "DATA \r\n".
+
+[Originally part of a larger commit; commit message added by smcv]
+
+Signed-off-by: Giuseppe Scrivano <giuseppe@scrivano.org>
+Co-authored-by: Simon McVittie <smcv@collabora.com>
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusauth.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c
+index ede21c8514..d2ca41a201 100644
+--- a/gio/gdbusauth.c
++++ b/gio/gdbusauth.c
+@@ -783,13 +783,13 @@ _g_dbus_auth_run_client (GDBusAuth     *auth,
+           if (line == NULL)
+             goto out;
+           debug_print ("CLIENT: WaitingForData, read='%s'", line);
+-          if (g_str_has_prefix (line, "DATA "))
++          if (g_str_equal (line, "DATA") || g_str_has_prefix (line, "DATA "))
+             {
+               gchar *encoded;
+               gchar *decoded_data;
+               gsize decoded_data_len = 0;
+ 
+-              encoded = g_strdup (line + 5);
++              encoded = g_strdup (line + 4);
+               g_free (line);
+               g_strstrip (encoded);
+               decoded_data = hexdecode (encoded, &decoded_data_len, error);
+@@ -1255,13 +1255,13 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+           debug_print ("SERVER: WaitingForData, read '%s'", line);
+           if (line == NULL)
+             goto out;
+-          if (g_str_has_prefix (line, "DATA "))
++          if (g_str_equal (line, "DATA") || g_str_has_prefix (line, "DATA "))
+             {
+               gchar *encoded;
+               gchar *decoded_data;
+               gsize decoded_data_len = 0;
+ 
+-              encoded = g_strdup (line + 5);
++              encoded = g_strdup (line + 4);
+               g_free (line);
+               g_strstrip (encoded);
+               decoded_data = hexdecode (encoded, &decoded_data_len, error);
+-- 
+GitLab
+
+
+From a7d2e727eefcf883bb463ad559f5632e8e448757 Mon Sep 17 00:00:00 2001
+From: Giuseppe Scrivano <giuseppe@scrivano.org>
+Date: Mon, 14 Sep 2020 16:28:10 +0200
+Subject: [PATCH 2/5] GDBusServer: If no initial response for EXTERNAL, send a
+ challenge
+
+Sending an "initial response" along with the AUTH command is meant
+to be an optional optimization, and clients are allowed to omit it.
+We must reply with our initial challenge, which in the case of EXTERNAL
+is an empty string: the client responds to that with the authorization
+identity.
+
+If we do not reply to the AUTH command, then the client will wait
+forever for our reply, while we wait forever for the reply that we
+expect the client to send, resulting in deadlock.
+
+D-Bus does not have a way to distinguish between an empty initial
+response and the absence of an initial response, so clients that want
+to use an empty authorization identity, such as systed's sd-bus,
+cannot use the initial-response optimization and will fail to connect
+to a GDBusServer that does not have this change.
+
+[Originally part of a larger commit; commit message added by smcv.]
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusauthmechanismexternal.c | 23 ++++++++++++++++++-----
+ 1 file changed, 18 insertions(+), 5 deletions(-)
+
+diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c
+index 617fe1d0e5..ddd06cbd5e 100644
+--- a/gio/gdbusauthmechanismexternal.c
++++ b/gio/gdbusauthmechanismexternal.c
+@@ -40,6 +40,7 @@ struct _GDBusAuthMechanismExternalPrivate
+   gboolean is_client;
+   gboolean is_server;
+   GDBusAuthMechanismState state;
++  gboolean empty_data_sent;
+ };
+ 
+ static gint                     mechanism_get_priority              (void);
+@@ -253,7 +254,9 @@ mechanism_server_initiate (GDBusAuthMechanism   *mechanism,
+     }
+   else
+     {
+-      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
++      /* The initial-response optimization was not used, so we need to
++       * send an empty challenge to prompt the client to respond. */
++      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND;
+     }
+ }
+ 
+@@ -288,12 +291,22 @@ mechanism_server_data_send (GDBusAuthMechanism   *mechanism,
+ 
+   g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
+   g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
+-  g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_HAVE_DATA_TO_SEND, NULL);
+ 
+-  /* can never end up here because we are never in the HAVE_DATA_TO_SEND state */
+-  g_assert_not_reached ();
++  if (out_data_len)
++    *out_data_len = 0;
+ 
+-  return NULL;
++  if (m->priv->empty_data_sent)
++    {
++      /* We have already sent an empty data response.
++         Reject the connection.  */
++      m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
++      return NULL;
++    }
++
++  m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_WAITING_FOR_DATA;
++  m->priv->empty_data_sent = TRUE;
++
++  return g_strdup ("");
+ }
+ 
+ static gchar *
+-- 
+GitLab
+
+
+From b51e3ab09e39c590c65a7be6228ecfa48a6189f6 Mon Sep 17 00:00:00 2001
+From: Giuseppe Scrivano <giuseppe@scrivano.org>
+Date: Mon, 14 Sep 2020 16:28:10 +0200
+Subject: [PATCH 3/5] GDBusServer: Accept empty authorization identity for
+ EXTERNAL mechanism
+
+RFC 4422 appendix A defines the empty authorization identity to mean
+the identity that the server associated with its authentication
+credentials. In this case, this means whatever uid is in the
+GCredentials object.
+
+In particular, this means that clients in a different Linux user
+namespace can authenticate against our server and will be authorized
+as the version of their uid that is visible in the server's namespace,
+even if the corresponding numeric uid returned by geteuid() in the
+client's namespace was different. systemd's sd-bus has relied on this
+since commit
+https://github.com/systemd/systemd/commit/1ed4723d38cd0d1423c8fe650f90fa86007ddf55.
+
+[Originally part of a larger commit; commit message added by smcv]
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusauthmechanismexternal.c | 16 +++++++++++++---
+ 1 file changed, 13 insertions(+), 3 deletions(-)
+
+diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c
+index ddd06cbd5e..a465862d12 100644
+--- a/gio/gdbusauthmechanismexternal.c
++++ b/gio/gdbusauthmechanismexternal.c
+@@ -201,14 +201,24 @@ data_matches_credentials (const gchar  *data,
+   if (credentials == NULL)
+     goto out;
+ 
+-  if (data == NULL || data_len == 0)
+-    goto out;
+-
+ #if defined(G_OS_UNIX)
+   {
+     gint64 alleged_uid;
+     gchar *endp;
+ 
++    /* If we were unable to find out the uid, then nothing
++     * can possibly match it.  */
++    if (g_credentials_get_unix_user (credentials, NULL) == (uid_t) -1)
++      goto out;
++
++    /* An empty authorization identity means we want to be
++     * whatever identity the out-of-band credentials say we have
++     * (RFC 4422 appendix A.1). This effectively matches any uid. */
++    if (data == NULL || data_len == 0)
++      {
++        match = TRUE;
++        goto out;
++      }
+     /* on UNIX, this is the uid as a string in base 10 */
+     alleged_uid = g_ascii_strtoll (data, &endp, 10);
+     if (*endp == '\0')
+-- 
+GitLab
+
+
+From 3f532af65c98e4ba8426c53f26c9ee15d3692f9c Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Mon, 18 Jul 2022 17:14:44 +0100
+Subject: [PATCH 4/5] gdbusauth: Represent empty data block as DATA\r\n, with
+ no space
+
+This is an interoperability fix. The reference implementation of D-Bus
+treats "DATA\r\n" as equivalent to "DATA \r\n", but sd-bus does not,
+and only accepts the former.
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusauth.c | 34 ++++++++++++++++++++++++++--------
+ 1 file changed, 26 insertions(+), 8 deletions(-)
+
+diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c
+index d2ca41a201..89cbbf67c6 100644
+--- a/gio/gdbusauth.c
++++ b/gio/gdbusauth.c
+@@ -807,11 +807,21 @@ _g_dbus_auth_run_client (GDBusAuth     *auth,
+                 {
+                   gchar *data;
+                   gsize data_len;
+-                  gchar *encoded_data;
++
+                   data = _g_dbus_auth_mechanism_client_data_send (mech, &data_len);
+-                  encoded_data = _g_dbus_hexencode (data, data_len);
+-                  s = g_strdup_printf ("DATA %s\r\n", encoded_data);
+-                  g_free (encoded_data);
++
++                  if (data_len == 0)
++                    {
++                      s = g_strdup ("DATA\r\n");
++                    }
++                  else
++                    {
++                      gchar *encoded_data = _g_dbus_hexencode (data, data_len);
++
++                      s = g_strdup_printf ("DATA %s\r\n", encoded_data);
++                      g_free (encoded_data);
++                    }
++
+                   g_free (data);
+                   debug_print ("CLIENT: writing '%s'", s);
+                   if (!g_data_output_stream_put_string (dos, s, cancellable, error))
+@@ -1209,13 +1219,21 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+                         gsize data_len;
+ 
+                         data = _g_dbus_auth_mechanism_server_data_send (mech, &data_len);
++
+                         if (data != NULL)
+                           {
+-                            gchar *encoded_data;
++                            if (data_len == 0)
++                              {
++                                s = g_strdup ("DATA\r\n");
++                              }
++                            else
++                              {
++                                gchar *encoded_data = _g_dbus_hexencode (data, data_len);
++
++                                s = g_strdup_printf ("DATA %s\r\n", encoded_data);
++                                g_free (encoded_data);
++                              }
+ 
+-                            encoded_data = _g_dbus_hexencode (data, data_len);
+-                            s = g_strdup_printf ("DATA %s\r\n", encoded_data);
+-                            g_free (encoded_data);
+                             g_free (data);
+ 
+                             debug_print ("SERVER: writing '%s'", s);
+-- 
+GitLab
diff --git a/base/rx/rx-glib2/3126.patch b/base/rx/rx-glib2/3126.patch
new file mode 100644
index 0000000..4bd6a17
--- /dev/null
+++ b/base/rx/rx-glib2/3126.patch
@@ -0,0 +1,3021 @@
+From b4ae1179cbcb91992adc0d5d4fa13341bbcd6c28 Mon Sep 17 00:00:00 2001
+From: William Manley <will@stb-tester.com>
+Date: Tue, 23 Jun 2020 22:59:58 +0100
+Subject: [PATCH 01/19] gvariant-core: Consolidate construction of
+ `GVariantSerialised`
+
+So I only need to change it in one place.
+
+This introduces no functional changes.
+
+Helps: #2121
+---
+ glib/gvariant-core.c | 49 ++++++++++++++++++++++----------------------
+ 1 file changed, 25 insertions(+), 24 deletions(-)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index b34ba8d8e..cd6a88768 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -349,6 +349,27 @@ g_variant_ensure_size (GVariant *value)
+     }
+ }
+ 
++/* < private >
++ * g_variant_to_serialised:
++ * @value: a #GVariant
++ *
++ * Gets a GVariantSerialised for a GVariant in state STATE_SERIALISED.
++ */
++inline static GVariantSerialised
++g_variant_to_serialised (GVariant *value)
++{
++  g_assert (value->state & STATE_SERIALISED);
++  {
++    GVariantSerialised serialised = {
++      value->type_info,
++      (gpointer) value->contents.serialised.data,
++      value->size,
++      value->depth,
++    };
++    return serialised;
++  }
++}
++
+ /* < private >
+  * g_variant_serialise:
+  * @value: a #GVariant
+@@ -1007,16 +1028,8 @@ g_variant_n_children (GVariant *value)
+   g_variant_lock (value);
+ 
+   if (value->state & STATE_SERIALISED)
+-    {
+-      GVariantSerialised serialised = {
+-        value->type_info,
+-        (gpointer) value->contents.serialised.data,
+-        value->size,
+-        value->depth,
+-      };
+-
+-      n_children = g_variant_serialised_n_children (serialised);
+-    }
++    n_children = g_variant_serialised_n_children (
++        g_variant_to_serialised (value));
+   else
+     n_children = value->contents.tree.n_children;
+ 
+@@ -1083,12 +1096,7 @@ g_variant_get_child_value (GVariant *value,
+     }
+ 
+   {
+-    GVariantSerialised serialised = {
+-      value->type_info,
+-      (gpointer) value->contents.serialised.data,
+-      value->size,
+-      value->depth,
+-    };
++    GVariantSerialised serialised = g_variant_to_serialised (value);
+     GVariantSerialised s_child;
+     GVariant *child;
+ 
+@@ -1201,14 +1209,7 @@ g_variant_is_normal_form (GVariant *value)
+ 
+   if (value->state & STATE_SERIALISED)
+     {
+-      GVariantSerialised serialised = {
+-        value->type_info,
+-        (gpointer) value->contents.serialised.data,
+-        value->size,
+-        value->depth
+-      };
+-
+-      if (g_variant_serialised_is_normal (serialised))
++      if (g_variant_serialised_is_normal (g_variant_to_serialised (value)))
+         value->state |= STATE_TRUSTED;
+     }
+   else
+-- 
+2.40.0
+
+From d51c16a7c8c3a57f1bad4305363ad85914ecab2e Mon Sep 17 00:00:00 2001
+From: William Manley <will@stb-tester.com>
+Date: Thu, 25 Jun 2020 17:08:21 +0100
+Subject: [PATCH 02/19] gvariant-serialiser: Factor out functions for dealing
+ with framing offsets
+
+This introduces no functional changes.
+
+Helps: #2121
+---
+ glib/gvariant-serialiser.c | 108 +++++++++++++++++++------------------
+ 1 file changed, 57 insertions(+), 51 deletions(-)
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 06f419fe4..ab6a56eb6 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -633,30 +633,62 @@ gvs_calculate_total_size (gsize body_size,
+   return body_size + 8 * offsets;
+ }
+ 
++struct Offsets
++{
++  gsize     data_size;
++
++  guchar   *array;
++  gsize     length;
++  guint     offset_size;
++
++  gboolean  is_normal;
++};
++
+ static gsize
+-gvs_variable_sized_array_n_children (GVariantSerialised value)
++gvs_offsets_get_offset_n (struct Offsets *offsets,
++                          gsize           n)
++{
++  return gvs_read_unaligned_le (
++      offsets->array + (offsets->offset_size * n), offsets->offset_size);
++}
++
++static struct Offsets
++gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value)
+ {
++  struct Offsets out = { 0, };
+   gsize offsets_array_size;
+-  gsize offset_size;
+   gsize last_end;
+ 
+   if (value.size == 0)
+-    return 0;
+-
+-  offset_size = gvs_get_offset_size (value.size);
++    {
++      out.is_normal = TRUE;
++      return out;
++    }
+ 
+-  last_end = gvs_read_unaligned_le (value.data + value.size -
+-                                    offset_size, offset_size);
++  out.offset_size = gvs_get_offset_size (value.size);
++  last_end = gvs_read_unaligned_le (value.data + value.size - out.offset_size,
++                                    out.offset_size);
+ 
+   if (last_end > value.size)
+-    return 0;
++    return out;  /* offsets not normal */
+ 
+   offsets_array_size = value.size - last_end;
+ 
+-  if (offsets_array_size % offset_size)
+-    return 0;
++  if (offsets_array_size % out.offset_size)
++    return out;  /* offsets not normal */
++
++  out.data_size = last_end;
++  out.array = value.data + last_end;
++  out.length = offsets_array_size / out.offset_size;
++  out.is_normal = TRUE;
+ 
+-  return offsets_array_size / offset_size;
++  return out;
++}
++
++static gsize
++gvs_variable_sized_array_n_children (GVariantSerialised value)
++{
++  return gvs_variable_sized_array_get_frame_offsets (value).length;
+ }
+ 
+ static GVariantSerialised
+@@ -664,8 +696,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+                                     gsize              index_)
+ {
+   GVariantSerialised child = { 0, };
+-  gsize offset_size;
+-  gsize last_end;
++
++  struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value);
++
+   gsize start;
+   gsize end;
+ 
+@@ -673,18 +706,11 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+   g_variant_type_info_ref (child.type_info);
+   child.depth = value.depth + 1;
+ 
+-  offset_size = gvs_get_offset_size (value.size);
+-
+-  last_end = gvs_read_unaligned_le (value.data + value.size -
+-                                    offset_size, offset_size);
+-
+   if (index_ > 0)
+     {
+       guint alignment;
+ 
+-      start = gvs_read_unaligned_le (value.data + last_end +
+-                                     (offset_size * (index_ - 1)),
+-                                     offset_size);
++      start = gvs_offsets_get_offset_n (&offsets, index_ - 1);
+ 
+       g_variant_type_info_query (child.type_info, &alignment, NULL);
+       start += (-start) & alignment;
+@@ -692,11 +718,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+   else
+     start = 0;
+ 
+-  end = gvs_read_unaligned_le (value.data + last_end +
+-                               (offset_size * index_),
+-                               offset_size);
++  end = gvs_offsets_get_offset_n (&offsets, index_);
+ 
+-  if (start < end && end <= value.size && end <= last_end)
++  if (start < end && end <= value.size && end <= offsets.data_size)
+     {
+       child.data = value.data + start;
+       child.size = end - start;
+@@ -768,34 +792,16 @@ static gboolean
+ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+ {
+   GVariantSerialised child = { 0, };
+-  gsize offsets_array_size;
+-  guchar *offsets_array;
+-  guint offset_size;
+   guint alignment;
+-  gsize last_end;
+-  gsize length;
+   gsize offset;
+   gsize i;
+ 
+-  if (value.size == 0)
+-    return TRUE;
+-
+-  offset_size = gvs_get_offset_size (value.size);
+-  last_end = gvs_read_unaligned_le (value.data + value.size -
+-                                    offset_size, offset_size);
++  struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value);
+ 
+-  if (last_end > value.size)
++  if (!offsets.is_normal)
+     return FALSE;
+ 
+-  offsets_array_size = value.size - last_end;
+-
+-  if (offsets_array_size % offset_size)
+-    return FALSE;
+-
+-  offsets_array = value.data + value.size - offsets_array_size;
+-  length = offsets_array_size / offset_size;
+-
+-  if (length == 0)
++  if (value.size != 0 && offsets.length == 0)
+     return FALSE;
+ 
+   child.type_info = g_variant_type_info_element (value.type_info);
+@@ -803,14 +809,14 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+   child.depth = value.depth + 1;
+   offset = 0;
+ 
+-  for (i = 0; i < length; i++)
++  for (i = 0; i < offsets.length; i++)
+     {
+       gsize this_end;
+ 
+-      this_end = gvs_read_unaligned_le (offsets_array + offset_size * i,
+-                                        offset_size);
++      this_end = gvs_read_unaligned_le (offsets.array + offsets.offset_size * i,
++                                        offsets.offset_size);
+ 
+-      if (this_end < offset || this_end > last_end)
++      if (this_end < offset || this_end > offsets.data_size)
+         return FALSE;
+ 
+       while (offset & alignment)
+@@ -832,7 +838,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+       offset = this_end;
+     }
+ 
+-  g_assert (offset == last_end);
++  g_assert (offset == offsets.data_size);
+ 
+   return TRUE;
+ }
+-- 
+2.40.0
+
+From fd215233ae37ce20cd84eb128c86b90c4dd610fa Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Tue, 25 Oct 2022 18:05:52 +0100
+Subject: [PATCH 03/19] gvariant: Zero-initialise various GVariantSerialised
+ objects
+
+The following few commits will add a couple of new fields to
+`GVariantSerialised`, and they should be zero-filled by default.
+
+Try and pre-empt that a bit by zero-filling `GVariantSerialised` by
+default in a few places.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2121
+---
+ glib/gvariant.c       |  2 +-
+ glib/tests/gvariant.c | 12 ++++++------
+ 2 files changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index e48dec1ad..8ebbaa662 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5944,7 +5944,7 @@ g_variant_byteswap (GVariant *value)
+   if (alignment)
+     /* (potentially) contains multi-byte numeric data */
+     {
+-      GVariantSerialised serialised;
++      GVariantSerialised serialised = { 0, };
+       GVariant *trusted;
+       GBytes *bytes;
+ 
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index 35434cae9..ebd617639 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -1438,7 +1438,7 @@ test_maybe (void)
+ 
+     for (flavour = 0; flavour < 8; flavour += alignment)
+       {
+-        GVariantSerialised serialised;
++        GVariantSerialised serialised = { 0, };
+         GVariantSerialised child;
+ 
+         serialised.type_info = type_info;
+@@ -1562,7 +1562,7 @@ test_array (void)
+ 
+     for (flavour = 0; flavour < 8; flavour += alignment)
+       {
+-        GVariantSerialised serialised;
++        GVariantSerialised serialised = { 0, };
+ 
+         serialised.type_info = array_info;
+         serialised.data = flavoured_malloc (needed_size, flavour);
+@@ -1726,7 +1726,7 @@ test_tuple (void)
+ 
+     for (flavour = 0; flavour < 8; flavour += alignment)
+       {
+-        GVariantSerialised serialised;
++        GVariantSerialised serialised = { 0, };
+ 
+         serialised.type_info = type_info;
+         serialised.data = flavoured_malloc (needed_size, flavour);
+@@ -1821,7 +1821,7 @@ test_variant (void)
+ 
+     for (flavour = 0; flavour < 8; flavour += alignment)
+       {
+-        GVariantSerialised serialised;
++        GVariantSerialised serialised = { 0, };
+         GVariantSerialised child;
+ 
+         serialised.type_info = type_info;
+@@ -2268,7 +2268,7 @@ serialise_tree (TreeInstance       *tree,
+ static void
+ test_byteswap (void)
+ {
+-  GVariantSerialised one, two;
++  GVariantSerialised one = { 0, }, two = { 0, };
+   TreeInstance *tree;
+ 
+   tree = tree_instance_new (NULL, 3);
+@@ -2342,7 +2342,7 @@ test_serialiser_children (void)
+ static void
+ test_fuzz (gdouble *fuzziness)
+ {
+-  GVariantSerialised serialised;
++  GVariantSerialised serialised = { 0, };
+   TreeInstance *tree;
+ 
+   /* make an instance */
+-- 
+2.40.0
+
+From 96e27afc7a1da0944d995ff0715679870ce8d503 Mon Sep 17 00:00:00 2001
+From: William Manley <will@stb-tester.com>
+Date: Mon, 29 Jun 2020 16:59:44 +0100
+Subject: [PATCH 04/19] =?UTF-8?q?gvariant:=20Don=E2=80=99t=20allow=20child?=
+ =?UTF-8?q?=20elements=20to=20overlap=20with=20each=20other?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If different elements of a variable sized array can overlap with each
+other then we can cause a `GVariant` to normalise to a much larger type.
+
+This commit changes the behaviour of `GVariant` with non-normal form data. If
+an invalid frame offset is found all subsequent elements are given their
+default value.
+
+When retrieving an element at index `n` we scan the frame offsets up to index
+`n` and if they are not in order we return an element with the default value
+for that type.  This guarantees that elements don't overlap with each
+other.  We remember the offset we've scanned up to so we don't need to
+repeat this work on subsequent accesses.  We skip these checks for trusted
+data.
+
+Unfortunately this makes random access of untrusted data O(n) — at least
+on first access.  It doesn't affect the algorithmic complexity of accessing
+elements in order, such as when using the `GVariantIter` interface.  Also:
+the cost of validation will be amortised as the `GVariant` instance is
+continued to be used.
+
+I've implemented this with 4 different functions, 1 for each element size,
+rather than looping calling `gvs_read_unaligned_le` in the hope that the
+compiler will find it easy to optimise and should produce fairly tight
+code.
+
+Fixes: #2121
+---
+ glib/gvariant-core.c       | 35 ++++++++++++++++
+ glib/gvariant-serialiser.c | 86 ++++++++++++++++++++++++++++++++++++--
+ glib/gvariant-serialiser.h |  9 ++++
+ glib/tests/gvariant.c      | 45 ++++++++++++++++++++
+ 4 files changed, 172 insertions(+), 3 deletions(-)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index cd6a88768..4bd7d0537 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -65,6 +65,7 @@ struct _GVariant
+     {
+       GBytes *bytes;
+       gconstpointer data;
++      gsize ordered_offsets_up_to;
+     } serialised;
+ 
+     struct
+@@ -162,6 +163,24 @@ struct _GVariant
+  *                if .data pointed to the appropriate number of nul
+  *                bytes.
+  *
++ *     .ordered_offsets_up_to: If ordered_offsets_up_to == n this means that all
++ *                             the frame offsets up to and including the frame
++ *                             offset determining the end of element n are in
++ *                             order. This guarantees that the bytes of element
++ *                             n don't overlap with any previous element.
++ *
++ *                             For trusted data this is set to G_MAXSIZE and we
++ *                             don't check that the frame offsets are in order.
++ *
++ *                             Note: This doesn't imply the offsets are good in
++ *                             any way apart from their ordering.  In particular
++ *                             offsets may be out of bounds for this value or
++ *                             may imply that the data overlaps the frame
++ *                             offsets themselves.
++ *
++ *                             This field is only relevant for arrays of non
++ *                             fixed width types.
++ *
+  *   .tree: Only valid when the instance is in tree form.
+  *
+  *          Note that accesses from other threads could result in
+@@ -365,6 +384,7 @@ g_variant_to_serialised (GVariant *value)
+       (gpointer) value->contents.serialised.data,
+       value->size,
+       value->depth,
++      value->contents.serialised.ordered_offsets_up_to,
+     };
+     return serialised;
+   }
+@@ -396,6 +416,7 @@ g_variant_serialise (GVariant *value,
+   serialised.size = value->size;
+   serialised.data = data;
+   serialised.depth = value->depth;
++  serialised.ordered_offsets_up_to = 0;
+ 
+   children = (gpointer *) value->contents.tree.children;
+   n_children = value->contents.tree.n_children;
+@@ -439,6 +460,15 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
+   g_assert (serialised->size == value->size);
+   serialised->depth = value->depth;
+ 
++  if (value->state & STATE_SERIALISED)
++    {
++      serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to;
++    }
++  else
++    {
++      serialised->ordered_offsets_up_to = 0;
++    }
++
+   if (serialised->data)
+     /* g_variant_store() is a public API, so it
+      * it will reacquire the lock if it needs to.
+@@ -481,6 +511,7 @@ g_variant_ensure_serialised (GVariant *value)
+       bytes = g_bytes_new_take (data, value->size);
+       value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
+       value->contents.serialised.bytes = bytes;
++      value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
+       value->state |= STATE_SERIALISED;
+     }
+ }
+@@ -561,6 +592,7 @@ g_variant_new_from_bytes (const GVariantType *type,
+   serialised.type_info = value->type_info;
+   serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
+   serialised.depth = 0;
++  serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
+ 
+   if (!g_variant_serialised_check (serialised))
+     {
+@@ -611,6 +643,8 @@ g_variant_new_from_bytes (const GVariantType *type,
+       value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
+     }
+ 
++  value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
++
+   g_clear_pointer (&owned_bytes, g_bytes_unref);
+ 
+   return value;
+@@ -1130,6 +1164,7 @@ g_variant_get_child_value (GVariant *value,
+     child->contents.serialised.bytes =
+       g_bytes_ref (value->contents.serialised.bytes);
+     child->contents.serialised.data = s_child.data;
++    child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
+ 
+     return child;
+   }
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index ab6a56eb6..8e9364e85 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -1,6 +1,7 @@
+ /*
+  * Copyright © 2007, 2008 Ryan Lortie
+  * Copyright © 2010 Codethink Limited
++ * Copyright © 2020 William Manley
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -264,6 +265,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
+   value.type_info = g_variant_type_info_element (value.type_info);
+   g_variant_type_info_ref (value.type_info);
+   value.depth++;
++  value.ordered_offsets_up_to = 0;
+ 
+   return value;
+ }
+@@ -295,7 +297,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised        value,
+ {
+   if (n_children)
+     {
+-      GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 };
++      GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
+ 
+       gvs_filler (&child, children[0]);
+     }
+@@ -317,6 +319,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
+       /* proper element size: "Just".  recurse to the child. */
+       value.type_info = g_variant_type_info_element (value.type_info);
+       value.depth++;
++      value.ordered_offsets_up_to = 0;
+ 
+       return g_variant_serialised_is_normal (value);
+     }
+@@ -358,6 +361,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
+     value.data = NULL;
+ 
+   value.depth++;
++  value.ordered_offsets_up_to = 0;
+ 
+   return value;
+ }
+@@ -388,7 +392,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised        value,
+ {
+   if (n_children)
+     {
+-      GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 };
++      GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
+ 
+       /* write the data for the child.  */
+       gvs_filler (&child, children[0]);
+@@ -408,6 +412,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
+   value.type_info = g_variant_type_info_element (value.type_info);
+   value.size--;
+   value.depth++;
++  value.ordered_offsets_up_to = 0;
+ 
+   return g_variant_serialised_is_normal (value);
+ }
+@@ -691,6 +696,32 @@ gvs_variable_sized_array_n_children (GVariantSerialised value)
+   return gvs_variable_sized_array_get_frame_offsets (value).length;
+ }
+ 
++/* Find the index of the first out-of-order element in @data, assuming that
++ * @data is an array of elements of given @type, starting at index @start and
++ * containing a further @len-@start elements. */
++#define DEFINE_FIND_UNORDERED(type) \
++  static gsize \
++  find_unordered_##type (const guint8 *data, gsize start, gsize len) \
++  { \
++    gsize off; \
++    type current, previous; \
++    \
++    memcpy (&previous, data + start * sizeof (current), sizeof (current)); \
++    for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \
++      { \
++        memcpy (&current, data + off, sizeof (current)); \
++        if (current < previous) \
++          break; \
++        previous = current; \
++      } \
++    return off / sizeof (current) - 1; \
++  }
++
++DEFINE_FIND_UNORDERED (guint8);
++DEFINE_FIND_UNORDERED (guint16);
++DEFINE_FIND_UNORDERED (guint32);
++DEFINE_FIND_UNORDERED (guint64);
++
+ static GVariantSerialised
+ gvs_variable_sized_array_get_child (GVariantSerialised value,
+                                     gsize              index_)
+@@ -706,6 +737,49 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+   g_variant_type_info_ref (child.type_info);
+   child.depth = value.depth + 1;
+ 
++  /* If the requested @index_ is beyond the set of indices whose framing offsets
++   * have been checked, check the remaining offsets to see whether they’re
++   * normal (in order, no overlapping array elements). */
++  if (index_ > value.ordered_offsets_up_to)
++    {
++      switch (offsets.offset_size)
++        {
++        case 1:
++          {
++            value.ordered_offsets_up_to = find_unordered_guint8 (
++                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++            break;
++          }
++        case 2:
++          {
++            value.ordered_offsets_up_to = find_unordered_guint16 (
++                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++            break;
++          }
++        case 4:
++          {
++            value.ordered_offsets_up_to = find_unordered_guint32 (
++                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++            break;
++          }
++        case 8:
++          {
++            value.ordered_offsets_up_to = find_unordered_guint64 (
++                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++            break;
++          }
++        default:
++          /* gvs_get_offset_size() only returns maximum 8 */
++          g_assert_not_reached ();
++        }
++    }
++
++  if (index_ > value.ordered_offsets_up_to)
++    {
++      /* Offsets are invalid somewhere, so return an empty child. */
++      return child;
++    }
++
+   if (index_ > 0)
+     {
+       guint alignment;
+@@ -840,6 +914,9 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+ 
+   g_assert (offset == offsets.data_size);
+ 
++  /* All offsets have now been checked. */
++  value.ordered_offsets_up_to = G_MAXSIZE;
++
+   return TRUE;
+ }
+ 
+@@ -1072,7 +1149,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
+   for (i = 0; i < length; i++)
+     {
+       const GVariantMemberInfo *member_info;
+-      GVariantSerialised child;
++      GVariantSerialised child = { 0, };
+       gsize fixed_size;
+       guint alignment;
+       gsize end;
+@@ -1132,6 +1209,9 @@ gvs_tuple_is_normal (GVariantSerialised value)
+       offset = end;
+     }
+ 
++  /* All element bounds have been checked above. */
++  value.ordered_offsets_up_to = G_MAXSIZE;
++
+   {
+     gsize fixed_size;
+     guint alignment;
+diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
+index 81343e9ca..bc636000c 100644
+--- a/glib/gvariant-serialiser.h
++++ b/glib/gvariant-serialiser.h
+@@ -29,6 +29,15 @@ typedef struct
+   guchar           *data;
+   gsize             size;
+   gsize             depth;  /* same semantics as GVariant.depth */
++
++  /* If ordered_offsets_up_to == n this means that all the frame offsets up to and
++   * including the frame offset determining the end of element n are in order.
++   * This guarantees that the bytes of element n don't overlap with any previous
++   * element.
++   *
++   * This is both read and set by g_variant_serialised_get_child for arrays of
++   * non-fixed-width types */
++  gsize             ordered_offsets_up_to;
+ } GVariantSerialised;
+ 
+ /* deserialisation */
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index ebd617639..7e5664e65 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -1,5 +1,6 @@
+ /*
+  * Copyright © 2010 Codethink Limited
++ * Copyright © 2020 William Manley
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -1279,6 +1280,7 @@ random_instance_filler (GVariantSerialised *serialised,
+     serialised->size = instance->size;
+ 
+   serialised->depth = 0;
++  serialised->ordered_offsets_up_to = 0;
+ 
+   g_assert_true (serialised->type_info == instance->type_info);
+   g_assert_cmpuint (serialised->size, ==, instance->size);
+@@ -5023,6 +5025,47 @@ test_normal_checking_array_offsets (void)
+   g_variant_unref (variant);
+ }
+ 
++/* This is a regression test that we can't have non-normal values that take up
++ * significantly more space than the normal equivalent, by specifying the
++ * offset table entries so that array elements overlap.
++ *
++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_832242 */
++static void
++test_normal_checking_array_offsets2 (void)
++{
++  const guint8 data[] = {
++    'h', 'i', '\0',
++    0x03, 0x00, 0x03,
++    0x06, 0x00, 0x06,
++    0x09, 0x00, 0x09,
++    0x0c, 0x00, 0x0c,
++    0x0f, 0x00, 0x0f,
++    0x12, 0x00, 0x12,
++    0x15, 0x00, 0x15,
++  };
++  gsize size = sizeof (data);
++  const GVariantType *aaaaaaas = G_VARIANT_TYPE ("aaaaaaas");
++  GVariant *variant = NULL;
++  GVariant *normal_variant = NULL;
++  GVariant *expected = NULL;
++
++  variant = g_variant_new_from_data (aaaaaaas, data, size, FALSE, NULL, NULL);
++  g_assert_nonnull (variant);
++
++  normal_variant = g_variant_get_normal_form (variant);
++  g_assert_nonnull (normal_variant);
++  g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 2);
++
++  expected = g_variant_new_parsed (
++      "[[[[[[['hi', '', ''], [], []], [], []], [], []], [], []], [], []], [], []]");
++  g_assert_cmpvariant (expected, variant);
++  g_assert_cmpvariant (expected, normal_variant);
++
++  g_variant_unref (expected);
++  g_variant_unref (normal_variant);
++  g_variant_unref (variant);
++}
++
+ /* Test that a tuple with invalidly large values in its offset table is
+  * normalised successfully without looping infinitely. */
+ static void
+@@ -5190,6 +5233,8 @@ main (int argc, char **argv)
+                    test_normal_checking_tuples);
+   g_test_add_func ("/gvariant/normal-checking/array-offsets",
+                    test_normal_checking_array_offsets);
++  g_test_add_func ("/gvariant/normal-checking/array-offsets2",
++                   test_normal_checking_array_offsets2);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
+                    test_normal_checking_tuple_offsets);
+   g_test_add_func ("/gvariant/normal-checking/empty-object-path",
+-- 
+2.40.0
+
+From 21fba6a534b35cc4fc245c6a8822b53c84b7fb48 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Fri, 7 Jan 2022 15:03:52 +0000
+Subject: [PATCH 05/19] gvariant-serialiser: Factor out code to get bounds of a
+ tuple member
+
+This introduces no functional changes.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2121
+---
+ glib/gvariant-serialiser.c | 73 ++++++++++++++++++++++++--------------
+ 1 file changed, 46 insertions(+), 27 deletions(-)
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 8e9364e85..cf4d2a2a5 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -942,6 +942,51 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+  * for the tuple.  See the notes in gvarianttypeinfo.h.
+  */
+ 
++static void
++gvs_tuple_get_member_bounds (GVariantSerialised  value,
++                             gsize               index_,
++                             gsize               offset_size,
++                             gsize              *out_member_start,
++                             gsize              *out_member_end)
++{
++  const GVariantMemberInfo *member_info;
++  gsize member_start, member_end;
++
++  member_info = g_variant_type_info_member_info (value.type_info, index_);
++
++  if (member_info->i + 1)
++    member_start = gvs_read_unaligned_le (value.data + value.size -
++                                          offset_size * (member_info->i + 1),
++                                          offset_size);
++  else
++    member_start = 0;
++
++  member_start += member_info->a;
++  member_start &= member_info->b;
++  member_start |= member_info->c;
++
++  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
++    member_end = value.size - offset_size * (member_info->i + 1);
++
++  else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
++    {
++      gsize fixed_size;
++
++      g_variant_type_info_query (member_info->type_info, NULL, &fixed_size);
++      member_end = member_start + fixed_size;
++    }
++
++  else /* G_VARIANT_MEMBER_ENDING_OFFSET */
++    member_end = gvs_read_unaligned_le (value.data + value.size -
++                                        offset_size * (member_info->i + 2),
++                                        offset_size);
++
++  if (out_member_start != NULL)
++    *out_member_start = member_start;
++  if (out_member_end != NULL)
++    *out_member_end = member_end;
++}
++
+ static gsize
+ gvs_tuple_n_children (GVariantSerialised value)
+ {
+@@ -997,33 +1042,7 @@ gvs_tuple_get_child (GVariantSerialised value,
+         }
+     }
+ 
+-  if (member_info->i + 1)
+-    start = gvs_read_unaligned_le (value.data + value.size -
+-                                   offset_size * (member_info->i + 1),
+-                                   offset_size);
+-  else
+-    start = 0;
+-
+-  start += member_info->a;
+-  start &= member_info->b;
+-  start |= member_info->c;
+-
+-  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
+-    end = value.size - offset_size * (member_info->i + 1);
+-
+-  else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
+-    {
+-      gsize fixed_size;
+-
+-      g_variant_type_info_query (child.type_info, NULL, &fixed_size);
+-      end = start + fixed_size;
+-      child.size = fixed_size;
+-    }
+-
+-  else /* G_VARIANT_MEMBER_ENDING_OFFSET */
+-    end = gvs_read_unaligned_le (value.data + value.size -
+-                                 offset_size * (member_info->i + 2),
+-                                 offset_size);
++  gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
+ 
+   /* The child should not extend into the offset table. */
+   if (index_ != g_variant_type_info_n_members (value.type_info) - 1)
+-- 
+2.40.0
+
+From 44ae51d04615867fe3bcb2842b93c059e764f8f7 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Fri, 7 Jan 2022 16:37:29 +0000
+Subject: [PATCH 06/19] gvariant-serialiser: Rework child size calculation
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This reduces a few duplicate calls to `g_variant_type_info_query()` and
+explains why they’re needed.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2121
+---
+ glib/gvariant-serialiser.c | 31 +++++++++----------------------
+ 1 file changed, 9 insertions(+), 22 deletions(-)
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index cf4d2a2a5..7ba7aed1a 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -1007,14 +1007,18 @@ gvs_tuple_get_child (GVariantSerialised value,
+   child.depth = value.depth + 1;
+   offset_size = gvs_get_offset_size (value.size);
+ 
++  /* Ensure the size is set for fixed-sized children, or
++   * g_variant_serialised_check() will fail, even if we return
++   * (child.data == NULL) to indicate an error. */
++  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
++    g_variant_type_info_query (child.type_info, NULL, &child.size);
++
+   /* tuples are the only (potentially) fixed-sized containers, so the
+    * only ones that have to deal with the possibility of having %NULL
+    * data with a non-zero %size if errors occurred elsewhere.
+    */
+   if G_UNLIKELY (value.data == NULL && value.size != 0)
+     {
+-      g_variant_type_info_query (child.type_info, NULL, &child.size);
+-
+       /* this can only happen in fixed-sized tuples,
+        * so the child must also be fixed sized.
+        */
+@@ -1032,29 +1036,12 @@ gvs_tuple_get_child (GVariantSerialised value,
+   else
+     {
+       if (offset_size * (member_info->i + 1) > value.size)
+-        {
+-          /* if the child is fixed size, return its size.
+-           * if child is not fixed-sized, return size = 0.
+-           */
+-          g_variant_type_info_query (child.type_info, NULL, &child.size);
+-
+-          return child;
+-        }
++        return child;
+     }
+ 
+-  gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
+-
+   /* The child should not extend into the offset table. */
+-  if (index_ != g_variant_type_info_n_members (value.type_info) - 1)
+-    {
+-      GVariantSerialised last_child;
+-      last_child = gvs_tuple_get_child (value,
+-                                        g_variant_type_info_n_members (value.type_info) - 1);
+-      last_end = last_child.data + last_child.size - value.data;
+-      g_variant_type_info_unref (last_child.type_info);
+-    }
+-  else
+-    last_end = end;
++  gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
++  gvs_tuple_get_member_bounds (value, g_variant_type_info_n_members (value.type_info) - 1, offset_size, NULL, &last_end);
+ 
+   if (start < end && end <= value.size && end <= last_end)
+     {
+-- 
+2.40.0
+
+From a4ddf8ea87a43c2ceffb5d806061d9c6297b9096 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Fri, 7 Jan 2022 16:42:14 +0000
+Subject: [PATCH 07/19] =?UTF-8?q?gvariant:=20Don=E2=80=99t=20allow=20child?=
+ =?UTF-8?q?=20elements=20of=20a=20tuple=20to=20overlap=20each=20other?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is similar to the earlier commit which prevents child elements of a
+variable-sized array from overlapping each other, but this time for
+tuples. It is based heavily on ideas by William Manley.
+
+Tuples are slightly different from variable-sized arrays in that they
+contain a mixture of fixed and variable sized elements. All but one of
+the variable sized elements have an entry in the frame offsets table.
+This means that if we were to just check the ordering of the frame
+offsets table, the variable sized elements could still overlap
+interleaving fixed sized elements, which would be bad.
+
+Therefore we have to check the elements rather than the frame offsets.
+
+The logic of checking the elements up to the index currently being
+requested, and caching the result in `ordered_offsets_up_to`, means that
+the algorithmic cost implications are the same for this commit as for
+variable-sized arrays: an O(N) cost for these checks is amortised out
+over N accesses to O(1) per access.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2121
+---
+ glib/gvariant-core.c       |   6 +-
+ glib/gvariant-serialiser.c |  40 ++++++++
+ glib/gvariant-serialiser.h |   7 +-
+ glib/gvariant.c            |   1 +
+ glib/tests/gvariant.c      | 181 +++++++++++++++++++++++++++++++++++++
+ 5 files changed, 232 insertions(+), 3 deletions(-)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index 4bd7d0537..9b268d977 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -1,6 +1,7 @@
+ /*
+  * Copyright © 2007, 2008 Ryan Lortie
+  * Copyright © 2010 Codethink Limited
++ * Copyright © 2022 Endless OS Foundation, LLC
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -179,7 +180,7 @@ struct _GVariant
+  *                             offsets themselves.
+  *
+  *                             This field is only relevant for arrays of non
+- *                             fixed width types.
++ *                             fixed width types and for tuples.
+  *
+  *   .tree: Only valid when the instance is in tree form.
+  *
+@@ -1139,6 +1140,9 @@ g_variant_get_child_value (GVariant *value,
+      */
+     s_child = g_variant_serialised_get_child (serialised, index_);
+ 
++    /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
++    value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
++
+     /* Check whether this would cause nesting too deep. If so, return a fake
+      * child. The only situation we expect this to happen in is with a variant,
+      * as all other deeply-nested types have a static type, and hence should
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 7ba7aed1a..470e21919 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -942,6 +942,10 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+  * for the tuple.  See the notes in gvarianttypeinfo.h.
+  */
+ 
++/* Note: This doesn’t guarantee that @out_member_end >= @out_member_start; that
++ * condition may not hold true for invalid serialised variants. The caller is
++ * responsible for checking the returned values and handling invalid ones
++ * appropriately. */
+ static void
+ gvs_tuple_get_member_bounds (GVariantSerialised  value,
+                              gsize               index_,
+@@ -1028,6 +1032,42 @@ gvs_tuple_get_child (GVariantSerialised value,
+       return child;
+     }
+ 
++  /* If the requested @index_ is beyond the set of indices whose framing offsets
++   * have been checked, check the remaining offsets to see whether they’re
++   * normal (in order, no overlapping tuple elements).
++   *
++   * Unlike the checks in gvs_variable_sized_array_get_child(), we have to check
++   * all the tuple *elements* here, not just all the framing offsets, since
++   * tuples contain a mix of elements which use framing offsets and ones which
++   * don’t. None of them are allowed to overlap. */
++  if (index_ > value.ordered_offsets_up_to)
++    {
++      gsize i, prev_i_end = 0;
++
++      if (value.ordered_offsets_up_to > 0)
++        gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
++
++      for (i = value.ordered_offsets_up_to; i <= index_; i++)
++        {
++          gsize i_start, i_end;
++
++          gvs_tuple_get_member_bounds (value, i, offset_size, &i_start, &i_end);
++
++          if (i_start > i_end || i_start < prev_i_end || i_end > value.size)
++            break;
++
++          prev_i_end = i_end;
++        }
++
++      value.ordered_offsets_up_to = i - 1;
++    }
++
++  if (index_ > value.ordered_offsets_up_to)
++    {
++      /* Offsets are invalid somewhere, so return an empty child. */
++      return child;
++    }
++
+   if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
+     {
+       if (offset_size * (member_info->i + 2) > value.size)
+diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
+index bc636000c..765bd43b9 100644
+--- a/glib/gvariant-serialiser.h
++++ b/glib/gvariant-serialiser.h
+@@ -35,8 +35,11 @@ typedef struct
+    * This guarantees that the bytes of element n don't overlap with any previous
+    * element.
+    *
+-   * This is both read and set by g_variant_serialised_get_child for arrays of
+-   * non-fixed-width types */
++   * This is both read and set by g_variant_serialised_get_child() for arrays of
++   * non-fixed-width types, and for tuples.
++   *
++   * Even when dealing with tuples, @ordered_offsets_up_to is an element index,
++   * rather than an index into the frame offsets. */
+   gsize             ordered_offsets_up_to;
+ } GVariantSerialised;
+ 
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 8ebbaa662..5f6eb63ee 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5953,6 +5953,7 @@ g_variant_byteswap (GVariant *value)
+       serialised.size = g_variant_get_size (trusted);
+       serialised.data = g_malloc (serialised.size);
+       serialised.depth = g_variant_get_depth (trusted);
++      serialised.ordered_offsets_up_to = G_MAXSIZE;  /* operating on the normal form */
+       g_variant_store (trusted, serialised.data);
+       g_variant_unref (trusted);
+ 
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index 7e5664e65..9ed56d66d 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -1,6 +1,7 @@
+ /*
+  * Copyright © 2010 Codethink Limited
+  * Copyright © 2020 William Manley
++ * Copyright © 2022 Endless OS Foundation, LLC
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+@@ -1447,6 +1448,7 @@ test_maybe (void)
+         serialised.data = flavoured_malloc (needed_size, flavour);
+         serialised.size = needed_size;
+         serialised.depth = 0;
++        serialised.ordered_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised,
+                                         random_instance_filler,
+@@ -1570,6 +1572,7 @@ test_array (void)
+         serialised.data = flavoured_malloc (needed_size, flavour);
+         serialised.size = needed_size;
+         serialised.depth = 0;
++        serialised.ordered_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                         (gpointer *) instances, n_children);
+@@ -1734,6 +1737,7 @@ test_tuple (void)
+         serialised.data = flavoured_malloc (needed_size, flavour);
+         serialised.size = needed_size;
+         serialised.depth = 0;
++        serialised.ordered_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                         (gpointer *) instances, n_children);
+@@ -1830,6 +1834,7 @@ test_variant (void)
+         serialised.data = flavoured_malloc (needed_size, flavour);
+         serialised.size = needed_size;
+         serialised.depth = 0;
++        serialised.ordered_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                         (gpointer *) &instance, 1);
+@@ -5090,6 +5095,176 @@ test_normal_checking_tuple_offsets (void)
+   g_variant_unref (variant);
+ }
+ 
++/* This is a regression test that we can't have non-normal values that take up
++ * significantly more space than the normal equivalent, by specifying the
++ * offset table entries so that tuple elements overlap.
++ *
++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838503 and
++ * https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838513 */
++static void
++test_normal_checking_tuple_offsets2 (void)
++{
++  const GVariantType *data_type = G_VARIANT_TYPE ("(yyaiyyaiyy)");
++  const guint8 data[] = {
++    0x12, 0x34, 0x56, 0x78, 0x01,
++    /*
++         ^───────────────────┘
++
++    ^^^^^^^^^^                   1st yy
++          ^^^^^^^^^^             2nd yy
++                ^^^^^^^^^^       3rd yy
++                            ^^^^ Framing offsets
++     */
++
++  /* If this variant was encoded normally, it would be something like this:
++   * 0x12, 0x34,  pad,  pad, [array bytes], 0x56, 0x78,  pad,  pad, [array bytes], 0x9A, 0xBC, 0xXX
++   *                                      ^─────────────────────────────────────────────────────┘
++   *
++   * ^^^^^^^^^^                                                                                     1st yy
++   *                                        ^^^^^^^^^^                                              2nd yy
++   *                                                                               ^^^^^^^^^^       3rd yy
++   *                                                                                           ^^^^ Framing offsets
++   */
++  };
++  gsize size = sizeof (data);
++  GVariant *variant = NULL;
++  GVariant *normal_variant = NULL;
++  GVariant *expected = NULL;
++
++  variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
++  g_assert_nonnull (variant);
++
++  normal_variant = g_variant_get_normal_form (variant);
++  g_assert_nonnull (normal_variant);
++  g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
++
++  expected = g_variant_new_parsed (
++      "@(yyaiyyaiyy) (0x12, 0x34, [], 0x00, 0x00, [], 0x00, 0x00)");
++  g_assert_cmpvariant (expected, variant);
++  g_assert_cmpvariant (expected, normal_variant);
++
++  g_variant_unref (expected);
++  g_variant_unref (normal_variant);
++  g_variant_unref (variant);
++}
++
++/* This is a regression test that overlapping entries in the offset table are
++ * decoded consistently, even though they’re non-normal.
++ *
++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
++static void
++test_normal_checking_tuple_offsets3 (void)
++{
++  /* The expected decoding of this non-normal byte stream is complex. See
++   * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
++   * specification.
++   *
++   * The rule “Child Values Overlapping Framing Offsets” from the specification
++   * says that the first `ay` must be decoded as `[0x01]` even though it
++   * overlaps the first byte of the offset table. However, since commit
++   * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
++   * this as it’s exploitable. So the first `ay` must be given a default value.
++   *
++   * The second and third `ay`s must be given default values because of rule
++   * “End Boundary Precedes Start Boundary”.
++   *
++   * The `i` must be given a default value because of rule “Start or End
++   * Boundary of a Child Falls Outside the Container”.
++   */
++  const GVariantType *data_type = G_VARIANT_TYPE ("(ayayiay)");
++  const guint8 data[] = {
++    0x01, 0x00, 0x02,
++    /*
++               ^──┘
++
++    ^^^^^^^^^^                   1st ay, bytes 0-2 (but given a default value anyway, see above)
++                                 2nd ay, bytes 2-0
++                                     i,  bytes 0-4
++                                 3rd ay, bytes 4-1
++          ^^^^^^^^^^ Framing offsets
++     */
++  };
++  gsize size = sizeof (data);
++  GVariant *variant = NULL;
++  GVariant *normal_variant = NULL;
++  GVariant *expected = NULL;
++
++  variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
++  g_assert_nonnull (variant);
++
++  g_assert_false (g_variant_is_normal_form (variant));
++
++  normal_variant = g_variant_get_normal_form (variant);
++  g_assert_nonnull (normal_variant);
++  g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
++
++  expected = g_variant_new_parsed ("@(ayayiay) ([], [], 0, [])");
++  g_assert_cmpvariant (expected, variant);
++  g_assert_cmpvariant (expected, normal_variant);
++
++  g_variant_unref (expected);
++  g_variant_unref (normal_variant);
++  g_variant_unref (variant);
++}
++
++/* This is a regression test that overlapping entries in the offset table are
++ * decoded consistently, even though they’re non-normal.
++ *
++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
++static void
++test_normal_checking_tuple_offsets4 (void)
++{
++  /* The expected decoding of this non-normal byte stream is complex. See
++   * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
++   * specification.
++   *
++   * The rule “Child Values Overlapping Framing Offsets” from the specification
++   * says that the first `ay` must be decoded as `[0x01]` even though it
++   * overlaps the first byte of the offset table. However, since commit
++   * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
++   * this as it’s exploitable. So the first `ay` must be given a default value.
++   *
++   * The second `ay` must be given a default value because of rule “End Boundary
++   * Precedes Start Boundary”.
++   *
++   * The third `ay` must be given a default value because its framing offsets
++   * overlap that of the first `ay`.
++   */
++  const GVariantType *data_type = G_VARIANT_TYPE ("(ayayay)");
++  const guint8 data[] = {
++    0x01, 0x00, 0x02,
++    /*
++               ^──┘
++
++    ^^^^^^^^^^                   1st ay, bytes 0-2 (but given a default value anyway, see above)
++                                 2nd ay, bytes 2-0
++                                 3rd ay, bytes 0-1
++          ^^^^^^^^^^ Framing offsets
++     */
++  };
++  gsize size = sizeof (data);
++  GVariant *variant = NULL;
++  GVariant *normal_variant = NULL;
++  GVariant *expected = NULL;
++
++  variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
++  g_assert_nonnull (variant);
++
++  g_assert_false (g_variant_is_normal_form (variant));
++
++  normal_variant = g_variant_get_normal_form (variant);
++  g_assert_nonnull (normal_variant);
++  g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
++
++  expected = g_variant_new_parsed ("@(ayayay) ([], [], [])");
++  g_assert_cmpvariant (expected, variant);
++  g_assert_cmpvariant (expected, normal_variant);
++
++  g_variant_unref (expected);
++  g_variant_unref (normal_variant);
++  g_variant_unref (variant);
++}
++
+ /* Test that an empty object path is normalised successfully to the base object
+  * path, ‘/’. */
+ static void
+@@ -5237,6 +5412,12 @@ main (int argc, char **argv)
+                    test_normal_checking_array_offsets2);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
+                    test_normal_checking_tuple_offsets);
++  g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
++                   test_normal_checking_tuple_offsets2);
++  g_test_add_func ("/gvariant/normal-checking/tuple-offsets3",
++                   test_normal_checking_tuple_offsets3);
++  g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
++                   test_normal_checking_tuple_offsets4);
+   g_test_add_func ("/gvariant/normal-checking/empty-object-path",
+                    test_normal_checking_empty_object_path);
+ 
+-- 
+2.40.0
+
+From 98d5b84c2cdff1431084909b47d1a7f6d0dd60b5 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Fri, 7 Jan 2022 16:42:14 +0000
+Subject: [PATCH 08/19] =?UTF-8?q?gvariant:=20Don=E2=80=99t=20allow=20child?=
+ =?UTF-8?q?=20elements=20of=20a=20tuple=20to=20overlap=20each=20other?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This is similar to the earlier commit which prevents child elements of a
+variable-sized array from overlapping each other, but this time for
+tuples. It is based heavily on ideas by William Manley.
+
+Tuples are slightly different from variable-sized arrays in that they
+contain a mixture of fixed and variable sized elements. All but one of
+the variable sized elements have an entry in the frame offsets table.
+This means that if we were to just check the ordering of the frame
+offsets table, the variable sized elements could still overlap
+interleaving fixed sized elements, which would be bad.
+
+Therefore we have to check the elements rather than the frame offsets.
+
+The logic of checking the elements up to the index currently being
+requested, and caching the result in `ordered_offsets_up_to`, means that
+the algorithmic cost implications are the same for this commit as for
+variable-sized arrays: an O(N) cost for these checks is amortised out
+over N accesses to O(1) per access.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2121
+---
+ glib/gvariant-core.c  | 2 ++
+ glib/tests/gvariant.c | 2 ++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index 9b268d977..c6c50a6ff 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -3,6 +3,8 @@
+  * Copyright © 2010 Codethink Limited
+  * Copyright © 2022 Endless OS Foundation, LLC
+  *
++ * SPDX-License-Identifier: LGPL-2.1-or-later
++ *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+  * License as published by the Free Software Foundation; either
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index 9ed56d66d..a565ea69a 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -3,6 +3,8 @@
+  * Copyright © 2020 William Manley
+  * Copyright © 2022 Endless OS Foundation, LLC
+  *
++ * SPDX-License-Identifier: LGPL-2.1-or-later
++ *
+  * This library is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public
+  * License as published by the Free Software Foundation; either
+-- 
+2.40.0
+
+From b53b26c5596a57d06bcc3947b9c8316526d9e70b Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Tue, 25 Oct 2022 15:14:14 +0100
+Subject: [PATCH 09/19] gvariant: Track checked and ordered offsets
+ independently
+
+The past few commits introduced the concept of known-good offsets in the
+offset table (which is used for variable-width arrays and tuples).
+Good offsets are ones which are non-overlapping with all the previous
+offsets in the table.
+
+If a bad offset is encountered when indexing into the array or tuple,
+the cached known-good offset index will not be increased. In this way,
+all child variants at and beyond the first bad offset can be returned as
+default values rather than dereferencing potentially invalid data.
+
+In this case, there was no information about the fact that the indexes
+between the highest known-good index and the requested one had been
+checked already. That could lead to a pathological case where an offset
+table with an invalid first offset is repeatedly checked in full when
+trying to access higher-indexed children.
+
+Avoid that by storing the index of the highest checked offset in the
+table, as well as the index of the highest good/ordered offset.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2121
+---
+ glib/gvariant-core.c       | 28 ++++++++++++++++++++++++
+ glib/gvariant-serialiser.c | 44 +++++++++++++++++++++++++++-----------
+ glib/gvariant-serialiser.h |  9 ++++++++
+ glib/gvariant.c            |  1 +
+ glib/tests/gvariant.c      |  5 +++++
+ 5 files changed, 75 insertions(+), 12 deletions(-)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index c6c50a6ff..1efcf200f 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -69,6 +69,7 @@ struct _GVariant
+       GBytes *bytes;
+       gconstpointer data;
+       gsize ordered_offsets_up_to;
++      gsize checked_offsets_up_to;
+     } serialised;
+ 
+     struct
+@@ -184,6 +185,24 @@ struct _GVariant
+  *                             This field is only relevant for arrays of non
+  *                             fixed width types and for tuples.
+  *
++ *     .checked_offsets_up_to: Similarly to .ordered_offsets_up_to, this stores
++ *                             the index of the highest element, n, whose frame
++ *                             offsets (and all the preceding frame offsets)
++ *                             have been checked for validity.
++ *
++ *                             It is always the case that
++ *                             .checked_offsets_up_to ≥ .ordered_offsets_up_to.
++ *
++ *                             If .checked_offsets_up_to == .ordered_offsets_up_to,
++ *                             then a bad offset has not been found so far.
++ *
++ *                             If .checked_offsets_up_to > .ordered_offsets_up_to,
++ *                             then a bad offset has been found at
++ *                             (.ordered_offsets_up_to + 1).
++ *
++ *                             This field is only relevant for arrays of non
++ *                             fixed width types and for tuples.
++ *
+  *   .tree: Only valid when the instance is in tree form.
+  *
+  *          Note that accesses from other threads could result in
+@@ -388,6 +407,7 @@ g_variant_to_serialised (GVariant *value)
+       value->size,
+       value->depth,
+       value->contents.serialised.ordered_offsets_up_to,
++      value->contents.serialised.checked_offsets_up_to,
+     };
+     return serialised;
+   }
+@@ -420,6 +440,7 @@ g_variant_serialise (GVariant *value,
+   serialised.data = data;
+   serialised.depth = value->depth;
+   serialised.ordered_offsets_up_to = 0;
++  serialised.checked_offsets_up_to = 0;
+ 
+   children = (gpointer *) value->contents.tree.children;
+   n_children = value->contents.tree.n_children;
+@@ -466,10 +487,12 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
+   if (value->state & STATE_SERIALISED)
+     {
+       serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to;
++      serialised->checked_offsets_up_to = value->contents.serialised.checked_offsets_up_to;
+     }
+   else
+     {
+       serialised->ordered_offsets_up_to = 0;
++      serialised->checked_offsets_up_to = 0;
+     }
+ 
+   if (serialised->data)
+@@ -515,6 +538,7 @@ g_variant_ensure_serialised (GVariant *value)
+       value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
+       value->contents.serialised.bytes = bytes;
+       value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
++      value->contents.serialised.checked_offsets_up_to = G_MAXSIZE;
+       value->state |= STATE_SERIALISED;
+     }
+ }
+@@ -596,6 +620,7 @@ g_variant_new_from_bytes (const GVariantType *type,
+   serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
+   serialised.depth = 0;
+   serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
++  serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;
+ 
+   if (!g_variant_serialised_check (serialised))
+     {
+@@ -647,6 +672,7 @@ g_variant_new_from_bytes (const GVariantType *type,
+     }
+ 
+   value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
++  value->contents.serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;
+ 
+   g_clear_pointer (&owned_bytes, g_bytes_unref);
+ 
+@@ -1144,6 +1170,7 @@ g_variant_get_child_value (GVariant *value,
+ 
+     /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
+     value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
++    value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);
+ 
+     /* Check whether this would cause nesting too deep. If so, return a fake
+      * child. The only situation we expect this to happen in is with a variant,
+@@ -1171,6 +1198,7 @@ g_variant_get_child_value (GVariant *value,
+       g_bytes_ref (value->contents.serialised.bytes);
+     child->contents.serialised.data = s_child.data;
+     child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
++    child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to;
+ 
+     return child;
+   }
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 470e21919..8e01ab53e 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -120,6 +120,8 @@
+  *
+  * @depth has no restrictions; the depth of a top-level serialised #GVariant is
+  * zero, and it increases for each level of nested child.
++ *
++ * @checked_offsets_up_to is always ≥ @ordered_offsets_up_to
+  */
+ 
+ /* < private >
+@@ -147,6 +149,9 @@ g_variant_serialised_check (GVariantSerialised serialised)
+            !(serialised.size == 0 || serialised.data != NULL))
+     return FALSE;
+ 
++  if (serialised.ordered_offsets_up_to > serialised.checked_offsets_up_to)
++    return FALSE;
++
+   /* Depending on the native alignment requirements of the machine, the
+    * compiler will insert either 3 or 7 padding bytes after the char.
+    * This will result in the sizeof() the struct being 12 or 16.
+@@ -266,6 +271,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
+   g_variant_type_info_ref (value.type_info);
+   value.depth++;
+   value.ordered_offsets_up_to = 0;
++  value.checked_offsets_up_to = 0;
+ 
+   return value;
+ }
+@@ -297,7 +303,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised        value,
+ {
+   if (n_children)
+     {
+-      GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
++      GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0, 0 };
+ 
+       gvs_filler (&child, children[0]);
+     }
+@@ -320,6 +326,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
+       value.type_info = g_variant_type_info_element (value.type_info);
+       value.depth++;
+       value.ordered_offsets_up_to = 0;
++      value.checked_offsets_up_to = 0;
+ 
+       return g_variant_serialised_is_normal (value);
+     }
+@@ -362,6 +369,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
+ 
+   value.depth++;
+   value.ordered_offsets_up_to = 0;
++  value.checked_offsets_up_to = 0;
+ 
+   return value;
+ }
+@@ -392,7 +400,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised        value,
+ {
+   if (n_children)
+     {
+-      GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
++      GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0, 0 };
+ 
+       /* write the data for the child.  */
+       gvs_filler (&child, children[0]);
+@@ -413,6 +421,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
+   value.size--;
+   value.depth++;
+   value.ordered_offsets_up_to = 0;
++  value.checked_offsets_up_to = 0;
+ 
+   return g_variant_serialised_is_normal (value);
+ }
+@@ -739,39 +748,46 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+ 
+   /* If the requested @index_ is beyond the set of indices whose framing offsets
+    * have been checked, check the remaining offsets to see whether they’re
+-   * normal (in order, no overlapping array elements). */
+-  if (index_ > value.ordered_offsets_up_to)
++   * normal (in order, no overlapping array elements).
++   *
++   * Don’t bother checking if the highest known-good offset is lower than the
++   * highest checked offset, as that means there’s an invalid element at that
++   * index, so there’s no need to check further. */
++  if (index_ > value.checked_offsets_up_to &&
++      value.ordered_offsets_up_to == value.checked_offsets_up_to)
+     {
+       switch (offsets.offset_size)
+         {
+         case 1:
+           {
+             value.ordered_offsets_up_to = find_unordered_guint8 (
+-                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++                offsets.array, value.checked_offsets_up_to, index_ + 1);
+             break;
+           }
+         case 2:
+           {
+             value.ordered_offsets_up_to = find_unordered_guint16 (
+-                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++                offsets.array, value.checked_offsets_up_to, index_ + 1);
+             break;
+           }
+         case 4:
+           {
+             value.ordered_offsets_up_to = find_unordered_guint32 (
+-                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++                offsets.array, value.checked_offsets_up_to, index_ + 1);
+             break;
+           }
+         case 8:
+           {
+             value.ordered_offsets_up_to = find_unordered_guint64 (
+-                offsets.array, value.ordered_offsets_up_to, index_ + 1);
++                offsets.array, value.checked_offsets_up_to, index_ + 1);
+             break;
+           }
+         default:
+           /* gvs_get_offset_size() only returns maximum 8 */
+           g_assert_not_reached ();
+         }
++
++      value.checked_offsets_up_to = index_;
+     }
+ 
+   if (index_ > value.ordered_offsets_up_to)
+@@ -916,6 +932,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+ 
+   /* All offsets have now been checked. */
+   value.ordered_offsets_up_to = G_MAXSIZE;
++  value.checked_offsets_up_to = G_MAXSIZE;
+ 
+   return TRUE;
+ }
+@@ -1040,14 +1057,15 @@ gvs_tuple_get_child (GVariantSerialised value,
+    * all the tuple *elements* here, not just all the framing offsets, since
+    * tuples contain a mix of elements which use framing offsets and ones which
+    * don’t. None of them are allowed to overlap. */
+-  if (index_ > value.ordered_offsets_up_to)
++  if (index_ > value.checked_offsets_up_to &&
++      value.ordered_offsets_up_to == value.checked_offsets_up_to)
+     {
+       gsize i, prev_i_end = 0;
+ 
+-      if (value.ordered_offsets_up_to > 0)
+-        gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
++      if (value.checked_offsets_up_to > 0)
++        gvs_tuple_get_member_bounds (value, value.checked_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
+ 
+-      for (i = value.ordered_offsets_up_to; i <= index_; i++)
++      for (i = value.checked_offsets_up_to; i <= index_; i++)
+         {
+           gsize i_start, i_end;
+ 
+@@ -1060,6 +1078,7 @@ gvs_tuple_get_child (GVariantSerialised value,
+         }
+ 
+       value.ordered_offsets_up_to = i - 1;
++      value.checked_offsets_up_to = index_;
+     }
+ 
+   if (index_ > value.ordered_offsets_up_to)
+@@ -1257,6 +1276,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
+ 
+   /* All element bounds have been checked above. */
+   value.ordered_offsets_up_to = G_MAXSIZE;
++  value.checked_offsets_up_to = G_MAXSIZE;
+ 
+   {
+     gsize fixed_size;
+diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
+index 765bd43b9..113df2ccc 100644
+--- a/glib/gvariant-serialiser.h
++++ b/glib/gvariant-serialiser.h
+@@ -41,6 +41,15 @@ typedef struct
+    * Even when dealing with tuples, @ordered_offsets_up_to is an element index,
+    * rather than an index into the frame offsets. */
+   gsize             ordered_offsets_up_to;
++
++  /* Similar to @ordered_offsets_up_to. This gives the index of the child element
++   * whose frame offset is the highest in the offset table which has been
++   * checked so far.
++   *
++   * This is always ≥ @ordered_offsets_up_to. It is always an element index.
++   *
++   * See documentation in gvariant-core.c for `struct GVariant` for details. */
++  gsize             checked_offsets_up_to;
+ } GVariantSerialised;
+ 
+ /* deserialisation */
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 5f6eb63ee..3e1adffaa 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5954,6 +5954,7 @@ g_variant_byteswap (GVariant *value)
+       serialised.data = g_malloc (serialised.size);
+       serialised.depth = g_variant_get_depth (trusted);
+       serialised.ordered_offsets_up_to = G_MAXSIZE;  /* operating on the normal form */
++      serialised.checked_offsets_up_to = G_MAXSIZE;
+       g_variant_store (trusted, serialised.data);
+       g_variant_unref (trusted);
+ 
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index a565ea69a..a13655eef 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -1284,6 +1284,7 @@ random_instance_filler (GVariantSerialised *serialised,
+ 
+   serialised->depth = 0;
+   serialised->ordered_offsets_up_to = 0;
++  serialised->checked_offsets_up_to = 0;
+ 
+   g_assert_true (serialised->type_info == instance->type_info);
+   g_assert_cmpuint (serialised->size, ==, instance->size);
+@@ -1451,6 +1452,7 @@ test_maybe (void)
+         serialised.size = needed_size;
+         serialised.depth = 0;
+         serialised.ordered_offsets_up_to = 0;
++        serialised.checked_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised,
+                                         random_instance_filler,
+@@ -1575,6 +1577,7 @@ test_array (void)
+         serialised.size = needed_size;
+         serialised.depth = 0;
+         serialised.ordered_offsets_up_to = 0;
++        serialised.checked_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                         (gpointer *) instances, n_children);
+@@ -1740,6 +1743,7 @@ test_tuple (void)
+         serialised.size = needed_size;
+         serialised.depth = 0;
+         serialised.ordered_offsets_up_to = 0;
++        serialised.checked_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                         (gpointer *) instances, n_children);
+@@ -1837,6 +1841,7 @@ test_variant (void)
+         serialised.size = needed_size;
+         serialised.depth = 0;
+         serialised.ordered_offsets_up_to = 0;
++        serialised.checked_offsets_up_to = 0;
+ 
+         g_variant_serialiser_serialise (serialised, random_instance_filler,
+                                         (gpointer *) &instance, 1);
+-- 
+2.40.0
+
+From 319f859c4ad021f636df67d61fa674afdd10d3c2 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <withnall@endlessm.com>
+Date: Fri, 12 Jun 2020 18:01:13 +0100
+Subject: [PATCH 10/19] tests: Add another test for overlapping offsets in
+ GVariant
+
+Signed-off-by: Philip Withnall <withnall@endlessm.com>
+
+Helps: #2121
+---
+ glib/tests/gvariant.c | 34 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 34 insertions(+)
+
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index a13655eef..965aa98af 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -5013,6 +5013,38 @@ test_recursion_limits_array_in_variant (void)
+   g_variant_unref (wrapper_variant);
+ }
+ 
++/* Test that a nested array with invalid values in its offset table (which point
++ * from the inner to the outer array) is normalised successfully without
++ * looping infinitely. */
++static void
++test_normal_checking_array_offsets_overlapped (void)
++{
++  const guint8 data[] = {
++    0x01, 0x00,
++  };
++  gsize size = sizeof (data);
++  GVariant *variant = NULL;
++  GVariant *normal_variant = NULL;
++  GVariant *expected_variant = NULL;
++
++  variant = g_variant_new_from_data (G_VARIANT_TYPE ("aay"), data, size,
++                                     FALSE, NULL, NULL);
++  g_assert_nonnull (variant);
++
++  normal_variant = g_variant_get_normal_form (variant);
++  g_assert_nonnull (normal_variant);
++
++  expected_variant = g_variant_new_parsed ("[@ay [], []]");
++  g_assert_cmpvariant (normal_variant, expected_variant);
++
++  g_assert_cmpmem (g_variant_get_data (normal_variant), g_variant_get_size (normal_variant),
++                   g_variant_get_data (expected_variant), g_variant_get_size (expected_variant));
++
++  g_variant_unref (expected_variant);
++  g_variant_unref (normal_variant);
++  g_variant_unref (variant);
++}
++
+ /* Test that an array with invalidly large values in its offset table is
+  * normalised successfully without looping infinitely. */
+ static void
+@@ -5413,6 +5445,8 @@ main (int argc, char **argv)
+ 
+   g_test_add_func ("/gvariant/normal-checking/tuples",
+                    test_normal_checking_tuples);
++  g_test_add_func ("/gvariant/normal-checking/array-offsets/overlapped",
++                   test_normal_checking_array_offsets_overlapped);
+   g_test_add_func ("/gvariant/normal-checking/array-offsets",
+                    test_normal_checking_array_offsets);
+   g_test_add_func ("/gvariant/normal-checking/array-offsets2",
+-- 
+2.40.0
+
+From 24ac75fab0b869fbf7e2cb970974ba59bc1a8a10 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Mon, 24 Oct 2022 16:43:23 +0100
+Subject: [PATCH 11/19] tests: Disable some random instance tests of GVariants
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Building a `GVariant` using entirely random data may result in a
+non-normally-formed `GVariant`. It’s always possible to read these
+`GVariant`s, but the API might return default values for some or all of
+their components.
+
+In particular, this can easily happen when randomly generating the
+offset tables for non-fixed-width container types.
+
+If it does happen, bytewise comparison of the parsed `GVariant` with the
+original bytes will not always match. So skip those checks.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2121
+---
+ glib/tests/gvariant.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index 965aa98af..f3b975652 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -1231,6 +1231,7 @@ random_instance_assert (RandomInstance *instance,
+   GRand *rand;
+   gsize i;
+ 
++  g_assert_true (size == 0 || buffer != NULL);
+   g_assert_cmpint ((gsize) buffer & ALIGN_BITS & instance->alignment, ==, 0);
+   g_assert_cmpint (size, ==, instance->size);
+ 
+@@ -1457,10 +1458,13 @@ test_maybe (void)
+         g_variant_serialiser_serialise (serialised,
+                                         random_instance_filler,
+                                         (gpointer *) &instance, 1);
++
+         child = g_variant_serialised_get_child (serialised, 0);
+         g_assert_true (child.type_info == instance->type_info);
+-        random_instance_assert (instance, child.data, child.size);
++        if (child.data != NULL)  /* could be NULL if element is non-normal */
++          random_instance_assert (instance, child.data, child.size);
+         g_variant_type_info_unref (child.type_info);
++
+         flavoured_free (serialised.data, flavour);
+       }
+   }
+@@ -1593,7 +1597,8 @@ test_array (void)
+ 
+             child = g_variant_serialised_get_child (serialised, i);
+             g_assert_true (child.type_info == instances[i]->type_info);
+-            random_instance_assert (instances[i], child.data, child.size);
++            if (child.data != NULL)  /* could be NULL if element is non-normal */
++              random_instance_assert (instances[i], child.data, child.size);
+             g_variant_type_info_unref (child.type_info);
+           }
+ 
+@@ -1759,7 +1764,8 @@ test_tuple (void)
+ 
+             child = g_variant_serialised_get_child (serialised, i);
+             g_assert_true (child.type_info == instances[i]->type_info);
+-            random_instance_assert (instances[i], child.data, child.size);
++            if (child.data != NULL)  /* could be NULL if element is non-normal */
++              random_instance_assert (instances[i], child.data, child.size);
+             g_variant_type_info_unref (child.type_info);
+           }
+ 
+-- 
+2.40.0
+
+From 88bbf60b61e4aada5b8ebd7deb91fff1f25e60c2 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Mon, 24 Oct 2022 18:14:57 +0100
+Subject: [PATCH 12/19] gvariant: Clarify the docs for
+ g_variant_get_normal_form()
+
+Document how non-normal parts of the `GVariant` are handled.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+---
+ glib/gvariant.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 3e1adffaa..2a6aaa1f4 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5879,7 +5879,9 @@ g_variant_deep_copy (GVariant *value)
+  * marked as trusted and a new reference to it is returned.
+  *
+  * If @value is found not to be in normal form then a new trusted
+- * #GVariant is created with the same value as @value.
++ * #GVariant is created with the same value as @value. The non-normal parts of
++ * @value will be replaced with default values which are guaranteed to be in
++ * normal form.
+  *
+  * It makes sense to call this function if you've received #GVariant
+  * data from untrusted sources and you want to ensure your serialised
+-- 
+2.40.0
+
+From 2f3b16d3439e4843718428c21aaedd1ecba868d7 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Mon, 24 Oct 2022 18:43:55 +0100
+Subject: [PATCH 13/19] gvariant: Port g_variant_deep_copy() to count its
+ iterations directly
+
+This is equivalent to what `GVariantIter` does, but it means that
+`g_variant_deep_copy()` is making its own `g_variant_get_child_value()`
+calls.
+
+This will be useful in an upcoming commit, where those child values will
+be inspected a little more deeply.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2121
+---
+ glib/gvariant.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 2a6aaa1f4..40d3639bc 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5806,14 +5806,13 @@ g_variant_deep_copy (GVariant *value)
+     case G_VARIANT_CLASS_VARIANT:
+       {
+         GVariantBuilder builder;
+-        GVariantIter iter;
+-        GVariant *child;
++        gsize i, n_children;
+ 
+         g_variant_builder_init (&builder, g_variant_get_type (value));
+-        g_variant_iter_init (&iter, value);
+ 
+-        while ((child = g_variant_iter_next_value (&iter)))
++        for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
+           {
++            GVariant *child = g_variant_get_child_value (value, i);
+             g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
+             g_variant_unref (child);
+           }
+-- 
+2.40.0
+
+From 81567d2bd81f303061b93be34da96ef2e2b11693 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Tue, 25 Oct 2022 13:03:22 +0100
+Subject: [PATCH 14/19] gvariant: Add internal
+ g_variant_maybe_get_child_value()
+
+This will be used in a following commit.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2540
+---
+ glib/gvariant-core.c | 68 ++++++++++++++++++++++++++++++++++++++++++++
+ glib/gvariant-core.h |  3 ++
+ 2 files changed, 71 insertions(+)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index 1efcf200f..dbcf60f69 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -1204,6 +1204,74 @@ g_variant_get_child_value (GVariant *value,
+   }
+ }
+ 
++/**
++ * g_variant_maybe_get_child_value:
++ * @value: a container #GVariant
++ * @index_: the index of the child to fetch
++ *
++ * Reads a child item out of a container #GVariant instance, if it is in normal
++ * form. If it is not in normal form, return %NULL.
++ *
++ * This function behaves the same as g_variant_get_child_value(), except that it
++ * returns %NULL if the child is not in normal form. g_variant_get_child_value()
++ * would instead return a new default value of the correct type.
++ *
++ * This is intended to be used internally to avoid unnecessary #GVariant
++ * allocations.
++ *
++ * The returned value is never floating.  You should free it with
++ * g_variant_unref() when you're done with it.
++ *
++ * This function is O(1).
++ *
++ * Returns: (transfer full): the child at the specified index
++ *
++ * Since: 2.74
++ */
++GVariant *
++g_variant_maybe_get_child_value (GVariant *value,
++                                 gsize     index_)
++{
++  g_return_val_if_fail (index_ < g_variant_n_children (value), NULL);
++  g_return_val_if_fail (value->depth < G_MAXSIZE, NULL);
++
++  if (~g_atomic_int_get (&value->state) & STATE_SERIALISED)
++    {
++      g_variant_lock (value);
++
++      if (~value->state & STATE_SERIALISED)
++        {
++          GVariant *child;
++
++          child = g_variant_ref (value->contents.tree.children[index_]);
++          g_variant_unlock (value);
++
++          return child;
++        }
++
++      g_variant_unlock (value);
++    }
++
++  {
++    GVariantSerialised serialised = g_variant_to_serialised (value);
++    GVariantSerialised s_child;
++
++    /* get the serializer to extract the serialized data for the child
++     * from the serialized data for the container
++     */
++    s_child = g_variant_serialised_get_child (serialised, index_);
++
++    if (!(value->state & STATE_TRUSTED) && s_child.data == NULL)
++      {
++        g_variant_type_info_unref (s_child.type_info);
++        return NULL;
++      }
++
++    g_variant_type_info_unref (s_child.type_info);
++    return g_variant_get_child_value (value, index_);
++  }
++}
++
+ /**
+  * g_variant_store:
+  * @value: the #GVariant to store
+diff --git a/glib/gvariant-core.h b/glib/gvariant-core.h
+index fc04711ac..947a98ca0 100644
+--- a/glib/gvariant-core.h
++++ b/glib/gvariant-core.h
+@@ -36,4 +36,7 @@ GVariantTypeInfo *      g_variant_get_type_info                         (GVarian
+ 
+ gsize                   g_variant_get_depth                             (GVariant            *value);
+ 
++GVariant *              g_variant_maybe_get_child_value                 (GVariant            *value,
++                                                                         gsize                index_);
++
+ #endif /* __G_VARIANT_CORE_H__ */
+-- 
+2.40.0
+
+From 2db1a61cabfc4919277f004c411c753bcfa71235 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Tue, 25 Oct 2022 13:03:45 +0100
+Subject: [PATCH 15/19] gvariant: Cut allocs of default values for children of
+ non-normal arrays
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This improves a slow case in `g_variant_get_normal_form()` where
+allocating many identical default values for the children of a
+variable-sized array which has a malformed offset table would take a lot
+of time.
+
+The fix is to make all child values after the first invalid one be
+references to the default value emitted for the first invalid one,
+rather than identical new `GVariant`s.
+
+In particular, this fixes a case where an attacker could create an array
+of length L of very large tuples of size T each, corrupt the offset table
+so they don’t have to specify the array content, and then induce
+`g_variant_get_normal_form()` into allocating L×T default values from an
+input which is significantly smaller than L×T in length.
+
+A pre-existing workaround for this issue is for code to call
+`g_variant_is_normal_form()` before calling
+`g_variant_get_normal_form()`, and to skip the latter call if the former
+returns false. This commit improves the behaviour in the case that
+`g_variant_get_normal_form()` is called anyway.
+
+This fix changes the time to run the `fuzz_variant_binary` test on the
+testcase from oss-fuzz#19777 from >60s (before being terminated) with
+2.3GB of memory usage and 580k page faults; to 32s, 8.3MB of memory
+usage and 1500 page faults (as measured by `time -v`).
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2540
+oss-fuzz#19777
+---
+ glib/gvariant.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 65 insertions(+), 1 deletion(-)
+
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 40d3639bc..63c134839 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5800,7 +5800,6 @@ g_variant_deep_copy (GVariant *value)
+   switch (g_variant_classify (value))
+     {
+     case G_VARIANT_CLASS_MAYBE:
+-    case G_VARIANT_CLASS_ARRAY:
+     case G_VARIANT_CLASS_TUPLE:
+     case G_VARIANT_CLASS_DICT_ENTRY:
+     case G_VARIANT_CLASS_VARIANT:
+@@ -5820,6 +5819,71 @@ g_variant_deep_copy (GVariant *value)
+         return g_variant_builder_end (&builder);
+       }
+ 
++    case G_VARIANT_CLASS_ARRAY:
++      {
++        GVariantBuilder builder;
++        gsize i, n_children;
++        GVariant *first_invalid_child_deep_copy = NULL;
++
++        /* Arrays are in theory treated the same as maybes, tuples, dict entries
++         * and variants, and could be another case in the above block of code.
++         *
++         * However, they have the property that when dealing with non-normal
++         * data (which is the only time g_variant_deep_copy() is currently
++         * called) in a variable-sized array, the code above can easily end up
++         * creating many default child values in order to return an array which
++         * is of the right length and type, but without containing non-normal
++         * data. This can happen if the offset table for the array is malformed.
++         *
++         * In this case, the code above would end up allocating the same default
++         * value for each one of the child indexes beyond the first malformed
++         * entry in the offset table. This can end up being a lot of identical
++         * allocations of default values, particularly if the non-normal array
++         * is crafted maliciously.
++         *
++         * Avoid that problem by returning a new reference to the same default
++         * value for every child after the first invalid one. This results in
++         * returning an equivalent array, in normal form and trusted — but with
++         * significantly fewer memory allocations.
++         *
++         * See https://gitlab.gnome.org/GNOME/glib/-/issues/2540 */
++
++        g_variant_builder_init (&builder, g_variant_get_type (value));
++
++        for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
++          {
++            /* Try maybe_get_child_value() first; if it returns NULL, this child
++             * is non-normal. get_child_value() would have constructed and
++             * returned a default value in that case. */
++            GVariant *child = g_variant_maybe_get_child_value (value, i);
++
++            if (child != NULL)
++              {
++                /* Non-normal children may not always be contiguous, as they may
++                 * be non-normal for reasons other than invalid offset table
++                 * entries. As they are all the same type, they will all have
++                 * the same default value though, so keep that around. */
++                g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
++              }
++            else if (child == NULL && first_invalid_child_deep_copy != NULL)
++              {
++                g_variant_builder_add_value (&builder, first_invalid_child_deep_copy);
++              }
++            else if (child == NULL)
++              {
++                child = g_variant_get_child_value (value, i);
++                first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child));
++                g_variant_builder_add_value (&builder, first_invalid_child_deep_copy);
++              }
++
++            g_clear_pointer (&child, g_variant_unref);
++          }
++
++        g_clear_pointer (&first_invalid_child_deep_copy, g_variant_unref);
++
++        return g_variant_builder_end (&builder);
++      }
++
+     case G_VARIANT_CLASS_BOOLEAN:
+       return g_variant_new_boolean (g_variant_get_boolean (value));
+ 
+-- 
+2.40.0
+
+From b4cfb50f42950b0f2f382865f23e9ff02ba05aa4 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Tue, 25 Oct 2022 18:03:56 +0100
+Subject: [PATCH 16/19] gvariant: Fix a leak of a GVariantTypeInfo on an error
+ handling path
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+---
+ glib/gvariant-core.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index dbcf60f69..2d2508244 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -1183,6 +1183,7 @@ g_variant_get_child_value (GVariant *value,
+         G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
+       {
+         g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
++        g_variant_type_info_unref (s_child.type_info);
+         return g_variant_new_tuple (NULL, 0);
+       }
+ 
+-- 
+2.40.0
+
+From 6dd5afa8a0daf574210ea21f1536f69960c0d007 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Thu, 27 Oct 2022 12:00:04 +0100
+Subject: [PATCH 17/19] gvariant-serialiser: Check offset table entry size is
+ minimal
+
+The entries in an offset table (which is used for variable sized arrays
+and tuples containing variable sized members) are sized so that they can
+address every byte in the overall variant.
+
+The specification requires that for a variant to be in normal form, its
+offset table entries must be the minimum width such that they can
+address every byte in the variant.
+
+That minimality requirement was not checked in
+`g_variant_is_normal_form()`, leading to two different byte arrays being
+interpreted as the normal form of a given variant tree. That kind of
+confusion could potentially be exploited, and is certainly a bug.
+
+Fix it by adding the necessary checks on offset table entry width, and
+unit tests.
+
+Spotted by William Manley.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2794
+---
+ glib/gvariant-serialiser.c |  19 +++-
+ glib/tests/gvariant.c      | 176 +++++++++++++++++++++++++++++++++++++
+ 2 files changed, 194 insertions(+), 1 deletion(-)
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 8e01ab53e..a6141f7d0 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -694,6 +694,10 @@ gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value)
+   out.data_size = last_end;
+   out.array = value.data + last_end;
+   out.length = offsets_array_size / out.offset_size;
++
++  if (out.length > 0 && gvs_calculate_total_size (last_end, out.length) != value.size)
++    return out;  /* offset size not minimal */
++
+   out.is_normal = TRUE;
+ 
+   return out;
+@@ -1201,6 +1205,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
+   gsize length;
+   gsize offset;
+   gsize i;
++  gsize offset_table_size;
+ 
+   /* as per the comment in gvs_tuple_get_child() */
+   if G_UNLIKELY (value.data == NULL && value.size != 0)
+@@ -1305,7 +1310,19 @@ gvs_tuple_is_normal (GVariantSerialised value)
+       }
+   }
+ 
+-  return offset_ptr == offset;
++  /* @offset_ptr has been counting backwards from the end of the variant, to
++   * find the beginning of the offset table. @offset has been counting forwards
++   * from the beginning of the variant to find the end of the data. They should
++   * have met in the middle. */
++  if (offset_ptr != offset)
++    return FALSE;
++
++  offset_table_size = value.size - offset_ptr;
++  if (value.size > 0 &&
++      gvs_calculate_total_size (offset, offset_table_size / offset_size) != value.size)
++    return FALSE;  /* offset size not minimal */
++
++  return TRUE;
+ }
+ 
+ /* Variants {{{2
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index f3b975652..028543833 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -5116,6 +5116,86 @@ test_normal_checking_array_offsets2 (void)
+   g_variant_unref (variant);
+ }
+ 
++/* Test that an otherwise-valid serialised GVariant is considered non-normal if
++ * its offset table entries are too wide.
++ *
++ * See §2.3.6 (Framing Offsets) of the GVariant specification. */
++static void
++test_normal_checking_array_offsets_minimal_sized (void)
++{
++  GVariantBuilder builder;
++  gsize i;
++  GVariant *aay_constructed = NULL;
++  const guint8 *data = NULL;
++  guint8 *data_owned = NULL;
++  GVariant *aay_deserialised = NULL;
++  GVariant *aay_normalised = NULL;
++
++  /* Construct an array of type aay, consisting of 128 elements which are each
++   * an empty array, i.e. `[[] * 128]`. This is chosen because the inner
++   * elements are variable sized (making the outer array variable sized, so it
++   * must have an offset table), but they are also zero-sized when serialised.
++   * So the serialised representation of @aay_constructed consists entirely of
++   * its offset table, which is entirely zeroes.
++   *
++   * The array is chosen to be 128 elements long because that means offset
++   * table entries which are 1 byte long. If the elements in the array were
++   * non-zero-sized (to the extent that the overall array is ≥256 bytes long),
++   * the offset table entries would end up being 2 bytes long. */
++  g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
++
++  for (i = 0; i < 128; i++)
++    g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0));
++
++  aay_constructed = g_variant_builder_end (&builder);
++
++  /* Verify that the constructed array is in normal form, and its serialised
++   * form is `b'\0' * 128`. */
++  g_assert_true (g_variant_is_normal_form (aay_constructed));
++  g_assert_cmpuint (g_variant_n_children (aay_constructed), ==, 128);
++  g_assert_cmpuint (g_variant_get_size (aay_constructed), ==, 128);
++
++  data = g_variant_get_data (aay_constructed);
++  for (i = 0; i < g_variant_get_size (aay_constructed); i++)
++    g_assert_cmpuint (data[i], ==, 0);
++
++  /* Construct a serialised `aay` GVariant which is `b'\0' * 256`. This has to
++   * be a non-normal form of `[[] * 128]`, with 2-byte-long offset table
++   * entries, because each offset table entry has to be able to reference all of
++   * the byte boundaries in the container. All the entries in the offset table
++   * are zero, so all the elements of the array are zero-sized. */
++  data = data_owned = g_malloc0 (256);
++  aay_deserialised = g_variant_new_from_data (G_VARIANT_TYPE ("aay"),
++                                              data,
++                                              256,
++                                              FALSE,
++                                              g_free,
++                                              g_steal_pointer (&data_owned));
++
++  g_assert_false (g_variant_is_normal_form (aay_deserialised));
++  g_assert_cmpuint (g_variant_n_children (aay_deserialised), ==, 128);
++  g_assert_cmpuint (g_variant_get_size (aay_deserialised), ==, 256);
++
++  data = g_variant_get_data (aay_deserialised);
++  for (i = 0; i < g_variant_get_size (aay_deserialised); i++)
++    g_assert_cmpuint (data[i], ==, 0);
++
++  /* Get its normal form. That should change the serialised size. */
++  aay_normalised = g_variant_get_normal_form (aay_deserialised);
++
++  g_assert_true (g_variant_is_normal_form (aay_normalised));
++  g_assert_cmpuint (g_variant_n_children (aay_normalised), ==, 128);
++  g_assert_cmpuint (g_variant_get_size (aay_normalised), ==, 128);
++
++  data = g_variant_get_data (aay_normalised);
++  for (i = 0; i < g_variant_get_size (aay_normalised); i++)
++    g_assert_cmpuint (data[i], ==, 0);
++
++  g_variant_unref (aay_normalised);
++  g_variant_unref (aay_deserialised);
++  g_variant_unref (aay_constructed);
++}
++
+ /* Test that a tuple with invalidly large values in its offset table is
+  * normalised successfully without looping infinitely. */
+ static void
+@@ -5310,6 +5390,98 @@ test_normal_checking_tuple_offsets4 (void)
+   g_variant_unref (variant);
+ }
+ 
++/* Test that an otherwise-valid serialised GVariant is considered non-normal if
++ * its offset table entries are too wide.
++ *
++ * See §2.3.6 (Framing Offsets) of the GVariant specification. */
++static void
++test_normal_checking_tuple_offsets_minimal_sized (void)
++{
++  GString *type_string = NULL;
++  GVariantBuilder builder;
++  gsize i;
++  GVariant *ray_constructed = NULL;
++  const guint8 *data = NULL;
++  guint8 *data_owned = NULL;
++  GVariant *ray_deserialised = NULL;
++  GVariant *ray_normalised = NULL;
++
++  /* Construct a tuple of type (ay…ay), consisting of 129 members which are each
++   * an empty array, i.e. `([] * 129)`. This is chosen because the inner
++   * members are variable sized, so the outer tuple must have an offset table,
++   * but they are also zero-sized when serialised. So the serialised
++   * representation of @ray_constructed consists entirely of its offset table,
++   * which is entirely zeroes.
++   *
++   * The tuple is chosen to be 129 members long because that means it has 128
++   * offset table entries which are 1 byte long each. If the members in the
++   * tuple were non-zero-sized (to the extent that the overall tuple is ≥256
++   * bytes long), the offset table entries would end up being 2 bytes long.
++   *
++   * 129 members are used unlike 128 array elements in
++   * test_normal_checking_array_offsets_minimal_sized(), because the last member
++   * in a tuple never needs an offset table entry. */
++  type_string = g_string_new ("");
++  g_string_append_c (type_string, '(');
++  for (i = 0; i < 129; i++)
++    g_string_append (type_string, "ay");
++  g_string_append_c (type_string, ')');
++
++  g_variant_builder_init (&builder, G_VARIANT_TYPE (type_string->str));
++
++  for (i = 0; i < 129; i++)
++    g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0));
++
++  ray_constructed = g_variant_builder_end (&builder);
++
++  /* Verify that the constructed tuple is in normal form, and its serialised
++   * form is `b'\0' * 128`. */
++  g_assert_true (g_variant_is_normal_form (ray_constructed));
++  g_assert_cmpuint (g_variant_n_children (ray_constructed), ==, 129);
++  g_assert_cmpuint (g_variant_get_size (ray_constructed), ==, 128);
++
++  data = g_variant_get_data (ray_constructed);
++  for (i = 0; i < g_variant_get_size (ray_constructed); i++)
++    g_assert_cmpuint (data[i], ==, 0);
++
++  /* Construct a serialised `(ay…ay)` GVariant which is `b'\0' * 256`. This has
++   * to be a non-normal form of `([] * 129)`, with 2-byte-long offset table
++   * entries, because each offset table entry has to be able to reference all of
++   * the byte boundaries in the container. All the entries in the offset table
++   * are zero, so all the members of the tuple are zero-sized. */
++  data = data_owned = g_malloc0 (256);
++  ray_deserialised = g_variant_new_from_data (G_VARIANT_TYPE (type_string->str),
++                                              data,
++                                              256,
++                                              FALSE,
++                                              g_free,
++                                              g_steal_pointer (&data_owned));
++
++  g_assert_false (g_variant_is_normal_form (ray_deserialised));
++  g_assert_cmpuint (g_variant_n_children (ray_deserialised), ==, 129);
++  g_assert_cmpuint (g_variant_get_size (ray_deserialised), ==, 256);
++
++  data = g_variant_get_data (ray_deserialised);
++  for (i = 0; i < g_variant_get_size (ray_deserialised); i++)
++    g_assert_cmpuint (data[i], ==, 0);
++
++  /* Get its normal form. That should change the serialised size. */
++  ray_normalised = g_variant_get_normal_form (ray_deserialised);
++
++  g_assert_true (g_variant_is_normal_form (ray_normalised));
++  g_assert_cmpuint (g_variant_n_children (ray_normalised), ==, 129);
++  g_assert_cmpuint (g_variant_get_size (ray_normalised), ==, 128);
++
++  data = g_variant_get_data (ray_normalised);
++  for (i = 0; i < g_variant_get_size (ray_normalised); i++)
++    g_assert_cmpuint (data[i], ==, 0);
++
++  g_variant_unref (ray_normalised);
++  g_variant_unref (ray_deserialised);
++  g_variant_unref (ray_constructed);
++  g_string_free (type_string, TRUE);
++}
++
+ /* Test that an empty object path is normalised successfully to the base object
+  * path, ‘/’. */
+ static void
+@@ -5457,6 +5629,8 @@ main (int argc, char **argv)
+                    test_normal_checking_array_offsets);
+   g_test_add_func ("/gvariant/normal-checking/array-offsets2",
+                    test_normal_checking_array_offsets2);
++  g_test_add_func ("/gvariant/normal-checking/array-offsets/minimal-sized",
++                   test_normal_checking_array_offsets_minimal_sized);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
+                    test_normal_checking_tuple_offsets);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
+@@ -5465,6 +5639,8 @@ main (int argc, char **argv)
+                    test_normal_checking_tuple_offsets3);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
+                    test_normal_checking_tuple_offsets4);
++  g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
++                   test_normal_checking_tuple_offsets_minimal_sized);
+   g_test_add_func ("/gvariant/normal-checking/empty-object-path",
+                    test_normal_checking_empty_object_path);
+ 
+-- 
+2.40.0
+
+From 5ebfcae9b5e409cc2ad0c1e73175af2bdab455ac Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Thu, 27 Oct 2022 16:13:54 +0100
+Subject: [PATCH 18/19] gvariant: Fix g_variant_byteswap() returning non-normal
+ data sometimes
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If `g_variant_byteswap()` was called on a non-normal variant of a type
+which doesn’t need byteswapping, it would return a non-normal output.
+
+That contradicts the documentation, which says that the return value is
+always in normal form.
+
+Fix the code so it matches the documentation.
+
+Includes a unit test.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Helps: #2797
+---
+ glib/gvariant.c       |  8 +++++---
+ glib/tests/gvariant.c | 24 ++++++++++++++++++++++++
+ 2 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 63c134839..9388c529d 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -6026,14 +6026,16 @@ g_variant_byteswap (GVariant *value)
+       g_variant_serialised_byteswap (serialised);
+ 
+       bytes = g_bytes_new_take (serialised.data, serialised.size);
+-      new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE);
++      new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
+       g_bytes_unref (bytes);
+     }
+   else
+     /* contains no multi-byte data */
+-    new = value;
++    new = g_variant_get_normal_form (value);
+ 
+-  return g_variant_ref_sink (new);
++  g_assert (g_variant_is_trusted (new));
++
++  return g_steal_pointer (&new);
+ }
+ 
+ /**
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index 028543833..871a6dab0 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -3826,6 +3826,29 @@ test_gv_byteswap (void)
+   g_free (string);
+ }
+ 
++static void
++test_gv_byteswap_non_normal_non_aligned (void)
++{
++  const guint8 data[] = { 0x02 };
++  GVariant *v = NULL;
++  GVariant *v_byteswapped = NULL;
++
++  g_test_summary ("Test that calling g_variant_byteswap() on a variant which "
++                  "is in non-normal form and doesn’t need byteswapping returns "
++                  "the same variant in normal form.");
++
++  v = g_variant_new_from_data (G_VARIANT_TYPE_BOOLEAN, data, sizeof (data), FALSE, NULL, NULL);
++  g_assert_false (g_variant_is_normal_form (v));
++
++  v_byteswapped = g_variant_byteswap (v);
++  g_assert_true (g_variant_is_normal_form (v_byteswapped));
++
++  g_assert_cmpvariant (v, v_byteswapped);
++
++  g_variant_unref (v);
++  g_variant_unref (v_byteswapped);
++}
++
+ static void
+ test_parser (void)
+ {
+@@ -5594,6 +5617,7 @@ main (int argc, char **argv)
+   g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
+   g_test_add_func ("/gvariant/hashing", test_hashing);
+   g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
++  g_test_add_func ("/gvariant/byteswap/non-normal-non-aligned", test_gv_byteswap_non_normal_non_aligned);
+   g_test_add_func ("/gvariant/parser", test_parses);
+   g_test_add_func ("/gvariant/parser/integer-bounds", test_parser_integer_bounds);
+   g_test_add_func ("/gvariant/parser/recursion", test_parser_recursion);
+-- 
+2.40.0
+
+From c6d0ae6c04a4257e9e06edd186d19e8ac4f37024 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Thu, 27 Oct 2022 22:53:13 +0100
+Subject: [PATCH 19/19] gvariant: Allow g_variant_byteswap() to operate on
+ tree-form variants
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This avoids needing to always serialise a variant before byteswapping it.
+With variants in non-normal forms, serialisation can result in a large
+increase in size of the variant, and a lot of allocations for leaf
+`GVariant`s. This can lead to a denial of service attack.
+
+Avoid that by changing byteswapping so that it happens on the tree form
+of the variant if the input is in non-normal form. If the input is in
+normal form (either serialised or in tree form), continue using the
+existing code as byteswapping an already-serialised normal variant is
+about 3× faster than byteswapping on the equivalent tree form.
+
+The existing unit tests cover byteswapping well, but need some
+adaptation so that they operate on tree form variants too.
+
+I considered dropping the serialised byteswapping code and doing all
+byteswapping on tree-form variants, as that would make maintenance
+simpler (avoiding having two parallel implementations of byteswapping).
+However, most inputs to `g_variant_byteswap()` are likely to be
+serialised variants (coming from a byte array of input from some foreign
+source) and most of them are going to be in normal form (as corruption
+and malicious action are rare). So getting rid of the serialised
+byteswapping code would impose quite a performance penalty on the common
+case.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2797
+---
+ glib/gvariant.c       | 87 ++++++++++++++++++++++++++++++++-----------
+ glib/tests/gvariant.c | 57 ++++++++++++++++++++++++----
+ 2 files changed, 115 insertions(+), 29 deletions(-)
+
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index 9388c529d..1f0eccb73 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -5795,7 +5795,8 @@ g_variant_iter_loop (GVariantIter *iter,
+ 
+ /* Serialised data {{{1 */
+ static GVariant *
+-g_variant_deep_copy (GVariant *value)
++g_variant_deep_copy (GVariant *value,
++                     gboolean  byteswap)
+ {
+   switch (g_variant_classify (value))
+     {
+@@ -5812,7 +5813,7 @@ g_variant_deep_copy (GVariant *value)
+         for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
+           {
+             GVariant *child = g_variant_get_child_value (value, i);
+-            g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
++            g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap));
+             g_variant_unref (child);
+           }
+ 
+@@ -5863,7 +5864,7 @@ g_variant_deep_copy (GVariant *value)
+                  * be non-normal for reasons other than invalid offset table
+                  * entries. As they are all the same type, they will all have
+                  * the same default value though, so keep that around. */
+-                g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
++                g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap));
+               }
+             else if (child == NULL && first_invalid_child_deep_copy != NULL)
+               {
+@@ -5872,7 +5873,7 @@ g_variant_deep_copy (GVariant *value)
+             else if (child == NULL)
+               {
+                 child = g_variant_get_child_value (value, i);
+-                first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child));
++                first_invalid_child_deep_copy = g_variant_ref_sink (g_variant_deep_copy (child, byteswap));
+                 g_variant_builder_add_value (&builder, first_invalid_child_deep_copy);
+               }
+ 
+@@ -5891,28 +5892,63 @@ g_variant_deep_copy (GVariant *value)
+       return g_variant_new_byte (g_variant_get_byte (value));
+ 
+     case G_VARIANT_CLASS_INT16:
+-      return g_variant_new_int16 (g_variant_get_int16 (value));
++      if (byteswap)
++        return g_variant_new_int16 (GUINT16_SWAP_LE_BE (g_variant_get_int16 (value)));
++      else
++        return g_variant_new_int16 (g_variant_get_int16 (value));
+ 
+     case G_VARIANT_CLASS_UINT16:
+-      return g_variant_new_uint16 (g_variant_get_uint16 (value));
++      if (byteswap)
++        return g_variant_new_uint16 (GUINT16_SWAP_LE_BE (g_variant_get_uint16 (value)));
++      else
++        return g_variant_new_uint16 (g_variant_get_uint16 (value));
+ 
+     case G_VARIANT_CLASS_INT32:
+-      return g_variant_new_int32 (g_variant_get_int32 (value));
++      if (byteswap)
++        return g_variant_new_int32 (GUINT32_SWAP_LE_BE (g_variant_get_int32 (value)));
++      else
++        return g_variant_new_int32 (g_variant_get_int32 (value));
+ 
+     case G_VARIANT_CLASS_UINT32:
+-      return g_variant_new_uint32 (g_variant_get_uint32 (value));
++      if (byteswap)
++        return g_variant_new_uint32 (GUINT32_SWAP_LE_BE (g_variant_get_uint32 (value)));
++      else
++        return g_variant_new_uint32 (g_variant_get_uint32 (value));
+ 
+     case G_VARIANT_CLASS_INT64:
+-      return g_variant_new_int64 (g_variant_get_int64 (value));
++      if (byteswap)
++        return g_variant_new_int64 (GUINT64_SWAP_LE_BE (g_variant_get_int64 (value)));
++      else
++        return g_variant_new_int64 (g_variant_get_int64 (value));
+ 
+     case G_VARIANT_CLASS_UINT64:
+-      return g_variant_new_uint64 (g_variant_get_uint64 (value));
++      if (byteswap)
++        return g_variant_new_uint64 (GUINT64_SWAP_LE_BE (g_variant_get_uint64 (value)));
++      else
++        return g_variant_new_uint64 (g_variant_get_uint64 (value));
+ 
+     case G_VARIANT_CLASS_HANDLE:
+-      return g_variant_new_handle (g_variant_get_handle (value));
++      if (byteswap)
++        return g_variant_new_handle (GUINT32_SWAP_LE_BE (g_variant_get_handle (value)));
++      else
++        return g_variant_new_handle (g_variant_get_handle (value));
+ 
+     case G_VARIANT_CLASS_DOUBLE:
+-      return g_variant_new_double (g_variant_get_double (value));
++      if (byteswap)
++        {
++          /* We have to convert the double to a uint64 here using a union,
++           * because a cast will round it numerically. */
++          union
++            {
++              guint64 u64;
++              gdouble dbl;
++            } u1, u2;
++          u1.dbl = g_variant_get_double (value);
++          u2.u64 = GUINT64_SWAP_LE_BE (u1.u64);
++          return g_variant_new_double (u2.dbl);
++        }
++      else
++        return g_variant_new_double (g_variant_get_double (value));
+ 
+     case G_VARIANT_CLASS_STRING:
+       return g_variant_new_string (g_variant_get_string (value, NULL));
+@@ -5969,7 +6005,7 @@ g_variant_get_normal_form (GVariant *value)
+   if (g_variant_is_normal_form (value))
+     return g_variant_ref (value);
+ 
+-  trusted = g_variant_deep_copy (value);
++  trusted = g_variant_deep_copy (value, FALSE);
+   g_assert (g_variant_is_trusted (trusted));
+ 
+   return g_variant_ref_sink (trusted);
+@@ -5989,6 +6025,11 @@ g_variant_get_normal_form (GVariant *value)
+  * contain multi-byte numeric data.  That include strings, booleans,
+  * bytes and containers containing only these things (recursively).
+  *
++ * While this function can safely handle untrusted, non-normal data, it is
++ * recommended to check whether the input is in normal form beforehand, using
++ * g_variant_is_normal_form(), and to reject non-normal inputs if your
++ * application can be strict about what inputs it rejects.
++ *
+  * The returned value is always in normal form and is marked as trusted.
+  *
+  * Returns: (transfer full): the byteswapped form of @value
+@@ -6006,22 +6047,21 @@ g_variant_byteswap (GVariant *value)
+ 
+   g_variant_type_info_query (type_info, &alignment, NULL);
+ 
+-  if (alignment)
+-    /* (potentially) contains multi-byte numeric data */
++  if (alignment && g_variant_is_normal_form (value))
+     {
++      /* (potentially) contains multi-byte numeric data, but is also already in
++       * normal form so we can use a faster byteswapping codepath on the
++       * serialised data */
+       GVariantSerialised serialised = { 0, };
+-      GVariant *trusted;
+       GBytes *bytes;
+ 
+-      trusted = g_variant_get_normal_form (value);
+-      serialised.type_info = g_variant_get_type_info (trusted);
+-      serialised.size = g_variant_get_size (trusted);
++      serialised.type_info = g_variant_get_type_info (value);
++      serialised.size = g_variant_get_size (value);
+       serialised.data = g_malloc (serialised.size);
+-      serialised.depth = g_variant_get_depth (trusted);
++      serialised.depth = g_variant_get_depth (value);
+       serialised.ordered_offsets_up_to = G_MAXSIZE;  /* operating on the normal form */
+       serialised.checked_offsets_up_to = G_MAXSIZE;
+-      g_variant_store (trusted, serialised.data);
+-      g_variant_unref (trusted);
++      g_variant_store (value, serialised.data);
+ 
+       g_variant_serialised_byteswap (serialised);
+ 
+@@ -6029,6 +6069,9 @@ g_variant_byteswap (GVariant *value)
+       new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
+       g_bytes_unref (bytes);
+     }
++  else if (alignment)
++    /* (potentially) contains multi-byte numeric data */
++    new = g_variant_ref_sink (g_variant_deep_copy (value, TRUE));
+   else
+     /* contains no multi-byte data */
+     new = g_variant_get_normal_form (value);
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index 871a6dab0..143057de7 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -2288,24 +2288,67 @@ serialise_tree (TreeInstance       *tree,
+ static void
+ test_byteswap (void)
+ {
+-  GVariantSerialised one = { 0, }, two = { 0, };
++  GVariantSerialised one = { 0, }, two = { 0, }, three = { 0, };
+   TreeInstance *tree;
+-
++  GVariant *one_variant = NULL;
++  GVariant *two_variant = NULL;
++  GVariant *two_byteswapped = NULL;
++  GVariant *three_variant = NULL;
++  GVariant *three_byteswapped = NULL;
++  guint8 *three_data_copy = NULL;
++  gsize three_size_copy = 0;
++
++  /* Write a tree out twice, once normally and once byteswapped. */
+   tree = tree_instance_new (NULL, 3);
+   serialise_tree (tree, &one);
+ 
++  one_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (one.type_info)),
++                                         one.data, one.size, FALSE, NULL, NULL);
++
+   i_am_writing_byteswapped = TRUE;
+   serialise_tree (tree, &two);
++  serialise_tree (tree, &three);
+   i_am_writing_byteswapped = FALSE;
+ 
+-  g_variant_serialised_byteswap (two);
+-
+-  g_assert_cmpmem (one.data, one.size, two.data, two.size);
+-  g_assert_cmpuint (one.depth, ==, two.depth);
+-
++  /* Swap the first byteswapped one back using the function we want to test. */
++  two_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (two.type_info)),
++                                         two.data, two.size, FALSE, NULL, NULL);
++  two_byteswapped = g_variant_byteswap (two_variant);
++
++  /* Make the second byteswapped one non-normal (hopefully), and then byteswap
++   * it back using the function we want to test in its non-normal mode.
++   * This might not work because it’s not necessarily possible to make an
++   * arbitrary random variant non-normal. Adding a single zero byte to the end
++   * often makes something non-normal but still readable. */
++  three_size_copy = three.size + 1;
++  three_data_copy = g_malloc (three_size_copy);
++  memcpy (three_data_copy, three.data, three.size);
++  three_data_copy[three.size] = '\0';
++
++  three_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (three.type_info)),
++                                           three_data_copy, three_size_copy, FALSE, NULL, NULL);
++  three_byteswapped = g_variant_byteswap (three_variant);
++
++  /* Check they’re the same. We can always compare @one_variant and
++   * @two_byteswapped. We can only compare @two_byteswapped and
++   * @three_byteswapped if @two_variant and @three_variant are equal: in that
++   * case, the corruption to @three_variant was enough to make it non-normal but
++   * not enough to change its value. */
++  g_assert_cmpvariant (one_variant, two_byteswapped);
++
++  if (g_variant_equal (two_variant, three_variant))
++    g_assert_cmpvariant (two_byteswapped, three_byteswapped);
++
++  g_variant_unref (three_byteswapped);
++  g_variant_unref (three_variant);
++  g_variant_unref (two_byteswapped);
++  g_variant_unref (two_variant);
++  g_variant_unref (one_variant);
+   tree_instance_free (tree);
+   g_free (one.data);
+   g_free (two.data);
++  g_free (three.data);
++  g_free (three_data_copy);
+ }
+ 
+ static void
+-- 
+2.40.0
diff --git a/base/rx/rx-glib2/3136.patch b/base/rx/rx-glib2/3136.patch
new file mode 100644
index 0000000..57e1b95
--- /dev/null
+++ b/base/rx/rx-glib2/3136.patch
@@ -0,0 +1,65 @@
+From ba2137b0d9ea3744155be81a5ba770c6535b46f3 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Thu, 15 Dec 2022 12:51:37 +0000
+Subject: [PATCH] gvariant-serialiser: Convert endianness of offsets
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The array of offsets is little-endian, even on big-endian architectures
+like s390x.
+
+Fixes: ade71fb5 "gvariant: Don’t allow child elements to overlap with each other"
+Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/2839
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ glib/gvariant-serialiser.c | 19 +++++++++++--------
+ 1 file changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index fadefab659..f443c2eb85 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -714,17 +714,19 @@ gvs_variable_sized_array_n_children (GVariantSerialised value)
+ /* Find the index of the first out-of-order element in @data, assuming that
+  * @data is an array of elements of given @type, starting at index @start and
+  * containing a further @len-@start elements. */
+-#define DEFINE_FIND_UNORDERED(type) \
++#define DEFINE_FIND_UNORDERED(type, le_to_native) \
+   static gsize \
+   find_unordered_##type (const guint8 *data, gsize start, gsize len) \
+   { \
+     gsize off; \
+-    type current, previous; \
++    type current_le, previous_le, current, previous; \
+     \
+-    memcpy (&previous, data + start * sizeof (current), sizeof (current)); \
++    memcpy (&previous_le, data + start * sizeof (current), sizeof (current)); \
++    previous = le_to_native (previous_le); \
+     for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \
+       { \
+-        memcpy (&current, data + off, sizeof (current)); \
++        memcpy (&current_le, data + off, sizeof (current)); \
++        current = le_to_native (current_le); \
+         if (current < previous) \
+           break; \
+         previous = current; \
+@@ -732,10 +734,11 @@ gvs_variable_sized_array_n_children (GVariantSerialised value)
+     return off / sizeof (current) - 1; \
+   }
+ 
+-DEFINE_FIND_UNORDERED (guint8);
+-DEFINE_FIND_UNORDERED (guint16);
+-DEFINE_FIND_UNORDERED (guint32);
+-DEFINE_FIND_UNORDERED (guint64);
++#define NO_CONVERSION(x) (x)
++DEFINE_FIND_UNORDERED (guint8, NO_CONVERSION);
++DEFINE_FIND_UNORDERED (guint16, GUINT16_FROM_LE);
++DEFINE_FIND_UNORDERED (guint32, GUINT32_FROM_LE);
++DEFINE_FIND_UNORDERED (guint64, GUINT64_FROM_LE);
+ 
+ static GVariantSerialised
+ gvs_variable_sized_array_get_child (GVariantSerialised value,
+-- 
+GitLab
+
diff --git a/base/rx/rx-glib2/3163.patch b/base/rx/rx-glib2/3163.patch
new file mode 100644
index 0000000..13fe47c
--- /dev/null
+++ b/base/rx/rx-glib2/3163.patch
@@ -0,0 +1,199 @@
+From 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Thu, 15 Dec 2022 13:00:39 +0000
+Subject: [PATCH 1/2] =?UTF-8?q?gvariant:=20Check=20offset=20table=20doesn?=
+ =?UTF-8?q?=E2=80=99t=20fall=20outside=20variant=20bounds?=
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When dereferencing the first entry in the offset table for a tuple,
+check that it doesn’t fall outside the bounds of the variant first.
+
+This prevents an out-of-bounds read from some non-normal tuples.
+
+This bug was introduced in commit 73d0aa81c2575a5c9ae77d.
+
+Includes a unit test, although the test will likely only catch the
+original bug if run with asan enabled.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2840
+oss-fuzz#54302
+---
+ glib/gvariant-serialiser.c | 12 ++++++--
+ glib/tests/gvariant.c      | 63 ++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 72 insertions(+), 3 deletions(-)
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index f443c2eb85..4e4a73ad17 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -984,7 +984,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
+ 
+   member_info = g_variant_type_info_member_info (value.type_info, index_);
+ 
+-  if (member_info->i + 1)
++  if (member_info->i + 1 &&
++      offset_size * (member_info->i + 1) <= value.size)
+     member_start = gvs_read_unaligned_le (value.data + value.size -
+                                           offset_size * (member_info->i + 1),
+                                           offset_size);
+@@ -995,7 +996,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
+   member_start &= member_info->b;
+   member_start |= member_info->c;
+ 
+-  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
++  if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST &&
++      offset_size * (member_info->i + 1) <= value.size)
+     member_end = value.size - offset_size * (member_info->i + 1);
+ 
+   else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
+@@ -1006,11 +1008,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised  value,
+       member_end = member_start + fixed_size;
+     }
+ 
+-  else /* G_VARIANT_MEMBER_ENDING_OFFSET */
++  else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET &&
++           offset_size * (member_info->i + 2) <= value.size)
+     member_end = gvs_read_unaligned_le (value.data + value.size -
+                                         offset_size * (member_info->i + 2),
+                                         offset_size);
+ 
++  else  /* invalid */
++    member_end = G_MAXSIZE;
++
+   if (out_member_start != NULL)
+     *out_member_start = member_start;
+   if (out_member_end != NULL)
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index b360888e4d..98c51a1d75 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -5576,6 +5576,67 @@ test_normal_checking_tuple_offsets4 (void)
+   g_variant_unref (variant);
+ }
+ 
++/* This is a regression test that dereferencing the first element in the offset
++ * table doesn’t dereference memory before the start of the GVariant. The first
++ * element in the offset table gives the offset of the final member in the
++ * tuple (the offset table is stored in reverse), and the position of this final
++ * member is needed to check that none of the tuple members overlap with the
++ * offset table
++ *
++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */
++static void
++test_normal_checking_tuple_offsets5 (void)
++{
++  /* A tuple of type (sss) in normal form would have an offset table with two
++   * entries:
++   *  - The first entry (lowest index in the table) gives the offset of the
++   *    third `s` in the tuple, as the offset table is reversed compared to the
++   *    tuple members.
++   *  - The second entry (highest index in the table) gives the offset of the
++   *    second `s` in the tuple.
++   *  - The offset of the first `s` in the tuple is always 0.
++   *
++   * See §2.5.4 (Structures) of the GVariant specification for details, noting
++   * that the table is only layed out this way because all three members of the
++   * tuple have non-fixed sizes.
++   *
++   * It’s not clear whether the 0xaa data of this variant is part of the strings
++   * in the tuple, or part of the offset table. It doesn’t really matter. This
++   * is a regression test to check that the code to validate the offset table
++   * doesn’t unconditionally try to access the first entry in the offset table
++   * by subtracting the table size from the end of the GVariant data.
++   *
++   * In this non-normal case, that would result in an address off the start of
++   * the GVariant data, and an out-of-bounds read, because the GVariant is one
++   * byte long, but the offset table is calculated as two bytes long (with 1B
++   * sized entries) from the tuple’s type.
++   */
++  const GVariantType *data_type = G_VARIANT_TYPE ("(sss)");
++  const guint8 data[] = { 0xaa };
++  gsize size = sizeof (data);
++  GVariant *variant = NULL;
++  GVariant *normal_variant = NULL;
++  GVariant *expected = NULL;
++
++  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840");
++
++  variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
++  g_assert_nonnull (variant);
++
++  g_assert_false (g_variant_is_normal_form (variant));
++
++  normal_variant = g_variant_get_normal_form (variant);
++  g_assert_nonnull (normal_variant);
++
++  expected = g_variant_new_parsed ("('', '', '')");
++  g_assert_cmpvariant (expected, variant);
++  g_assert_cmpvariant (expected, normal_variant);
++
++  g_variant_unref (expected);
++  g_variant_unref (normal_variant);
++  g_variant_unref (variant);
++}
++
+ /* Test that an otherwise-valid serialised GVariant is considered non-normal if
+  * its offset table entries are too wide.
+  *
+@@ -5827,6 +5888,8 @@ main (int argc, char **argv)
+                    test_normal_checking_tuple_offsets3);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
+                    test_normal_checking_tuple_offsets4);
++  g_test_add_func ("/gvariant/normal-checking/tuple-offsets5",
++                   test_normal_checking_tuple_offsets5);
+   g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
+                    test_normal_checking_tuple_offsets_minimal_sized);
+   g_test_add_func ("/gvariant/normal-checking/empty-object-path",
+-- 
+GitLab
+
+
+From 21a204147b16539b3eda3143b32844c49e29f4d4 Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@endlessos.org>
+Date: Thu, 15 Dec 2022 16:49:28 +0000
+Subject: [PATCH 2/2] gvariant: Propagate trust when getting a child of a
+ serialised variant
+
+If a variant is trusted, that means all its children are trusted, so
+ensure that their checked offsets are set as such.
+
+This allows a lot of the offset table checks to be avoided when getting
+children from trusted serialised tuples, which speeds things up.
+
+No unit test is included because this is just a performance fix. If
+there are other slownesses, or regressions, in serialised `GVariant`
+performance, the fuzzing setup will catch them like it did this one.
+
+This change does reduce the time to run the oss-fuzz reproducer from 80s
+to about 0.7s on my machine.
+
+Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
+
+Fixes: #2841
+oss-fuzz#54314
+---
+ glib/gvariant-core.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index f441c4757e..4778022829 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -1198,8 +1198,8 @@ g_variant_get_child_value (GVariant *value,
+     child->contents.serialised.bytes =
+       g_bytes_ref (value->contents.serialised.bytes);
+     child->contents.serialised.data = s_child.data;
+-    child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
+-    child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to;
++    child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to;
++    child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to;
+ 
+     return child;
+   }
+-- 
+GitLab
+
diff --git a/base/rx/rx-glib2/3272.patch b/base/rx/rx-glib2/3272.patch
new file mode 100644
index 0000000..c97e428
--- /dev/null
+++ b/base/rx/rx-glib2/3272.patch
@@ -0,0 +1,141 @@
+From 059f4f3999f1de506417611318c6f27db57fb689 Mon Sep 17 00:00:00 2001
+From: Marius Vollmer <mvollmer@redhat.com>
+Date: Mon, 13 Feb 2023 14:12:52 +0200
+Subject: [PATCH] gdbus: Never buffer reads during server authentication
+
+Otherwise, the content of the buffer is thrown away when switching
+from reading via a GDataInputStream to unbuffered reads when waiting
+for the "BEGIN" line.
+
+(The code already tried to protect against over-reading like this by
+using unbuffered reads for the last few lines of the auth protocol,
+but it might already be too late at that point.  The buffer of the
+GDataInputStream might already contain the "BEGIN" line for example.)
+
+This matters when connecting a sd-bus client directly to a GDBus
+client.  A sd-bus client optimistically sends the whole auth
+conversation in one go without waiting for intermediate replies.  This
+is done to improve performance for the many short-lived connections
+that are typically made.
+---
+ gio/gdbusauth.c | 50 ++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 31 insertions(+), 19 deletions(-)
+
+diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c
+index c430f0cf0..17c7d47b7 100644
+--- a/gio/gdbusauth.c
++++ b/gio/gdbusauth.c
+@@ -933,7 +933,6 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+ {
+   gboolean ret;
+   ServerState state;
+-  GDataInputStream *dis;
+   GDataOutputStream *dos;
+   GError *local_error;
+   gchar *line;
+@@ -949,7 +948,6 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+   _g_dbus_auth_add_mechs (auth, observer);
+ 
+   ret = FALSE;
+-  dis = NULL;
+   dos = NULL;
+   mech = NULL;
+   negotiated_capabilities = 0;
+@@ -965,13 +963,18 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+       goto out;
+     }
+ 
+-  dis = G_DATA_INPUT_STREAM (g_data_input_stream_new (g_io_stream_get_input_stream (auth->priv->stream)));
++  /* We use an extremely slow (but reliable) line reader for input
++   * instead of something buffered - this basically does a recvfrom()
++   * system call per character
++   *
++   * (the problem with using GDataInputStream's read_line is that
++   * because of buffering it might start reading into the first D-Bus
++   * message that appears after "BEGIN\r\n"....)
++   */
++
+   dos = G_DATA_OUTPUT_STREAM (g_data_output_stream_new (g_io_stream_get_output_stream (auth->priv->stream)));
+-  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (dis), FALSE);
+   g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (dos), FALSE);
+ 
+-  g_data_input_stream_set_newline_type (dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+-
+   /* read the NUL-byte, possibly with credentials attached */
+ #ifdef G_OS_UNIX
+ #ifndef G_CREDENTIALS_PREFER_MESSAGE_PASSING
+@@ -1010,11 +1013,22 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+     }
+   else
+     {
++      gchar c;
++      gssize num_read;
++
+       local_error = NULL;
+-      (void)g_data_input_stream_read_byte (dis, cancellable, &local_error);
+-      if (local_error != NULL)
++      num_read = g_input_stream_read (g_io_stream_get_input_stream (auth->priv->stream),
++                                      &c, 1,
++                                      cancellable, &local_error);
++      if (num_read != 1 || local_error != NULL)
+         {
+-          g_propagate_error (error, local_error);
++          if (local_error == NULL)
++            g_set_error_literal (error,
++                                 G_IO_ERROR,
++                                 G_IO_ERROR_FAILED,
++                                 _ ("Unexpected lack of content trying to read a byte"));
++          else
++            g_propagate_error (error, local_error);
+           goto out;
+         }
+     }
+@@ -1050,7 +1064,10 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+         {
+         case SERVER_STATE_WAITING_FOR_AUTH:
+           debug_print ("SERVER: WaitingForAuth");
+-          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
++          line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
++                                                    &line_length,
++                                                    cancellable,
++                                                    error);
+           debug_print ("SERVER: WaitingForAuth, read '%s'", line);
+           if (line == NULL)
+             goto out;
+@@ -1260,7 +1277,10 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+ 
+         case SERVER_STATE_WAITING_FOR_DATA:
+           debug_print ("SERVER: WaitingForData");
+-          line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
++          line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
++                                                    &line_length,
++                                                    cancellable,
++                                                    error);
+           debug_print ("SERVER: WaitingForData, read '%s'", line);
+           if (line == NULL)
+             goto out;
+@@ -1299,13 +1319,6 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+ 
+         case SERVER_STATE_WAITING_FOR_BEGIN:
+           debug_print ("SERVER: WaitingForBegin");
+-          /* Use extremely slow (but reliable) line reader - this basically
+-           * does a recvfrom() system call per character
+-           *
+-           * (the problem with using GDataInputStream's read_line is that because of
+-           * buffering it might start reading into the first D-Bus message that
+-           * appears after "BEGIN\r\n"....)
+-           */
+           line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
+                                                     &line_length,
+                                                     cancellable,
+@@ -1364,7 +1377,6 @@ _g_dbus_auth_run_server (GDBusAuth              *auth,
+ 
+  out:
+   g_clear_object (&mech);
+-  g_clear_object (&dis);
+   g_clear_object (&dos);
+   g_clear_object (&own_credentials);
+ 
+-- 
+2.41.0
+
diff --git a/base/rx/rx-glib2/3353.patch b/base/rx/rx-glib2/3353.patch
new file mode 100644
index 0000000..eb9020a
--- /dev/null
+++ b/base/rx/rx-glib2/3353.patch
@@ -0,0 +1,1078 @@
+From 68f0cd7f3d351da1f011a65e080d8e3f26a31525 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 28 Mar 2023 14:38:08 +0200
+Subject: [PATCH 1/7] gmain: unify win/unix implementations for child watcher
+
+Let's move the difference between the win/unix implementations closer to
+where the difference is. Thereby, we easier see the two implementations
+side by side. Splitting it at a higher layer makes the code harder to
+read.
+
+This is just a preparation for what comes next.
+---
+ glib/gmain.c | 192 ++++++++++++++++++++++++---------------------------
+ 1 file changed, 92 insertions(+), 100 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index f0cf700c0..228737efa 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -446,6 +446,11 @@ static gboolean g_child_watch_dispatch (GSource     *source,
+ 					GSourceFunc  callback,
+ 					gpointer     user_data);
+ static void     g_child_watch_finalize (GSource     *source);
++
++#ifndef G_OS_WIN32
++static void unref_unix_signal_handler_unlocked (int signum);
++#endif
++
+ #ifdef G_OS_UNIX
+ static void g_unix_signal_handler (int signum);
+ static gboolean g_unix_signal_watch_prepare  (GSource     *source,
+@@ -5214,17 +5219,49 @@ g_timeout_add_seconds (guint       interval,
+ 
+ /* Child watch functions */
+ 
+-#ifdef G_OS_WIN32
++#ifdef HAVE_PIDFD
++static int
++siginfo_t_to_wait_status (const siginfo_t *info)
++{
++  /* Each of these returns is essentially the inverse of WIFEXITED(),
++   * WIFSIGNALED(), etc. */
++  switch (info->si_code)
++    {
++    case CLD_EXITED:
++      return W_EXITCODE (info->si_status, 0);
++    case CLD_KILLED:
++      return W_EXITCODE (0, info->si_status);
++    case CLD_DUMPED:
++      return W_EXITCODE (0, info->si_status | WCOREFLAG);
++    case CLD_CONTINUED:
++      return __W_CONTINUED;
++    case CLD_STOPPED:
++    case CLD_TRAPPED:
++    default:
++      return W_STOPCODE (info->si_status);
++    }
++}
++#endif  /* HAVE_PIDFD */
+ 
+ static gboolean
+ g_child_watch_prepare (GSource *source,
+ 		       gint    *timeout)
+ {
++#ifdef G_OS_WIN32
+   *timeout = -1;
+   return FALSE;
++#else /* G_OS_WIN32 */
++  {
++    GChildWatchSource *child_watch_source;
++
++    child_watch_source = (GChildWatchSource *) source;
++
++    return g_atomic_int_get (&child_watch_source->child_exited);
++  }
++#endif /* G_OS_WIN32 */
+ }
+ 
+-static gboolean 
++static gboolean
+ g_child_watch_check (GSource  *source)
+ {
+   GChildWatchSource *child_watch_source;
+@@ -5232,6 +5269,7 @@ g_child_watch_check (GSource  *source)
+ 
+   child_watch_source = (GChildWatchSource *) source;
+ 
++#ifdef G_OS_WIN32
+   child_exited = child_watch_source->poll.revents & G_IO_IN;
+ 
+   if (child_exited)
+@@ -5246,15 +5284,45 @@ g_child_watch_check (GSource  *source)
+        */
+       if (!GetExitCodeProcess (child_watch_source->pid, &child_status))
+         {
+-	  gchar *emsg = g_win32_error_message (GetLastError ());
+-	  g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg);
+-	  g_free (emsg);
++          gchar *emsg = g_win32_error_message (GetLastError ());
++          g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg);
++          g_free (emsg);
+ 
+-	  child_watch_source->child_status = -1;
+-	}
++          child_watch_source->child_status = -1;
++        }
+       else
+-	child_watch_source->child_status = child_status;
++        child_watch_source->child_status = child_status;
+     }
++#else /* G_OS_WIN32 */
++#ifdef HAVE_PIDFD
++  if (child_watch_source->using_pidfd)
++    {
++      child_exited = child_watch_source->poll.revents & G_IO_IN;
++
++      if (child_exited)
++        {
++          siginfo_t child_info = { 0, };
++
++          /* Get the exit status */
++          if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 &&
++              child_info.si_pid != 0)
++            {
++              /* waitid() helpfully provides the wait status in a decomposed
++               * form which is quite useful. Unfortunately we have to report it
++               * to the #GChildWatchFunc as a waitpid()-style platform-specific
++               * wait status, so that the user code in #GChildWatchFunc can then
++               * call WIFEXITED() (etc.) on it. That means re-composing the
++               * status information. */
++              child_watch_source->child_status = siginfo_t_to_wait_status (&child_info);
++              child_watch_source->child_exited = TRUE;
++            }
++        }
++
++      return child_exited;
++    }
++#endif  /* HAVE_PIDFD */
++  child_exited = g_atomic_int_get (&child_watch_source->child_exited);
++#endif /* G_OS_WIN32 */
+ 
+   return child_exited;
+ }
+@@ -5262,9 +5330,24 @@ g_child_watch_check (GSource  *source)
+ static void
+ g_child_watch_finalize (GSource *source)
+ {
++#ifndef G_OS_WIN32
++  GChildWatchSource *child_watch_source = (GChildWatchSource *) source;
++
++  if (child_watch_source->using_pidfd)
++    {
++      if (child_watch_source->poll.fd >= 0)
++        close (child_watch_source->poll.fd);
++      return;
++    }
++
++  G_LOCK (unix_signal_lock);
++  unix_child_watches = g_slist_remove (unix_child_watches, source);
++  unref_unix_signal_handler_unlocked (SIGCHLD);
++  G_UNLOCK (unix_signal_lock);
++#endif /* G_OS_WIN32 */
+ }
+ 
+-#else /* G_OS_WIN32 */
++#ifndef G_OS_WIN32
+ 
+ static void
+ wake_source (GSource *source)
+@@ -5397,79 +5480,6 @@ dispatch_unix_signals (void)
+   G_UNLOCK(unix_signal_lock);
+ }
+ 
+-static gboolean
+-g_child_watch_prepare (GSource *source,
+-		       gint    *timeout)
+-{
+-  GChildWatchSource *child_watch_source;
+-
+-  child_watch_source = (GChildWatchSource *) source;
+-
+-  return g_atomic_int_get (&child_watch_source->child_exited);
+-}
+-
+-#ifdef HAVE_PIDFD
+-static int
+-siginfo_t_to_wait_status (const siginfo_t *info)
+-{
+-  /* Each of these returns is essentially the inverse of WIFEXITED(),
+-   * WIFSIGNALED(), etc. */
+-  switch (info->si_code)
+-    {
+-    case CLD_EXITED:
+-      return W_EXITCODE (info->si_status, 0);
+-    case CLD_KILLED:
+-      return W_EXITCODE (0, info->si_status);
+-    case CLD_DUMPED:
+-      return W_EXITCODE (0, info->si_status | WCOREFLAG);
+-    case CLD_CONTINUED:
+-      return __W_CONTINUED;
+-    case CLD_STOPPED:
+-    case CLD_TRAPPED:
+-    default:
+-      return W_STOPCODE (info->si_status);
+-    }
+-}
+-#endif  /* HAVE_PIDFD */
+-
+-static gboolean
+-g_child_watch_check (GSource *source)
+-{
+-  GChildWatchSource *child_watch_source;
+-
+-  child_watch_source = (GChildWatchSource *) source;
+-
+-#ifdef HAVE_PIDFD
+-  if (child_watch_source->using_pidfd)
+-    {
+-      gboolean child_exited = child_watch_source->poll.revents & G_IO_IN;
+-
+-      if (child_exited)
+-        {
+-          siginfo_t child_info = { 0, };
+-
+-          /* Get the exit status */
+-          if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 &&
+-              child_info.si_pid != 0)
+-            {
+-              /* waitid() helpfully provides the wait status in a decomposed
+-               * form which is quite useful. Unfortunately we have to report it
+-               * to the #GChildWatchFunc as a waitpid()-style platform-specific
+-               * wait status, so that the user code in #GChildWatchFunc can then
+-               * call WIFEXITED() (etc.) on it. That means re-composing the
+-               * status information. */
+-              child_watch_source->child_status = siginfo_t_to_wait_status (&child_info);
+-              child_watch_source->child_exited = TRUE;
+-            }
+-        }
+-
+-      return child_exited;
+-    }
+-#endif  /* HAVE_PIDFD */
+-
+-  return g_atomic_int_get (&child_watch_source->child_exited);
+-}
+-
+ static gboolean
+ g_unix_signal_watch_prepare (GSource *source,
+ 			     gint    *timeout)
+@@ -5648,24 +5658,6 @@ g_unix_signal_watch_finalize (GSource    *source)
+   G_UNLOCK (unix_signal_lock);
+ }
+ 
+-static void
+-g_child_watch_finalize (GSource *source)
+-{
+-  GChildWatchSource *child_watch_source = (GChildWatchSource *) source;
+-
+-  if (child_watch_source->using_pidfd)
+-    {
+-      if (child_watch_source->poll.fd >= 0)
+-        close (child_watch_source->poll.fd);
+-      return;
+-    }
+-
+-  G_LOCK (unix_signal_lock);
+-  unix_child_watches = g_slist_remove (unix_child_watches, source);
+-  unref_unix_signal_handler_unlocked (SIGCHLD);
+-  G_UNLOCK (unix_signal_lock);
+-}
+-
+ #endif /* G_OS_WIN32 */
+ 
+ static gboolean
+-- 
+2.41.0
+
+
+From 984e04a77b29c60f66fd60b1f9690e6ff7880574 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 28 Mar 2023 15:44:30 +0200
+Subject: [PATCH 2/7] gmain: simplify handling child watchers in
+ dispatch_unix_signals_unlocked()
+
+- if a child watch source has "using_pidfd", it is never linked in the
+  unix_child_watches list. Drop that check.
+- replace the deep nested if, with an early "continue" in the loop,
+  if we detect there is nothing to do. It makes the code easier to
+  read.
+---
+ glib/gmain.c | 39 +++++++++++++++++++--------------------
+ 1 file changed, 19 insertions(+), 20 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 228737efa..8a030a409 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -5430,31 +5430,30 @@ dispatch_unix_signals_unlocked (void)
+       for (node = unix_child_watches; node; node = node->next)
+         {
+           GChildWatchSource *source = node->data;
++          pid_t pid;
+ 
+-          if (!source->using_pidfd &&
+-              !g_atomic_int_get (&source->child_exited))
++          if (g_atomic_int_get (&source->child_exited))
++             continue;
++
++          do
+             {
+-              pid_t pid;
+-              do
+-                {
+-                  g_assert (source->pid > 0);
++              g_assert (source->pid > 0);
+ 
+-                  pid = waitpid (source->pid, &source->child_status, WNOHANG);
+-                  if (pid > 0)
+-                    {
+-                      g_atomic_int_set (&source->child_exited, TRUE);
+-                      wake_source ((GSource *) source);
+-                    }
+-                  else if (pid == -1 && errno == ECHILD)
+-                    {
+-                      g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes.");
+-                      source->child_status = 0;
+-                      g_atomic_int_set (&source->child_exited, TRUE);
+-                      wake_source ((GSource *) source);
+-                    }
++              pid = waitpid (source->pid, &source->child_status, WNOHANG);
++              if (pid > 0)
++                {
++                  g_atomic_int_set (&source->child_exited, TRUE);
++                  wake_source ((GSource *) source);
++                }
++              else if (pid == -1 && errno == ECHILD)
++                {
++                  g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes.");
++                  source->child_status = 0;
++                  g_atomic_int_set (&source->child_exited, TRUE);
++                  wake_source ((GSource *) source);
+                 }
+-              while (pid == -1 && errno == EINTR);
+             }
++          while (pid == -1 && errno == EINTR);
+         }
+     }
+ 
+-- 
+2.41.0
+
+
+From aaac91a86288e103f7f413b9d075acc803178da9 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 17 May 2023 08:04:02 +0200
+Subject: [PATCH 3/7] gmain: remove unnecessary initialization of
+ source_timeout in g_main_context_prepare_unlocked()
+
+Note that the variable source_timeout is already initialized upon
+definition, at the beginning of the block.
+
+It's easy to see, that no code changes the variable between the variable
+definition, and the place where it was initialized. It was thus
+unnecessary.
+
+It's not about dropping the unnecessary code (the compiler could do that
+just fine too). It's that there is the other branch of the "if/else", where
+the variable is also not initialized. But the other branch also requires
+that the variable is in fact initialized to -1, because prepare()
+callbacks are free not to explicitly set the output value. So both
+branches require the variable to be initialized to -1, but only one of
+them did. This poses unnecessary questions about whether anything is
+wrong. Avoid that by dropping the redundant code.
+---
+ glib/gmain.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 8a030a409..28fbcc015 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -3695,10 +3695,7 @@ g_main_context_prepare (GMainContext *context,
+               context->in_check_or_prepare--;
+             }
+           else
+-            {
+-              source_timeout = -1;
+-              result = FALSE;
+-            }
++            result = FALSE;
+ 
+           if (result == FALSE && source->priv->ready_time != -1)
+             {
+-- 
+2.41.0
+
+
+From b71ae65f14843cc09819e5b482667e5c941a27af Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 17 May 2023 08:15:16 +0200
+Subject: [PATCH 4/7] gmain: remove unnecessary initialization of *timeout in
+ prepare() callbacks
+
+Note that the prepare callback only has one caller, which pre-initializes
+the timeout argument to -1. That may be an implementation detail and not
+publicly promised, but it wouldn't make sense to do it any other way in
+the caller.
+
+Also, note that g_unix_signal_watch_prepare() and the UNIX branch of
+g_child_watch_prepare() already relied on that.
+---
+ gio/gsocket.c         | 2 --
+ glib/giounix.c        | 2 --
+ glib/giowin32.c       | 2 --
+ glib/gmain.c          | 1 -
+ glib/tests/mainloop.c | 2 ++
+ 5 files changed, 2 insertions(+), 7 deletions(-)
+
+diff --git a/gio/gsocket.c b/gio/gsocket.c
+index f39a568b3..c624eb1ae 100644
+--- a/gio/gsocket.c
++++ b/gio/gsocket.c
+@@ -3955,8 +3955,6 @@ socket_source_prepare (GSource *source,
+ {
+   GSocketSource *socket_source = (GSocketSource *)source;
+ 
+-  *timeout = -1;
+-
+ #ifdef G_OS_WIN32
+   if ((socket_source->pollfd.revents & G_IO_NVAL) != 0)
+     return TRUE;
+diff --git a/glib/giounix.c b/glib/giounix.c
+index b86d79db7..94b33253f 100644
+--- a/glib/giounix.c
++++ b/glib/giounix.c
+@@ -129,8 +129,6 @@ g_io_unix_prepare (GSource  *source,
+   GIOUnixWatch *watch = (GIOUnixWatch *)source;
+   GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
+ 
+-  *timeout = -1;
+-
+   /* Only return TRUE here if _all_ bits in watch->condition will be set
+    */
+   return ((watch->condition & buffer_condition) == watch->condition);
+diff --git a/glib/giowin32.c b/glib/giowin32.c
+index b0b6c3d85..e4b171b0d 100644
+--- a/glib/giowin32.c
++++ b/glib/giowin32.c
+@@ -707,8 +707,6 @@ g_io_win32_prepare (GSource *source,
+   GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
+   int event_mask;
+   
+-  *timeout = -1;
+-  
+   if (channel->debug)
+     g_print ("g_io_win32_prepare: source=%p channel=%p", source, channel);
+ 
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 28fbcc015..aec04314c 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -5245,7 +5245,6 @@ g_child_watch_prepare (GSource *source,
+ 		       gint    *timeout)
+ {
+ #ifdef G_OS_WIN32
+-  *timeout = -1;
+   return FALSE;
+ #else /* G_OS_WIN32 */
+   {
+diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c
+index d43b2cf08..a7c5b33d1 100644
+--- a/glib/tests/mainloop.c
++++ b/glib/tests/mainloop.c
+@@ -34,6 +34,8 @@ cb (gpointer data)
+ static gboolean
+ prepare (GSource *source, gint *time)
+ {
++  g_assert_nonnull (time);
++  g_assert_cmpint (*time, ==, -1);
+   return FALSE;
+ }
+ static gboolean
+-- 
+2.41.0
+
+
+From ccbebd3bd2d9163da3e2ad7f213ded04cd92136d Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 28 Mar 2023 14:30:58 +0200
+Subject: [PATCH 5/7] gmain: fix race with waitpid() and child watcher sources
+
+GChildWatchSource uses waitpid(), among pidfd and GetExitCodeProcess().
+It thus only works for child processes which the user must ensure to
+exist and not being reaped yet. Also, the user must not kill() the PID
+after the child process is reaped and must not race kill() against
+waitpid(). Also, the user must not call waitpid()/kill() after the child
+process is reaped.
+
+Previously, GChildWatchSource would call waitpid() already when adding
+the source (g_child_watch_source_new()) and from the worker thread
+(dispatch_unix_signals_unlocked()). That is racy:
+
+- if a child watcher is attached and did not yet fire, you cannot call
+  kill() on the PID without racing against the PID being reaped on the
+  worker thread. That would then lead to ESRCH or even worse, killing
+  the wrong process.
+
+- if you g_source_destroy() the source that didn't fire yet, the user
+  doesn't know whether the PID was reaped in the background. Any
+  subsequent kill()/waitpid() may fail with ESRCH/ECHILD or even address
+  the wrong process.
+
+The race is most visible on Unix without pidfd support, because then the
+process gets reaped on the worker thread or during g_child_watch_source_new().
+But it's also with Windows and pidfd, because we would have waited for
+the process in g_child_watch_check(), where other callbacks could fire
+between reaping the process status and emitting the source's callback.
+
+Fix all that by calling waitpid() right before dispatching the callback.
+---
+ glib/gmain.c      | 209 +++++++++++++++++++++++++++-------------------
+ glib/tests/unix.c |  93 +++++++++++++++++++++
+ 2 files changed, 218 insertions(+), 84 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index aec04314c..d1ab421fb 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -350,12 +350,11 @@ struct _GChildWatchSource
+ {
+   GSource     source;
+   GPid        pid;
+-  gint        child_status;
+   /* @poll is always used on Windows, and used on Unix iff @using_pidfd is set: */
+   GPollFD     poll;
+ #ifndef G_OS_WIN32
+-  gboolean    child_exited; /* (atomic); not used iff @using_pidfd is set */
+-  gboolean    using_pidfd;
++  gboolean child_maybe_exited; /* (atomic) */
++  gboolean using_pidfd;
+ #endif /* G_OS_WIN32 */
+ };
+ 
+@@ -5238,7 +5237,7 @@ siginfo_t_to_wait_status (const siginfo_t *info)
+       return W_STOPCODE (info->si_status);
+     }
+ }
+-#endif  /* HAVE_PIDFD */
++#endif /* HAVE_PIDFD */
+ 
+ static gboolean
+ g_child_watch_prepare (GSource *source,
+@@ -5246,19 +5245,19 @@ g_child_watch_prepare (GSource *source,
+ {
+ #ifdef G_OS_WIN32
+   return FALSE;
+-#else /* G_OS_WIN32 */
++#else  /* G_OS_WIN32 */
+   {
+     GChildWatchSource *child_watch_source;
+ 
+     child_watch_source = (GChildWatchSource *) source;
+ 
+-    return g_atomic_int_get (&child_watch_source->child_exited);
++    return !child_watch_source->using_pidfd && g_atomic_int_get (&child_watch_source->child_maybe_exited);
+   }
+ #endif /* G_OS_WIN32 */
+ }
+ 
+ static gboolean
+-g_child_watch_check (GSource  *source)
++g_child_watch_check (GSource *source)
+ {
+   GChildWatchSource *child_watch_source;
+   gboolean child_exited;
+@@ -5267,57 +5266,15 @@ g_child_watch_check (GSource  *source)
+ 
+ #ifdef G_OS_WIN32
+   child_exited = child_watch_source->poll.revents & G_IO_IN;
+-
+-  if (child_exited)
+-    {
+-      DWORD child_status;
+-
+-      /*
+-       * Note: We do _not_ check for the special value of STILL_ACTIVE
+-       * since we know that the process has exited and doing so runs into
+-       * problems if the child process "happens to return STILL_ACTIVE(259)"
+-       * as Microsoft's Platform SDK puts it.
+-       */
+-      if (!GetExitCodeProcess (child_watch_source->pid, &child_status))
+-        {
+-          gchar *emsg = g_win32_error_message (GetLastError ());
+-          g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg);
+-          g_free (emsg);
+-
+-          child_watch_source->child_status = -1;
+-        }
+-      else
+-        child_watch_source->child_status = child_status;
+-    }
+ #else /* G_OS_WIN32 */
+ #ifdef HAVE_PIDFD
+   if (child_watch_source->using_pidfd)
+     {
+       child_exited = child_watch_source->poll.revents & G_IO_IN;
+-
+-      if (child_exited)
+-        {
+-          siginfo_t child_info = { 0, };
+-
+-          /* Get the exit status */
+-          if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 &&
+-              child_info.si_pid != 0)
+-            {
+-              /* waitid() helpfully provides the wait status in a decomposed
+-               * form which is quite useful. Unfortunately we have to report it
+-               * to the #GChildWatchFunc as a waitpid()-style platform-specific
+-               * wait status, so that the user code in #GChildWatchFunc can then
+-               * call WIFEXITED() (etc.) on it. That means re-composing the
+-               * status information. */
+-              child_watch_source->child_status = siginfo_t_to_wait_status (&child_info);
+-              child_watch_source->child_exited = TRUE;
+-            }
+-        }
+-
+       return child_exited;
+     }
+-#endif  /* HAVE_PIDFD */
+-  child_exited = g_atomic_int_get (&child_watch_source->child_exited);
++#endif /* HAVE_PIDFD */
++  child_exited = g_atomic_int_get (&child_watch_source->child_maybe_exited);
+ #endif /* G_OS_WIN32 */
+ 
+   return child_exited;
+@@ -5426,30 +5383,9 @@ dispatch_unix_signals_unlocked (void)
+       for (node = unix_child_watches; node; node = node->next)
+         {
+           GChildWatchSource *source = node->data;
+-          pid_t pid;
+-
+-          if (g_atomic_int_get (&source->child_exited))
+-             continue;
+ 
+-          do
+-            {
+-              g_assert (source->pid > 0);
+-
+-              pid = waitpid (source->pid, &source->child_status, WNOHANG);
+-              if (pid > 0)
+-                {
+-                  g_atomic_int_set (&source->child_exited, TRUE);
+-                  wake_source ((GSource *) source);
+-                }
+-              else if (pid == -1 && errno == ECHILD)
+-                {
+-                  g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes.");
+-                  source->child_status = 0;
+-                  g_atomic_int_set (&source->child_exited, TRUE);
+-                  wake_source ((GSource *) source);
+-                }
+-            }
+-          while (pid == -1 && errno == EINTR);
++          if (g_atomic_int_compare_and_exchange (&source->child_maybe_exited, FALSE, TRUE))
++            wake_source ((GSource *) source);
+         }
+     }
+ 
+@@ -5662,9 +5598,106 @@ g_child_watch_dispatch (GSource    *source,
+ {
+   GChildWatchSource *child_watch_source;
+   GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback;
++  int wait_status;
+ 
+   child_watch_source = (GChildWatchSource *) source;
+ 
++  /* We only (try to) reap the child process right before dispatching the callback.
++   * That way, the caller can rely that the process is there until the callback
++   * is invoked; or, if the caller calls g_source_destroy() without the callback
++   * being dispatched, the process is still not reaped. */
++
++#ifdef G_OS_WIN32
++  {
++    DWORD child_status;
++
++    /*
++     * Note: We do _not_ check for the special value of STILL_ACTIVE
++     * since we know that the process has exited and doing so runs into
++     * problems if the child process "happens to return STILL_ACTIVE(259)"
++     * as Microsoft's Platform SDK puts it.
++     */
++    if (!GetExitCodeProcess (child_watch_source->pid, &child_status))
++      {
++        gchar *emsg = g_win32_error_message (GetLastError ());
++        g_warning (G_STRLOC ": GetExitCodeProcess() failed: %s", emsg);
++        g_free (emsg);
++
++        /* Unknown error. We got signaled that the process might be exited,
++         * but now we failed to reap it? Assume the process is gone and proceed. */
++        wait_status = -1;
++      }
++    else
++      wait_status = child_status;
++  }
++#else /* G_OS_WIN32 */
++  {
++    gboolean child_exited = FALSE;
++
++    wait_status = -1;
++
++#ifdef HAVE_PIDFD
++    if (child_watch_source->using_pidfd)
++      {
++        siginfo_t child_info = {
++          0,
++        };
++
++        /* Get the exit status */
++        if (waitid (P_PIDFD, child_watch_source->poll.fd, &child_info, WEXITED | WNOHANG) >= 0 &&
++            child_info.si_pid != 0)
++          {
++            /* waitid() helpfully provides the wait status in a decomposed
++             * form which is quite useful. Unfortunately we have to report it
++             * to the #GChildWatchFunc as a waitpid()-style platform-specific
++             * wait status, so that the user code in #GChildWatchFunc can then
++             * call WIFEXITED() (etc.) on it. That means re-composing the
++             * status information. */
++            wait_status = siginfo_t_to_wait_status (&child_info);
++          }
++        else
++          {
++            /* Unknown error. We got signaled that the process might be exited,
++             * but now we failed to reap it? Assume the process is gone and proceed. */
++            g_warning (G_STRLOC ": pidfd signaled ready but failed");
++          }
++        child_exited = TRUE;
++      }
++#endif /* HAVE_PIDFD*/
++
++    if (!child_exited)
++      {
++        pid_t pid;
++        int wstatus;
++
++      waitpid_again:
++
++        /* We must reset the flag before waitpid(). Otherwise, there would be a
++         * race. */
++        g_atomic_int_set (&child_watch_source->child_maybe_exited, FALSE);
++
++        pid = waitpid (child_watch_source->pid, &wstatus, WNOHANG);
++
++        if (pid == 0)
++          {
++            /* Not exited yet. Wait longer. */
++            return TRUE;
++          }
++
++        if (pid > 0)
++          wait_status = wstatus;
++        else if (errno == ECHILD)
++          g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). See the documentation of g_child_watch_source_new() for possible causes.");
++        else if (errno == EINTR)
++          goto waitpid_again;
++        else
++          {
++            /* Unexpected error. Whatever happened, we are done waiting for this child. */
++          }
++      }
++  }
++#endif /* G_OS_WIN32 */
++
+   if (!callback)
+     {
+       g_warning ("Child watch source dispatched without callback. "
+@@ -5672,7 +5705,7 @@ g_child_watch_dispatch (GSource    *source,
+       return FALSE;
+     }
+ 
+-  (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data);
++  (child_watch_callback) (child_watch_source->pid, wait_status, user_data);
+ 
+   /* We never keep a child watch source around as the child is gone */
+   return FALSE;
+@@ -5731,6 +5764,14 @@ g_unix_signal_handler (int signum)
+  *   mechanism, including `waitpid(pid, ...)` or a second child-watch
+  *   source for the same @pid
+  * * the application must not ignore `SIGCHLD`
++ * * Before 2.78, the application could not send a signal (`kill()`) to the
++ *   watched @pid in a race free manner. Since 2.78, you can do that while the
++ *   associated #GMainContext is acquired.
++ * * Before 2.78, even after destroying the #GSource, you could not
++ *   be sure that @pid wasn't already reaped. Hence, it was also not
++ *   safe to `kill()` or `waitpid()` on the process ID after the child watch
++ *   source was gone. Destroying the source before it fired made it
++ *   impossible to reliably reap the process.
+  *
+  * If any of those conditions are not met, this and related APIs will
+  * not work correctly. This can often be diagnosed via a GLib warning
+@@ -5791,19 +5832,19 @@ g_child_watch_source_new (GPid pid)
+ 
+       return source;
+     }
+-  else
+-    {
+-      g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s",
+-               pid, g_strerror (errsv));
+-      /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */
+-    }
+-#endif  /* HAVE_PIDFD */
++
++  g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s",
++           pid, g_strerror (errsv));
++  /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */
++#endif /* HAVE_PIDFD */
++
++  /* We can do that without atomic, as the source is not yet added in
++   * unix_child_watches (which we do next under a lock). */
++  child_watch_source->child_maybe_exited = TRUE;
+ 
+   G_LOCK (unix_signal_lock);
+   ref_unix_signal_handler_unlocked (SIGCHLD);
+   unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source);
+-  if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0)
+-    child_watch_source->child_exited = TRUE;
+   G_UNLOCK (unix_signal_lock);
+ #endif /* !G_OS_WIN32 */
+ 
+diff --git a/glib/tests/unix.c b/glib/tests/unix.c
+index 7639d066a..6f40ff893 100644
+--- a/glib/tests/unix.c
++++ b/glib/tests/unix.c
+@@ -330,6 +330,99 @@ test_get_passwd_entry_nonexistent (void)
+   g_clear_error (&local_error);
+ }
+ 
++static void
++_child_wait_watch_cb (GPid pid,
++                      gint wait_status,
++                      gpointer user_data)
++{
++  gboolean *p_got_callback = user_data;
++
++  g_assert_nonnull (p_got_callback);
++  g_assert_false (*p_got_callback);
++  *p_got_callback = TRUE;
++}
++
++static void
++test_child_wait (void)
++{
++  gboolean r;
++  GPid pid;
++  guint id;
++  pid_t pid2;
++  int wstatus;
++  gboolean got_callback = FALSE;
++  gboolean iterate_maincontext = g_test_rand_bit ();
++  char **argv;
++  int errsv;
++
++  /* - We spawn a trivial child process that exits after a short time.
++   * - We schedule a g_child_watch_add()
++   * - we may iterate the GMainContext a bit. Randomly we either get the
++   *   child-watcher callback or not.
++   * - if we didn't get the callback, we g_source_remove() the child watcher.
++   *
++   * Afterwards, if the callback didn't fire, we check that we are able to waitpid()
++   * on the process ourselves. Of course, if the child watcher notified, the waitpid()
++   * will fail with ECHILD.
++   */
++
++  argv = g_test_rand_bit () ? ((char *[]){ "/bin/sleep", "0.05", NULL }) : ((char *[]){ "/bin/true", NULL });
++
++  r = g_spawn_async (NULL,
++                     argv,
++                     NULL,
++                     G_SPAWN_DO_NOT_REAP_CHILD,
++                     NULL,
++                     NULL,
++                     &pid,
++                     NULL);
++  if (!r)
++    {
++      /* Some odd system without /bin/sleep? Skip the test. */
++      g_test_skip ("failure to spawn test process in test_child_wait()");
++      return;
++    }
++
++  g_assert_cmpint (pid, >=, 1);
++
++  if (g_test_rand_bit ())
++    g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)));
++
++  id = g_child_watch_add (pid, _child_wait_watch_cb, &got_callback);
++
++  if (g_test_rand_bit ())
++    g_usleep (g_test_rand_int_range (0, (G_USEC_PER_SEC / 10)));
++
++  if (iterate_maincontext)
++    {
++      gint64 start_usec = g_get_monotonic_time ();
++      gint64 end_usec = start_usec + g_test_rand_int_range (0, (G_USEC_PER_SEC / 10));
++
++      while (!got_callback && g_get_monotonic_time () < end_usec)
++        g_main_context_iteration (NULL, FALSE);
++    }
++
++  if (!got_callback)
++    g_source_remove (id);
++
++  errno = 0;
++  pid2 = waitpid (pid, &wstatus, 0);
++  errsv = errno;
++  if (got_callback)
++    {
++      g_assert_true (iterate_maincontext);
++      g_assert_cmpint (errsv, ==, ECHILD);
++      g_assert_cmpint (pid2, <, 0);
++    }
++  else
++    {
++      g_assert_cmpint (errsv, ==, 0);
++      g_assert_cmpint (pid2, ==, pid);
++      g_assert_true (WIFEXITED (wstatus));
++      g_assert_cmpint (WEXITSTATUS (wstatus), ==, 0);
++    }
++}
++
+ int
+ main (int   argc,
+       char *argv[])
+-- 
+2.41.0
+
+
+From ad18d2dad0b7105a379e1b2feae653bf30418397 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Tue, 28 Mar 2023 19:53:02 +0200
+Subject: [PATCH 6/7] gmain: drop redundant using_pidfd field from
+ GChildWatchSource
+
+It's redundant, which leads to impossible code like:
+
+   if (child_watch_source->using_pidfd)
+     {
+       if (child_watch_source->poll.fd >= 0)
+         close (child_watch_source->poll.fd);
+---
+ glib/gmain.c | 23 ++++++++++++-----------
+ 1 file changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index d1ab421fb..3813b032a 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -350,11 +350,11 @@ struct _GChildWatchSource
+ {
+   GSource     source;
+   GPid        pid;
+-  /* @poll is always used on Windows, and used on Unix iff @using_pidfd is set: */
++  /* @poll is always used on Windows.
++   * On Unix, poll.fd will be negative if PIDFD is unavailable. */
+   GPollFD     poll;
+ #ifndef G_OS_WIN32
+   gboolean child_maybe_exited; /* (atomic) */
+-  gboolean using_pidfd;
+ #endif /* G_OS_WIN32 */
+ };
+ 
+@@ -5251,7 +5251,10 @@ g_child_watch_prepare (GSource *source,
+ 
+     child_watch_source = (GChildWatchSource *) source;
+ 
+-    return !child_watch_source->using_pidfd && g_atomic_int_get (&child_watch_source->child_maybe_exited);
++    if (child_watch_source->poll.fd >= 0)
++      return FALSE;
++
++    return g_atomic_int_get (&child_watch_source->child_maybe_exited);
+   }
+ #endif /* G_OS_WIN32 */
+ }
+@@ -5268,7 +5271,7 @@ g_child_watch_check (GSource *source)
+   child_exited = child_watch_source->poll.revents & G_IO_IN;
+ #else /* G_OS_WIN32 */
+ #ifdef HAVE_PIDFD
+-  if (child_watch_source->using_pidfd)
++  if (child_watch_source->poll.fd >= 0)
+     {
+       child_exited = child_watch_source->poll.revents & G_IO_IN;
+       return child_exited;
+@@ -5286,10 +5289,9 @@ g_child_watch_finalize (GSource *source)
+ #ifndef G_OS_WIN32
+   GChildWatchSource *child_watch_source = (GChildWatchSource *) source;
+ 
+-  if (child_watch_source->using_pidfd)
++  if (child_watch_source->poll.fd >= 0)
+     {
+-      if (child_watch_source->poll.fd >= 0)
+-        close (child_watch_source->poll.fd);
++      close (child_watch_source->poll.fd);
+       return;
+     }
+ 
+@@ -5637,7 +5639,7 @@ g_child_watch_dispatch (GSource    *source,
+     wait_status = -1;
+ 
+ #ifdef HAVE_PIDFD
+-    if (child_watch_source->using_pidfd)
++    if (child_watch_source->poll.fd >= 0)
+       {
+         siginfo_t child_info = {
+           0,
+@@ -5822,17 +5824,15 @@ g_child_watch_source_new (GPid pid)
+    * better than SIGCHLD.
+    */
+   child_watch_source->poll.fd = (int) syscall (SYS_pidfd_open, pid, 0);
+-  errsv = errno;
+ 
+   if (child_watch_source->poll.fd >= 0)
+     {
+-      child_watch_source->using_pidfd = TRUE;
+       child_watch_source->poll.events = G_IO_IN;
+       g_source_add_poll (source, &child_watch_source->poll);
+-
+       return source;
+     }
+ 
++  errsv = errno;
+   g_debug ("pidfd_open(%" G_PID_FORMAT ") failed with error: %s",
+            pid, g_strerror (errsv));
+   /* Fall through; likely the kernel isn’t new enough to support pidfd_open() */
+@@ -5841,6 +5841,7 @@ g_child_watch_source_new (GPid pid)
+   /* We can do that without atomic, as the source is not yet added in
+    * unix_child_watches (which we do next under a lock). */
+   child_watch_source->child_maybe_exited = TRUE;
++  child_watch_source->poll.fd = -1;
+ 
+   G_LOCK (unix_signal_lock);
+   ref_unix_signal_handler_unlocked (SIGCHLD);
+-- 
+2.41.0
+
+
+From bbdcc6e72ac97e577486aeb1baad0060ad8e1cd6 Mon Sep 17 00:00:00 2001
+From: Thomas Haller <thaller@redhat.com>
+Date: Wed, 29 Mar 2023 07:51:26 +0200
+Subject: [PATCH 7/7] gmain: ensure boolean value in g_child_watch_check() is
+ strictly 0 or 1
+
+No problem in practice, but it seems nice to ensure that a gboolean is
+always either FALSE or TRUE.
+---
+ glib/gmain.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/glib/gmain.c b/glib/gmain.c
+index 3813b032a..b6ffc6dd0 100644
+--- a/glib/gmain.c
++++ b/glib/gmain.c
+@@ -5268,12 +5268,12 @@ g_child_watch_check (GSource *source)
+   child_watch_source = (GChildWatchSource *) source;
+ 
+ #ifdef G_OS_WIN32
+-  child_exited = child_watch_source->poll.revents & G_IO_IN;
++  child_exited = !!(child_watch_source->poll.revents & G_IO_IN);
+ #else /* G_OS_WIN32 */
+ #ifdef HAVE_PIDFD
+   if (child_watch_source->poll.fd >= 0)
+     {
+-      child_exited = child_watch_source->poll.revents & G_IO_IN;
++      child_exited = !!(child_watch_source->poll.revents & G_IO_IN);
+       return child_exited;
+     }
+ #endif /* HAVE_PIDFD */
+-- 
+2.41.0
+
diff --git a/base/rx/rx-glib2/3845.patch b/base/rx/rx-glib2/3845.patch
new file mode 100644
index 0000000..8cd6b01
--- /dev/null
+++ b/base/rx/rx-glib2/3845.patch
@@ -0,0 +1,195 @@
+From 37e323f1d16720d662611866cde567b1d2a01d48 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Mon, 22 Jan 2024 15:29:37 +0100
+Subject: [PATCH 1/2] gunixmounts: Use libmnt_monitor API for monitoring
+
+The `GUnixMountMonitor` object implements monitoring on its own currently.
+Only the `/proc/mounts` file changes are monitored. It is not aware of the
+`/run/mount/utab` file changes. This file contains the userspace mount
+options (e.g. `x-gvfs-notrash`, `x-gvfs-hide`) among others. There is a
+problem when `/sbin/mount.<type>` (e.g. `mount.nfs`) helper programs are
+used. In that case, the `/run/mount/utab` file is updated later than the
+`/proc/mounts` file and thus the `GUnixMountMonitor` clients (e.g.
+`gvfs-udisks2-volume-monitor`, `gvfsd-trash`) don't see the userspace
+options until the next `mount-changed` signal. Let's use the `libmnt_monitor`
+API for monitoring instead and emit the `mount-changed` signal also when the
+`/run/mount/utab` file is changed.
+
+Related: https://issues.redhat.com/browse/RHEL-14607
+Related: https://github.com/util-linux/util-linux/pull/2607
+---
+ gio/gunixmounts.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 71 insertions(+), 1 deletion(-)
+
+diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
+index 32b936259..e11b34a7d 100644
+--- a/gio/gunixmounts.c
++++ b/gio/gunixmounts.c
+@@ -202,6 +202,11 @@ static GSource *proc_mounts_watch_source;
+ #define endmntent(f) fclose(f)
+ #endif
+ 
++#ifdef HAVE_LIBMOUNT
++/* Protected by proc_mounts_source lock */
++static struct libmnt_monitor *proc_mounts_monitor = NULL;
++#endif
++
+ static gboolean
+ is_in (const char *value, const char *set[])
+ {
+@@ -1859,7 +1864,36 @@ proc_mounts_changed (GIOChannel   *channel,
+                      GIOCondition  cond,
+                      gpointer      user_data)
+ {
++  gboolean has_changed = FALSE;
++
++#ifdef HAVE_LIBMOUNT
++  if (cond & G_IO_IN)
++    {
++      G_LOCK (proc_mounts_source);
++      if (proc_mounts_monitor != NULL)
++        {
++          int ret;
++
++          /* The mnt_monitor_next_change function needs to be used to avoid false-positives. */
++          ret = mnt_monitor_next_change (proc_mounts_monitor, NULL, NULL);
++          if (ret == 0)
++            {
++              has_changed = TRUE;
++              ret = mnt_monitor_event_cleanup (proc_mounts_monitor);
++            }
++
++          if (ret < 0)
++            g_debug ("mnt_monitor_next_change failed: %s", g_strerror (-ret));
++        }
++      G_UNLOCK (proc_mounts_source);
++    }
++
++#else
+   if (cond & G_IO_ERR)
++    has_changed = TRUE;
++#endif
++
++  if (has_changed)
+     {
+       G_LOCK (proc_mounts_source);
+       mount_poller_time = (guint64) g_get_monotonic_time ();
+@@ -1924,6 +1958,10 @@ mount_monitor_stop (void)
+       g_source_destroy (proc_mounts_watch_source);
+       proc_mounts_watch_source = NULL;
+     }
++
++#ifdef HAVE_LIBMOUNT
++  g_clear_pointer (&proc_mounts_monitor, mnt_unref_monitor);
++#endif
+   G_UNLOCK (proc_mounts_source);
+ 
+   if (mtab_monitor)
+@@ -1965,9 +2003,37 @@ mount_monitor_start (void)
+        */
+       if (g_str_has_prefix (mtab_path, "/proc/"))
+         {
+-          GIOChannel *proc_mounts_channel;
++          GIOChannel *proc_mounts_channel = NULL;
+           GError *error = NULL;
++#ifdef HAVE_LIBMOUNT
++          int ret;
++
++          G_LOCK (proc_mounts_source);
++
++          proc_mounts_monitor = mnt_new_monitor ();
++          ret = mnt_monitor_enable_kernel (proc_mounts_monitor, TRUE);
++          if (ret < 0)
++            g_warning ("mnt_monitor_enable_kernel failed: %s", g_strerror (-ret));
++
++          ret = mnt_monitor_enable_userspace (proc_mounts_monitor, TRUE, NULL);
++          if (ret < 0)
++            g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret));
++
++          ret = mnt_monitor_get_fd (proc_mounts_monitor);
++          if (ret >= 0)
++            {
++              proc_mounts_channel = g_io_channel_unix_new (ret);
++            }
++          else
++            {
++              g_set_error_literal (&error, G_IO_ERROR, g_io_error_from_errno (-ret),
++                                   g_strerror (-ret));
++            }
++
++          G_UNLOCK (proc_mounts_source);
++#else
+           proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error);
++#endif
+           if (proc_mounts_channel == NULL)
+             {
+               g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path,
+@@ -1978,7 +2044,11 @@ mount_monitor_start (void)
+             {
+               G_LOCK (proc_mounts_source);
+ 
++#ifdef HAVE_LIBMOUNT
++              proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_IN);
++#else
+               proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
++#endif
+               mount_poller_time = (guint64) g_get_monotonic_time ();
+               g_source_set_callback (proc_mounts_watch_source,
+                                      (GSourceFunc) proc_mounts_changed,
+-- 
+2.43.0
+
+
+From bb7d6b8fcef36af5452071c8758f89955888469a Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Wed, 31 Jan 2024 13:35:39 +0100
+Subject: [PATCH 2/2] gunixmounts: Use mnt_monitor_veil_kernel option
+
+The previous commit enabled the `/run/mount/utab` monitoring. The problem
+is that the `mount-changed` signal can be emitted twice for one mount. One
+for the `/proc/mounts` file change and another one for the `/run/media/utab`
+file change. This is still not ideal because e.g. the `GMount` objects for
+mounts with the `x-gvfs-hide` option are added and immediately removed.
+Let's enable the `mnt_monitor_veil_kernel` option to avoid this.
+
+Related: https://github.com/util-linux/util-linux/pull/2725
+---
+ gio/gunixmounts.c | 6 ++++++
+ meson.build       | 4 ++++
+ 2 files changed, 10 insertions(+)
+
+diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
+index e11b34a7d..6abe87414 100644
+--- a/gio/gunixmounts.c
++++ b/gio/gunixmounts.c
+@@ -2019,6 +2019,12 @@ mount_monitor_start (void)
+           if (ret < 0)
+             g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret));
+ 
++#ifdef HAVE_MNT_MONITOR_VEIL_KERNEL
++          ret = mnt_monitor_veil_kernel (proc_mounts_monitor, TRUE);
++          if (ret < 0)
++            g_warning ("mnt_monitor_veil_kernel failed: %s", g_strerror (-ret));
++#endif
++
+           ret = mnt_monitor_get_fd (proc_mounts_monitor);
+           if (ret >= 0)
+             {
+diff --git a/meson.build b/meson.build
+index a0502fe69..159703557 100644
+--- a/meson.build
++++ b/meson.build
+@@ -2102,6 +2102,10 @@ libmount_dep = []
+ if host_system == 'linux'
+   libmount_dep = dependency('mount', version : '>=2.23', required : get_option('libmount'))
+   glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found())
++
++  if libmount_dep.found() and cc.has_function('mnt_monitor_veil_kernel', dependencies: libmount_dep)
++    glib_conf.set('HAVE_MNT_MONITOR_VEIL_KERNEL', 1)
++  endif
+ endif
+ 
+ # gnutls is used optionally by GHmac
+-- 
+2.43.0
+
diff --git a/base/rx/rx-glib2/4038.patch b/base/rx/rx-glib2/4038.patch
new file mode 100644
index 0000000..bf1ba0f
--- /dev/null
+++ b/base/rx/rx-glib2/4038.patch
@@ -0,0 +1,3359 @@
+From 82f62517eb327aa3ea255294a90911091f39d7ae Mon Sep 17 00:00:00 2001
+From: Philip Withnall <pwithnall@gnome.org>
+Date: Tue, 28 Nov 2023 12:58:20 +0000
+Subject: [PATCH 01/19] gdbusmessage: Cache the arg0 value
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Technically we can’t rely on it being kept alive by the `message->body`
+pointer, unless we can guarantee that the `GVariant` is always
+serialised. That’s not necessarily the case, so keep a separate ref on
+the arg0 value at all times.
+
+This avoids a potential use-after-free.
+
+Spotted by Thomas Haller in
+https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3720#note_1924707.
+
+[This is a prerequisite for having tests pass after fixing the
+vulnerability described in glib#3268, because after fixing that
+vulnerability, the use-after-free genuinely does happen during
+regression testing. -smcv]
+
+Signed-off-by: Philip Withnall <pwithnall@gnome.org>
+
+Helps: #3183, #3268
+(cherry picked from commit 10e9a917be7fb92b6b27837ef7a7f1d0be6095d5)
+---
+ gio/gdbusmessage.c | 35 ++++++++++++++++++++++-------------
+ 1 file changed, 22 insertions(+), 13 deletions(-)
+
+diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
+index bc9386ee7..40a77dd92 100644
+--- a/gio/gdbusmessage.c
++++ b/gio/gdbusmessage.c
+@@ -471,6 +471,7 @@ struct _GDBusMessage
+   guint32 serial;
+   GHashTable *headers;
+   GVariant *body;
++  GVariant *arg0_cache;  /* (nullable) (owned) */
+ #ifdef G_OS_UNIX
+   GUnixFDList *fd_list;
+ #endif
+@@ -493,6 +494,7 @@ g_dbus_message_finalize (GObject *object)
+     g_hash_table_unref (message->headers);
+   if (message->body != NULL)
+     g_variant_unref (message->body);
++  g_clear_pointer (&message->arg0_cache, g_variant_unref);
+ #ifdef G_OS_UNIX
+   if (message->fd_list != NULL)
+     g_object_unref (message->fd_list);
+@@ -1128,6 +1130,7 @@ g_dbus_message_set_body (GDBusMessage  *message,
+   if (body == NULL)
+     {
+       message->body = NULL;
++      message->arg0_cache = NULL;
+       g_dbus_message_set_signature (message, NULL);
+     }
+   else
+@@ -1138,6 +1141,12 @@ g_dbus_message_set_body (GDBusMessage  *message,
+ 
+       message->body = g_variant_ref_sink (body);
+ 
++      if (g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) &&
++          g_variant_n_children (message->body) > 0)
++        message->arg0_cache = g_variant_get_child_value (message->body, 0);
++      else
++        message->arg0_cache = NULL;
++
+       type_string = g_variant_get_type_string (body);
+       type_string_len = strlen (type_string);
+       g_assert (type_string_len >= 2);
+@@ -2230,6 +2239,14 @@ g_dbus_message_new_from_blob (guchar                *blob,
+                                                  2,
+                                                  error);
+           g_variant_type_free (variant_type);
++
++          if (message->body != NULL &&
++              g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) &&
++              g_variant_n_children (message->body) > 0)
++            message->arg0_cache = g_variant_get_child_value (message->body, 0);
++          else
++            message->arg0_cache = NULL;
++
+           if (message->body == NULL)
+             goto out;
+         }
+@@ -3265,22 +3282,13 @@ g_dbus_message_set_signature (GDBusMessage  *message,
+ const gchar *
+ g_dbus_message_get_arg0 (GDBusMessage  *message)
+ {
+-  const gchar *ret;
+-
+   g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
+ 
+-  ret = NULL;
++  if (message->arg0_cache != NULL &&
++      g_variant_is_of_type (message->arg0_cache, G_VARIANT_TYPE_STRING))
++    return g_variant_get_string (message->arg0_cache, NULL);
+ 
+-  if (message->body != NULL && g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE))
+-    {
+-      GVariant *item;
+-      item = g_variant_get_child_value (message->body, 0);
+-      if (g_variant_is_of_type (item, G_VARIANT_TYPE_STRING))
+-        ret = g_variant_get_string (item, NULL);
+-      g_variant_unref (item);
+-    }
+-
+-  return ret;
++  return NULL;
+ }
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -3723,6 +3731,7 @@ g_dbus_message_copy (GDBusMessage  *message,
+    * to just ref (as opposed to deep-copying) the GVariant instances
+    */
+   ret->body = message->body != NULL ? g_variant_ref (message->body) : NULL;
++  ret->arg0_cache = message->arg0_cache != NULL ? g_variant_ref (message->arg0_cache) : NULL;
+   g_hash_table_iter_init (&iter, message->headers);
+   while (g_hash_table_iter_next (&iter, &header_key, (gpointer) &header_value))
+     g_hash_table_insert (ret->headers, header_key, g_variant_ref (header_value));
+-- 
+2.45.0
+
+
+From 3649ce253433149dea16d158542e7285655eb182 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Wed, 1 May 2024 15:51:42 +0100
+Subject: [PATCH 02/19] gdbusconnection: Make a backport of g_set_str()
+ available
+
+A subsequent commit will need this. Copying all of g_set_str() into a
+private header seems cleaner than replacing the call to it.
+
+Helps: GNOME/glib#3268
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c |  1 +
+ glib/glib-private.h   | 18 ++++++++++++++++++
+ 2 files changed, 19 insertions(+)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index a37611275..2808b1e46 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -95,6 +95,7 @@
+ #include <stdlib.h>
+ #include <string.h>
+ 
++#include "glib-private.h"
+ #include "gdbusauth.h"
+ #include "gdbusutils.h"
+ #include "gdbusaddress.h"
+diff --git a/glib/glib-private.h b/glib/glib-private.h
+index 8de380d12..acdfa4911 100644
+--- a/glib/glib-private.h
++++ b/glib/glib-private.h
+@@ -161,4 +161,22 @@ GLibPrivateVTable *glib__private__ (void);
+ # define GLIB_DEFAULT_LOCALE ""
+ #endif
+ 
++/* Backported from GLib 2.78.x, where it is public API in gstrfuncs.h */
++static inline gboolean
++g_set_str (char       **str_pointer,
++           const char  *new_str)
++{
++  char *copy;
++
++  if (*str_pointer == new_str ||
++      (*str_pointer && new_str && strcmp (*str_pointer, new_str) == 0))
++    return FALSE;
++
++  copy = g_strdup (new_str);
++  g_free (*str_pointer);
++  *str_pointer = copy;
++
++  return TRUE;
++}
++
+ #endif /* __GLIB_PRIVATE_H__ */
+-- 
+2.45.0
+
+
+From cef2cd7a033d3df50a8b5b3db28df93feb1d14a9 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Fri, 8 Mar 2024 14:19:46 +0000
+Subject: [PATCH 03/19] tests: Add a data-driven test for signal subscriptions
+
+This somewhat duplicates test_connection_signals(), but is easier to
+extend to cover different scenarios.
+
+Each scenario is tested three times: once with lower-level
+GDBusConnection APIs, once with the higher-level GDBusProxy (which
+cannot implement all of the subscription scenarios, so some message
+counts are lower), and once with both (to check that delivery of the
+same message to multiple destinations is handled appropriately).
+
+[Backported to glib-2-74, resolving conflicts in gio/tests/meson.build]
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 938 ++++++++++++++++++++++++++++++++++++
+ gio/tests/meson.build       |   1 +
+ 2 files changed, 939 insertions(+)
+ create mode 100644 gio/tests/gdbus-subscribe.c
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+new file mode 100644
+index 000000000..3f53e1d7f
+--- /dev/null
++++ b/gio/tests/gdbus-subscribe.c
+@@ -0,0 +1,938 @@
++/*
++ * Copyright 2024 Collabora Ltd.
++ * SPDX-License-Identifier: LGPL-2.1-or-later
++ */
++
++#include <gio/gio.h>
++
++#include "gdbus-tests.h"
++
++#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
++#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
++#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS
++
++/* A signal that each connection emits to indicate that it has finished
++ * emitting other signals */
++#define FINISHED_PATH "/org/gtk/Test/Finished"
++#define FINISHED_INTERFACE "org.gtk.Test.Finished"
++#define FINISHED_SIGNAL "Finished"
++
++/* A signal emitted during testing */
++#define EXAMPLE_PATH "/org/gtk/GDBus/ExampleInterface"
++#define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface"
++#define FOO_SIGNAL "Foo"
++
++/* Log @s in a debug message. */
++static inline const char *
++nonnull (const char *s,
++         const char *if_null)
++{
++  return (s == NULL) ? if_null : s;
++}
++
++typedef enum
++{
++  TEST_CONN_NONE,
++  TEST_CONN_FIRST,
++  /* A connection that subscribes to signals */
++  TEST_CONN_SUBSCRIBER = TEST_CONN_FIRST,
++  /* A mockup of a legitimate service */
++  TEST_CONN_SERVICE,
++  /* A mockup of a second legitimate service */
++  TEST_CONN_SERVICE2,
++  /* A connection that tries to trick @subscriber into processing its signals
++   * as if they came from @service */
++  TEST_CONN_ATTACKER,
++  NUM_TEST_CONNS
++} TestConn;
++
++static const char * const test_conn_descriptions[NUM_TEST_CONNS] =
++{
++  "(unused)",
++  "subscriber",
++  "service",
++  "service 2",
++  "attacker"
++};
++
++typedef enum
++{
++  SUBSCRIPTION_MODE_CONN,
++  SUBSCRIPTION_MODE_PROXY,
++  SUBSCRIPTION_MODE_PARALLEL
++} SubscriptionMode;
++
++typedef struct
++{
++  GDBusProxy *received_by_proxy;
++  TestConn sender;
++  char *path;
++  char *iface;
++  char *member;
++  GVariant *parameters;
++  char *arg0;
++  guint32 step;
++} ReceivedMessage;
++
++static void
++received_message_free (ReceivedMessage *self)
++{
++
++  g_clear_object (&self->received_by_proxy);
++  g_free (self->path);
++  g_free (self->iface);
++  g_free (self->member);
++  g_clear_pointer (&self->parameters, g_variant_unref);
++  g_free (self->arg0);
++  g_free (self);
++}
++
++typedef struct
++{
++  TestConn sender;
++  TestConn unicast_to;
++  const char *path;
++  const char *iface;
++  const char *member;
++  const char *arg0;
++  guint received_by_conn;
++  guint received_by_proxy;
++} TestEmitSignal;
++
++typedef struct
++{
++  TestConn sender;
++  const char *path;
++  const char *iface;
++  const char *member;
++  const char *arg0;
++  GDBusSignalFlags flags;
++} TestSubscribe;
++
++typedef enum
++{
++  TEST_ACTION_NONE = 0,
++  TEST_ACTION_SUBSCRIBE,
++  TEST_ACTION_EMIT_SIGNAL,
++} TestAction;
++
++typedef struct
++{
++  TestAction action;
++  union {
++    TestEmitSignal signal;
++    TestSubscribe subscribe;
++  } u;
++} TestStep;
++
++/* Arbitrary, extend as necessary to accommodate the longest test */
++#define MAX_TEST_STEPS 10
++
++typedef struct
++{
++  const char *description;
++  TestStep steps[MAX_TEST_STEPS];
++} TestPlan;
++
++static const TestPlan plan_simple =
++{
++  .description = "A broadcast is only received after subscribing to it",
++  .steps = {
++    {
++      /* We don't receive a signal if we haven't subscribed yet */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      /* Now it works */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        /* The proxy can't be used in this case, because it needs
++         * a bus name to subscribe to */
++        .received_by_proxy = 0
++      },
++    },
++  },
++};
++
++static const TestPlan plan_broadcast_from_anyone =
++{
++  .description = "A subscription with NULL sender accepts broadcast and unicast",
++  .steps = {
++    {
++      /* Subscriber wants to receive signals from anyone */
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      /* First service sends a broadcast */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* Second service also sends a broadcast */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE2,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* First service sends a unicast signal */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .unicast_to = TEST_CONN_SUBSCRIBER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* Second service also sends a unicast signal */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE2,
++        .unicast_to = TEST_CONN_SUBSCRIBER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        .received_by_proxy = 0
++      },
++    },
++  },
++};
++
++static const TestPlan plan_match_twice =
++{
++  .description = "A message matching more than one subscription is received "
++                 "once per subscription",
++  .steps = {
++    {
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .path = EXAMPLE_PATH,
++      },
++    },
++    {
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 4,
++        /* Only the first and last work with GDBusProxy */
++        .received_by_proxy = 2
++      },
++    },
++  },
++};
++
++static const TestPlan plan_limit_by_unique_name =
++{
++  .description = "A subscription via a unique name only accepts messages "
++                 "sent by that same unique name",
++  .steps = {
++    {
++      /* Subscriber wants to receive signals from service */
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      /* Attacker wants to trick subscriber into thinking that service
++       * sent a signal */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* Attacker tries harder, by sending a signal unicast directly to
++       * the subscriber */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .unicast_to = TEST_CONN_SUBSCRIBER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* When the real service sends a signal, it should still get through */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        .received_by_proxy = 1
++      },
++    },
++  },
++};
++
++typedef struct
++{
++  const TestPlan *plan;
++  SubscriptionMode mode;
++  GError *error;
++  /* (element-type ReceivedMessage) */
++  GPtrArray *received;
++  /* conns[TEST_CONN_NONE] is unused and remains NULL */
++  GDBusConnection *conns[NUM_TEST_CONNS];
++  /* Proxies on conns[TEST_CONN_SUBSCRIBER] */
++  GPtrArray *proxies;
++  /* unique_names[TEST_CONN_NONE] is unused and remains NULL */
++  const char *unique_names[NUM_TEST_CONNS];
++  /* finished[TEST_CONN_NONE] is unused and remains FALSE */
++  gboolean finished[NUM_TEST_CONNS];
++  /* Remains 0 for any step that is not a subscription */
++  guint subscriptions[MAX_TEST_STEPS];
++  /* Number of times the signal from step n was received */
++  guint received_by_conn[MAX_TEST_STEPS];
++  /* Number of times the signal from step n was received */
++  guint received_by_proxy[MAX_TEST_STEPS];
++  guint finished_subscription;
++} Fixture;
++
++/* Wait for asynchronous messages from @conn to have been processed
++ * by the message bus, as a sequence point so that we can make
++ * "happens before" and "happens after" assertions relative to this.
++ * The easiest way to achieve this is to call a message bus method that has
++ * no arguments and wait for it to return: because the message bus processes
++ * messages in-order, anything we sent before this must have been processed
++ * by the time this call arrives. */
++static void
++connection_wait_for_bus (GDBusConnection *conn)
++{
++  GError *error = NULL;
++  GVariant *call_result;
++
++  call_result = g_dbus_connection_call_sync (conn,
++                                             DBUS_SERVICE_DBUS,
++                                             DBUS_PATH_DBUS,
++                                             DBUS_INTERFACE_DBUS,
++                                             "GetId",
++                                             NULL,   /* arguments */
++                                             NULL,   /* result type */
++                                             G_DBUS_CALL_FLAGS_NONE,
++                                             -1,
++                                             NULL,
++                                             &error);
++  g_assert_no_error (error);
++  g_assert_nonnull (call_result);
++  g_variant_unref (call_result);
++}
++
++/*
++ * Called when the subscriber receives a message from any connection
++ * announcing that it has emitted all the signals that it plans to emit.
++ */
++static void
++subscriber_finished_cb (GDBusConnection *conn,
++                        const char      *sender_name,
++                        const char      *path,
++                        const char      *iface,
++                        const char      *member,
++                        GVariant        *parameters,
++                        void            *user_data)
++{
++  Fixture *f = user_data;
++  GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
++  guint i;
++
++  g_assert_true (conn == subscriber);
++
++  for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
++    {
++      if (g_str_equal (sender_name, f->unique_names[i]))
++        {
++          g_assert_false (f->finished[i]);
++          f->finished[i] = TRUE;
++
++          g_test_message ("Received Finished signal from %s %s",
++                          test_conn_descriptions[i], sender_name);
++          return;
++        }
++    }
++
++  g_error ("Received Finished signal from unknown sender %s", sender_name);
++}
++
++/*
++ * Called when we receive a signal, either via the GDBusProxy (proxy != NULL)
++ * or via the GDBusConnection (proxy == NULL).
++ */
++static void
++fixture_received_signal (Fixture    *f,
++                         GDBusProxy *proxy,
++                         const char *sender_name,
++                         const char *path,
++                         const char *iface,
++                         const char *member,
++                         GVariant   *parameters)
++{
++  guint i;
++  ReceivedMessage *received;
++
++  /* Ignore the Finished signal if it matches a wildcard subscription */
++  if (g_str_equal (member, FINISHED_SIGNAL))
++    return;
++
++  received = g_new0 (ReceivedMessage, 1);
++
++  if (proxy != NULL)
++    received->received_by_proxy = g_object_ref (proxy);
++  else
++    received->received_by_proxy = NULL;
++
++  received->path = g_strdup (path);
++  received->iface = g_strdup (iface);
++  received->member = g_strdup (member);
++  received->parameters = g_variant_ref (parameters);
++
++  for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
++    {
++      if (g_str_equal (sender_name, f->unique_names[i]))
++        {
++          received->sender = i;
++          g_assert_false (f->finished[i]);
++          break;
++        }
++    }
++
++  g_assert_cmpint (received->sender, !=, TEST_CONN_NONE);
++
++  g_test_message ("Signal received from %s %s via %s",
++                  test_conn_descriptions[received->sender],
++                  sender_name,
++                  proxy != NULL ? "proxy" : "connection");
++  g_test_message ("\tPath: %s", path);
++  g_test_message ("\tInterface: %s", iface);
++  g_test_message ("\tMember: %s", member);
++
++  if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(su)")))
++    {
++      g_variant_get (parameters, "(su)", &received->arg0, &received->step);
++      g_test_message ("\tString argument 0: %s", received->arg0);
++      g_test_message ("\tSent in step: %u", received->step);
++    }
++  else
++    {
++      g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(uu)");
++      g_variant_get (parameters, "(uu)", NULL, &received->step);
++      g_test_message ("\tArgument 0: (not a string)");
++      g_test_message ("\tSent in step: %u", received->step);
++    }
++
++  g_ptr_array_add (f->received, g_steal_pointer (&received));
++}
++
++static void
++proxy_signal_cb (GDBusProxy *proxy,
++                 const char *sender_name,
++                 const char *member,
++                 GVariant   *parameters,
++                 void       *user_data)
++{
++  Fixture *f = user_data;
++
++  fixture_received_signal (f, proxy, sender_name,
++                           g_dbus_proxy_get_object_path (proxy),
++                           g_dbus_proxy_get_interface_name (proxy),
++                           member, parameters);
++}
++
++static void
++subscribed_signal_cb (GDBusConnection *conn,
++                      const char      *sender_name,
++                      const char      *path,
++                      const char      *iface,
++                      const char      *member,
++                      GVariant        *parameters,
++                      void            *user_data)
++{
++  Fixture *f = user_data;
++  GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
++
++  g_assert_true (conn == subscriber);
++
++  fixture_received_signal (f, NULL, sender_name, path, iface, member, parameters);
++}
++
++static void
++fixture_subscribe (Fixture             *f,
++                   const TestSubscribe *subscribe,
++                   guint                step_number)
++{
++  GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
++  const char *sender;
++
++  if (subscribe->sender != TEST_CONN_NONE)
++    {
++      sender = f->unique_names[subscribe->sender];
++      g_test_message ("\tSender: %s %s",
++                      test_conn_descriptions[subscribe->sender],
++                      sender);
++    }
++  else
++    {
++      sender = NULL;
++      g_test_message ("\tSender: (any)");
++    }
++
++  g_test_message ("\tPath: %s", nonnull (subscribe->path, "(any)"));
++  g_test_message ("\tInterface: %s",
++                  nonnull (subscribe->iface, "(any)"));
++  g_test_message ("\tMember: %s",
++                  nonnull (subscribe->member, "(any)"));
++  g_test_message ("\tString argument 0: %s",
++                  nonnull (subscribe->arg0, "(any)"));
++  g_test_message ("\tFlags: %x", subscribe->flags);
++
++  if (f->mode != SUBSCRIPTION_MODE_PROXY)
++    {
++      /* CONN or PARALLEL */
++      guint id;
++
++      g_test_message ("\tSubscribing via connection");
++      id = g_dbus_connection_signal_subscribe (subscriber,
++                                               sender,
++                                               subscribe->iface,
++                                               subscribe->member,
++                                               subscribe->path,
++                                               subscribe->arg0,
++                                               subscribe->flags,
++                                               subscribed_signal_cb,
++                                               f, NULL);
++      g_assert_cmpuint (id, !=, 0);
++      f->subscriptions[step_number] = id;
++    }
++
++  if (f->mode != SUBSCRIPTION_MODE_CONN)
++    {
++      /* PROXY or PARALLEL */
++
++      if (sender == NULL)
++        {
++          g_test_message ("\tCannot subscribe via proxy: no bus name");
++        }
++      else if (subscribe->path == NULL)
++        {
++          g_test_message ("\tCannot subscribe via proxy: no path");
++        }
++      else if (subscribe->iface == NULL)
++        {
++          g_test_message ("\tCannot subscribe via proxy: no interface");
++        }
++      else
++        {
++          GDBusProxy *proxy;
++
++          g_test_message ("\tSubscribing via proxy");
++          proxy = g_dbus_proxy_new_sync (subscriber,
++                                         (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
++                                          | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
++                                         NULL,    /* GDBusInterfaceInfo */
++                                         sender,
++                                         subscribe->path,
++                                         subscribe->iface,
++                                         NULL,    /* GCancellable */
++                                         &f->error);
++          g_assert_no_error (f->error);
++          g_assert_nonnull (proxy);
++          g_signal_connect (proxy, "g-signal", G_CALLBACK (proxy_signal_cb), f);
++          g_ptr_array_add (f->proxies, g_steal_pointer (&proxy));
++        }
++    }
++
++  /* As in setup(), we need to wait for AddMatch to happen. */
++  g_test_message ("Waiting for AddMatch to be processed");
++  connection_wait_for_bus (subscriber);
++}
++
++static void
++fixture_emit_signal (Fixture              *f,
++                     const TestEmitSignal *signal,
++                     guint                 step_number)
++{
++  GVariant *body;
++  const char *destination;
++  gboolean ok;
++
++  g_test_message ("\tSender: %s",
++                  test_conn_descriptions[signal->sender]);
++
++  if (signal->unicast_to != TEST_CONN_NONE)
++    {
++      destination = f->unique_names[signal->unicast_to];
++      g_test_message ("\tDestination: %s %s",
++                      test_conn_descriptions[signal->unicast_to],
++                      destination);
++    }
++  else
++    {
++      destination = NULL;
++      g_test_message ("\tDestination: (broadcast)");
++    }
++
++  g_assert_nonnull (signal->path);
++  g_test_message ("\tPath: %s", signal->path);
++  g_assert_nonnull (signal->iface);
++  g_test_message ("\tInterface: %s", signal->iface);
++  g_assert_nonnull (signal->member);
++  g_test_message ("\tMember: %s", signal->member);
++
++  /* If arg0 is non-NULL, put it in the message's argument 0.
++   * Otherwise put something that will not match any arg0.
++   * Either way, put the sequence number in argument 1 so we can
++   * correlate sent messages with received messages later. */
++  if (signal->arg0 != NULL)
++    {
++      g_test_message ("\tString argument 0: %s", signal->arg0);
++      /* floating */
++      body = g_variant_new ("(su)", signal->arg0, (guint32) step_number);
++    }
++  else
++    {
++      g_test_message ("\tArgument 0: (not a string)");
++      body = g_variant_new ("(uu)", (guint32) 0, (guint32) step_number);
++    }
++
++  ok = g_dbus_connection_emit_signal (f->conns[signal->sender],
++                                      destination,
++                                      signal->path,
++                                      signal->iface,
++                                      signal->member,
++                                      /* steals floating reference */
++                                      g_steal_pointer (&body),
++                                      &f->error);
++  g_assert_no_error (f->error);
++  g_assert_true (ok);
++
++  /* Emitting the signal is asynchronous, so if we want subsequent steps
++   * to be guaranteed to happen after the signal from the message bus's
++   * perspective, we have to do a round-trip to the message bus to sync up. */
++  g_test_message ("Waiting for signal to reach message bus");
++  connection_wait_for_bus (f->conns[signal->sender]);
++}
++
++static void
++fixture_run_plan (Fixture          *f,
++                  const TestPlan   *plan,
++                  SubscriptionMode  mode)
++{
++  guint i;
++
++  G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->subscriptions));
++  G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_conn));
++  G_STATIC_ASSERT (G_N_ELEMENTS (plan->steps) == G_N_ELEMENTS (f->received_by_proxy));
++
++  f->mode = mode;
++  f->plan = plan;
++
++  g_test_summary (plan->description);
++
++  for (i = 0; i < G_N_ELEMENTS (plan->steps); i++)
++    {
++      const TestStep *step = &plan->steps[i];
++
++      switch (step->action)
++        {
++          case TEST_ACTION_SUBSCRIBE:
++            g_test_message ("Step %u: adding subscription", i);
++            fixture_subscribe (f, &step->u.subscribe, i);
++            break;
++
++          case TEST_ACTION_EMIT_SIGNAL:
++            g_test_message ("Step %u: emitting signal", i);
++            fixture_emit_signal (f, &step->u.signal, i);
++            break;
++
++          case TEST_ACTION_NONE:
++            /* Padding to fill the rest of the array, do nothing */
++            break;
++
++          default:
++            g_return_if_reached ();
++        }
++    }
++
++  /* Now that we have done everything we wanted to do, emit Finished
++   * from each connection. */
++  for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
++    {
++      gboolean ok;
++
++      ok = g_dbus_connection_emit_signal (f->conns[i],
++                                          NULL,
++                                          FINISHED_PATH,
++                                          FINISHED_INTERFACE,
++                                          FINISHED_SIGNAL,
++                                          NULL,
++                                          &f->error);
++      g_assert_no_error (f->error);
++      g_assert_true (ok);
++    }
++
++  /* Wait until we have seen the Finished signal from each sender */
++  while (TRUE)
++    {
++      gboolean all_finished = TRUE;
++
++      for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
++        all_finished = all_finished && f->finished[i];
++
++      if (all_finished)
++        break;
++
++      g_main_context_iteration (NULL, TRUE);
++    }
++
++  /* Assert that the correct things happened before each Finished signal */
++  for (i = 0; i < f->received->len; i++)
++    {
++      const ReceivedMessage *received = g_ptr_array_index (f->received, i);
++
++      g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn));
++      g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy));
++      g_assert_cmpint (plan->steps[received->step].action,
++                       ==, TEST_ACTION_EMIT_SIGNAL);
++
++      if (received->received_by_proxy != NULL)
++        f->received_by_proxy[received->step] += 1;
++      else
++        f->received_by_conn[received->step] += 1;
++    }
++
++  for (i = 0; i < G_N_ELEMENTS (plan->steps); i++)
++    {
++      const TestStep *step = &plan->steps[i];
++
++      if (step->action == TEST_ACTION_EMIT_SIGNAL)
++        {
++          const TestEmitSignal *signal = &plan->steps[i].u.signal;
++
++          if (mode != SUBSCRIPTION_MODE_PROXY)
++            {
++              g_test_message ("Signal from step %u was received %u times by "
++                              "GDBusConnection, expected %u",
++                              i, f->received_by_conn[i], signal->received_by_conn);
++              g_assert_cmpuint (f->received_by_conn[i], ==, signal->received_by_conn);
++            }
++          else
++            {
++              g_assert_cmpuint (f->received_by_conn[i], ==, 0);
++            }
++
++          if (mode != SUBSCRIPTION_MODE_CONN)
++            {
++              g_test_message ("Signal from step %u was received %u times by "
++                              "GDBusProxy, expected %u",
++                              i, f->received_by_proxy[i], signal->received_by_proxy);
++              g_assert_cmpuint (f->received_by_proxy[i], ==, signal->received_by_proxy);
++            }
++          else
++            {
++              g_assert_cmpuint (f->received_by_proxy[i], ==, 0);
++            }
++        }
++    }
++}
++
++static void
++setup (Fixture *f,
++       G_GNUC_UNUSED const void *context)
++{
++  GDBusConnection *subscriber;
++  guint i;
++
++  session_bus_up ();
++
++  f->proxies = g_ptr_array_new_full (MAX_TEST_STEPS, g_object_unref);
++  f->received = g_ptr_array_new_full (MAX_TEST_STEPS,
++                                      (GDestroyNotify) received_message_free);
++
++  for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
++    {
++      f->conns[i] = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &f->error);
++      g_assert_no_error (f->error);
++      g_assert_nonnull (f->conns[i]);
++
++      f->unique_names[i] = g_dbus_connection_get_unique_name (f->conns[i]);
++      g_assert_nonnull (f->unique_names[i]);
++      g_test_message ("%s is %s",
++                      test_conn_descriptions[i],
++                      f->unique_names[i]);
++    }
++
++  subscriber = f->conns[TEST_CONN_SUBSCRIBER];
++
++  /* Used to wait for all connections to finish sending whatever they
++   * wanted to send */
++  f->finished_subscription = g_dbus_connection_signal_subscribe (subscriber,
++                                                                 NULL,
++                                                                 FINISHED_INTERFACE,
++                                                                 FINISHED_SIGNAL,
++                                                                 FINISHED_PATH,
++                                                                 NULL,
++                                                                 G_DBUS_SIGNAL_FLAGS_NONE,
++                                                                 subscriber_finished_cb,
++                                                                 f, NULL);
++  /* AddMatch is sent asynchronously, so we don't know how
++   * soon it will be processed. Before emitting signals, we
++   * need to wait for the message bus to get as far as processing
++   * AddMatch. */
++  g_test_message ("Waiting for AddMatch to be processed");
++  connection_wait_for_bus (subscriber);
++}
++
++static void
++test_conn_subscribe (Fixture *f,
++                     const void *context)
++{
++  fixture_run_plan (f, context, SUBSCRIPTION_MODE_CONN);
++}
++
++static void
++test_proxy_subscribe (Fixture *f,
++                      const void *context)
++{
++  fixture_run_plan (f, context, SUBSCRIPTION_MODE_PROXY);
++}
++
++static void
++test_parallel_subscribe (Fixture *f,
++                         const void *context)
++{
++  fixture_run_plan (f, context, SUBSCRIPTION_MODE_PARALLEL);
++}
++
++static void
++teardown (Fixture *f,
++          G_GNUC_UNUSED const void *context)
++{
++  GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
++  guint i;
++
++  g_ptr_array_unref (f->proxies);
++
++  if (f->finished_subscription != 0)
++    g_dbus_connection_signal_unsubscribe (subscriber, f->finished_subscription);
++
++  for (i = 0; i < G_N_ELEMENTS (f->subscriptions); i++)
++    {
++      if (f->subscriptions[i] != 0)
++        g_dbus_connection_signal_unsubscribe (subscriber, f->subscriptions[i]);
++    }
++
++  g_ptr_array_unref (f->received);
++
++  for (i = TEST_CONN_FIRST; i < G_N_ELEMENTS (f->conns); i++)
++    g_clear_object (&f->conns[i]);
++
++  g_clear_error (&f->error);
++
++  session_bus_down ();
++}
++
++int
++main (int   argc,
++      char *argv[])
++{
++  g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
++
++  g_test_dbus_unset ();
++
++#define ADD_SUBSCRIBE_TEST(name) \
++  do { \
++    g_test_add ("/gdbus/subscribe/conn/" #name, \
++                Fixture, &plan_ ## name, \
++                setup, test_conn_subscribe, teardown); \
++    g_test_add ("/gdbus/subscribe/proxy/" #name, \
++                Fixture, &plan_ ## name, \
++                setup, test_proxy_subscribe, teardown); \
++    g_test_add ("/gdbus/subscribe/parallel/" #name, \
++                Fixture, &plan_ ## name, \
++                setup, test_parallel_subscribe, teardown); \
++  } while (0)
++
++  ADD_SUBSCRIBE_TEST (simple);
++  ADD_SUBSCRIBE_TEST (broadcast_from_anyone);
++  ADD_SUBSCRIBE_TEST (match_twice);
++  ADD_SUBSCRIBE_TEST (limit_by_unique_name);
++
++  return g_test_run();
++}
+diff --git a/gio/tests/meson.build b/gio/tests/meson.build
+index e1f583c70..1cd55e4ad 100644
+--- a/gio/tests/meson.build
++++ b/gio/tests/meson.build
+@@ -314,6 +314,7 @@ if host_machine.system() != 'windows'
+       },
+       'gdbus-proxy-unique-name' : {'extra_sources' : extra_sources},
+       'gdbus-proxy-well-known-name' : {'extra_sources' : extra_sources},
++      'gdbus-subscribe' : {'extra_sources' : extra_sources},
+       'gdbus-test-codegen' : {
+         'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info],
+         'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'],
+-- 
+2.45.0
+
+
+From 38ee87713f8cc99c67ef00a4960027676cc16b25 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Fri, 8 Mar 2024 19:28:15 +0000
+Subject: [PATCH 04/19] tests: Add support for subscribing to signals from a
+ well-known name
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 133 ++++++++++++++++++++++++++++++++++--
+ 1 file changed, 126 insertions(+), 7 deletions(-)
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+index 3f53e1d7f..3d2a14e03 100644
+--- a/gio/tests/gdbus-subscribe.c
++++ b/gio/tests/gdbus-subscribe.c
+@@ -7,6 +7,9 @@
+ 
+ #include "gdbus-tests.h"
+ 
++/* From the D-Bus Specification */
++#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
++
+ #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
+ #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
+ #define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS
+@@ -22,6 +25,9 @@
+ #define EXAMPLE_INTERFACE "org.gtk.GDBus.ExampleInterface"
+ #define FOO_SIGNAL "Foo"
+ 
++#define ALREADY_OWNED_NAME "org.gtk.Test.AlreadyOwned"
++#define OWNED_LATER_NAME "org.gtk.Test.OwnedLater"
++
+ /* Log @s in a debug message. */
+ static inline const char *
+ nonnull (const char *s,
+@@ -101,7 +107,8 @@ typedef struct
+ 
+ typedef struct
+ {
+-  TestConn sender;
++  const char *string_sender;
++  TestConn unique_sender;
+   const char *path;
+   const char *iface;
+   const char *member;
+@@ -109,11 +116,18 @@ typedef struct
+   GDBusSignalFlags flags;
+ } TestSubscribe;
+ 
++typedef struct
++{
++  const char *name;
++  TestConn owner;
++} TestOwnName;
++
+ typedef enum
+ {
+   TEST_ACTION_NONE = 0,
+   TEST_ACTION_SUBSCRIBE,
+   TEST_ACTION_EMIT_SIGNAL,
++  TEST_ACTION_OWN_NAME,
+ } TestAction;
+ 
+ typedef struct
+@@ -122,6 +136,7 @@ typedef struct
+   union {
+     TestEmitSignal signal;
+     TestSubscribe subscribe;
++    TestOwnName own_name;
+   } u;
+ } TestStep;
+ 
+@@ -247,7 +262,7 @@ static const TestPlan plan_match_twice =
+     {
+       .action = TEST_ACTION_SUBSCRIBE,
+       .u.subscribe = {
+-        .sender = TEST_CONN_SERVICE,
++        .unique_sender = TEST_CONN_SERVICE,
+         .path = EXAMPLE_PATH,
+         .iface = EXAMPLE_INTERFACE,
+       },
+@@ -267,7 +282,7 @@ static const TestPlan plan_match_twice =
+     {
+       .action = TEST_ACTION_SUBSCRIBE,
+       .u.subscribe = {
+-        .sender = TEST_CONN_SERVICE,
++        .unique_sender = TEST_CONN_SERVICE,
+         .path = EXAMPLE_PATH,
+         .iface = EXAMPLE_INTERFACE,
+       },
+@@ -296,7 +311,7 @@ static const TestPlan plan_limit_by_unique_name =
+       /* Subscriber wants to receive signals from service */
+       .action = TEST_ACTION_SUBSCRIBE,
+       .u.subscribe = {
+-        .sender = TEST_CONN_SERVICE,
++        .unique_sender = TEST_CONN_SERVICE,
+         .path = EXAMPLE_PATH,
+         .iface = EXAMPLE_INTERFACE,
+       },
+@@ -343,6 +358,62 @@ static const TestPlan plan_limit_by_unique_name =
+   },
+ };
+ 
++static const TestPlan plan_limit_by_well_known_name =
++{
++  .description = "A subscription via a well-known name only accepts messages "
++                 "sent by the owner of that well-known name",
++  .steps = {
++    {
++      /* Service already owns one name */
++      .action = TEST_ACTION_OWN_NAME,
++      .u.own_name = {
++        .name = ALREADY_OWNED_NAME,
++        .owner = TEST_CONN_SERVICE
++      },
++    },
++    {
++      /* Subscriber wants to receive signals from service */
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .string_sender = ALREADY_OWNED_NAME,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      /* Subscriber wants to receive signals from service by another name */
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .string_sender = OWNED_LATER_NAME,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      /* Service claims another name */
++      .action = TEST_ACTION_OWN_NAME,
++      .u.own_name = {
++        .name = OWNED_LATER_NAME,
++        .owner = TEST_CONN_SERVICE
++      },
++    },
++    {
++      /* Now the subscriber gets this signal twice, once for each
++       * subscription; and similarly each of the two proxies gets this
++       * signal twice */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 2,
++        .received_by_proxy = 2
++      },
++    },
++  },
++};
++
+ typedef struct
+ {
+   const TestPlan *plan;
+@@ -540,11 +611,16 @@ fixture_subscribe (Fixture             *f,
+   GDBusConnection *subscriber = f->conns[TEST_CONN_SUBSCRIBER];
+   const char *sender;
+ 
+-  if (subscribe->sender != TEST_CONN_NONE)
++  if (subscribe->string_sender != NULL)
++    {
++      sender = subscribe->string_sender;
++      g_test_message ("\tSender: %s", sender);
++    }
++  else if (subscribe->unique_sender != TEST_CONN_NONE)
+     {
+-      sender = f->unique_names[subscribe->sender];
++      sender = f->unique_names[subscribe->unique_sender];
+       g_test_message ("\tSender: %s %s",
+-                      test_conn_descriptions[subscribe->sender],
++                      test_conn_descriptions[subscribe->unique_sender],
+                       sender);
+     }
+   else
+@@ -689,6 +765,43 @@ fixture_emit_signal (Fixture              *f,
+   connection_wait_for_bus (f->conns[signal->sender]);
+ }
+ 
++static void
++fixture_own_name (Fixture *f,
++                  const TestOwnName *own_name)
++{
++  GVariant *call_result;
++  guint32 flags;
++  guint32 result_code;
++
++  g_test_message ("\tName: %s", own_name->name);
++  g_test_message ("\tOwner: %s",
++                  test_conn_descriptions[own_name->owner]);
++
++  /* For simplicity, we do this via a direct bus call rather than
++   * using g_bus_own_name_on_connection(). The flags in
++   * GBusNameOwnerFlags are numerically equal to those in the
++   * D-Bus wire protocol. */
++  flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE;
++  call_result = g_dbus_connection_call_sync (f->conns[own_name->owner],
++                                             DBUS_SERVICE_DBUS,
++                                             DBUS_PATH_DBUS,
++                                             DBUS_INTERFACE_DBUS,
++                                             "RequestName",
++                                             g_variant_new ("(su)",
++                                                           own_name->name,
++                                                           flags),
++                                             G_VARIANT_TYPE ("(u)"),
++                                             G_DBUS_CALL_FLAGS_NONE,
++                                             -1,
++                                             NULL,
++                                             &f->error);
++  g_assert_no_error (f->error);
++  g_assert_nonnull (call_result);
++  g_variant_get (call_result, "(u)", &result_code);
++  g_assert_cmpuint (result_code, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
++  g_variant_unref (call_result);
++}
++
+ static void
+ fixture_run_plan (Fixture          *f,
+                   const TestPlan   *plan,
+@@ -721,6 +834,11 @@ fixture_run_plan (Fixture          *f,
+             fixture_emit_signal (f, &step->u.signal, i);
+             break;
+ 
++          case TEST_ACTION_OWN_NAME:
++            g_test_message ("Step %u: claiming bus name", i);
++            fixture_own_name (f, &step->u.own_name);
++            break;
++
+           case TEST_ACTION_NONE:
+             /* Padding to fill the rest of the array, do nothing */
+             break;
+@@ -933,6 +1051,7 @@ main (int   argc,
+   ADD_SUBSCRIBE_TEST (broadcast_from_anyone);
+   ADD_SUBSCRIBE_TEST (match_twice);
+   ADD_SUBSCRIBE_TEST (limit_by_unique_name);
++  ADD_SUBSCRIBE_TEST (limit_by_well_known_name);
+ 
+   return g_test_run();
+ }
+-- 
+2.45.0
+
+
+From 59ff44e332059175a47af3ed02da28714b70c51b Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Fri, 8 Mar 2024 19:44:03 +0000
+Subject: [PATCH 05/19] tests: Add a test-case for what happens if a unique
+ name doesn't exist
+
+On GNOME/glib#3268 there was some concern about whether this would
+allow an attacker to send signals and have them be matched to a
+GDBusProxy in this situation, but it seems that was a false alarm.
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 48 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+index 3d2a14e03..350ec9f52 100644
+--- a/gio/tests/gdbus-subscribe.c
++++ b/gio/tests/gdbus-subscribe.c
+@@ -358,6 +358,53 @@ static const TestPlan plan_limit_by_unique_name =
+   },
+ };
+ 
++static const TestPlan plan_nonexistent_unique_name =
++{
++  .description = "A subscription via a unique name that doesn't exist "
++                 "accepts no messages",
++  .steps = {
++    {
++      /* Subscriber wants to receive signals from service */
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        /* This relies on the implementation detail that the dbus-daemon
++         * (and presumably other bus implementations) never actually generates
++         * a unique name in this format */
++        .string_sender = ":0.this.had.better.not.exist",
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++      },
++    },
++    {
++      /* Attacker wants to trick subscriber into thinking that service
++       * sent a signal */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* Attacker tries harder, by sending a signal unicast directly to
++       * the subscriber */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .unicast_to = TEST_CONN_SUBSCRIBER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++  },
++};
++
+ static const TestPlan plan_limit_by_well_known_name =
+ {
+   .description = "A subscription via a well-known name only accepts messages "
+@@ -1051,6 +1098,7 @@ main (int   argc,
+   ADD_SUBSCRIBE_TEST (broadcast_from_anyone);
+   ADD_SUBSCRIBE_TEST (match_twice);
+   ADD_SUBSCRIBE_TEST (limit_by_unique_name);
++  ADD_SUBSCRIBE_TEST (nonexistent_unique_name);
+   ADD_SUBSCRIBE_TEST (limit_by_well_known_name);
+ 
+   return g_test_run();
+-- 
+2.45.0
+
+
+From 600d631e0978fd529877f481c10a7b3e971337cf Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Fri, 8 Mar 2024 20:10:29 +0000
+Subject: [PATCH 06/19] tests: Add test coverage for signals that match the
+ message bus's name
+
+This is a special case of unique names, even though it's syntactically
+a well-known name.
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 161 ++++++++++++++++++++++++++++++++++--
+ 1 file changed, 154 insertions(+), 7 deletions(-)
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+index 350ec9f52..af100de7d 100644
+--- a/gio/tests/gdbus-subscribe.c
++++ b/gio/tests/gdbus-subscribe.c
+@@ -13,6 +13,7 @@
+ #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
+ #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
+ #define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS
++#define NAME_OWNER_CHANGED "NameOwnerChanged"
+ 
+ /* A signal that each connection emits to indicate that it has finished
+  * emitting other signals */
+@@ -101,6 +102,7 @@ typedef struct
+   const char *iface;
+   const char *member;
+   const char *arg0;
++  const char *args;
+   guint received_by_conn;
+   guint received_by_proxy;
+ } TestEmitSignal;
+@@ -120,6 +122,8 @@ typedef struct
+ {
+   const char *name;
+   TestConn owner;
++  guint received_by_conn;
++  guint received_by_proxy;
+ } TestOwnName;
+ 
+ typedef enum
+@@ -461,6 +465,63 @@ static const TestPlan plan_limit_by_well_known_name =
+   },
+ };
+ 
++static const TestPlan plan_limit_to_message_bus =
++{
++  .description = "A subscription to the message bus only accepts messages "
++                 "from the message bus",
++  .steps = {
++    {
++      /* Subscriber wants to receive signals from the message bus itself */
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .string_sender = DBUS_SERVICE_DBUS,
++        .path = DBUS_PATH_DBUS,
++        .iface = DBUS_INTERFACE_DBUS,
++      },
++    },
++    {
++      /* Attacker wants to trick subscriber into thinking that the message
++       * bus sent a signal */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .path = DBUS_PATH_DBUS,
++        .iface = DBUS_INTERFACE_DBUS,
++        .member = NAME_OWNER_CHANGED,
++        .arg0 = "would I lie to you?",
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* Attacker tries harder, by sending a signal unicast directly to
++       * the subscriber, and using more realistic arguments */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .unicast_to = TEST_CONN_SUBSCRIBER,
++        .sender = TEST_CONN_ATTACKER,
++        .path = DBUS_PATH_DBUS,
++        .iface = DBUS_INTERFACE_DBUS,
++        .member = NAME_OWNER_CHANGED,
++        .args = "('com.example.Name', '', ':1.12')",
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* When the message bus sends a signal (in this case triggered by
++       * owning a name), it should still get through */
++      .action = TEST_ACTION_OWN_NAME,
++      .u.own_name = {
++        .name = OWNED_LATER_NAME,
++        .owner = TEST_CONN_SERVICE,
++        .received_by_conn = 1,
++        .received_by_proxy = 1
++      },
++    },
++  },
++};
++
+ typedef struct
+ {
+   const TestPlan *plan;
+@@ -591,7 +652,18 @@ fixture_received_signal (Fixture    *f,
+         }
+     }
+ 
+-  g_assert_cmpint (received->sender, !=, TEST_CONN_NONE);
++  if (g_str_equal (sender_name, DBUS_SERVICE_DBUS))
++    {
++      g_test_message ("Signal received from message bus %s",
++                      sender_name);
++    }
++  else
++    {
++      g_test_message ("Signal received from %s %s",
++                      test_conn_descriptions[received->sender],
++                      sender_name);
++      g_assert_cmpint (received->sender, !=, TEST_CONN_NONE);
++    }
+ 
+   g_test_message ("Signal received from %s %s via %s",
+                   test_conn_descriptions[received->sender],
+@@ -607,13 +679,56 @@ fixture_received_signal (Fixture    *f,
+       g_test_message ("\tString argument 0: %s", received->arg0);
+       g_test_message ("\tSent in step: %u", received->step);
+     }
+-  else
++  else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(uu)")))
+     {
+-      g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(uu)");
+       g_variant_get (parameters, "(uu)", NULL, &received->step);
+       g_test_message ("\tArgument 0: (not a string)");
+       g_test_message ("\tSent in step: %u", received->step);
+     }
++  else if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sss)")))
++    {
++      const char *name;
++      const char *old_owner;
++      const char *new_owner;
++
++      /* The only signal of this signature that we legitimately receive
++       * during this test is NameOwnerChanged, so just assert that it
++       * is from the message bus and can be matched to a plausible step.
++       * (This is less thorough than the above, and will not work if we
++       * add a test scenario where a name's ownership is repeatedly
++       * changed while watching NameOwnerChanged - so don't do that.) */
++      g_assert_cmpstr (sender_name, ==, DBUS_SERVICE_DBUS);
++      g_assert_cmpstr (path, ==, DBUS_PATH_DBUS);
++      g_assert_cmpstr (iface, ==, DBUS_INTERFACE_DBUS);
++      g_assert_cmpstr (member, ==, NAME_OWNER_CHANGED);
++
++      g_variant_get (parameters, "(&s&s&s)", &name, &old_owner, &new_owner);
++
++      for (i = 0; i < G_N_ELEMENTS (f->plan->steps); i++)
++        {
++          const TestStep *step = &f->plan->steps[i];
++
++          if (step->action == TEST_ACTION_OWN_NAME)
++            {
++              const TestOwnName *own_name = &step->u.own_name;
++
++              if (g_str_equal (name, own_name->name)
++                  && g_str_equal (new_owner, f->unique_names[own_name->owner])
++                  && own_name->received_by_conn > 0)
++                {
++                  received->step = i;
++                  break;
++                }
++            }
++
++          if (i >= G_N_ELEMENTS (f->plan->steps))
++            g_error ("Could not match message to a test step");
++        }
++    }
++  else
++    {
++      g_error ("Unexpected message received");
++    }
+ 
+   g_ptr_array_add (f->received, g_steal_pointer (&received));
+ }
+@@ -782,10 +897,15 @@ fixture_emit_signal (Fixture              *f,
+    * Otherwise put something that will not match any arg0.
+    * Either way, put the sequence number in argument 1 so we can
+    * correlate sent messages with received messages later. */
+-  if (signal->arg0 != NULL)
++  if (signal->args != NULL)
+     {
+-      g_test_message ("\tString argument 0: %s", signal->arg0);
+       /* floating */
++      body = g_variant_new_parsed (signal->args);
++      g_assert_nonnull (body);
++    }
++  else if (signal->arg0 != NULL)
++    {
++      g_test_message ("\tString argument 0: %s", signal->arg0);
+       body = g_variant_new ("(su)", signal->arg0, (guint32) step_number);
+     }
+   else
+@@ -933,8 +1053,6 @@ fixture_run_plan (Fixture          *f,
+ 
+       g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_conn));
+       g_assert_cmpuint (received->step, <, G_N_ELEMENTS (f->received_by_proxy));
+-      g_assert_cmpint (plan->steps[received->step].action,
+-                       ==, TEST_ACTION_EMIT_SIGNAL);
+ 
+       if (received->received_by_proxy != NULL)
+         f->received_by_proxy[received->step] += 1;
+@@ -974,6 +1092,34 @@ fixture_run_plan (Fixture          *f,
+               g_assert_cmpuint (f->received_by_proxy[i], ==, 0);
+             }
+         }
++      else if (step->action == TEST_ACTION_OWN_NAME)
++        {
++          const TestOwnName *own_name = &plan->steps[i].u.own_name;
++
++          if (mode != SUBSCRIPTION_MODE_PROXY)
++            {
++              g_test_message ("NameOwnerChanged from step %u was received %u "
++                              "times by GDBusConnection, expected %u",
++                              i, f->received_by_conn[i], own_name->received_by_conn);
++              g_assert_cmpuint (f->received_by_conn[i], ==, own_name->received_by_conn);
++            }
++          else
++            {
++              g_assert_cmpuint (f->received_by_conn[i], ==, 0);
++            }
++
++          if (mode != SUBSCRIPTION_MODE_CONN)
++            {
++              g_test_message ("NameOwnerChanged from step %u was received %u "
++                              "times by GDBusProxy, expected %u",
++                              i, f->received_by_proxy[i], own_name->received_by_proxy);
++              g_assert_cmpuint (f->received_by_proxy[i], ==, own_name->received_by_proxy);
++            }
++          else
++            {
++              g_assert_cmpuint (f->received_by_proxy[i], ==, 0);
++            }
++        }
+     }
+ }
+ 
+@@ -1100,6 +1246,7 @@ main (int   argc,
+   ADD_SUBSCRIBE_TEST (limit_by_unique_name);
+   ADD_SUBSCRIBE_TEST (nonexistent_unique_name);
+   ADD_SUBSCRIBE_TEST (limit_by_well_known_name);
++  ADD_SUBSCRIBE_TEST (limit_to_message_bus);
+ 
+   return g_test_run();
+ }
+-- 
+2.45.0
+
+
+From d338bad6baa8a198953b109c00a96b02a18a8103 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Thu, 14 Mar 2024 19:18:15 +0000
+Subject: [PATCH 07/19] gdbusprivate: Add symbolic constants for the message
+ bus itself
+
+Using these is a bit more clearly correct than repeating them everywhere.
+To avoid excessive diffstat in a branch for a bug fix, I'm not
+immediately replacing all existing occurrences of the same literals with
+these names.
+
+The names of these constants are chosen to be consistent with libdbus,
+despite using somewhat outdated terminology (D-Bus now uses the term
+"well-known bus name" for what used to be called a service name,
+reserving the word "service" to mean specifically the programs that
+have .service files and participate in service activation).
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusprivate.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h
+index 8f8a93ba7..e9bca11ca 100644
+--- a/gio/gdbusprivate.h
++++ b/gio/gdbusprivate.h
+@@ -29,6 +29,11 @@
+ 
+ G_BEGIN_DECLS
+ 
++/* Bus name, interface and object path of the message bus itself */
++#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
++#define DBUS_INTERFACE_DBUS DBUS_SERVICE_DBUS
++#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
++
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
+ typedef struct GDBusWorker GDBusWorker;
+-- 
+2.45.0
+
+
+From a207a6f5adc9e6cea929c07a9d7533cc90043d01 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Wed, 1 May 2024 19:43:24 +0100
+Subject: [PATCH 08/19] gdbusconnection: Move SignalData, SignalSubscriber
+ higher up
+
+Subsequent changes will need to access these data structures from
+on_worker_message_received(). No functional change here, only moving
+code around.
+
+[Backport to 2.66.x: fix minor conflicts]
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 128 +++++++++++++++++++++---------------------
+ 1 file changed, 65 insertions(+), 63 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index 2808b1e46..8d841d78b 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -299,6 +299,71 @@ _g_strv_has_string (const gchar* const *haystack,
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
++typedef struct
++{
++  /* All fields are immutable after construction. */
++  gatomicrefcount ref_count;
++  GDBusSignalCallback callback;
++  gpointer user_data;
++  GDestroyNotify user_data_free_func;
++  guint id;
++  GMainContext *context;
++} SignalSubscriber;
++
++static SignalSubscriber *
++signal_subscriber_ref (SignalSubscriber *subscriber)
++{
++  g_atomic_ref_count_inc (&subscriber->ref_count);
++  return subscriber;
++}
++
++static void
++signal_subscriber_unref (SignalSubscriber *subscriber)
++{
++  if (g_atomic_ref_count_dec (&subscriber->ref_count))
++    {
++      /* Destroy the user data. It doesn’t matter which thread
++       * signal_subscriber_unref() is called in (or whether it’s called with a
++       * lock held), as call_destroy_notify() always defers to the next
++       * #GMainContext iteration. */
++      call_destroy_notify (subscriber->context,
++                           subscriber->user_data_free_func,
++                           subscriber->user_data);
++
++      g_main_context_unref (subscriber->context);
++      g_free (subscriber);
++    }
++}
++
++typedef struct
++{
++  gchar *rule;
++  gchar *sender;
++  gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */
++  gchar *interface_name;
++  gchar *member;
++  gchar *object_path;
++  gchar *arg0;
++  GDBusSignalFlags flags;
++  GPtrArray *subscribers;  /* (owned) (element-type SignalSubscriber) */
++} SignalData;
++
++static void
++signal_data_free (SignalData *signal_data)
++{
++  g_free (signal_data->rule);
++  g_free (signal_data->sender);
++  g_free (signal_data->sender_unique_name);
++  g_free (signal_data->interface_name);
++  g_free (signal_data->member);
++  g_free (signal_data->object_path);
++  g_free (signal_data->arg0);
++  g_ptr_array_unref (signal_data->subscribers);
++  g_free (signal_data);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
+ #ifdef G_OS_WIN32
+ #define CONNECTION_ENSURE_LOCK(obj) do { ; } while (FALSE)
+ #else
+@@ -3253,69 +3318,6 @@ g_dbus_connection_remove_filter (GDBusConnection *connection,
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
+-typedef struct
+-{
+-  gchar *rule;
+-  gchar *sender;
+-  gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */
+-  gchar *interface_name;
+-  gchar *member;
+-  gchar *object_path;
+-  gchar *arg0;
+-  GDBusSignalFlags flags;
+-  GPtrArray *subscribers;  /* (owned) (element-type SignalSubscriber) */
+-} SignalData;
+-
+-static void
+-signal_data_free (SignalData *signal_data)
+-{
+-  g_free (signal_data->rule);
+-  g_free (signal_data->sender);
+-  g_free (signal_data->sender_unique_name);
+-  g_free (signal_data->interface_name);
+-  g_free (signal_data->member);
+-  g_free (signal_data->object_path);
+-  g_free (signal_data->arg0);
+-  g_ptr_array_unref (signal_data->subscribers);
+-  g_free (signal_data);
+-}
+-
+-typedef struct
+-{
+-  /* All fields are immutable after construction. */
+-  gatomicrefcount ref_count;
+-  GDBusSignalCallback callback;
+-  gpointer user_data;
+-  GDestroyNotify user_data_free_func;
+-  guint id;
+-  GMainContext *context;
+-} SignalSubscriber;
+-
+-static SignalSubscriber *
+-signal_subscriber_ref (SignalSubscriber *subscriber)
+-{
+-  g_atomic_ref_count_inc (&subscriber->ref_count);
+-  return subscriber;
+-}
+-
+-static void
+-signal_subscriber_unref (SignalSubscriber *subscriber)
+-{
+-  if (g_atomic_ref_count_dec (&subscriber->ref_count))
+-    {
+-      /* Destroy the user data. It doesn’t matter which thread
+-       * signal_subscriber_unref() is called in (or whether it’s called with a
+-       * lock held), as call_destroy_notify() always defers to the next
+-       * #GMainContext iteration. */
+-      call_destroy_notify (subscriber->context,
+-                           subscriber->user_data_free_func,
+-                           subscriber->user_data);
+-
+-      g_main_context_unref (subscriber->context);
+-      g_free (subscriber);
+-    }
+-}
+-
+ static gchar *
+ args_to_rule (const gchar      *sender,
+               const gchar      *interface_name,
+-- 
+2.45.0
+
+
+From 998cee11197dda4f48334921900bca0e6a8cd62b Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Thu, 14 Mar 2024 19:30:12 +0000
+Subject: [PATCH 09/19] gdbusconnection: Factor out signal_data_new_take()
+
+No functional changes, except that the implicit ownership-transfer
+for the rule field becomes explicit (the local variable is set to NULL
+afterwards).
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 42 ++++++++++++++++++++++++++++++++----------
+ 1 file changed, 32 insertions(+), 10 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index 8d841d78b..67091574a 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -348,6 +348,30 @@ typedef struct
+   GPtrArray *subscribers;  /* (owned) (element-type SignalSubscriber) */
+ } SignalData;
+ 
++static SignalData *
++signal_data_new_take (gchar *rule,
++                      gchar *sender,
++                      gchar *sender_unique_name,
++                      gchar *interface_name,
++                      gchar *member,
++                      gchar *object_path,
++                      gchar *arg0,
++                      GDBusSignalFlags flags)
++{
++  SignalData *signal_data = g_new0 (SignalData, 1);
++
++  signal_data->rule = rule;
++  signal_data->sender = sender;
++  signal_data->sender_unique_name = sender_unique_name;
++  signal_data->interface_name = interface_name;
++  signal_data->member = member;
++  signal_data->object_path = object_path;
++  signal_data->arg0 = arg0;
++  signal_data->flags = flags;
++  signal_data->subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
++  return g_steal_pointer (&signal_data);
++}
++
+ static void
+ signal_data_free (SignalData *signal_data)
+ {
+@@ -3584,16 +3608,14 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+       goto out;
+     }
+ 
+-  signal_data = g_new0 (SignalData, 1);
+-  signal_data->rule                  = rule;
+-  signal_data->sender                = g_strdup (sender);
+-  signal_data->sender_unique_name    = g_strdup (sender_unique_name);
+-  signal_data->interface_name        = g_strdup (interface_name);
+-  signal_data->member                = g_strdup (member);
+-  signal_data->object_path           = g_strdup (object_path);
+-  signal_data->arg0                  = g_strdup (arg0);
+-  signal_data->flags                 = flags;
+-  signal_data->subscribers           = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
++  signal_data = signal_data_new_take (g_steal_pointer (&rule),
++                                      g_strdup (sender),
++                                      g_strdup (sender_unique_name),
++                                      g_strdup (interface_name),
++                                      g_strdup (member),
++                                      g_strdup (object_path),
++                                      g_strdup (arg0),
++                                      flags);
+   g_ptr_array_add (signal_data->subscribers, subscriber);
+ 
+   g_hash_table_insert (connection->map_rule_to_signal_data,
+-- 
+2.45.0
+
+
+From 6df7b138b1f5b4c53ad124182b80e05cdba16c3c Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Tue, 23 Apr 2024 20:31:57 +0100
+Subject: [PATCH 10/19] gdbusconnection: Factor out add_signal_data()
+
+No functional changes.
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 64 +++++++++++++++++++++++++------------------
+ 1 file changed, 37 insertions(+), 27 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index 67091574a..a570e5856 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -3462,6 +3462,42 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
++/* called in any thread, connection lock is held */
++static void
++add_signal_data (GDBusConnection *connection,
++                 SignalData      *signal_data)
++{
++  GPtrArray *signal_data_array;
++
++  g_hash_table_insert (connection->map_rule_to_signal_data,
++                       signal_data->rule,
++                       signal_data);
++
++  /* Add the match rule to the bus...
++   *
++   * Avoid adding match rules for NameLost and NameAcquired messages - the bus will
++   * always send such messages to us.
++   */
++  if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
++    {
++      if (!is_signal_data_for_name_lost_or_acquired (signal_data))
++        add_match_rule (connection, signal_data->rule);
++    }
++
++  signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
++                                           signal_data->sender_unique_name);
++  if (signal_data_array == NULL)
++    {
++      signal_data_array = g_ptr_array_new ();
++      g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array,
++                           g_strdup (signal_data->sender_unique_name),
++                           signal_data_array);
++    }
++  g_ptr_array_add (signal_data_array, signal_data);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
+ /**
+  * g_dbus_connection_signal_subscribe:
+  * @connection: a #GDBusConnection
+@@ -3551,7 +3587,6 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+   gchar *rule;
+   SignalData *signal_data;
+   SignalSubscriber *subscriber;
+-  GPtrArray *signal_data_array;
+   const gchar *sender_unique_name;
+ 
+   /* Right now we abort if AddMatch() fails since it can only fail with the bus being in
+@@ -3617,32 +3652,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+                                       g_strdup (arg0),
+                                       flags);
+   g_ptr_array_add (signal_data->subscribers, subscriber);
+-
+-  g_hash_table_insert (connection->map_rule_to_signal_data,
+-                       signal_data->rule,
+-                       signal_data);
+-
+-  /* Add the match rule to the bus...
+-   *
+-   * Avoid adding match rules for NameLost and NameAcquired messages - the bus will
+-   * always send such messages to us.
+-   */
+-  if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
+-    {
+-      if (!is_signal_data_for_name_lost_or_acquired (signal_data))
+-        add_match_rule (connection, signal_data->rule);
+-    }
+-
+-  signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
+-                                           signal_data->sender_unique_name);
+-  if (signal_data_array == NULL)
+-    {
+-      signal_data_array = g_ptr_array_new ();
+-      g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array,
+-                           g_strdup (signal_data->sender_unique_name),
+-                           signal_data_array);
+-    }
+-  g_ptr_array_add (signal_data_array, signal_data);
++  add_signal_data (connection, signal_data);
+ 
+  out:
+   g_hash_table_insert (connection->map_id_to_signal_data,
+-- 
+2.45.0
+
+
+From dd4a94708ef1bd63763bedcb93161746cbc9c7e6 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Thu, 14 Mar 2024 19:51:59 +0000
+Subject: [PATCH 11/19] gdbusconnection: Factor out
+ remove_signal_data_if_unused
+
+No functional change, just removing some nesting. The check for whether
+signal_data->subscribers is empty changes from a conditional that tests
+whether it is into an early-return if it isn't.
+
+A subsequent commit will add additional conditions that make us consider
+a SignalData to be still in use and therefore not eligible to be removed.
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 83 +++++++++++++++++++++++++------------------
+ 1 file changed, 48 insertions(+), 35 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index a570e5856..b2268e637 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -3666,6 +3666,52 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
++/*
++ * Called in any thread.
++ * Must hold the connection lock when calling this, unless
++ * connection->finalizing is TRUE.
++ * May free signal_data, so do not dereference it after this.
++ */
++static void
++remove_signal_data_if_unused (GDBusConnection *connection,
++                              SignalData *signal_data)
++{
++  GPtrArray *signal_data_array;
++
++  if (signal_data->subscribers->len != 0)
++    return;
++
++  g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule));
++
++  signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
++                                           signal_data->sender_unique_name);
++  g_warn_if_fail (signal_data_array != NULL);
++  g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data));
++
++  if (signal_data_array->len == 0)
++    {
++      g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array,
++                                           signal_data->sender_unique_name));
++    }
++
++  /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */
++  if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) &&
++      !is_signal_data_for_name_lost_or_acquired (signal_data) &&
++      !g_dbus_connection_is_closed (connection) &&
++      !connection->finalizing)
++    {
++      /* The check for g_dbus_connection_is_closed() means that
++       * sending the RemoveMatch message can't fail with
++       * G_IO_ERROR_CLOSED, because we're holding the lock,
++       * so on_worker_closed() can't happen between the check we just
++       * did, and releasing the lock later.
++       */
++      remove_match_rule (connection, signal_data->rule);
++    }
++
++  signal_data_free (signal_data);
++}
++
+ /* called in any thread */
+ /* must hold lock when calling this (except if connection->finalizing is TRUE)
+  * returns the number of removed subscribers */
+@@ -3674,7 +3720,6 @@ unsubscribe_id_internal (GDBusConnection *connection,
+                          guint            subscription_id)
+ {
+   SignalData *signal_data;
+-  GPtrArray *signal_data_array;
+   guint n;
+   guint n_removed = 0;
+ 
+@@ -3701,40 +3746,8 @@ unsubscribe_id_internal (GDBusConnection *connection,
+                                            GUINT_TO_POINTER (subscription_id)));
+       n_removed++;
+       g_ptr_array_remove_index_fast (signal_data->subscribers, n);
+-
+-      if (signal_data->subscribers->len == 0)
+-        {
+-          g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule));
+-
+-          signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
+-                                                   signal_data->sender_unique_name);
+-          g_warn_if_fail (signal_data_array != NULL);
+-          g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data));
+-
+-          if (signal_data_array->len == 0)
+-            {
+-              g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array,
+-                                                   signal_data->sender_unique_name));
+-            }
+-
+-          /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */
+-          if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) &&
+-              !is_signal_data_for_name_lost_or_acquired (signal_data) &&
+-              !g_dbus_connection_is_closed (connection) &&
+-              !connection->finalizing)
+-            {
+-              /* The check for g_dbus_connection_is_closed() means that
+-               * sending the RemoveMatch message can't fail with
+-               * G_IO_ERROR_CLOSED, because we're holding the lock,
+-               * so on_worker_closed() can't happen between the check we just
+-               * did, and releasing the lock later.
+-               */
+-              remove_match_rule (connection, signal_data->rule);
+-            }
+-
+-          signal_data_free (signal_data);
+-        }
+-
++      /* May free signal_data */
++      remove_signal_data_if_unused (connection, signal_data);
+       goto out;
+     }
+ 
+-- 
+2.45.0
+
+
+From 64758288c6e71dd92270771b6b81891e4be87fa9 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Tue, 23 Apr 2024 20:39:05 +0100
+Subject: [PATCH 12/19] gdbusconnection: Stop storing sender_unique_name in
+ SignalData
+
+This will become confusing when we start tracking the owner of a
+well-known-name sender, and it's redundant anyway. Instead, track the
+1 bit of data that we actually need: whether it's a well-known name.
+
+Strictly speaking this too is redundant, because it's syntactically
+derivable from the sender, but only via extra string operations.
+A subsequent commit will add a data structure to keep track of the
+owner of a well-known-name sender, at which point this boolean will
+be replaced by the presence or absence of that data structure.
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 36 ++++++++++++++++++++++++------------
+ 1 file changed, 24 insertions(+), 12 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index b2268e637..e17703d0f 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -339,19 +339,19 @@ typedef struct
+ {
+   gchar *rule;
+   gchar *sender;
+-  gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */
+   gchar *interface_name;
+   gchar *member;
+   gchar *object_path;
+   gchar *arg0;
+   GDBusSignalFlags flags;
+   GPtrArray *subscribers;  /* (owned) (element-type SignalSubscriber) */
++  gboolean sender_is_its_own_owner;
+ } SignalData;
+ 
+ static SignalData *
+ signal_data_new_take (gchar *rule,
+                       gchar *sender,
+-                      gchar *sender_unique_name,
++                      gboolean sender_is_its_own_owner,
+                       gchar *interface_name,
+                       gchar *member,
+                       gchar *object_path,
+@@ -362,7 +362,7 @@ signal_data_new_take (gchar *rule,
+ 
+   signal_data->rule = rule;
+   signal_data->sender = sender;
+-  signal_data->sender_unique_name = sender_unique_name;
++  signal_data->sender_is_its_own_owner = sender_is_its_own_owner;
+   signal_data->interface_name = interface_name;
+   signal_data->member = member;
+   signal_data->object_path = object_path;
+@@ -377,7 +377,6 @@ signal_data_free (SignalData *signal_data)
+ {
+   g_free (signal_data->rule);
+   g_free (signal_data->sender);
+-  g_free (signal_data->sender_unique_name);
+   g_free (signal_data->interface_name);
+   g_free (signal_data->member);
+   g_free (signal_data->object_path);
+@@ -3453,7 +3452,7 @@ remove_match_rule (GDBusConnection *connection,
+ static gboolean
+ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
+ {
+-  return g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0 &&
++  return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 &&
+          g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 &&
+          g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 &&
+          (g_strcmp0 (signal_data->member, "NameLost") == 0 ||
+@@ -3465,7 +3464,8 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
+ /* called in any thread, connection lock is held */
+ static void
+ add_signal_data (GDBusConnection *connection,
+-                 SignalData      *signal_data)
++                 SignalData      *signal_data,
++                 const char      *sender_unique_name)
+ {
+   GPtrArray *signal_data_array;
+ 
+@@ -3485,12 +3485,12 @@ add_signal_data (GDBusConnection *connection,
+     }
+ 
+   signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
+-                                           signal_data->sender_unique_name);
++                                           sender_unique_name);
+   if (signal_data_array == NULL)
+     {
+       signal_data_array = g_ptr_array_new ();
+       g_hash_table_insert (connection->map_sender_unique_name_to_signal_data_array,
+-                           g_strdup (signal_data->sender_unique_name),
++                           g_strdup (sender_unique_name),
+                            signal_data_array);
+     }
+   g_ptr_array_add (signal_data_array, signal_data);
+@@ -3587,6 +3587,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+   gchar *rule;
+   SignalData *signal_data;
+   SignalSubscriber *subscriber;
++  gboolean sender_is_its_own_owner;
+   const gchar *sender_unique_name;
+ 
+   /* Right now we abort if AddMatch() fails since it can only fail with the bus being in
+@@ -3622,6 +3623,11 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+   rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags);
+ 
+   if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0))
++    sender_is_its_own_owner = TRUE;
++  else
++    sender_is_its_own_owner = FALSE;
++
++  if (sender_is_its_own_owner)
+     sender_unique_name = sender;
+   else
+     sender_unique_name = "";
+@@ -3645,14 +3651,14 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+ 
+   signal_data = signal_data_new_take (g_steal_pointer (&rule),
+                                       g_strdup (sender),
+-                                      g_strdup (sender_unique_name),
++                                      sender_is_its_own_owner,
+                                       g_strdup (interface_name),
+                                       g_strdup (member),
+                                       g_strdup (object_path),
+                                       g_strdup (arg0),
+                                       flags);
+   g_ptr_array_add (signal_data->subscribers, subscriber);
+-  add_signal_data (connection, signal_data);
++  add_signal_data (connection, signal_data, sender_unique_name);
+ 
+  out:
+   g_hash_table_insert (connection->map_id_to_signal_data,
+@@ -3676,22 +3682,28 @@ static void
+ remove_signal_data_if_unused (GDBusConnection *connection,
+                               SignalData *signal_data)
+ {
++  const gchar *sender_unique_name;
+   GPtrArray *signal_data_array;
+ 
+   if (signal_data->subscribers->len != 0)
+     return;
+ 
++  if (signal_data->sender_is_its_own_owner)
++    sender_unique_name = signal_data->sender;
++  else
++    sender_unique_name = "";
++
+   g_warn_if_fail (g_hash_table_remove (connection->map_rule_to_signal_data, signal_data->rule));
+ 
+   signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
+-                                           signal_data->sender_unique_name);
++                                           sender_unique_name);
+   g_warn_if_fail (signal_data_array != NULL);
+   g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data));
+ 
+   if (signal_data_array->len == 0)
+     {
+       g_warn_if_fail (g_hash_table_remove (connection->map_sender_unique_name_to_signal_data_array,
+-                                           signal_data->sender_unique_name));
++                                           sender_unique_name));
+     }
+ 
+   /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */
+-- 
+2.45.0
+
+
+From 3dd78b5a67c0dea7c453d29345cf42f6c30a600b Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Wed, 1 May 2024 15:43:09 +0100
+Subject: [PATCH 13/19] gdbus: Track name owners for signal subscriptions
+
+We will use this in a subsequent commit to prevent signals from an
+impostor from being delivered to a subscriber.
+
+To avoid message reordering leading to misleading situations, this does
+not use the existing mechanism for watching bus name ownership, which
+delivers the ownership changes to other main-contexts. Instead, it all
+happens on the single thread used by the GDBusWorker, so the order in
+which messages are received is the order in which they are processed.
+
+[Backported to glib-2-74, resolving minor conflicts]
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 350 +++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 343 insertions(+), 7 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index e17703d0f..6d18b2457 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -336,6 +336,31 @@ signal_subscriber_unref (SignalSubscriber *subscriber)
+ }
+ 
+ typedef struct
++{
++  /*
++   * 1 reference while waiting for GetNameOwner() to finish
++   * 1 reference for each SignalData that points to this one as its
++   *   shared_name_watcher
++   */
++  grefcount ref_count;
++
++  gchar *owner;
++  guint32 get_name_owner_serial;
++} WatchedName;
++
++static WatchedName *
++watched_name_new (void)
++{
++  WatchedName *watched_name = g_new0 (WatchedName, 1);
++
++  g_ref_count_init (&watched_name->ref_count);
++  watched_name->owner = NULL;
++  return g_steal_pointer (&watched_name);
++}
++
++typedef struct SignalData SignalData;
++
++struct SignalData
+ {
+   gchar *rule;
+   gchar *sender;
+@@ -345,13 +370,36 @@ typedef struct
+   gchar *arg0;
+   GDBusSignalFlags flags;
+   GPtrArray *subscribers;  /* (owned) (element-type SignalSubscriber) */
+-  gboolean sender_is_its_own_owner;
+-} SignalData;
++
++  /*
++   * If the sender is a well-known name, this is an unowned SignalData
++   * representing the NameOwnerChanged signal that tracks its owner.
++   * NULL if sender is NULL.
++   * NULL if sender is its own owner (a unique name or DBUS_SERVICE_DBUS).
++   *
++   * Invariants: if not NULL, then
++   * shared_name_watcher->sender == DBUS_SERVICE_DBUS
++   * shared_name_watcher->interface_name == DBUS_INTERFACE_DBUS
++   * shared_name_watcher->member == "NameOwnerChanged"
++   * shared_name_watcher->object_path == DBUS_PATH_DBUS
++   * shared_name_watcher->arg0 == sender
++   * shared_name_watcher->flags == NONE
++   * shared_name_watcher->watched_name == NULL
++   */
++  SignalData *shared_name_watcher;
++
++  /*
++   * Non-NULL if this SignalData is another SignalData's shared_name_watcher.
++   * One reference for each SignalData that has this one as its
++   * shared_name_watcher.
++   * Otherwise NULL.
++   */
++  WatchedName *watched_name;
++};
+ 
+ static SignalData *
+ signal_data_new_take (gchar *rule,
+                       gchar *sender,
+-                      gboolean sender_is_its_own_owner,
+                       gchar *interface_name,
+                       gchar *member,
+                       gchar *object_path,
+@@ -362,7 +410,6 @@ signal_data_new_take (gchar *rule,
+ 
+   signal_data->rule = rule;
+   signal_data->sender = sender;
+-  signal_data->sender_is_its_own_owner = sender_is_its_own_owner;
+   signal_data->interface_name = interface_name;
+   signal_data->member = member;
+   signal_data->object_path = object_path;
+@@ -375,6 +422,17 @@ signal_data_new_take (gchar *rule,
+ static void
+ signal_data_free (SignalData *signal_data)
+ {
++  /* The SignalData should not be freed while it still has subscribers */
++  g_assert (signal_data->subscribers->len == 0);
++
++  /* The SignalData should not be freed while it is watching for
++   * NameOwnerChanged on behalf of another SignalData */
++  g_assert (signal_data->watched_name == NULL);
++
++  /* The SignalData should be detached from its name watcher, if any,
++   * before it is freed */
++  g_assert (signal_data->shared_name_watcher == NULL);
++
+   g_free (signal_data->rule);
+   g_free (signal_data->sender);
+   g_free (signal_data->interface_name);
+@@ -382,6 +440,7 @@ signal_data_free (SignalData *signal_data)
+   g_free (signal_data->object_path);
+   g_free (signal_data->arg0);
+   g_ptr_array_unref (signal_data->subscribers);
++
+   g_free (signal_data);
+ }
+ 
+@@ -513,6 +572,7 @@ struct _GDBusConnection
+ 
+   /* Map used for managing method replies, protected by @lock */
+   GHashTable *map_method_serial_to_task;  /* guint32 -> GTask* */
++  GHashTable *map_method_serial_to_name_watcher;  /* guint32 -> unowned SignalData* */
+ 
+   /* Maps used for managing signal subscription, protected by @lock */
+   GHashTable *map_rule_to_signal_data;                      /* match rule (gchar*)    -> SignalData */
+@@ -760,6 +820,7 @@ g_dbus_connection_finalize (GObject *object)
+     g_error_free (connection->initialization_error);
+ 
+   g_hash_table_unref (connection->map_method_serial_to_task);
++  g_hash_table_unref (connection->map_method_serial_to_name_watcher);
+ 
+   g_hash_table_unref (connection->map_rule_to_signal_data);
+   g_hash_table_unref (connection->map_id_to_signal_data);
+@@ -1155,6 +1216,7 @@ g_dbus_connection_init (GDBusConnection *connection)
+   g_mutex_init (&connection->init_lock);
+ 
+   connection->map_method_serial_to_task = g_hash_table_new (g_direct_hash, g_direct_equal);
++  connection->map_method_serial_to_name_watcher = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+ 
+   connection->map_rule_to_signal_data = g_hash_table_new (g_str_hash,
+                                                           g_str_equal);
+@@ -2281,6 +2343,191 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection        *connecti
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
++/*
++ * Called in any thread.
++ * Must hold the connection lock when calling this, unless
++ * connection->finalizing is TRUE.
++ */
++static void
++name_watcher_unref_watched_name (GDBusConnection *connection,
++                                 SignalData *name_watcher)
++{
++  WatchedName *watched_name = name_watcher->watched_name;
++
++  g_assert (watched_name != NULL);
++
++  if (!g_ref_count_dec (&watched_name->ref_count))
++    return;
++
++  /* Removing watched_name from the name_watcher may result in
++   * name_watcher being freed, so we must make sure name_watcher is no
++   * longer in map_method_serial_to_name_watcher.
++   *
++   * If we stop watching the name while our GetNameOwner call was still
++   * in-flight, then when the reply eventually arrives, we will not find
++   * its serial number in the map and harmlessly ignore it as a result. */
++  if (watched_name->get_name_owner_serial != 0)
++    g_hash_table_remove (connection->map_method_serial_to_name_watcher,
++                         GUINT_TO_POINTER (watched_name->get_name_owner_serial));
++
++  name_watcher->watched_name = NULL;
++  g_free (watched_name->owner);
++  g_free (watched_name);
++}
++
++/* called in GDBusWorker thread with lock held */
++static void
++name_watcher_set_name_owner_unlocked (SignalData *name_watcher,
++                                      const char *new_owner)
++{
++  if (new_owner != NULL && new_owner[0] == '\0')
++    new_owner = NULL;
++
++  g_assert (name_watcher->watched_name != NULL);
++  g_set_str (&name_watcher->watched_name->owner, new_owner);
++}
++
++/* called in GDBusWorker thread with lock held */
++static void
++name_watcher_deliver_name_owner_changed_unlocked (SignalData *name_watcher,
++                                                  GDBusMessage *message)
++{
++  GVariant *body;
++
++  body = g_dbus_message_get_body (message);
++
++  if (G_LIKELY (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(sss)"))))
++    {
++      const char *name;
++      const char *new_owner;
++
++      g_variant_get (body, "(&s&s&s)", &name, NULL, &new_owner);
++
++      /* Our caller already checked this */
++      g_assert (g_strcmp0 (name_watcher->arg0, name) == 0);
++
++      if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_unique_name (new_owner)))
++        name_watcher_set_name_owner_unlocked (name_watcher, new_owner);
++      else
++        g_warning ("Received NameOwnerChanged signal with invalid owner \"%s\" for \"%s\"",
++                   new_owner, name);
++    }
++  else
++    {
++      g_warning ("Received NameOwnerChanged signal with unexpected "
++                 "signature %s",
++                 body == NULL ? "()" : g_variant_get_type_string (body));
++
++    }
++}
++
++/* called in GDBusWorker thread with lock held */
++static void
++name_watcher_deliver_get_name_owner_reply_unlocked (SignalData *name_watcher,
++                                                    GDBusConnection *connection,
++                                                    GDBusMessage *message)
++{
++  GDBusMessageType type;
++  GVariant *body;
++  WatchedName *watched_name;
++
++  watched_name = name_watcher->watched_name;
++  g_assert (watched_name != NULL);
++  g_assert (watched_name->get_name_owner_serial != 0);
++
++  type = g_dbus_message_get_message_type (message);
++  body = g_dbus_message_get_body (message);
++
++  if (type == G_DBUS_MESSAGE_TYPE_ERROR)
++    {
++      if (g_strcmp0 (g_dbus_message_get_error_name (message),
++                     "org.freedesktop.DBus.Error.NameHasNoOwner"))
++        name_watcher_set_name_owner_unlocked (name_watcher, NULL);
++      /* else it's something like NoReply or AccessDenied, which tells
++       * us nothing - leave the owner set to whatever we most recently
++       * learned from NameOwnerChanged, or NULL */
++    }
++  else if (type != G_DBUS_MESSAGE_TYPE_METHOD_RETURN)
++    {
++      g_warning ("Received GetNameOwner reply with unexpected type %d",
++                 type);
++    }
++  else if (G_LIKELY (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)"))))
++    {
++      const char *new_owner;
++
++      g_variant_get (body, "(&s)", &new_owner);
++
++      if (G_LIKELY (g_dbus_is_unique_name (new_owner)))
++        name_watcher_set_name_owner_unlocked (name_watcher, new_owner);
++      else
++        g_warning ("Received GetNameOwner reply with invalid owner \"%s\" for \"%s\"",
++                   new_owner, name_watcher->arg0);
++    }
++  else
++    {
++      g_warning ("Received GetNameOwner reply with unexpected signature %s",
++                 body == NULL ? "()" : g_variant_get_type_string (body));
++    }
++
++  g_hash_table_remove (connection->map_method_serial_to_name_watcher,
++                       GUINT_TO_POINTER (watched_name->get_name_owner_serial));
++  watched_name->get_name_owner_serial = 0;
++}
++
++/* Called in a user thread, lock is held */
++static void
++name_watcher_call_get_name_owner_unlocked (GDBusConnection *connection,
++                                           SignalData *name_watcher)
++{
++  GDBusMessage *message;
++  GError *local_error = NULL;
++  WatchedName *watched_name;
++
++  g_assert (g_strcmp0 (name_watcher->sender, DBUS_SERVICE_DBUS) == 0);
++  g_assert (g_strcmp0 (name_watcher->interface_name, DBUS_INTERFACE_DBUS) == 0);
++  g_assert (g_strcmp0 (name_watcher->member, "NameOwnerChanged") == 0);
++  g_assert (g_strcmp0 (name_watcher->object_path, DBUS_PATH_DBUS) == 0);
++  /* arg0 of the NameOwnerChanged message is the well-known name whose owner
++   * we are interested in */
++  g_assert (g_dbus_is_name (name_watcher->arg0));
++  g_assert (name_watcher->flags == G_DBUS_SIGNAL_FLAGS_NONE);
++
++  watched_name = name_watcher->watched_name;
++  g_assert (watched_name != NULL);
++  g_assert (watched_name->owner == NULL);
++  g_assert (watched_name->get_name_owner_serial == 0);
++  g_assert (name_watcher->shared_name_watcher == NULL);
++
++  message = g_dbus_message_new_method_call (DBUS_SERVICE_DBUS,
++                                            DBUS_PATH_DBUS,
++                                            DBUS_INTERFACE_DBUS,
++                                            "GetNameOwner");
++  g_dbus_message_set_body (message, g_variant_new ("(s)", name_watcher->arg0));
++
++  if (g_dbus_connection_send_message_unlocked (connection, message,
++                                               G_DBUS_SEND_MESSAGE_FLAGS_NONE,
++                                               &watched_name->get_name_owner_serial,
++                                               &local_error))
++    {
++      g_assert (watched_name->get_name_owner_serial != 0);
++      g_hash_table_insert (connection->map_method_serial_to_name_watcher,
++                           GUINT_TO_POINTER (watched_name->get_name_owner_serial),
++                           name_watcher);
++    }
++  else
++    {
++      g_critical ("Error while sending GetNameOwner() message: %s",
++                  local_error->message);
++      g_clear_error (&local_error);
++      g_assert (watched_name->get_name_owner_serial == 0);
++    }
++
++  g_object_unref (message);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
+ typedef struct
+ {
+   guint                       id;
+@@ -2392,6 +2639,7 @@ on_worker_message_received (GDBusWorker  *worker,
+         {
+           guint32 reply_serial;
+           GTask *task;
++          SignalData *name_watcher;
+ 
+           reply_serial = g_dbus_message_get_reply_serial (message);
+           CONNECTION_LOCK (connection);
+@@ -2407,6 +2655,19 @@ on_worker_message_received (GDBusWorker  *worker,
+             {
+               //g_debug ("message reply/error for serial %d but no SendMessageData found for %p", reply_serial, connection);
+             }
++
++          name_watcher = g_hash_table_lookup (connection->map_method_serial_to_name_watcher,
++                                              GUINT_TO_POINTER (reply_serial));
++
++          if (name_watcher != NULL)
++            {
++              g_assert (name_watcher->watched_name != NULL);
++              g_assert (name_watcher->watched_name->get_name_owner_serial == reply_serial);
++              name_watcher_deliver_get_name_owner_reply_unlocked (name_watcher,
++                                                                  connection,
++                                                                  message);
++            }
++
+           CONNECTION_UNLOCK (connection);
+         }
+       else if (message_type == G_DBUS_MESSAGE_TYPE_SIGNAL)
+@@ -3586,6 +3847,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+ {
+   gchar *rule;
+   SignalData *signal_data;
++  SignalData *name_watcher = NULL;
+   SignalSubscriber *subscriber;
+   gboolean sender_is_its_own_owner;
+   const gchar *sender_unique_name;
+@@ -3651,13 +3913,59 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
+ 
+   signal_data = signal_data_new_take (g_steal_pointer (&rule),
+                                       g_strdup (sender),
+-                                      sender_is_its_own_owner,
+                                       g_strdup (interface_name),
+                                       g_strdup (member),
+                                       g_strdup (object_path),
+                                       g_strdup (arg0),
+                                       flags);
+   g_ptr_array_add (signal_data->subscribers, subscriber);
++
++  /* If subscribing to a signal from a specific sender with a well-known
++   * name, we must first subscribe to NameOwnerChanged signals for that
++   * well-known name, so that we can match the current owner of the name
++   * against the sender of each signal. */
++  if (sender != NULL && !sender_is_its_own_owner)
++    {
++      gchar *name_owner_rule = NULL;
++
++      /* We already checked that sender != NULL implies MESSAGE_BUS_CONNECTION */
++      g_assert (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION);
++
++      name_owner_rule = args_to_rule (DBUS_SERVICE_DBUS,
++                                      DBUS_INTERFACE_DBUS,
++                                      "NameOwnerChanged",
++                                      DBUS_PATH_DBUS,
++                                      sender,
++                                      G_DBUS_SIGNAL_FLAGS_NONE);
++      name_watcher = g_hash_table_lookup (connection->map_rule_to_signal_data, name_owner_rule);
++
++      if (name_watcher == NULL)
++        {
++          name_watcher = signal_data_new_take (g_steal_pointer (&name_owner_rule),
++                                               g_strdup (DBUS_SERVICE_DBUS),
++                                               g_strdup (DBUS_INTERFACE_DBUS),
++                                               g_strdup ("NameOwnerChanged"),
++                                               g_strdup (DBUS_PATH_DBUS),
++                                               g_strdup (sender),
++                                               G_DBUS_SIGNAL_FLAGS_NONE);
++          add_signal_data (connection, name_watcher, DBUS_SERVICE_DBUS);
++        }
++
++      if (name_watcher->watched_name == NULL)
++        {
++          name_watcher->watched_name = watched_name_new ();
++          name_watcher_call_get_name_owner_unlocked (connection, name_watcher);
++        }
++      else
++        {
++          g_ref_count_inc (&name_watcher->watched_name->ref_count);
++        }
++
++      signal_data->shared_name_watcher = name_watcher;
++
++      g_clear_pointer (&name_owner_rule, g_free);
++    }
++
+   add_signal_data (connection, signal_data, sender_unique_name);
+ 
+  out:
+@@ -3685,10 +3993,18 @@ remove_signal_data_if_unused (GDBusConnection *connection,
+   const gchar *sender_unique_name;
+   GPtrArray *signal_data_array;
+ 
++  /* Cannot remove while there are still subscribers */
+   if (signal_data->subscribers->len != 0)
+     return;
+ 
+-  if (signal_data->sender_is_its_own_owner)
++  /* Cannot remove while another SignalData is still using this one
++   * as its shared_name_watcher, which holds watched_name->ref_count > 0 */
++  if (signal_data->watched_name != NULL)
++    return;
++
++  /* Point of no return: we have committed to removing it */
++
++  if (signal_data->sender != NULL && signal_data->shared_name_watcher == NULL)
+     sender_unique_name = signal_data->sender;
+   else
+     sender_unique_name = "";
+@@ -3721,6 +4037,15 @@ remove_signal_data_if_unused (GDBusConnection *connection,
+       remove_match_rule (connection, signal_data->rule);
+     }
+ 
++  if (signal_data->shared_name_watcher != NULL)
++    {
++      SignalData *name_watcher = g_steal_pointer (&signal_data->shared_name_watcher);
++
++      name_watcher_unref_watched_name (connection, name_watcher);
++      /* May free signal_data */
++      remove_signal_data_if_unused (connection, name_watcher);
++    }
++
+   signal_data_free (signal_data);
+ }
+ 
+@@ -3990,6 +4315,17 @@ schedule_callbacks (GDBusConnection *connection,
+             continue;
+         }
+ 
++      if (signal_data->watched_name != NULL)
++        {
++          /* Invariant: SignalData should only have a watched_name if it
++           * represents the NameOwnerChanged signal */
++          g_assert (g_strcmp0 (sender, DBUS_SERVICE_DBUS) == 0);
++          g_assert (g_strcmp0 (interface, DBUS_INTERFACE_DBUS) == 0);
++          g_assert (g_strcmp0 (path, DBUS_PATH_DBUS) == 0);
++          g_assert (g_strcmp0 (member, "NameOwnerChanged") == 0);
++          name_watcher_deliver_name_owner_changed_unlocked (signal_data, message);
++        }
++
+       for (m = 0; m < signal_data->subscribers->len; m++)
+         {
+           SignalSubscriber *subscriber = signal_data->subscribers->pdata[m];
+@@ -4051,7 +4387,7 @@ distribute_signals (GDBusConnection *connection,
+         schedule_callbacks (connection, signal_data_array, message, sender);
+     }
+ 
+-  /* collect subscribers not matching on sender */
++  /* collect subscribers not matching on sender, or matching a well-known name */
+   signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, "");
+   if (signal_data_array != NULL)
+     schedule_callbacks (connection, signal_data_array, message, sender);
+-- 
+2.45.0
+
+
+From ccaea1caa6ebafd931b6221f241dc90d5fd96e62 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Thu, 14 Mar 2024 20:42:41 +0000
+Subject: [PATCH 14/19] gdbusconnection: Don't deliver signals if the sender
+ doesn't match
+
+Otherwise a malicious connection on a shared bus, especially the system
+bus, could trick GDBus clients into processing signals sent by the
+malicious connection as though they had come from the real owner of a
+well-known service name.
+
+Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/3268
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/gdbusconnection.c | 40 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index 6d18b2457..85f99c5c6 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -4296,6 +4296,46 @@ schedule_callbacks (GDBusConnection *connection,
+       if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0)
+         continue;
+ 
++      if (signal_data->shared_name_watcher != NULL)
++        {
++          /* We want signals from a specified well-known name, which means
++           * the signal's sender needs to be the unique name that currently
++           * owns that well-known name, and we will have found this
++           * SignalData in
++           * connection->map_sender_unique_name_to_signal_data_array[""]. */
++          const WatchedName *watched_name;
++          const char *current_owner;
++
++          g_assert (signal_data->sender != NULL);
++          /* Invariant: We never need to watch for the owner of a unique
++           * name, or for the owner of DBUS_SERVICE_DBUS, either of which
++           * is always its own owner */
++          g_assert (!g_dbus_is_unique_name (signal_data->sender));
++          g_assert (g_strcmp0 (signal_data->sender, DBUS_SERVICE_DBUS) != 0);
++
++          watched_name = signal_data->shared_name_watcher->watched_name;
++          g_assert (watched_name != NULL);
++          current_owner = watched_name->owner;
++
++          /* Skip the signal if the actual sender is not known to own
++           * the required name */
++          if (current_owner == NULL || g_strcmp0 (current_owner, sender) != 0)
++            continue;
++        }
++      else if (signal_data->sender != NULL)
++        {
++          /* We want signals from a unique name or o.fd.DBus... */
++          g_assert (g_dbus_is_unique_name (signal_data->sender)
++                    || g_str_equal (signal_data->sender, DBUS_SERVICE_DBUS));
++
++          /* ... which means we must have found this SignalData in
++           * connection->map_sender_unique_name_to_signal_data_array[signal_data->sender],
++           * therefore we would only have found it if the signal's
++           * actual sender matches the required signal_data->sender */
++          g_assert (g_strcmp0 (signal_data->sender, sender) == 0);
++        }
++      /* else the sender is unspecified and we will accept anything */
++
+       if (signal_data->arg0 != NULL)
+         {
+           if (arg0 == NULL)
+-- 
+2.45.0
+
+
+From 9fe1b11d43ca8f8d77ecf5e3f0976306d6d099ec Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Fri, 8 Mar 2024 19:51:50 +0000
+Subject: [PATCH 15/19] tests: Add a test for matching by two well-known names
+
+The expected result is that because TEST_CONN_SERVICE owns
+ALREADY_OWNED_NAME but not (yet) OWNED_LATER_NAME, the signal will be
+delivered to the subscriber for the former but not the latter.
+Before #3268 was fixed, it was incorrectly delivered to both.
+
+Reproduces: https://gitlab.gnome.org/GNOME/glib/-/issues/3268 (partially)
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+index af100de7d..171d6107d 100644
+--- a/gio/tests/gdbus-subscribe.c
++++ b/gio/tests/gdbus-subscribe.c
+@@ -440,6 +440,19 @@ static const TestPlan plan_limit_by_well_known_name =
+         .iface = EXAMPLE_INTERFACE,
+       },
+     },
++    {
++      /* When the service sends a signal with the name it already owns,
++       * it should get through */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 1,
++        .received_by_proxy = 1
++      },
++    },
+     {
+       /* Service claims another name */
+       .action = TEST_ACTION_OWN_NAME,
+-- 
+2.45.0
+
+
+From d9c20bc802c6f2fc8e36c51acad7a751de5f1b3f Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Fri, 8 Mar 2024 19:53:22 +0000
+Subject: [PATCH 16/19] tests: Add a test for signal filtering by well-known
+ name
+
+The vulnerability reported as GNOME/glib#3268 can be characterized
+as: these signals from an attacker should not be delivered to either
+the GDBusConnection or the GDBusProxy, but in fact they are (in at
+least some scenarios).
+
+Reproduces: https://gitlab.gnome.org/GNOME/glib/-/issues/3268
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+index 171d6107d..5406ba7e2 100644
+--- a/gio/tests/gdbus-subscribe.c
++++ b/gio/tests/gdbus-subscribe.c
+@@ -440,6 +440,33 @@ static const TestPlan plan_limit_by_well_known_name =
+         .iface = EXAMPLE_INTERFACE,
+       },
+     },
++    {
++      /* Attacker wants to trick subscriber into thinking that service
++       * sent a signal */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
++    {
++      /* Attacker tries harder, by sending a signal unicast directly to
++       * the subscriber */
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_ATTACKER,
++        .unicast_to = TEST_CONN_SUBSCRIBER,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        .received_by_proxy = 0
++      },
++    },
+     {
+       /* When the service sends a signal with the name it already owns,
+        * it should get through */
+-- 
+2.45.0
+
+
+From 420402151349d1e34ea13b7585c76d3a51ba48f7 Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Tue, 23 Apr 2024 21:39:43 +0100
+Subject: [PATCH 17/19] tests: Ensure that unsubscribing with GetNameOwner
+ in-flight doesn't crash
+
+This was a bug that existed during development of this branch; make sure
+it doesn't come back.
+
+This test fails with a use-after-free and crash if we comment out the
+part of name_watcher_unref_watched_name() that removes the name watcher
+from `map_method_serial_to_name_watcher`.
+
+It would also fail with an assertion failure if we asserted in
+name_watcher_unref_watched_name() that get_name_owner_serial == 0
+(i.e. that GetNameOwner is not in-flight at destruction).
+
+Signed-off-by: Simon McVittie <smcv@collabora.com>
+---
+ gio/tests/gdbus-subscribe.c | 52 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 51 insertions(+), 1 deletion(-)
+
+diff --git a/gio/tests/gdbus-subscribe.c b/gio/tests/gdbus-subscribe.c
+index 5406ba7e2..4cba4f565 100644
+--- a/gio/tests/gdbus-subscribe.c
++++ b/gio/tests/gdbus-subscribe.c
+@@ -116,6 +116,7 @@ typedef struct
+   const char *member;
+   const char *arg0;
+   GDBusSignalFlags flags;
++  gboolean unsubscribe_immediately;
+ } TestSubscribe;
+ 
+ typedef struct
+@@ -141,6 +142,7 @@ typedef struct
+     TestEmitSignal signal;
+     TestSubscribe subscribe;
+     TestOwnName own_name;
++    guint unsubscribe_undo_step;
+   } u;
+ } TestStep;
+ 
+@@ -505,6 +507,43 @@ static const TestPlan plan_limit_by_well_known_name =
+   },
+ };
+ 
++static const TestPlan plan_unsubscribe_immediately =
++{
++  .description = "Unsubscribing before GetNameOwner can return doesn't result in a crash",
++  .steps = {
++    {
++      /* Service already owns one name */
++      .action = TEST_ACTION_OWN_NAME,
++      .u.own_name = {
++        .name = ALREADY_OWNED_NAME,
++        .owner = TEST_CONN_SERVICE
++      },
++    },
++    {
++      .action = TEST_ACTION_SUBSCRIBE,
++      .u.subscribe = {
++        .string_sender = ALREADY_OWNED_NAME,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .unsubscribe_immediately = TRUE
++      },
++    },
++    {
++      .action = TEST_ACTION_EMIT_SIGNAL,
++      .u.signal = {
++        .sender = TEST_CONN_SERVICE,
++        .path = EXAMPLE_PATH,
++        .iface = EXAMPLE_INTERFACE,
++        .member = FOO_SIGNAL,
++        .received_by_conn = 0,
++        /* The proxy can't unsubscribe, except by destroying the proxy
++         * completely, which we don't currently implement in this test */
++        .received_by_proxy = 1
++      },
++    },
++  },
++};
++
+ static const TestPlan plan_limit_to_message_bus =
+ {
+   .description = "A subscription to the message bus only accepts messages "
+@@ -855,8 +894,18 @@ fixture_subscribe (Fixture             *f,
+                                                subscribe->flags,
+                                                subscribed_signal_cb,
+                                                f, NULL);
++
+       g_assert_cmpuint (id, !=, 0);
+-      f->subscriptions[step_number] = id;
++
++      if (subscribe->unsubscribe_immediately)
++        {
++          g_test_message ("\tImmediately unsubscribing");
++          g_dbus_connection_signal_unsubscribe (subscriber, id);
++        }
++      else
++        {
++          f->subscriptions[step_number] = id;
++        }
+     }
+ 
+   if (f->mode != SUBSCRIPTION_MODE_CONN)
+@@ -1287,6 +1336,7 @@ main (int   argc,
+   ADD_SUBSCRIBE_TEST (nonexistent_unique_name);
+   ADD_SUBSCRIBE_TEST (limit_by_well_known_name);
+   ADD_SUBSCRIBE_TEST (limit_to_message_bus);
++  ADD_SUBSCRIBE_TEST (unsubscribe_immediately);
+ 
+   return g_test_run();
+ }
+-- 
+2.45.0
+
+
+From 6762e278c870dfaad4b26329da5e24b2e9bdceda Mon Sep 17 00:00:00 2001
+From: Simon McVittie <smcv@collabora.com>
+Date: Wed, 8 May 2024 14:46:08 +0000
+Subject: [PATCH 18/19] gdbusconnection: Allow name owners to have the syntax
+ of a well-known name
+
+In a D-Bus-Specification-compliant message bus, the owner of a well-known
+name is a unique name. However, ibus has its own small implementation
+of a message bus (src/ibusbus.c) in which org.freedesktop.IBus is
+special-cased to also have itself as its owner (like org.freedesktop.DBus
+on a standard message bus), and connects to that bus with the
+G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION flag. The ability to do
+this regressed when CVE-2024-34397 was fixed.
+
+Relax the checks to allow the owner of a well-known name to be any valid
+D-Bus name, even if it is not syntactically a unique name.
+
+Fixes: 683b14b9 "gdbus: Track name owners for signal subscriptions"
+Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/3353
+Bug-Debian: https://bugs.debian.org/1070730
+Bug-Debian: https://bugs.debian.org/1070736
+Bug-Debian: https://bugs.debian.org/1070743
+Bug-Debian: https://bugs.debian.org/1070745
+Signed-off-by: Simon McVittie <smcv@debian.org>
+---
+ gio/gdbusconnection.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+index 85f99c5c6..348b5b985 100644
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -2406,7 +2406,10 @@ name_watcher_deliver_name_owner_changed_unlocked (SignalData *name_watcher,
+       /* Our caller already checked this */
+       g_assert (g_strcmp0 (name_watcher->arg0, name) == 0);
+ 
+-      if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_unique_name (new_owner)))
++      /* FIXME: This should be validating that `new_owner` is a unique name,
++       * but IBus’ implementation of a message bus is not compliant with the spec.
++       * See https://gitlab.gnome.org/GNOME/glib/-/issues/3353 */
++      if (G_LIKELY (new_owner[0] == '\0' || g_dbus_is_name (new_owner)))
+         name_watcher_set_name_owner_unlocked (name_watcher, new_owner);
+       else
+         g_warning ("Received NameOwnerChanged signal with invalid owner \"%s\" for \"%s\"",
+@@ -2458,7 +2461,10 @@ name_watcher_deliver_get_name_owner_reply_unlocked (SignalData *name_watcher,
+ 
+       g_variant_get (body, "(&s)", &new_owner);
+ 
+-      if (G_LIKELY (g_dbus_is_unique_name (new_owner)))
++      /* FIXME: This should be validating that `new_owner` is a unique name,
++       * but IBus’ implementation of a message bus is not compliant with the spec.
++       * See https://gitlab.gnome.org/GNOME/glib/-/issues/3353 */
++      if (G_LIKELY (g_dbus_is_name (new_owner)))
+         name_watcher_set_name_owner_unlocked (name_watcher, new_owner);
+       else
+         g_warning ("Received GetNameOwner reply with invalid owner \"%s\" for \"%s\"",
+-- 
+2.45.0
+
+
+From 9ad45e2d6699157d8b233bc67881e55f9b71f613 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
+Date: Wed, 8 May 2024 22:53:51 +0200
+Subject: [PATCH 19/19] gdbusmessage: Clean the cached arg0 when setting the
+ message body
+
+We're now caching arg0 but such value is not cleared when a new body is
+set as it's in the connection filter test cases where we've a leak as
+highlighted by both valgrind and leak sanitizer
+---
+ gio/gdbusmessage.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
+index 40a77dd92..7489c0ac4 100644
+--- a/gio/gdbusmessage.c
++++ b/gio/gdbusmessage.c
+@@ -1127,10 +1127,12 @@ g_dbus_message_set_body (GDBusMessage  *message,
+ 
+   if (message->body != NULL)
+     g_variant_unref (message->body);
++
++  g_clear_pointer (&message->arg0_cache, g_variant_unref);
++
+   if (body == NULL)
+     {
+       message->body = NULL;
+-      message->arg0_cache = NULL;
+       g_dbus_message_set_signature (message, NULL);
+     }
+   else
+@@ -1144,8 +1146,6 @@ g_dbus_message_set_body (GDBusMessage  *message,
+       if (g_variant_is_of_type (message->body, G_VARIANT_TYPE_TUPLE) &&
+           g_variant_n_children (message->body) > 0)
+         message->arg0_cache = g_variant_get_child_value (message->body, 0);
+-      else
+-        message->arg0_cache = NULL;
+ 
+       type_string = g_variant_get_type_string (body);
+       type_string_len = strlen (type_string);
+-- 
+2.45.0
+
diff --git a/base/rx/rx-glib2/glib-change-version.patch b/base/rx/rx-glib2/glib-change-version.patch
new file mode 100644
index 0000000..a8b378b
--- /dev/null
+++ b/base/rx/rx-glib2/glib-change-version.patch
@@ -0,0 +1,2133 @@
+diff -Naur a/gio/gdbus-2.0/codegen/meson.build b/gio/gdbus-2.0/codegen/meson.build
+--- a/gio/gdbus-2.0/codegen/meson.build	2021-08-19 21:27:25.686339000 +0600
++++ b/gio/gdbus-2.0/codegen/meson.build	2024-06-20 09:59:13.236516507 +0600
+@@ -24,7 +24,7 @@
+   # Provide tools for others when we're a subproject and they use the Meson GNOME module
+ meson.override_find_program('gdbus-codegen', gdbus_codegen)
+ 
+-codegen_dir = join_paths(glib_datadir, 'glib-2.0', 'codegen')
++codegen_dir = join_paths(glib_datadir, 'glib-2.00', 'codegen')
+ 
+ gdbus_codegen_built_files = []
+ gdbus_codegen_built_files += configure_file(input : 'config.py.in',
+diff -Naur a/gio/gschema-2.00.its b/gio/gschema-2.00.its
+--- a/gio/gschema-2.00.its	1970-01-01 06:00:00.000000000 +0600
++++ b/gio/gschema-2.00.its	2021-08-19 21:27:25.713339800 +0600
+@@ -0,0 +1,25 @@
++<?xml version="1.0"?>
++<!--
++See the gettext documentation for the file format:
++http://www.gnu.org/software/gettext/manual/html_node/Preparing-ITS-Rules.html
++-->
++<its:rules xmlns:its="http://www.w3.org/2005/11/its"
++           xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
++           version="2.0">
++  <its:translateRule selector="/schemalist" translate="no"/>
++
++  <its:translateRule selector="//summary | //description" translate="yes"/>
++
++  <its:translateRule selector="//default[@l10n]" translate="yes"/>
++
++  <!-- The 'context' attribute should be extracted as msgctxt.  -->
++  <gt:contextRule selector="//default[@context]" contextPointer="@context"/>
++  <gt:escapeRule selector="//default/@context" escape="no"/>
++
++  <!-- Remove whitespaces around the default value.  -->
++  <gt:preserveSpaceRule selector="//default" space="trim"/>
++
++  <!-- Extracted strings are consumed by the library and are never
++       merged back; we don't want to escape special characters.  -->
++  <gt:escapeRule selector="/schemalist" escape="no"/>
++</its:rules>
+diff -Naur a/gio/gschema-2.00.loc b/gio/gschema-2.00.loc
+--- a/gio/gschema-2.00.loc	1970-01-01 06:00:00.000000000 +0600
++++ b/gio/gschema-2.00.loc	2021-08-19 21:27:25.713339800 +0600
+@@ -0,0 +1,10 @@
++<?xml version="1.0"?>
++<!--
++See the gettext documentation for the file format:
++http://www.gnu.org/software/gettext/manual/html_node/Preparing-ITS-Rules.html
++-->
++<locatingRules>
++  <locatingRule name="GSettings" pattern="*.gschema.xml">
++    <documentRule localName="schemalist" target="gschema.its"/>
++  </locatingRule>
++</locatingRules>
+diff -Naur a/gio/gschema.its b/gio/gschema.its
+--- a/gio/gschema.its	2021-08-19 21:27:25.713339000 +0600
++++ b/gio/gschema.its	1970-01-01 06:00:00.000000000 +0600
+@@ -1,25 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+-See the gettext documentation for the file format:
+-http://www.gnu.org/software/gettext/manual/html_node/Preparing-ITS-Rules.html
+--->
+-<its:rules xmlns:its="http://www.w3.org/2005/11/its"
+-           xmlns:gt="https://www.gnu.org/s/gettext/ns/its/extensions/1.0"
+-           version="2.0">
+-  <its:translateRule selector="/schemalist" translate="no"/>
+-
+-  <its:translateRule selector="//summary | //description" translate="yes"/>
+-
+-  <its:translateRule selector="//default[@l10n]" translate="yes"/>
+-
+-  <!-- The 'context' attribute should be extracted as msgctxt.  -->
+-  <gt:contextRule selector="//default[@context]" contextPointer="@context"/>
+-  <gt:escapeRule selector="//default/@context" escape="no"/>
+-
+-  <!-- Remove whitespaces around the default value.  -->
+-  <gt:preserveSpaceRule selector="//default" space="trim"/>
+-
+-  <!-- Extracted strings are consumed by the library and are never
+-       merged back; we don't want to escape special characters.  -->
+-  <gt:escapeRule selector="/schemalist" escape="no"/>
+-</its:rules>
+diff -Naur a/gio/gschema.loc b/gio/gschema.loc
+--- a/gio/gschema.loc	2021-08-19 21:27:25.713339000 +0600
++++ b/gio/gschema.loc	1970-01-01 06:00:00.000000000 +0600
+@@ -1,10 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+-See the gettext documentation for the file format:
+-http://www.gnu.org/software/gettext/manual/html_node/Preparing-ITS-Rules.html
+--->
+-<locatingRules>
+-  <locatingRule name="GSettings" pattern="*.gschema.xml">
+-    <documentRule localName="schemalist" target="gschema.its"/>
+-  </locatingRule>
+-</locatingRules>
+diff -Naur a/gio/meson.build b/gio/meson.build
+--- a/gio/meson.build	2021-08-19 21:27:25.727340000 +0600
++++ b/gio/meson.build	2024-06-20 10:39:38.014030594 +0600
+@@ -170,7 +170,7 @@
+ 
+ gnetworking_h = configure_file(input : 'gnetworking.h.in',
+                                output : 'gnetworking.h',
+-                               install_dir : join_paths(get_option('includedir'), 'glib-2.0/gio'),
++                               install_dir : join_paths(get_option('includedir'), 'glib-2.00/gio'),
+                                configuration : gnetworking_h_conf)
+ 
+ gdbus_headers = files(
+@@ -420,7 +420,7 @@
+   internal_deps += [xdgmime_lib]
+   internal_objects += [xdgmime_lib.extract_all_objects()]
+ 
+-  install_headers(gio_unix_include_headers, subdir : 'gio-unix-2.0/gio')
++  install_headers(gio_unix_include_headers, subdir : 'gio-unix-2.00/gio')
+ 
+   if glib_conf.has('HAVE_NETLINK')
+     unix_sources += files(
+@@ -725,7 +725,7 @@
+ gio_headers += application_headers
+ gio_headers += settings_headers
+ gio_headers += gdbus_headers
+-install_headers(gio_headers, subdir : 'glib-2.0/gio/')
++install_headers(gio_headers, subdir : 'glib-2.00/gio/')
+ 
+ # We can't use gnome.mkenums() because the GNOME module looks for glib-mkenums
+ # in PATH, which means you can't bootstrap glib with its own glib-mkenums.
+@@ -734,7 +734,7 @@
+   capture : true,
+   input : gio_headers,
+   install : true,
+-  install_dir : join_paths(get_option('includedir'), 'glib-2.0/gio'),
++  install_dir : join_paths(get_option('includedir'), 'glib-2.00/gio'),
+   command : [python, glib_mkenums,
+              '--template', files('gioenumtypes.h.template'),
+              '@INPUT@', gnetworking_h])
+@@ -800,7 +800,7 @@
+   gio_dtrace_hdr = []
+ endif
+ 
+-libgio = library('gio-2.0',
++libgio = library('gio-2.00',
+   gioenumtypes_h, gioenumtypes_c, gnetworking_h, gio_sources,
+   gio_dtrace_hdr, gio_dtrace_obj,
+   objects : internal_objects,
+@@ -825,7 +825,7 @@
+   pkgconfig_giomodulesdir = join_paths('${libdir}', 'gio', 'modules')
+ endif
+ 
+-schemas_subdir = join_paths('glib-2.0', 'schemas')
++schemas_subdir = join_paths('glib-2.00', 'schemas')
+ 
+ libgio_dep = declare_dependency(link_with : libgio,
+   dependencies : [libgmodule_dep, libgobject_dep, gioenumtypes_dep],
+@@ -833,7 +833,7 @@
+ 
+ pkg.generate(libgio,
+   libraries_private : [osx_ldflags],
+-  requires : ['glib-2.0', 'gobject-2.0'],
++  requires : ['glib-2.00', 'gobject-2.00'],
+   variables : ['datadir=' + join_paths('${prefix}', get_option('datadir')),
+                'schemasdir=' + join_paths('${datadir}', schemas_subdir),
+                'bindir=' + join_paths('${prefix}', get_option('bindir')),
+@@ -848,13 +848,13 @@
+                'gsettings=' + join_paths('${bindir}', 'gsettings')],
+   version : glib_version,
+   install_dir : glib_pkgconfigreldir,
+-  filebase : 'gio-2.0',
++  filebase : 'gio-2.00',
+   name : 'GIO',
+   description : 'glib I/O library',
+ )
+ 
+ if meson.version().version_compare('>=0.54.0')
+-  meson.override_dependency('gio-2.0', libgio_dep)
++  meson.override_dependency('gio-2.00', libgio_dep)
+ endif
+ 
+ 
+@@ -871,16 +871,16 @@
+     meson.override_dependency('gio-win32-2.0', libgio_dep)
+   endif
+ else
+-  pkg.generate(requires : ['gobject-2.0', 'gio-2.0'],
+-    subdirs : ['gio-unix-2.0'],
++  pkg.generate(requires : ['gobject-2.00', 'gio-2.00'],
++    subdirs : ['gio-unix-2.00'],
+     version : glib_version,
+     install_dir : glib_pkgconfigreldir,
+-    filebase : 'gio-unix-2.0',
++    filebase : 'gio-unix-2.00',
+     name : 'GIO unix specific APIs',
+     description : 'unix specific headers for glib I/O library',
+   )
+   if meson.version().version_compare('>=0.54.0')
+-    meson.override_dependency('gio-unix-2.0', libgio_dep)
++    meson.override_dependency('gio-unix-2.00', libgio_dep)
+   endif
+ endif
+ 
+@@ -997,7 +997,7 @@
+ install_data('gschema.dtd',
+   install_dir : join_paths(get_option('datadir'), schemas_subdir))
+ 
+-install_data(['gschema.loc', 'gschema.its'],
++install_data(['gschema-2.00.loc', 'gschema-2.00.its'],
+   install_dir : join_paths(get_option('datadir'), 'gettext/its'))
+ 
+ executable('gdbus', 'gdbus-tool.c',
+diff -Naur a/glib/meson.build b/glib/meson.build
+--- a/glib/meson.build	2021-08-19 21:27:25.779340000 +0600
++++ b/glib/meson.build	2024-06-20 09:41:17.032294858 +0600
+@@ -1,5 +1,5 @@
+ configure_file(input : 'glibconfig.h.in', output : 'glibconfig.h',
+-  install_dir : join_paths(get_option('libdir'), 'glib-2.0/include'),
++  install_dir : join_paths(get_option('libdir'), 'glib-2.00/include'),
+   configuration : glibconfig_conf)
+ 
+ subdir('libcharset')
+@@ -128,7 +128,7 @@
+   'glib-unix.h',
+   'glib-object.h',
+ )
+-install_headers(glib_headers, subdir : 'glib-2.0')
++install_headers(glib_headers, subdir : 'glib-2.00')
+ 
+ glib_deprecated_headers = files(
+   'deprecated/gallocator.h',
+@@ -138,7 +138,7 @@
+   'deprecated/grel.h',
+   'deprecated/gthread.h',
+ )
+-install_headers(glib_deprecated_headers, subdir : 'glib-2.0/glib/deprecated')
++install_headers(glib_deprecated_headers, subdir : 'glib-2.00/glib/deprecated')
+ 
+ glib_sub_headers = files(
+   'glib-autocleanups.h',
+@@ -220,7 +220,7 @@
+   'gwin32.h',
+   'gprintf.h',
+ )
+-install_headers(glib_sub_headers, subdir : 'glib-2.0/glib')
++install_headers(glib_sub_headers, subdir : 'glib-2.00/glib')
+ 
+ deprecated_sources = files(
+   'deprecated/gallocator.c',
+@@ -366,7 +366,7 @@
+ endif
+ 
+ glib_c_args = ['-DG_LOG_DOMAIN="GLib"', '-DGLIB_COMPILATION'] + pcre_static_args + glib_hidden_visibility_args
+-libglib = library('glib-2.0',
++libglib = library('glib-2.00',
+   glib_dtrace_obj, glib_dtrace_hdr,
+   sources : [deprecated_sources, glib_sources],
+   objects : [charset_lib.extract_all_objects()] + gnulib_objects + pcre_objects,
+@@ -391,8 +391,8 @@
+ pkg.generate(libglib,
+   libraries : [libintl_deps],
+   libraries_private : [osx_ldflags, win32_ldflags],
+-  subdirs : ['glib-2.0'],
+-  extra_cflags : ['-I${libdir}/glib-2.0/include'] + win32_cflags,
++  subdirs : ['glib-2.00'],
++  extra_cflags : ['-I${libdir}/glib-2.00/include'] + win32_cflags,
+   variables : ['bindir=' + join_paths('${prefix}', get_option('bindir')),
+                'glib_genmarshal=' + join_paths('${bindir}', 'glib-genmarshal'),
+                'gobject_query=' + join_paths('${bindir}', 'gobject-query'),
+@@ -405,7 +405,7 @@
+ )
+ 
+ if meson.version().version_compare('>=0.54.0')
+-  meson.override_dependency('glib-2.0', libglib_dep)
++  meson.override_dependency('glib-2.00', libglib_dep)
+ endif
+ 
+ # On Windows, glib needs a spawn helper for g_spawn* API
+@@ -468,7 +468,7 @@
+ endif
+ configure_file(
+   input: 'libglib-gdb.py.in',
+-  output: 'libglib-2.0.so.@0@-gdb.py'.format(library_version),
++  output: 'libglib-2.00.so.@0@-gdb.py'.format(library_version),
+   configuration: gdb_conf,
+   install_dir: gdb_install_dir,
+ )
+diff -Naur a/glib-gettextize.in b/glib-gettextize.in
+--- a/glib-gettextize.in	2021-08-19 21:27:25.746340000 +0600
++++ b/glib-gettextize.in	2024-06-20 10:31:50.068973445 +0600
+@@ -51,7 +51,7 @@
+ datarootdir=@datarootdir@
+ datadir=@datadir@
+ 
+-gettext_dir=$datadir/glib-2.0/gettext
++gettext_dir=$datadir/glib-2.00/gettext
+ 
+ while test $# -gt 0; do
+   case "$1" in
+diff -Naur a/gmodule/meson.build b/gmodule/meson.build
+--- a/gmodule/meson.build	2021-08-19 21:27:25.798340000 +0600
++++ b/gmodule/meson.build	2024-06-20 09:41:58.491030409 +0600
+@@ -59,7 +59,7 @@
+                                output : 'gmoduleconf.h',
+                                configuration : gmoduleconf_conf)
+ 
+-install_headers(['gmodule.h'], subdir : 'glib-2.0')
++install_headers(['gmodule.h'], subdir : 'glib-2.00')
+ 
+ gmodule_sources = ['gmodule.c']
+ if host_system == 'windows'
+@@ -72,7 +72,7 @@
+   gmodule_sources += [gmodule_win_res]
+ endif
+ 
+-libgmodule = library('gmodule-2.0',
++libgmodule = library('gmodule-2.00',
+   sources : gmodule_sources,
+   version : library_version,
+   soversion : soversion,
+@@ -88,31 +88,31 @@
+ 
+ pkg.generate(libgmodule,
+   libraries : [thread_dep],
+-  requires : ['glib-2.0'],
++  requires : ['glib-2.00'],
+   version : glib_version,
+   variables : [supported_var],
+   install_dir : glib_pkgconfigreldir,
+-  filebase : 'gmodule-no-export-2.0',
++  filebase : 'gmodule-no-export-2.00',
+   name : 'GModule',
+   description : 'Dynamic module loader for GLib',
+ )
+ 
+ pkg.generate(libraries : [libgmodule, export_dynamic_ldflags],
+-  requires : ['glib-2.0'],
++  requires : ['glib-2.00'],
+   version : glib_version,
+   variables : [supported_var],
+   install_dir : glib_pkgconfigreldir,
+-  filebase : 'gmodule-export-2.0',
++  filebase : 'gmodule-export-2.00',
+   name : 'GModule',
+   description : 'Dynamic module loader for GLib',
+ )
+ 
+ pkg.generate(libraries : [libgmodule, export_dynamic_ldflags],
+-  requires : ['glib-2.0'],
++  requires : ['glib-2.00'],
+   version : glib_version,
+   variables : [supported_var],
+   install_dir : glib_pkgconfigreldir,
+-  filebase : 'gmodule-2.0',
++  filebase : 'gmodule-2.00',
+   name : 'GModule',
+   description : 'Dynamic module loader for GLib',
+ )
+@@ -122,7 +122,7 @@
+   dependencies : [libglib_dep])
+ 
+ if meson.version().version_compare('>=0.54.0')
+-  meson.override_dependency('gmodule-no-export-2.0', libgmodule_dep)
+-  meson.override_dependency('gmodule-export-2.0', libgmodule_dep)
+-  meson.override_dependency('gmodule-2.0', libgmodule_dep)
++  meson.override_dependency('gmodule-no-export-2.00', libgmodule_dep)
++  meson.override_dependency('gmodule-export-2.00', libgmodule_dep)
++  meson.override_dependency('gmodule-2.00', libgmodule_dep)
+ endif
+diff -Naur a/gobject/meson.build b/gobject/meson.build
+--- a/gobject/meson.build	2021-08-19 21:27:25.803341000 +0600
++++ b/gobject/meson.build	2024-06-20 09:42:28.155841189 +0600
+@@ -20,7 +20,7 @@
+   'gvaluetypes.h',
+   'gobjectnotifyqueue.c', # sic
+ )
+-install_headers(gobject_install_headers, subdir : 'glib-2.0/gobject')
++install_headers(gobject_install_headers, subdir : 'glib-2.00/gobject')
+ 
+ gobject_sources = files(
+   'gatomicarray.c',
+@@ -101,7 +101,7 @@
+   capture : true,
+   input : glib_enumtypes_input_headers,
+   install : true,
+-  install_dir : join_paths(get_option('includedir'), 'glib-2.0/gobject'),
++  install_dir : join_paths(get_option('includedir'), 'glib-2.00/gobject'),
+   command : [python, glib_mkenums,
+              '--template', files('glib-enumtypes.h.template'),
+              '@INPUT@'])
+@@ -117,7 +117,7 @@
+ 
+ glib_enumtypes_dep = declare_dependency(sources : [glib_enumtypes_h])
+ 
+-libgobject = library('gobject-2.0',
++libgobject = library('gobject-2.00',
+   gobject_dtrace_obj, gobject_dtrace_hdr, glib_enumtypes_h, glib_enumtypes_c,
+   sources : gobject_sources,
+   version : library_version,
+@@ -131,10 +131,10 @@
+ )
+ 
+ pkg.generate(libgobject,
+-  requires : ['glib-2.0'],
++  requires : ['glib-2.00'],
+   version : glib_version,
+   install_dir : glib_pkgconfigreldir,
+-  filebase : 'gobject-2.0',
++  filebase : 'gobject-2.00',
+   name : 'GObject',
+   description : 'GLib Type, Object, Parameter and Signal Library',
+ )
+@@ -144,7 +144,7 @@
+   dependencies : [libglib_dep, glib_enumtypes_dep])
+ 
+ if meson.version().version_compare('>=0.54.0')
+-  meson.override_dependency('gobject-2.0', libgobject_dep)
++  meson.override_dependency('gobject-2.00', libgobject_dep)
+ endif
+ 
+ executable('gobject-query', 'gobject-query.c',
+@@ -156,7 +156,7 @@
+ gdb_conf.set('datadir', glib_datadir)
+ configure_file(
+   input: 'libgobject-gdb.py.in',
+-  output: 'libgobject-2.0.so.@0@-gdb.py'.format(library_version),
++  output: 'libgobject-2.00.so.@0@-gdb.py'.format(library_version),
+   configuration: gdb_conf,
+   install_dir: gdb_install_dir,
+ )
+diff -Naur a/gthread/meson.build b/gthread/meson.build
+--- a/gthread/meson.build	2021-08-19 21:27:25.805341000 +0600
++++ b/gthread/meson.build	2024-06-20 09:42:40.519762324 +0600
+@@ -12,7 +12,7 @@
+   gthread_sources += [gthread_win_res]
+ endif
+ 
+-libgthread = library('gthread-2.0',
++libgthread = library('gthread-2.00',
+   sources : gthread_sources,
+   version : library_version,
+   soversion : soversion,
+@@ -25,10 +25,10 @@
+ 
+ pkg.generate(libgthread,
+   libraries : [thread_dep],
+-  requires : ['glib-2.0'],
++  requires : ['glib-2.00'],
+   version : glib_version,
+   install_dir : glib_pkgconfigreldir,
+-  filebase : 'gthread-2.0',
++  filebase : 'gthread-2.00',
+   name : 'GThread',
+   description : 'Thread support for GLib',
+ )
+@@ -36,5 +36,5 @@
+ libgthread_dep = declare_dependency(link_with : libgthread)
+ 
+ if meson.version().version_compare('>=0.54.0')
+-  meson.override_dependency('gthread-2.0', libgthread_dep)
++  meson.override_dependency('gthread-2.00', libgthread_dep)
+ endif
+diff -Naur a/m4macros/glib-2.00.m4 b/m4macros/glib-2.00.m4
+--- a/m4macros/glib-2.00.m4	1970-01-01 06:00:00.000000000 +0600
++++ b/m4macros/glib-2.00.m4	2024-06-20 09:43:31.857434862 +0600
+@@ -0,0 +1,215 @@
++# Configure paths for GLIB
++# Owen Taylor     1997-2001
++
++# Increment this whenever this file is changed.
++#serial 4
++
++dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
++dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject,
++dnl gthread, or gio is specified in MODULES, pass to pkg-config
++dnl
++AC_DEFUN([AM_PATH_GLIB_2_0],
++[dnl 
++dnl Get the cflags and libraries from pkg-config
++dnl
++
++dnl We can't use PKG_PREREQ because that needs 0.29.
++m4_ifndef([PKG_PROG_PKG_CONFIG],
++          [pkg.m4 version 0.28 or later is required])
++
++AC_ARG_ENABLE(glibtest, [  --disable-glibtest      do not try to compile and run a test GLIB program],
++		    , enable_glibtest=yes)
++
++  min_glib_version=ifelse([$1], [], [2.0.0], [$1])
++  pkg_config_args="glib-2.00 >= $min_glib_version"
++  for module in . $4
++  do
++      case "$module" in
++         gmodule) 
++             pkg_config_args="$pkg_config_args gmodule-2.00"
++         ;;
++         gmodule-no-export) 
++             pkg_config_args="$pkg_config_args gmodule-no-export-2.00"
++         ;;
++         gobject) 
++             pkg_config_args="$pkg_config_args gobject-2.00"
++         ;;
++         gthread) 
++             pkg_config_args="$pkg_config_args gthread-2.00"
++         ;;
++         gio*) 
++             pkg_config_args="$pkg_config_args $module-2.00"
++         ;;
++      esac
++  done
++
++  PKG_PROG_PKG_CONFIG([0.16])
++
++  no_glib=""
++
++  if test "x$PKG_CONFIG" = x ; then
++    no_glib=yes
++    PKG_CONFIG=no
++  fi
++
++  dnl For GLIB_CFLAGS and GLIB_LIBS
++  PKG_CHECK_MODULES([GLIB], [$pkg_config_args], [:], [:])
++
++  dnl For the tools
++  PKG_CHECK_VAR([GLIB_GENMARSHAL], [glib-2.00], [glib_genmarshal])
++  PKG_CHECK_VAR([GOBJECT_QUERY], [glib-2.00], [gobject_query])
++  PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.00], [glib_mkenums])
++  PKG_CHECK_VAR([GLIB_COMPILE_RESOURCES], [gio-2.00], [glib_compile_resources])
++
++  AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
++
++  if test x$PKG_CONFIG != xno ; then
++    ## don't try to run the test against uninstalled libtool libs
++    if $PKG_CONFIG --uninstalled $pkg_config_args; then
++	  echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH"
++	  enable_glibtest=no
++    fi
++
++    if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then
++	  :
++    else
++	  no_glib=yes
++    fi
++  fi
++
++  if test x"$no_glib" = x ; then
++    glib_config_major_version=`$PKG_CONFIG --modversion glib-2.00 | \
++           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
++    glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.00 | \
++           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
++    glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.00 | \
++           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
++    if test "x$enable_glibtest" = "xyes" ; then
++      ac_save_CFLAGS="$CFLAGS"
++      ac_save_LIBS="$LIBS"
++      CFLAGS="$CFLAGS $GLIB_CFLAGS"
++      LIBS="$GLIB_LIBS $LIBS"
++dnl
++dnl Now check if the installed GLib is sufficiently new. (Also sanity
++dnl checks the results of pkg-config to some extent)
++dnl
++      rm -f conf.glibtest
++      AC_RUN_IFELSE([AC_LANG_SOURCE([[
++#include <glib.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++int 
++main (void)
++{
++  unsigned int major, minor, micro;
++
++  fclose (fopen ("conf.glibtest", "w"));
++
++  if (sscanf("$min_glib_version", "%u.%u.%u", &major, &minor, &micro) != 3) {
++     printf("%s, bad version string\n", "$min_glib_version");
++     exit(1);
++   }
++
++  if ((glib_major_version != $glib_config_major_version) ||
++      (glib_minor_version != $glib_config_minor_version) ||
++      (glib_micro_version != $glib_config_micro_version))
++    {
++      printf("\n*** 'pkg-config --modversion glib-2.00' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", 
++             $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
++             glib_major_version, glib_minor_version, glib_micro_version);
++      printf ("*** was found! If pkg-config was correct, then it is best\n");
++      printf ("*** to remove the old version of GLib. You may also be able to fix the error\n");
++      printf("*** by modifying your LD_LIBRARY_PATH environment variable, or by editing\n");
++      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
++      printf("*** required on your system.\n");
++      printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
++      printf("*** to point to the correct configuration files\n");
++    } 
++  else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
++	   (glib_minor_version != GLIB_MINOR_VERSION) ||
++           (glib_micro_version != GLIB_MICRO_VERSION))
++    {
++      printf("*** GLib header files (version %d.%d.%d) do not match\n",
++	     GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
++      printf("*** library (version %d.%d.%d)\n",
++	     glib_major_version, glib_minor_version, glib_micro_version);
++    }
++  else
++    {
++      if ((glib_major_version > major) ||
++        ((glib_major_version == major) && (glib_minor_version > minor)) ||
++        ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
++      {
++        return 0;
++       }
++     else
++      {
++        printf("\n*** An old version of GLib (%u.%u.%u) was found.\n",
++               glib_major_version, glib_minor_version, glib_micro_version);
++        printf("*** You need a version of GLib newer than %u.%u.%u. The latest version of\n",
++	       major, minor, micro);
++        printf("*** GLib is always available from ftp://ftp.gtk.org.\n");
++        printf("***\n");
++        printf("*** If you have already installed a sufficiently new version, this error\n");
++        printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
++        printf("*** being found. The easiest way to fix this is to remove the old version\n");
++        printf("*** of GLib, but you can also set the PKG_CONFIG environment to point to the\n");
++        printf("*** correct copy of pkg-config. (In this case, you will have to\n");
++        printf("*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf\n");
++        printf("*** so that the correct libraries are found at run-time))\n");
++      }
++    }
++  return 1;
++}
++]])],[],[no_glib=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"])
++       CFLAGS="$ac_save_CFLAGS"
++       LIBS="$ac_save_LIBS"
++     fi
++  fi
++  if test "x$no_glib" = x ; then
++     AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version))
++     ifelse([$2], , :, [$2])     
++  else
++     AC_MSG_RESULT(no)
++     if test "$PKG_CONFIG" = "no" ; then
++       echo "*** A new enough version of pkg-config was not found."
++       echo "*** See http://www.freedesktop.org/software/pkgconfig/"
++     else
++       if test -f conf.glibtest ; then
++        :
++       else
++          echo "*** Could not run GLib test program, checking why..."
++          ac_save_CFLAGS="$CFLAGS"
++          ac_save_LIBS="$LIBS"
++          CFLAGS="$CFLAGS $GLIB_CFLAGS"
++          LIBS="$LIBS $GLIB_LIBS"
++          AC_LINK_IFELSE([AC_LANG_PROGRAM([[
++#include <glib.h>
++#include <stdio.h>
++]],      [[ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ]])],
++        [ echo "*** The test program compiled, but did not run. This usually means"
++          echo "*** that the run-time linker is not finding GLib or finding the wrong"
++          echo "*** version of GLib. If it is not finding GLib, you'll need to set your"
++          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
++          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
++          echo "*** is required on your system"
++	  echo "***"
++          echo "*** If you have an old version installed, it is best to remove it, although"
++          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
++        [ echo "*** The test program failed to compile or link. See the file config.log for the"
++          echo "*** exact error that occurred. This usually means GLib is incorrectly installed."])
++          CFLAGS="$ac_save_CFLAGS"
++          LIBS="$ac_save_LIBS"
++       fi
++     fi
++     GLIB_CFLAGS=""
++     GLIB_LIBS=""
++     GLIB_GENMARSHAL=""
++     GOBJECT_QUERY=""
++     GLIB_MKENUMS=""
++     GLIB_COMPILE_RESOURCES=""
++     ifelse([$3], , :, [$3])
++  fi
++  rm -f conf.glibtest
++])
+diff -Naur a/m4macros/glib-2.0.m4 b/m4macros/glib-2.0.m4
+--- a/m4macros/glib-2.0.m4	2021-08-19 21:27:25.805341000 +0600
++++ b/m4macros/glib-2.0.m4	1970-01-01 06:00:00.000000000 +0600
+@@ -1,215 +0,0 @@
+-# Configure paths for GLIB
+-# Owen Taylor     1997-2001
+-
+-# Increment this whenever this file is changed.
+-#serial 4
+-
+-dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]])
+-dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject,
+-dnl gthread, or gio is specified in MODULES, pass to pkg-config
+-dnl
+-AC_DEFUN([AM_PATH_GLIB_2_0],
+-[dnl 
+-dnl Get the cflags and libraries from pkg-config
+-dnl
+-
+-dnl We can't use PKG_PREREQ because that needs 0.29.
+-m4_ifndef([PKG_PROG_PKG_CONFIG],
+-          [pkg.m4 version 0.28 or later is required])
+-
+-AC_ARG_ENABLE(glibtest, [  --disable-glibtest      do not try to compile and run a test GLIB program],
+-		    , enable_glibtest=yes)
+-
+-  min_glib_version=ifelse([$1], [], [2.0.0], [$1])
+-  pkg_config_args="glib-2.0 >= $min_glib_version"
+-  for module in . $4
+-  do
+-      case "$module" in
+-         gmodule) 
+-             pkg_config_args="$pkg_config_args gmodule-2.0"
+-         ;;
+-         gmodule-no-export) 
+-             pkg_config_args="$pkg_config_args gmodule-no-export-2.0"
+-         ;;
+-         gobject) 
+-             pkg_config_args="$pkg_config_args gobject-2.0"
+-         ;;
+-         gthread) 
+-             pkg_config_args="$pkg_config_args gthread-2.0"
+-         ;;
+-         gio*) 
+-             pkg_config_args="$pkg_config_args $module-2.0"
+-         ;;
+-      esac
+-  done
+-
+-  PKG_PROG_PKG_CONFIG([0.16])
+-
+-  no_glib=""
+-
+-  if test "x$PKG_CONFIG" = x ; then
+-    no_glib=yes
+-    PKG_CONFIG=no
+-  fi
+-
+-  dnl For GLIB_CFLAGS and GLIB_LIBS
+-  PKG_CHECK_MODULES([GLIB], [$pkg_config_args], [:], [:])
+-
+-  dnl For the tools
+-  PKG_CHECK_VAR([GLIB_GENMARSHAL], [glib-2.0], [glib_genmarshal])
+-  PKG_CHECK_VAR([GOBJECT_QUERY], [glib-2.0], [gobject_query])
+-  PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.0], [glib_mkenums])
+-  PKG_CHECK_VAR([GLIB_COMPILE_RESOURCES], [gio-2.0], [glib_compile_resources])
+-
+-  AC_MSG_CHECKING(for GLIB - version >= $min_glib_version)
+-
+-  if test x$PKG_CONFIG != xno ; then
+-    ## don't try to run the test against uninstalled libtool libs
+-    if $PKG_CONFIG --uninstalled $pkg_config_args; then
+-	  echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH"
+-	  enable_glibtest=no
+-    fi
+-
+-    if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then
+-	  :
+-    else
+-	  no_glib=yes
+-    fi
+-  fi
+-
+-  if test x"$no_glib" = x ; then
+-    glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \
+-           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
+-    glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \
+-           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
+-    glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \
+-           sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
+-    if test "x$enable_glibtest" = "xyes" ; then
+-      ac_save_CFLAGS="$CFLAGS"
+-      ac_save_LIBS="$LIBS"
+-      CFLAGS="$CFLAGS $GLIB_CFLAGS"
+-      LIBS="$GLIB_LIBS $LIBS"
+-dnl
+-dnl Now check if the installed GLib is sufficiently new. (Also sanity
+-dnl checks the results of pkg-config to some extent)
+-dnl
+-      rm -f conf.glibtest
+-      AC_RUN_IFELSE([AC_LANG_SOURCE([[
+-#include <glib.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-
+-int 
+-main (void)
+-{
+-  unsigned int major, minor, micro;
+-
+-  fclose (fopen ("conf.glibtest", "w"));
+-
+-  if (sscanf("$min_glib_version", "%u.%u.%u", &major, &minor, &micro) != 3) {
+-     printf("%s, bad version string\n", "$min_glib_version");
+-     exit(1);
+-   }
+-
+-  if ((glib_major_version != $glib_config_major_version) ||
+-      (glib_minor_version != $glib_config_minor_version) ||
+-      (glib_micro_version != $glib_config_micro_version))
+-    {
+-      printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", 
+-             $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version,
+-             glib_major_version, glib_minor_version, glib_micro_version);
+-      printf ("*** was found! If pkg-config was correct, then it is best\n");
+-      printf ("*** to remove the old version of GLib. You may also be able to fix the error\n");
+-      printf("*** by modifying your LD_LIBRARY_PATH environment variable, or by editing\n");
+-      printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n");
+-      printf("*** required on your system.\n");
+-      printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n");
+-      printf("*** to point to the correct configuration files\n");
+-    } 
+-  else if ((glib_major_version != GLIB_MAJOR_VERSION) ||
+-	   (glib_minor_version != GLIB_MINOR_VERSION) ||
+-           (glib_micro_version != GLIB_MICRO_VERSION))
+-    {
+-      printf("*** GLib header files (version %d.%d.%d) do not match\n",
+-	     GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
+-      printf("*** library (version %d.%d.%d)\n",
+-	     glib_major_version, glib_minor_version, glib_micro_version);
+-    }
+-  else
+-    {
+-      if ((glib_major_version > major) ||
+-        ((glib_major_version == major) && (glib_minor_version > minor)) ||
+-        ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro)))
+-      {
+-        return 0;
+-       }
+-     else
+-      {
+-        printf("\n*** An old version of GLib (%u.%u.%u) was found.\n",
+-               glib_major_version, glib_minor_version, glib_micro_version);
+-        printf("*** You need a version of GLib newer than %u.%u.%u. The latest version of\n",
+-	       major, minor, micro);
+-        printf("*** GLib is always available from ftp://ftp.gtk.org.\n");
+-        printf("***\n");
+-        printf("*** If you have already installed a sufficiently new version, this error\n");
+-        printf("*** probably means that the wrong copy of the pkg-config shell script is\n");
+-        printf("*** being found. The easiest way to fix this is to remove the old version\n");
+-        printf("*** of GLib, but you can also set the PKG_CONFIG environment to point to the\n");
+-        printf("*** correct copy of pkg-config. (In this case, you will have to\n");
+-        printf("*** modify your LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf\n");
+-        printf("*** so that the correct libraries are found at run-time))\n");
+-      }
+-    }
+-  return 1;
+-}
+-]])],[],[no_glib=yes],[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+-       CFLAGS="$ac_save_CFLAGS"
+-       LIBS="$ac_save_LIBS"
+-     fi
+-  fi
+-  if test "x$no_glib" = x ; then
+-     AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version))
+-     ifelse([$2], , :, [$2])     
+-  else
+-     AC_MSG_RESULT(no)
+-     if test "$PKG_CONFIG" = "no" ; then
+-       echo "*** A new enough version of pkg-config was not found."
+-       echo "*** See http://www.freedesktop.org/software/pkgconfig/"
+-     else
+-       if test -f conf.glibtest ; then
+-        :
+-       else
+-          echo "*** Could not run GLib test program, checking why..."
+-          ac_save_CFLAGS="$CFLAGS"
+-          ac_save_LIBS="$LIBS"
+-          CFLAGS="$CFLAGS $GLIB_CFLAGS"
+-          LIBS="$LIBS $GLIB_LIBS"
+-          AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+-#include <glib.h>
+-#include <stdio.h>
+-]],      [[ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ]])],
+-        [ echo "*** The test program compiled, but did not run. This usually means"
+-          echo "*** that the run-time linker is not finding GLib or finding the wrong"
+-          echo "*** version of GLib. If it is not finding GLib, you'll need to set your"
+-          echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+-          echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+-          echo "*** is required on your system"
+-	  echo "***"
+-          echo "*** If you have an old version installed, it is best to remove it, although"
+-          echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
+-        [ echo "*** The test program failed to compile or link. See the file config.log for the"
+-          echo "*** exact error that occurred. This usually means GLib is incorrectly installed."])
+-          CFLAGS="$ac_save_CFLAGS"
+-          LIBS="$ac_save_LIBS"
+-       fi
+-     fi
+-     GLIB_CFLAGS=""
+-     GLIB_LIBS=""
+-     GLIB_GENMARSHAL=""
+-     GOBJECT_QUERY=""
+-     GLIB_MKENUMS=""
+-     GLIB_COMPILE_RESOURCES=""
+-     ifelse([$3], , :, [$3])
+-  fi
+-  rm -f conf.glibtest
+-])
+diff -Naur a/m4macros/glib-gettext-2.00.m4 b/m4macros/glib-gettext-2.00.m4
+--- a/m4macros/glib-gettext-2.00.m4	1970-01-01 06:00:00.000000000 +0600
++++ b/m4macros/glib-gettext-2.00.m4	2021-08-19 21:27:25.805341000 +0600
+@@ -0,0 +1,486 @@
++# Copyright (C) 1995-2002 Free Software Foundation, Inc.
++# Copyright (C) 2001-2003,2004 Red Hat, Inc.
++#
++# This file is free software, distributed under the terms of the GNU
++# General Public License.  As a special exception to the GNU General
++# Public License, this file may be distributed as part of a program
++# that contains a configuration script generated by Autoconf, under
++# the same distribution terms as the rest of that program.
++#
++# This file can be copied and used freely without restrictions.  It can
++# be used in projects which are not available under the GNU Public License
++# but which still want to provide support for the GNU gettext functionality.
++#
++# Macro to add for using GNU gettext.
++# Ulrich Drepper <drepper@cygnus.com>, 1995, 1996
++#
++# Modified to never use included libintl. 
++# Owen Taylor <otaylor@redhat.com>, 12/15/1998
++#
++# Major rework to remove unused code
++# Owen Taylor <otaylor@redhat.com>, 12/11/2002
++#
++# Added better handling of ALL_LINGUAS from GNU gettext version 
++# written by Bruno Haible, Owen Taylor <otaylor.redhat.com> 5/30/3002
++#
++# Modified to require ngettext
++# Matthias Clasen <mclasen@redhat.com> 08/06/2004
++
++# Increment this whenever this file is changed.
++#serial 1
++
++# We need this here as well, since someone might use autoconf-2.5x
++# to configure GLib then an older version to configure a package
++# using AM_GLIB_GNU_GETTEXT
++AC_PREREQ(2.53)
++
++dnl
++dnl We go to great lengths to make sure that aclocal won't 
++dnl try to pull in the installed version of these macros
++dnl when running aclocal in the glib directory.
++dnl
++m4_copy([AC_DEFUN],[glib_DEFUN])
++m4_copy([AC_REQUIRE],[glib_REQUIRE])
++dnl
++dnl At the end, if we're not within glib, we'll define the public
++dnl definitions in terms of our private definitions.
++dnl
++
++# GLIB_LC_MESSAGES
++#--------------------
++glib_DEFUN([GLIB_LC_MESSAGES],
++  [AC_CHECK_HEADERS([locale.h])
++    if test $ac_cv_header_locale_h = yes; then
++    AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES,
++      [AC_TRY_LINK([#include <locale.h>], [return LC_MESSAGES],
++       am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)])
++    if test $am_cv_val_LC_MESSAGES = yes; then
++      AC_DEFINE(HAVE_LC_MESSAGES, 1,
++        [Define if your <locale.h> file defines LC_MESSAGES.])
++    fi
++  fi])
++
++# GLIB_PATH_PROG_WITH_TEST
++#----------------------------
++dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR,
++dnl   TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]])
++glib_DEFUN([GLIB_PATH_PROG_WITH_TEST],
++[# Extract the first word of "$2", so it can be a program name with args.
++set dummy $2; ac_word=[$]2
++AC_MSG_CHECKING([for $ac_word])
++AC_CACHE_VAL(ac_cv_path_$1,
++[case "[$]$1" in
++  /*)
++  ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
++  ;;
++  *)
++  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:"
++  for ac_dir in ifelse([$5], , $PATH, [$5]); do
++    test -z "$ac_dir" && ac_dir=.
++    if test -f $ac_dir/$ac_word; then
++      if [$3]; then
++	ac_cv_path_$1="$ac_dir/$ac_word"
++	break
++      fi
++    fi
++  done
++  IFS="$ac_save_ifs"
++dnl If no 4th arg is given, leave the cache variable unset,
++dnl so AC_PATH_PROGS will keep looking.
++ifelse([$4], , , [  test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
++])dnl
++  ;;
++esac])dnl
++$1="$ac_cv_path_$1"
++if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
++  AC_MSG_RESULT([$]$1)
++else
++  AC_MSG_RESULT(no)
++fi
++AC_SUBST($1)dnl
++])
++
++dnl Checks for special options needed on Mac OS X.
++dnl Defines INTL_MACOSX_LIBS.
++dnl
++dnl Copied from intlmacosx.m4 in gettext, GPL.
++dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
++glib_DEFUN([glib_gt_INTL_MACOSX],
++[
++  dnl Check for API introduced in Mac OS X 10.2.
++  AC_CACHE_CHECK([for CFPreferencesCopyAppValue],
++    [gt_cv_func_CFPreferencesCopyAppValue],
++    [gt_save_LIBS="$LIBS"
++     LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
++     AC_LINK_IFELSE(
++       [AC_LANG_PROGRAM(
++          [[#include <CoreFoundation/CFPreferences.h>]],
++          [[CFPreferencesCopyAppValue(NULL, NULL)]])],
++       [gt_cv_func_CFPreferencesCopyAppValue=yes],
++       [gt_cv_func_CFPreferencesCopyAppValue=no])
++     LIBS="$gt_save_LIBS"])
++  if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
++    AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1],
++      [Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework.])
++  fi
++  dnl Check for API introduced in Mac OS X 10.3.
++  AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent],
++    [gt_save_LIBS="$LIBS"
++     LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
++     AC_LINK_IFELSE(
++       [AC_LANG_PROGRAM(
++          [[#include <CoreFoundation/CFLocale.h>]],
++          [[CFLocaleCopyCurrent();]])],
++       [gt_cv_func_CFLocaleCopyCurrent=yes],
++       [gt_cv_func_CFLocaleCopyCurrent=no])
++     LIBS="$gt_save_LIBS"])
++  if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
++    AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1],
++      [Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework.])
++  fi
++  INTL_MACOSX_LIBS=
++  if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
++    INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
++  fi
++  AC_SUBST([INTL_MACOSX_LIBS])
++])
++
++# GLIB_WITH_NLS
++#-----------------
++glib_DEFUN([GLIB_WITH_NLS],
++  dnl NLS is obligatory
++  [USE_NLS=yes
++    AC_SUBST(USE_NLS)
++
++    gt_cv_have_gettext=no
++
++    CATOBJEXT=NONE
++    XGETTEXT=:
++    INTLLIBS=
++
++    glib_gt_INTL_MACOSX
++
++    AC_CHECK_HEADER(libintl.h,
++     [gt_cv_func_dgettext_libintl="no"
++      libintl_extra_libs=""
++
++      #
++      # First check in libc
++      #
++      AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc,
++        [AC_TRY_LINK([
++#include <libintl.h>
++],
++         [return !ngettext ("","", 1)],
++	  gt_cv_func_ngettext_libc=yes,
++          gt_cv_func_ngettext_libc=no)
++        ])
++  
++      if test "$gt_cv_func_ngettext_libc" = "yes" ; then
++	      AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc,
++        	[AC_TRY_LINK([
++#include <libintl.h>
++],
++	          [return !dgettext ("","")],
++		  gt_cv_func_dgettext_libc=yes,
++	          gt_cv_func_dgettext_libc=no)
++        	])
++      fi
++  
++      if test "$gt_cv_func_ngettext_libc" = "yes" ; then
++        AC_CHECK_FUNCS(bind_textdomain_codeset)
++      fi
++
++      #
++      # If we don't have everything we want, check in libintl
++      #
++      if test "$gt_cv_func_dgettext_libc" != "yes" \
++	 || test "$gt_cv_func_ngettext_libc" != "yes" \
++         || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then
++        
++        AC_CHECK_LIB(intl, bindtextdomain,
++	    [AC_CHECK_LIB(intl, ngettext,
++		    [AC_CHECK_LIB(intl, dgettext,
++			          gt_cv_func_dgettext_libintl=yes)])])
++
++	if test "$gt_cv_func_dgettext_libintl" != "yes" ; then
++	  AC_MSG_CHECKING([if -liconv is needed to use gettext])
++	  AC_MSG_RESULT([])
++  	  AC_CHECK_LIB(intl, ngettext,
++          	[AC_CHECK_LIB(intl, dcgettext,
++		       [gt_cv_func_dgettext_libintl=yes
++			libintl_extra_libs=-liconv],
++			:,-liconv)],
++		:,-liconv)
++        fi
++
++        #
++        # If we found libintl, then check in it for bind_textdomain_codeset();
++        # we'll prefer libc if neither have bind_textdomain_codeset(),
++        # and both have dgettext and ngettext
++        #
++        if test "$gt_cv_func_dgettext_libintl" = "yes" ; then
++          glib_save_LIBS="$LIBS"
++          LIBS="$LIBS -lintl $libintl_extra_libs"
++          unset ac_cv_func_bind_textdomain_codeset
++          AC_CHECK_FUNCS(bind_textdomain_codeset)
++          LIBS="$glib_save_LIBS"
++
++          if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then
++            gt_cv_func_dgettext_libc=no
++          else
++            if test "$gt_cv_func_dgettext_libc" = "yes" \
++		&& test "$gt_cv_func_ngettext_libc" = "yes"; then
++              gt_cv_func_dgettext_libintl=no
++            fi
++          fi
++        fi
++      fi
++
++      if test "$gt_cv_func_dgettext_libc" = "yes" \
++	|| test "$gt_cv_func_dgettext_libintl" = "yes"; then
++        gt_cv_have_gettext=yes
++      fi
++  
++      if test "$gt_cv_func_dgettext_libintl" = "yes"; then
++        INTLLIBS="-lintl $libintl_extra_libs $INTL_MACOSX_LIBS"
++      fi
++  
++      if test "$gt_cv_have_gettext" = "yes"; then
++	AC_DEFINE(HAVE_GETTEXT,1,
++	  [Define if the GNU gettext() function is already present or preinstalled.])
++	GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
++	  [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl
++	if test "$MSGFMT" != "no"; then
++          glib_save_LIBS="$LIBS"
++          LIBS="$LIBS $INTLLIBS"
++	  AC_CHECK_FUNCS(dcgettext)
++	  MSGFMT_OPTS=
++	  AC_MSG_CHECKING([if msgfmt accepts -c])
++	  GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[
++msgid ""
++msgstr ""
++"Content-Type: text/plain; charset=UTF-8\n"
++"Project-Id-Version: test 1.0\n"
++"PO-Revision-Date: 2007-02-15 12:01+0100\n"
++"Last-Translator: test <foo@bar.xx>\n"
++"Language-Team: C <LL@li.org>\n"
++"MIME-Version: 1.0\n"
++"Content-Transfer-Encoding: 8bit\n"
++], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])
++	  AC_SUBST(MSGFMT_OPTS)
++	  AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
++	  GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
++	    [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :)
++	  AC_TRY_LINK(, [extern int _nl_msg_cat_cntr;
++			 return _nl_msg_cat_cntr],
++	    [CATOBJEXT=.gmo 
++             DATADIRNAME=share],
++	    [case $host in
++	    *-*-solaris*)
++	    dnl On Solaris, if bind_textdomain_codeset is in libc,
++	    dnl GNU format message catalog is always supported,
++            dnl since both are added to the libc all together.
++	    dnl Hence, we'd like to go with DATADIRNAME=share and
++	    dnl and CATOBJEXT=.gmo in this case.
++            AC_CHECK_FUNC(bind_textdomain_codeset,
++	      [CATOBJEXT=.gmo 
++               DATADIRNAME=share],
++	      [CATOBJEXT=.mo
++               DATADIRNAME=lib])
++	    ;;
++	    *-*-openbsd*)
++	    CATOBJEXT=.mo
++            DATADIRNAME=share
++	    ;;
++	    *)
++	    CATOBJEXT=.mo
++            DATADIRNAME=lib
++	    ;;
++	    esac])
++          LIBS="$glib_save_LIBS"
++	  INSTOBJEXT=.mo
++	else
++	  gt_cv_have_gettext=no
++	fi
++      fi
++    ])
++
++    if test "$gt_cv_have_gettext" = "yes" ; then
++      AC_DEFINE(ENABLE_NLS, 1,
++        [always defined to indicate that i18n is enabled])
++    fi
++
++    dnl Test whether we really found GNU xgettext.
++    if test "$XGETTEXT" != ":"; then
++      dnl If it is not GNU xgettext we define it as : so that the
++      dnl Makefiles still can work.
++      if $XGETTEXT --omit-header /dev/null 2> /dev/null; then
++        : ;
++      else
++        AC_MSG_RESULT(
++	  [found xgettext program is not GNU xgettext; ignore it])
++        XGETTEXT=":"
++      fi
++    fi
++
++    # We need to process the po/ directory.
++    POSUB=po
++
++    AC_OUTPUT_COMMANDS(
++      [case "$CONFIG_FILES" in *po/Makefile.in*)
++        sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile
++      esac])
++
++    dnl These rules are solely for the distribution goal.  While doing this
++    dnl we only have to keep exactly one list of the available catalogs
++    dnl in configure.ac.
++    for lang in $ALL_LINGUAS; do
++      GMOFILES="$GMOFILES $lang.gmo"
++      POFILES="$POFILES $lang.po"
++    done
++
++    dnl Make all variables we use known to autoconf.
++    AC_SUBST(CATALOGS)
++    AC_SUBST(CATOBJEXT)
++    AC_SUBST(DATADIRNAME)
++    AC_SUBST(GMOFILES)
++    AC_SUBST(INSTOBJEXT)
++    AC_SUBST(INTLLIBS)
++    AC_SUBST(PO_IN_DATADIR_TRUE)
++    AC_SUBST(PO_IN_DATADIR_FALSE)
++    AC_SUBST(POFILES)
++    AC_SUBST(POSUB)
++  ])
++
++# AM_GLIB_GNU_GETTEXT
++# -------------------
++# Do checks necessary for use of gettext. If a suitable implementation 
++# of gettext is found in either in libintl or in the C library,
++# it will set INTLLIBS to the libraries needed for use of gettext
++# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable
++# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST()
++# on various variables needed by the Makefile.in.in installed by 
++# glib-gettextize.
++dnl
++AU_DEFUN([GLIB_GNU_GETTEXT],
++  [AC_REQUIRE([AC_PROG_CC])dnl
++   
++   GLIB_LC_MESSAGES
++   GLIB_WITH_NLS
++
++   if test "$gt_cv_have_gettext" = "yes"; then
++     if test "x$ALL_LINGUAS" = "x"; then
++       LINGUAS=
++     else
++       AC_MSG_CHECKING(for catalogs to be installed)
++       NEW_LINGUAS=
++       for presentlang in $ALL_LINGUAS; do
++         useit=no
++         if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then
++           desiredlanguages="$LINGUAS"
++         else
++           desiredlanguages="$ALL_LINGUAS"
++         fi
++         for desiredlang in $desiredlanguages; do
++ 	   # Use the presentlang catalog if desiredlang is
++           #   a. equal to presentlang, or
++           #   b. a variant of presentlang (because in this case,
++           #      presentlang can be used as a fallback for messages
++           #      which are not translated in the desiredlang catalog).
++           case "$desiredlang" in
++             "$presentlang"*) useit=yes;;
++           esac
++         done
++         if test $useit = yes; then
++           NEW_LINGUAS="$NEW_LINGUAS $presentlang"
++         fi
++       done
++       LINGUAS=$NEW_LINGUAS
++       AC_MSG_RESULT($LINGUAS)
++     fi
++
++     dnl Construct list of names of catalog files to be constructed.
++     if test -n "$LINGUAS"; then
++       for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done
++     fi
++   fi
++
++   dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly
++   dnl find the mkinstalldirs script in another subdir but ($top_srcdir).
++   dnl Try to locate is.
++   MKINSTALLDIRS=
++   if test -n "$ac_aux_dir"; then
++     MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs"
++   fi
++   if test -z "$MKINSTALLDIRS"; then
++     MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs"
++   fi
++   AC_SUBST(MKINSTALLDIRS)
++
++   dnl Generate list of files to be processed by xgettext which will
++   dnl be included in po/Makefile.
++   test -d po || mkdir po
++   if test "x$srcdir" != "x."; then
++     if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then
++       posrcprefix="$srcdir/"
++     else
++       posrcprefix="../$srcdir/"
++     fi
++   else
++     posrcprefix="../"
++   fi
++   rm -f po/POTFILES
++   sed -e "/^#/d" -e "/^\$/d" -e "s,.*,	$posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \
++	< $srcdir/po/POTFILES.in > po/POTFILES
++  ],
++  [[$0: This macro is deprecated. You should use upstream gettext instead.]])
++
++# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE)
++# -------------------------------
++# Define VARIABLE to the location where catalog files will
++# be installed by po/Makefile.
++glib_DEFUN([GLIB_DEFINE_LOCALEDIR],
++[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl
++glib_save_prefix="$prefix"
++glib_save_exec_prefix="$exec_prefix"
++glib_save_datarootdir="$datarootdir"
++test "x$prefix" = xNONE && prefix=$ac_default_prefix
++test "x$exec_prefix" = xNONE && exec_prefix=$prefix
++datarootdir=`eval echo "${datarootdir}"`
++if test "x$CATOBJEXT" = "x.mo" ; then
++  localedir=`eval echo "${libdir}/locale"`
++else
++  localedir=`eval echo "${datadir}/locale"`
++fi
++prefix="$glib_save_prefix"
++exec_prefix="$glib_save_exec_prefix"
++datarootdir="$glib_save_datarootdir"
++AC_DEFINE_UNQUOTED($1, "$localedir",
++  [Define the location where the catalogs will be installed])
++])
++
++dnl
++dnl Now the definitions that aclocal will find
++dnl
++ifdef(glib_configure_ac,[],[
++AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)])
++AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)])
++])dnl
++
++# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL])
++# 
++# Create a temporary file with TEST-FILE as its contents and pass the
++# file name to PROGRAM.  Perform ACTION-IF-PASS if PROGRAM exits with
++# 0 and perform ACTION-IF-FAIL for any other exit status.
++AC_DEFUN([GLIB_RUN_PROG],
++[cat >conftest.foo <<_ACEOF
++$2
++_ACEOF
++if AC_RUN_LOG([$1 conftest.foo]); then
++  m4_ifval([$3], [$3], [:])
++m4_ifvaln([$4], [else $4])dnl
++echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD
++sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD
++fi])
++
+diff -Naur a/m4macros/glib-gettext.m4 b/m4macros/glib-gettext.m4
+--- a/m4macros/glib-gettext.m4	2021-08-19 21:27:25.805341000 +0600
++++ b/m4macros/glib-gettext.m4	1970-01-01 06:00:00.000000000 +0600
+@@ -1,486 +0,0 @@
+-# Copyright (C) 1995-2002 Free Software Foundation, Inc.
+-# Copyright (C) 2001-2003,2004 Red Hat, Inc.
+-#
+-# This file is free software, distributed under the terms of the GNU
+-# General Public License.  As a special exception to the GNU General
+-# Public License, this file may be distributed as part of a program
+-# that contains a configuration script generated by Autoconf, under
+-# the same distribution terms as the rest of that program.
+-#
+-# This file can be copied and used freely without restrictions.  It can
+-# be used in projects which are not available under the GNU Public License
+-# but which still want to provide support for the GNU gettext functionality.
+-#
+-# Macro to add for using GNU gettext.
+-# Ulrich Drepper <drepper@cygnus.com>, 1995, 1996
+-#
+-# Modified to never use included libintl. 
+-# Owen Taylor <otaylor@redhat.com>, 12/15/1998
+-#
+-# Major rework to remove unused code
+-# Owen Taylor <otaylor@redhat.com>, 12/11/2002
+-#
+-# Added better handling of ALL_LINGUAS from GNU gettext version 
+-# written by Bruno Haible, Owen Taylor <otaylor.redhat.com> 5/30/3002
+-#
+-# Modified to require ngettext
+-# Matthias Clasen <mclasen@redhat.com> 08/06/2004
+-
+-# Increment this whenever this file is changed.
+-#serial 1
+-
+-# We need this here as well, since someone might use autoconf-2.5x
+-# to configure GLib then an older version to configure a package
+-# using AM_GLIB_GNU_GETTEXT
+-AC_PREREQ(2.53)
+-
+-dnl
+-dnl We go to great lengths to make sure that aclocal won't 
+-dnl try to pull in the installed version of these macros
+-dnl when running aclocal in the glib directory.
+-dnl
+-m4_copy([AC_DEFUN],[glib_DEFUN])
+-m4_copy([AC_REQUIRE],[glib_REQUIRE])
+-dnl
+-dnl At the end, if we're not within glib, we'll define the public
+-dnl definitions in terms of our private definitions.
+-dnl
+-
+-# GLIB_LC_MESSAGES
+-#--------------------
+-glib_DEFUN([GLIB_LC_MESSAGES],
+-  [AC_CHECK_HEADERS([locale.h])
+-    if test $ac_cv_header_locale_h = yes; then
+-    AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES,
+-      [AC_TRY_LINK([#include <locale.h>], [return LC_MESSAGES],
+-       am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)])
+-    if test $am_cv_val_LC_MESSAGES = yes; then
+-      AC_DEFINE(HAVE_LC_MESSAGES, 1,
+-        [Define if your <locale.h> file defines LC_MESSAGES.])
+-    fi
+-  fi])
+-
+-# GLIB_PATH_PROG_WITH_TEST
+-#----------------------------
+-dnl GLIB_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR,
+-dnl   TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]])
+-glib_DEFUN([GLIB_PATH_PROG_WITH_TEST],
+-[# Extract the first word of "$2", so it can be a program name with args.
+-set dummy $2; ac_word=[$]2
+-AC_MSG_CHECKING([for $ac_word])
+-AC_CACHE_VAL(ac_cv_path_$1,
+-[case "[$]$1" in
+-  /*)
+-  ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+-  ;;
+-  *)
+-  IFS="${IFS= 	}"; ac_save_ifs="$IFS"; IFS="${IFS}:"
+-  for ac_dir in ifelse([$5], , $PATH, [$5]); do
+-    test -z "$ac_dir" && ac_dir=.
+-    if test -f $ac_dir/$ac_word; then
+-      if [$3]; then
+-	ac_cv_path_$1="$ac_dir/$ac_word"
+-	break
+-      fi
+-    fi
+-  done
+-  IFS="$ac_save_ifs"
+-dnl If no 4th arg is given, leave the cache variable unset,
+-dnl so AC_PATH_PROGS will keep looking.
+-ifelse([$4], , , [  test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+-])dnl
+-  ;;
+-esac])dnl
+-$1="$ac_cv_path_$1"
+-if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+-  AC_MSG_RESULT([$]$1)
+-else
+-  AC_MSG_RESULT(no)
+-fi
+-AC_SUBST($1)dnl
+-])
+-
+-dnl Checks for special options needed on Mac OS X.
+-dnl Defines INTL_MACOSX_LIBS.
+-dnl
+-dnl Copied from intlmacosx.m4 in gettext, GPL.
+-dnl Copyright (C) 2004-2013 Free Software Foundation, Inc.
+-glib_DEFUN([glib_gt_INTL_MACOSX],
+-[
+-  dnl Check for API introduced in Mac OS X 10.2.
+-  AC_CACHE_CHECK([for CFPreferencesCopyAppValue],
+-    [gt_cv_func_CFPreferencesCopyAppValue],
+-    [gt_save_LIBS="$LIBS"
+-     LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+-     AC_LINK_IFELSE(
+-       [AC_LANG_PROGRAM(
+-          [[#include <CoreFoundation/CFPreferences.h>]],
+-          [[CFPreferencesCopyAppValue(NULL, NULL)]])],
+-       [gt_cv_func_CFPreferencesCopyAppValue=yes],
+-       [gt_cv_func_CFPreferencesCopyAppValue=no])
+-     LIBS="$gt_save_LIBS"])
+-  if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+-    AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], [1],
+-      [Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in the CoreFoundation framework.])
+-  fi
+-  dnl Check for API introduced in Mac OS X 10.3.
+-  AC_CACHE_CHECK([for CFLocaleCopyCurrent], [gt_cv_func_CFLocaleCopyCurrent],
+-    [gt_save_LIBS="$LIBS"
+-     LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+-     AC_LINK_IFELSE(
+-       [AC_LANG_PROGRAM(
+-          [[#include <CoreFoundation/CFLocale.h>]],
+-          [[CFLocaleCopyCurrent();]])],
+-       [gt_cv_func_CFLocaleCopyCurrent=yes],
+-       [gt_cv_func_CFLocaleCopyCurrent=no])
+-     LIBS="$gt_save_LIBS"])
+-  if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+-    AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], [1],
+-      [Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the CoreFoundation framework.])
+-  fi
+-  INTL_MACOSX_LIBS=
+-  if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+-    INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+-  fi
+-  AC_SUBST([INTL_MACOSX_LIBS])
+-])
+-
+-# GLIB_WITH_NLS
+-#-----------------
+-glib_DEFUN([GLIB_WITH_NLS],
+-  dnl NLS is obligatory
+-  [USE_NLS=yes
+-    AC_SUBST(USE_NLS)
+-
+-    gt_cv_have_gettext=no
+-
+-    CATOBJEXT=NONE
+-    XGETTEXT=:
+-    INTLLIBS=
+-
+-    glib_gt_INTL_MACOSX
+-
+-    AC_CHECK_HEADER(libintl.h,
+-     [gt_cv_func_dgettext_libintl="no"
+-      libintl_extra_libs=""
+-
+-      #
+-      # First check in libc
+-      #
+-      AC_CACHE_CHECK([for ngettext in libc], gt_cv_func_ngettext_libc,
+-        [AC_TRY_LINK([
+-#include <libintl.h>
+-],
+-         [return !ngettext ("","", 1)],
+-	  gt_cv_func_ngettext_libc=yes,
+-          gt_cv_func_ngettext_libc=no)
+-        ])
+-  
+-      if test "$gt_cv_func_ngettext_libc" = "yes" ; then
+-	      AC_CACHE_CHECK([for dgettext in libc], gt_cv_func_dgettext_libc,
+-        	[AC_TRY_LINK([
+-#include <libintl.h>
+-],
+-	          [return !dgettext ("","")],
+-		  gt_cv_func_dgettext_libc=yes,
+-	          gt_cv_func_dgettext_libc=no)
+-        	])
+-      fi
+-  
+-      if test "$gt_cv_func_ngettext_libc" = "yes" ; then
+-        AC_CHECK_FUNCS(bind_textdomain_codeset)
+-      fi
+-
+-      #
+-      # If we don't have everything we want, check in libintl
+-      #
+-      if test "$gt_cv_func_dgettext_libc" != "yes" \
+-	 || test "$gt_cv_func_ngettext_libc" != "yes" \
+-         || test "$ac_cv_func_bind_textdomain_codeset" != "yes" ; then
+-        
+-        AC_CHECK_LIB(intl, bindtextdomain,
+-	    [AC_CHECK_LIB(intl, ngettext,
+-		    [AC_CHECK_LIB(intl, dgettext,
+-			          gt_cv_func_dgettext_libintl=yes)])])
+-
+-	if test "$gt_cv_func_dgettext_libintl" != "yes" ; then
+-	  AC_MSG_CHECKING([if -liconv is needed to use gettext])
+-	  AC_MSG_RESULT([])
+-  	  AC_CHECK_LIB(intl, ngettext,
+-          	[AC_CHECK_LIB(intl, dcgettext,
+-		       [gt_cv_func_dgettext_libintl=yes
+-			libintl_extra_libs=-liconv],
+-			:,-liconv)],
+-		:,-liconv)
+-        fi
+-
+-        #
+-        # If we found libintl, then check in it for bind_textdomain_codeset();
+-        # we'll prefer libc if neither have bind_textdomain_codeset(),
+-        # and both have dgettext and ngettext
+-        #
+-        if test "$gt_cv_func_dgettext_libintl" = "yes" ; then
+-          glib_save_LIBS="$LIBS"
+-          LIBS="$LIBS -lintl $libintl_extra_libs"
+-          unset ac_cv_func_bind_textdomain_codeset
+-          AC_CHECK_FUNCS(bind_textdomain_codeset)
+-          LIBS="$glib_save_LIBS"
+-
+-          if test "$ac_cv_func_bind_textdomain_codeset" = "yes" ; then
+-            gt_cv_func_dgettext_libc=no
+-          else
+-            if test "$gt_cv_func_dgettext_libc" = "yes" \
+-		&& test "$gt_cv_func_ngettext_libc" = "yes"; then
+-              gt_cv_func_dgettext_libintl=no
+-            fi
+-          fi
+-        fi
+-      fi
+-
+-      if test "$gt_cv_func_dgettext_libc" = "yes" \
+-	|| test "$gt_cv_func_dgettext_libintl" = "yes"; then
+-        gt_cv_have_gettext=yes
+-      fi
+-  
+-      if test "$gt_cv_func_dgettext_libintl" = "yes"; then
+-        INTLLIBS="-lintl $libintl_extra_libs $INTL_MACOSX_LIBS"
+-      fi
+-  
+-      if test "$gt_cv_have_gettext" = "yes"; then
+-	AC_DEFINE(HAVE_GETTEXT,1,
+-	  [Define if the GNU gettext() function is already present or preinstalled.])
+-	GLIB_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+-	  [test -z "`$ac_dir/$ac_word -h 2>&1 | grep 'dv '`"], no)dnl
+-	if test "$MSGFMT" != "no"; then
+-          glib_save_LIBS="$LIBS"
+-          LIBS="$LIBS $INTLLIBS"
+-	  AC_CHECK_FUNCS(dcgettext)
+-	  MSGFMT_OPTS=
+-	  AC_MSG_CHECKING([if msgfmt accepts -c])
+-	  GLIB_RUN_PROG([$MSGFMT -c -o /dev/null],[
+-msgid ""
+-msgstr ""
+-"Content-Type: text/plain; charset=UTF-8\n"
+-"Project-Id-Version: test 1.0\n"
+-"PO-Revision-Date: 2007-02-15 12:01+0100\n"
+-"Last-Translator: test <foo@bar.xx>\n"
+-"Language-Team: C <LL@li.org>\n"
+-"MIME-Version: 1.0\n"
+-"Content-Transfer-Encoding: 8bit\n"
+-], [MSGFMT_OPTS=-c; AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])])
+-	  AC_SUBST(MSGFMT_OPTS)
+-	  AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
+-	  GLIB_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+-	    [test -z "`$ac_dir/$ac_word -h 2>&1 | grep '(HELP)'`"], :)
+-	  AC_TRY_LINK(, [extern int _nl_msg_cat_cntr;
+-			 return _nl_msg_cat_cntr],
+-	    [CATOBJEXT=.gmo 
+-             DATADIRNAME=share],
+-	    [case $host in
+-	    *-*-solaris*)
+-	    dnl On Solaris, if bind_textdomain_codeset is in libc,
+-	    dnl GNU format message catalog is always supported,
+-            dnl since both are added to the libc all together.
+-	    dnl Hence, we'd like to go with DATADIRNAME=share and
+-	    dnl and CATOBJEXT=.gmo in this case.
+-            AC_CHECK_FUNC(bind_textdomain_codeset,
+-	      [CATOBJEXT=.gmo 
+-               DATADIRNAME=share],
+-	      [CATOBJEXT=.mo
+-               DATADIRNAME=lib])
+-	    ;;
+-	    *-*-openbsd*)
+-	    CATOBJEXT=.mo
+-            DATADIRNAME=share
+-	    ;;
+-	    *)
+-	    CATOBJEXT=.mo
+-            DATADIRNAME=lib
+-	    ;;
+-	    esac])
+-          LIBS="$glib_save_LIBS"
+-	  INSTOBJEXT=.mo
+-	else
+-	  gt_cv_have_gettext=no
+-	fi
+-      fi
+-    ])
+-
+-    if test "$gt_cv_have_gettext" = "yes" ; then
+-      AC_DEFINE(ENABLE_NLS, 1,
+-        [always defined to indicate that i18n is enabled])
+-    fi
+-
+-    dnl Test whether we really found GNU xgettext.
+-    if test "$XGETTEXT" != ":"; then
+-      dnl If it is not GNU xgettext we define it as : so that the
+-      dnl Makefiles still can work.
+-      if $XGETTEXT --omit-header /dev/null 2> /dev/null; then
+-        : ;
+-      else
+-        AC_MSG_RESULT(
+-	  [found xgettext program is not GNU xgettext; ignore it])
+-        XGETTEXT=":"
+-      fi
+-    fi
+-
+-    # We need to process the po/ directory.
+-    POSUB=po
+-
+-    AC_OUTPUT_COMMANDS(
+-      [case "$CONFIG_FILES" in *po/Makefile.in*)
+-        sed -e "/POTFILES =/r po/POTFILES" po/Makefile.in > po/Makefile
+-      esac])
+-
+-    dnl These rules are solely for the distribution goal.  While doing this
+-    dnl we only have to keep exactly one list of the available catalogs
+-    dnl in configure.ac.
+-    for lang in $ALL_LINGUAS; do
+-      GMOFILES="$GMOFILES $lang.gmo"
+-      POFILES="$POFILES $lang.po"
+-    done
+-
+-    dnl Make all variables we use known to autoconf.
+-    AC_SUBST(CATALOGS)
+-    AC_SUBST(CATOBJEXT)
+-    AC_SUBST(DATADIRNAME)
+-    AC_SUBST(GMOFILES)
+-    AC_SUBST(INSTOBJEXT)
+-    AC_SUBST(INTLLIBS)
+-    AC_SUBST(PO_IN_DATADIR_TRUE)
+-    AC_SUBST(PO_IN_DATADIR_FALSE)
+-    AC_SUBST(POFILES)
+-    AC_SUBST(POSUB)
+-  ])
+-
+-# AM_GLIB_GNU_GETTEXT
+-# -------------------
+-# Do checks necessary for use of gettext. If a suitable implementation 
+-# of gettext is found in either in libintl or in the C library,
+-# it will set INTLLIBS to the libraries needed for use of gettext
+-# and AC_DEFINE() HAVE_GETTEXT and ENABLE_NLS. (The shell variable
+-# gt_cv_have_gettext will be set to "yes".) It will also call AC_SUBST()
+-# on various variables needed by the Makefile.in.in installed by 
+-# glib-gettextize.
+-dnl
+-AU_DEFUN([GLIB_GNU_GETTEXT],
+-  [AC_REQUIRE([AC_PROG_CC])dnl
+-   
+-   GLIB_LC_MESSAGES
+-   GLIB_WITH_NLS
+-
+-   if test "$gt_cv_have_gettext" = "yes"; then
+-     if test "x$ALL_LINGUAS" = "x"; then
+-       LINGUAS=
+-     else
+-       AC_MSG_CHECKING(for catalogs to be installed)
+-       NEW_LINGUAS=
+-       for presentlang in $ALL_LINGUAS; do
+-         useit=no
+-         if test "%UNSET%" != "${LINGUAS-%UNSET%}"; then
+-           desiredlanguages="$LINGUAS"
+-         else
+-           desiredlanguages="$ALL_LINGUAS"
+-         fi
+-         for desiredlang in $desiredlanguages; do
+- 	   # Use the presentlang catalog if desiredlang is
+-           #   a. equal to presentlang, or
+-           #   b. a variant of presentlang (because in this case,
+-           #      presentlang can be used as a fallback for messages
+-           #      which are not translated in the desiredlang catalog).
+-           case "$desiredlang" in
+-             "$presentlang"*) useit=yes;;
+-           esac
+-         done
+-         if test $useit = yes; then
+-           NEW_LINGUAS="$NEW_LINGUAS $presentlang"
+-         fi
+-       done
+-       LINGUAS=$NEW_LINGUAS
+-       AC_MSG_RESULT($LINGUAS)
+-     fi
+-
+-     dnl Construct list of names of catalog files to be constructed.
+-     if test -n "$LINGUAS"; then
+-       for lang in $LINGUAS; do CATALOGS="$CATALOGS $lang$CATOBJEXT"; done
+-     fi
+-   fi
+-
+-   dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly
+-   dnl find the mkinstalldirs script in another subdir but ($top_srcdir).
+-   dnl Try to locate is.
+-   MKINSTALLDIRS=
+-   if test -n "$ac_aux_dir"; then
+-     MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs"
+-   fi
+-   if test -z "$MKINSTALLDIRS"; then
+-     MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs"
+-   fi
+-   AC_SUBST(MKINSTALLDIRS)
+-
+-   dnl Generate list of files to be processed by xgettext which will
+-   dnl be included in po/Makefile.
+-   test -d po || mkdir po
+-   if test "x$srcdir" != "x."; then
+-     if test "x`echo $srcdir | sed 's@/.*@@'`" = "x"; then
+-       posrcprefix="$srcdir/"
+-     else
+-       posrcprefix="../$srcdir/"
+-     fi
+-   else
+-     posrcprefix="../"
+-   fi
+-   rm -f po/POTFILES
+-   sed -e "/^#/d" -e "/^\$/d" -e "s,.*,	$posrcprefix& \\\\," -e "\$s/\(.*\) \\\\/\1/" \
+-	< $srcdir/po/POTFILES.in > po/POTFILES
+-  ],
+-  [[$0: This macro is deprecated. You should use upstream gettext instead.]])
+-
+-# AM_GLIB_DEFINE_LOCALEDIR(VARIABLE)
+-# -------------------------------
+-# Define VARIABLE to the location where catalog files will
+-# be installed by po/Makefile.
+-glib_DEFUN([GLIB_DEFINE_LOCALEDIR],
+-[glib_REQUIRE([GLIB_GNU_GETTEXT])dnl
+-glib_save_prefix="$prefix"
+-glib_save_exec_prefix="$exec_prefix"
+-glib_save_datarootdir="$datarootdir"
+-test "x$prefix" = xNONE && prefix=$ac_default_prefix
+-test "x$exec_prefix" = xNONE && exec_prefix=$prefix
+-datarootdir=`eval echo "${datarootdir}"`
+-if test "x$CATOBJEXT" = "x.mo" ; then
+-  localedir=`eval echo "${libdir}/locale"`
+-else
+-  localedir=`eval echo "${datadir}/locale"`
+-fi
+-prefix="$glib_save_prefix"
+-exec_prefix="$glib_save_exec_prefix"
+-datarootdir="$glib_save_datarootdir"
+-AC_DEFINE_UNQUOTED($1, "$localedir",
+-  [Define the location where the catalogs will be installed])
+-])
+-
+-dnl
+-dnl Now the definitions that aclocal will find
+-dnl
+-ifdef(glib_configure_ac,[],[
+-AC_DEFUN([AM_GLIB_GNU_GETTEXT],[GLIB_GNU_GETTEXT($@)])
+-AC_DEFUN([AM_GLIB_DEFINE_LOCALEDIR],[GLIB_DEFINE_LOCALEDIR($@)])
+-])dnl
+-
+-# GLIB_RUN_PROG(PROGRAM, TEST-FILE, [ACTION-IF-PASS], [ACTION-IF-FAIL])
+-# 
+-# Create a temporary file with TEST-FILE as its contents and pass the
+-# file name to PROGRAM.  Perform ACTION-IF-PASS if PROGRAM exits with
+-# 0 and perform ACTION-IF-FAIL for any other exit status.
+-AC_DEFUN([GLIB_RUN_PROG],
+-[cat >conftest.foo <<_ACEOF
+-$2
+-_ACEOF
+-if AC_RUN_LOG([$1 conftest.foo]); then
+-  m4_ifval([$3], [$3], [:])
+-m4_ifvaln([$4], [else $4])dnl
+-echo "$as_me: failed input was:" >&AS_MESSAGE_LOG_FD
+-sed 's/^/| /' conftest.foo >&AS_MESSAGE_LOG_FD
+-fi])
+-
+diff -Naur a/m4macros/gsettings-2.00.m4 b/m4macros/gsettings-2.00.m4
+--- a/m4macros/gsettings-2.00.m4	1970-01-01 06:00:00.000000000 +0600
++++ b/m4macros/gsettings-2.00.m4	2024-06-20 09:44:11.288183349 +0600
+@@ -0,0 +1,88 @@
++# Increment this whenever this file is changed.
++#serial 2
++
++dnl GLIB_GSETTINGS
++dnl Defines GSETTINGS_SCHEMAS_INSTALL which controls whether
++dnl the schema should be compiled
++dnl
++
++AC_DEFUN([GLIB_GSETTINGS],
++[
++  dnl We can't use PKG_PREREQ because that needs 0.29.
++  m4_ifndef([PKG_PROG_PKG_CONFIG],
++            [pkg.m4 version 0.28 or later is required])
++
++  m4_pattern_allow([AM_V_GEN])
++  AC_ARG_ENABLE(schemas-compile,
++                AS_HELP_STRING([--disable-schemas-compile],
++                               [Disable regeneration of gschemas.compiled on install]),
++                [case ${enableval} in
++                  yes) GSETTINGS_DISABLE_SCHEMAS_COMPILE=""  ;;
++                  no)  GSETTINGS_DISABLE_SCHEMAS_COMPILE="1" ;;
++                  *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-compile]) ;;
++                 esac])
++  AC_SUBST([GSETTINGS_DISABLE_SCHEMAS_COMPILE])
++  PKG_PROG_PKG_CONFIG([0.16])
++  AC_SUBST(gsettingsschemadir, [${datadir}/glib-2.00/schemas])
++  AS_IF([test x$cross_compiling != xyes],
++        [PKG_CHECK_VAR([GLIB_COMPILE_SCHEMAS], [gio-2.00], [glib_compile_schemas])],
++        [AC_PATH_PROG([GLIB_COMPILE_SCHEMAS], [glib-compile-schemas])])
++  AC_SUBST(GLIB_COMPILE_SCHEMAS)
++  if test "x$GLIB_COMPILE_SCHEMAS" = "x"; then
++    ifelse([$2],,[AC_MSG_ERROR([glib-compile-schemas not found.])],[$2])
++  else
++    ifelse([$1],,[:],[$1])
++  fi
++
++  GSETTINGS_RULES='
++.PHONY : uninstall-gsettings-schemas install-gsettings-schemas clean-gsettings-schemas
++
++mostlyclean-am: clean-gsettings-schemas
++
++gsettings__enum_file = $(addsuffix .enums.xml,$(gsettings_ENUM_NAMESPACE))
++
++%.gschema.valid: %.gschema.xml $(gsettings__enum_file)
++	$(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run $(addprefix --schema-file=,$(gsettings__enum_file)) --schema-file=$< && mkdir -p [$](@D) && touch [$]@
++
++all-am: $(gsettings_SCHEMAS:.xml=.valid)
++uninstall-am: uninstall-gsettings-schemas
++install-data-am: install-gsettings-schemas
++
++.SECONDARY: $(gsettings_SCHEMAS)
++
++install-gsettings-schemas: $(gsettings_SCHEMAS) $(gsettings__enum_file)
++	@$(NORMAL_INSTALL)
++	if test -n "$^"; then \
++		test -z "$(gsettingsschemadir)" || $(MKDIR_P) "$(DESTDIR)$(gsettingsschemadir)"; \
++		$(INSTALL_DATA) $^ "$(DESTDIR)$(gsettingsschemadir)"; \
++		test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir); \
++	fi
++
++uninstall-gsettings-schemas:
++	@$(NORMAL_UNINSTALL)
++	@list='\''$(gsettings_SCHEMAS) $(gsettings__enum_file)'\''; test -n "$(gsettingsschemadir)" || list=; \
++	files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \
++	test -n "$$files" || exit 0; \
++	echo " ( cd '\''$(DESTDIR)$(gsettingsschemadir)'\'' && rm -f" $$files ")"; \
++	cd "$(DESTDIR)$(gsettingsschemadir)" && rm -f $$files
++	test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir)
++
++clean-gsettings-schemas:
++	rm -f $(gsettings_SCHEMAS:.xml=.valid) $(gsettings__enum_file)
++
++ifdef gsettings_ENUM_NAMESPACE
++$(gsettings__enum_file): $(gsettings_ENUM_FILES)
++	$(AM_V_GEN) glib-mkenums --comments '\''<!-- @comment@ -->'\'' --fhead "<schemalist>" --vhead "  <@type@ id='\''$(gsettings_ENUM_NAMESPACE).@EnumName@'\''>" --vprod "    <value nick='\''@valuenick@'\'' value='\''@valuenum@'\''/>" --vtail "  </@type@>" --ftail "</schemalist>" [$]^ > [$]@.tmp && mv [$]@.tmp [$]@
++endif
++'
++  _GSETTINGS_SUBST(GSETTINGS_RULES)
++])
++
++dnl _GSETTINGS_SUBST(VARIABLE)
++dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST
++AC_DEFUN([_GSETTINGS_SUBST],
++[
++AC_SUBST([$1])
++m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])])
++]
++)
+diff -Naur a/m4macros/gsettings.m4 b/m4macros/gsettings.m4
+--- a/m4macros/gsettings.m4	2021-08-19 21:27:25.805341000 +0600
++++ b/m4macros/gsettings.m4	1970-01-01 06:00:00.000000000 +0600
+@@ -1,88 +0,0 @@
+-# Increment this whenever this file is changed.
+-#serial 2
+-
+-dnl GLIB_GSETTINGS
+-dnl Defines GSETTINGS_SCHEMAS_INSTALL which controls whether
+-dnl the schema should be compiled
+-dnl
+-
+-AC_DEFUN([GLIB_GSETTINGS],
+-[
+-  dnl We can't use PKG_PREREQ because that needs 0.29.
+-  m4_ifndef([PKG_PROG_PKG_CONFIG],
+-            [pkg.m4 version 0.28 or later is required])
+-
+-  m4_pattern_allow([AM_V_GEN])
+-  AC_ARG_ENABLE(schemas-compile,
+-                AS_HELP_STRING([--disable-schemas-compile],
+-                               [Disable regeneration of gschemas.compiled on install]),
+-                [case ${enableval} in
+-                  yes) GSETTINGS_DISABLE_SCHEMAS_COMPILE=""  ;;
+-                  no)  GSETTINGS_DISABLE_SCHEMAS_COMPILE="1" ;;
+-                  *) AC_MSG_ERROR([bad value ${enableval} for --enable-schemas-compile]) ;;
+-                 esac])
+-  AC_SUBST([GSETTINGS_DISABLE_SCHEMAS_COMPILE])
+-  PKG_PROG_PKG_CONFIG([0.16])
+-  AC_SUBST(gsettingsschemadir, [${datadir}/glib-2.0/schemas])
+-  AS_IF([test x$cross_compiling != xyes],
+-        [PKG_CHECK_VAR([GLIB_COMPILE_SCHEMAS], [gio-2.0], [glib_compile_schemas])],
+-        [AC_PATH_PROG([GLIB_COMPILE_SCHEMAS], [glib-compile-schemas])])
+-  AC_SUBST(GLIB_COMPILE_SCHEMAS)
+-  if test "x$GLIB_COMPILE_SCHEMAS" = "x"; then
+-    ifelse([$2],,[AC_MSG_ERROR([glib-compile-schemas not found.])],[$2])
+-  else
+-    ifelse([$1],,[:],[$1])
+-  fi
+-
+-  GSETTINGS_RULES='
+-.PHONY : uninstall-gsettings-schemas install-gsettings-schemas clean-gsettings-schemas
+-
+-mostlyclean-am: clean-gsettings-schemas
+-
+-gsettings__enum_file = $(addsuffix .enums.xml,$(gsettings_ENUM_NAMESPACE))
+-
+-%.gschema.valid: %.gschema.xml $(gsettings__enum_file)
+-	$(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --strict --dry-run $(addprefix --schema-file=,$(gsettings__enum_file)) --schema-file=$< && mkdir -p [$](@D) && touch [$]@
+-
+-all-am: $(gsettings_SCHEMAS:.xml=.valid)
+-uninstall-am: uninstall-gsettings-schemas
+-install-data-am: install-gsettings-schemas
+-
+-.SECONDARY: $(gsettings_SCHEMAS)
+-
+-install-gsettings-schemas: $(gsettings_SCHEMAS) $(gsettings__enum_file)
+-	@$(NORMAL_INSTALL)
+-	if test -n "$^"; then \
+-		test -z "$(gsettingsschemadir)" || $(MKDIR_P) "$(DESTDIR)$(gsettingsschemadir)"; \
+-		$(INSTALL_DATA) $^ "$(DESTDIR)$(gsettingsschemadir)"; \
+-		test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir); \
+-	fi
+-
+-uninstall-gsettings-schemas:
+-	@$(NORMAL_UNINSTALL)
+-	@list='\''$(gsettings_SCHEMAS) $(gsettings__enum_file)'\''; test -n "$(gsettingsschemadir)" || list=; \
+-	files=`for p in $$list; do echo $$p; done | sed -e '\''s|^.*/||'\''`; \
+-	test -n "$$files" || exit 0; \
+-	echo " ( cd '\''$(DESTDIR)$(gsettingsschemadir)'\'' && rm -f" $$files ")"; \
+-	cd "$(DESTDIR)$(gsettingsschemadir)" && rm -f $$files
+-	test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir)
+-
+-clean-gsettings-schemas:
+-	rm -f $(gsettings_SCHEMAS:.xml=.valid) $(gsettings__enum_file)
+-
+-ifdef gsettings_ENUM_NAMESPACE
+-$(gsettings__enum_file): $(gsettings_ENUM_FILES)
+-	$(AM_V_GEN) glib-mkenums --comments '\''<!-- @comment@ -->'\'' --fhead "<schemalist>" --vhead "  <@type@ id='\''$(gsettings_ENUM_NAMESPACE).@EnumName@'\''>" --vprod "    <value nick='\''@valuenick@'\'' value='\''@valuenum@'\''/>" --vtail "  </@type@>" --ftail "</schemalist>" [$]^ > [$]@.tmp && mv [$]@.tmp [$]@
+-endif
+-'
+-  _GSETTINGS_SUBST(GSETTINGS_RULES)
+-])
+-
+-dnl _GSETTINGS_SUBST(VARIABLE)
+-dnl Abstract macro to do either _AM_SUBST_NOTMAKE or AC_SUBST
+-AC_DEFUN([_GSETTINGS_SUBST],
+-[
+-AC_SUBST([$1])
+-m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([$1])])
+-]
+-)
+diff -Naur a/meson.build b/meson.build
+--- a/meson.build	2021-08-19 21:27:25.806341000 +0600
++++ b/meson.build	2024-06-20 10:30:45.898377006 +0600
+@@ -71,7 +71,7 @@
+ glib_libdir = join_paths(glib_prefix, get_option('libdir'))
+ glib_libexecdir = join_paths(glib_prefix, get_option('libexecdir'))
+ glib_datadir = join_paths(glib_prefix, get_option('datadir'))
+-glib_pkgdatadir = join_paths(glib_datadir, 'glib-2.0')
++glib_pkgdatadir = join_paths(glib_datadir, 'glib-2.00')
+ glib_includedir = join_paths(glib_prefix, get_option('includedir'))
+ if get_option('gio_module_dir') != ''
+   glib_giomodulesdir = join_paths(glib_prefix, get_option('gio_module_dir'))
+@@ -129,10 +129,10 @@
+ glib_conf.set('GLIB_MICRO_VERSION', micro_version)
+ glib_conf.set('GLIB_INTERFACE_AGE', interface_age)
+ glib_conf.set('GLIB_BINARY_AGE', binary_age)
+-glib_conf.set_quoted('GETTEXT_PACKAGE', 'glib20')
++glib_conf.set_quoted('GETTEXT_PACKAGE', 'glib200')
+ glib_conf.set_quoted('PACKAGE_BUGREPORT', 'https://gitlab.gnome.org/GNOME/glib/issues/new')
+ glib_conf.set_quoted('PACKAGE_NAME', 'glib')
+-glib_conf.set_quoted('PACKAGE_STRING', 'glib @0@'.format(meson.project_version()))
++glib_conf.set_quoted('PACKAGE_STRING', 'glib @00@'.format(meson.project_version()))
+ glib_conf.set_quoted('PACKAGE_TARNAME', 'glib')
+ glib_conf.set_quoted('PACKAGE_URL', '')
+ glib_conf.set_quoted('PACKAGE_VERSION', meson.project_version())
+@@ -2356,20 +2356,20 @@
+ endif
+ 
+ # Install m4 macros that other projects use
+-install_data('m4macros/glib-2.0.m4', 'm4macros/glib-gettext.m4', 'm4macros/gsettings.m4',
++install_data('m4macros/glib-2.00.m4', 'm4macros/glib-gettext-2.00.m4', 'm4macros/gsettings-2.00.m4',
+   install_dir : join_paths(get_option('datadir'), 'aclocal'))
+ 
+ if host_system != 'windows'
+   # Install Valgrind suppression file (except on Windows,
+   # as Valgrind is currently not supported on Windows)
+   install_data('glib.supp',
+-    install_dir : join_paths(get_option('datadir'), 'glib-2.0', 'valgrind'))
++    install_dir : join_paths(get_option('datadir'), 'glib-2.00', 'valgrind'))
+ endif
+ 
+ configure_file(output : 'config.h', configuration : glib_conf)
+ 
+ if host_system == 'windows'
+-  install_headers([ 'msvc_recommended_pragmas.h' ], subdir : 'glib-2.0')
++  install_headers([ 'msvc_recommended_pragmas.h' ], subdir : 'glib-2.00')
+ endif
+ 
+ if get_option('man')
+diff -Naur a/po/Makefile.in.in b/po/Makefile.in.in
+--- a/po/Makefile.in.in	2021-08-19 21:27:25.806341000 +0600
++++ b/po/Makefile.in.in	2024-06-20 09:44:43.530977685 +0600
+@@ -30,7 +30,7 @@
+ libdir = @libdir@
+ localedir = $(libdir)/locale
+ gnulocaledir = $(datadir)/locale
+-gettextsrcdir = $(datadir)/glib-2.0/gettext/po
++gettextsrcdir = $(datadir)/glib-2.00/gettext/po
+ subdir = po
+ 
+ INSTALL = @INSTALL@
+diff -Naur a/po/meson.build b/po/meson.build
+--- a/po/meson.build	2021-08-19 21:27:25.851341000 +0600
++++ b/po/meson.build	2024-06-20 09:44:23.055108292 +0600
+@@ -1,5 +1,5 @@
+ i18n = import('i18n')
+ 
+-i18n.gettext('glib20', preset: 'glib')
++i18n.gettext('glib200', preset: 'glib')
+ 
+ install_data('Makefile.in.in', install_dir : glib_pkgdatadir + '/gettext/po')
diff --git a/base/rx/rx-glib2/glib2.spec b/base/rx/rx-glib2/glib2.spec
new file mode 100644
index 0000000..e6266cf
--- /dev/null
+++ b/base/rx/rx-glib2/glib2.spec
@@ -0,0 +1,1012 @@
+%bcond_without sysprof
+
+Name: glib2.00
+Version: 2.68.4
+Release: 15%{?dist}
+Summary: A library of handy utility functions
+
+License: LGPLv2+
+URL: http://www.gtk.org
+Source0: http://download.gnome.org/sources/glib/2.68/glib-%{version}.tar.xz
+
+# Required for RHEL core crypto components policy. Good for Fedora too.
+# https://bugzilla.redhat.com/show_bug.cgi?id=1630260
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903
+Patch0: gnutls-hmac.patch
+
+# Add GPowerProfileMonitor
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1965
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2194
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2222
+Patch1: 1965.patch
+Patch2: 2194.patch
+Patch3: 2222.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2244
+Patch4: 2244.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2291
+Patch5: 2291.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1968
+Patch6: 1968.patch
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2435
+Patch7: 2435.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3126
+Patch8: 3126.patch
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3136
+Patch9: 3136.patch
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3163
+Patch10: 3163.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2826
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3272
+Patch11: 2826.patch
+Patch12: 3272.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2408
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2816
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2847
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3158
+Patch13: 2408.patch
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3353
+Patch14: 3353.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3845
+Patch15: 3845.patch
+
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3720
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4038
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4053
+# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4057
+Patch16: 4038.patch
+
+
+Patch200: glib-change-version.patch
+
+BuildRequires: chrpath
+BuildRequires: gcc
+BuildRequires: gcc-c++
+BuildRequires: gettext
+BuildRequires: gtk-doc
+BuildRequires: perl-interpreter
+# for sys/inotify.h
+BuildRequires: glibc-devel
+BuildRequires: libattr-devel
+BuildRequires: libselinux-devel
+BuildRequires: meson
+# for sys/sdt.h
+BuildRequires: systemtap-sdt-devel
+BuildRequires: pkgconfig(libelf)
+BuildRequires: pkgconfig(libffi)
+BuildRequires: pkgconfig(libpcre)
+BuildRequires: pkgconfig(mount)
+%if %{with sysprof}
+BuildRequires: pkgconfig(sysprof-capture-4)
+%endif
+BuildRequires: pkgconfig(zlib)
+BuildRequires: python3-devel
+
+# For gnutls-hmac.patch. We now dlopen libgnutls.so.30 so that we can build a
+# static glib2 without depending on a static build of GnuTLS as well. This will
+# ensure we notice if the GnuTLS soname bumps, so that we can update our patch.
+%if 0%{?__isa_bits} == 64
+Requires: libgnutls.so.30()(64bit)
+%else
+Requires: libgnutls.so.30
+%endif
+
+# Remove gamin dependency
+#Obsoletes: glib2-fam < 2.67.1-3
+
+# glib 2.59.0 hash table changes broke older gcr versions / password prompts in gnome-shell
+#Conflicts: gcr < 3.28.1
+
+Provides: bundled(gnulib)
+Provides: bundled(gvdb)
+Provides: bundled(libcharset)
+Provides: bundled(xdgmime)
+
+%description
+GLib is the low-level core library that forms the basis for projects
+such as GTK+ and GNOME. It provides data structure handling for C,
+portability wrappers, and interfaces for such runtime functionality
+as an event loop, threads, dynamic loading, and an object system.
+
+%package devel
+Summary: A library of handy utility functions
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description devel
+The glib2-devel package includes the header files for the GLib library.
+
+
+%package static
+Summary: glib static
+Requires: %{name}-devel = %{version}-%{release}
+
+%description static
+The %{name}-static subpackage contains static libraries for %{name}.
+
+
+%prep
+%autosetup -n glib-%{version} -p1
+
+%build
+# Bug 1324770: Also explicitly remove PCRE sources since we use --with-pcre=system
+rm glib/pcre/*.[ch]
+
+export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
+export LDFLAGS="-L%{_libdir} -L%{_syslibdir} -Wl,-rpath=%{_libdir} $LDFLAGS"
+
+%meson \
+    -Dman=false \
+    -Ddtrace=true \
+    -Dsystemtap=true \
+%if %{with sysprof}
+    -Dsysprof=enabled \
+%endif
+    -Dglib_debug=disabled \
+    -Dgtk_doc=false \
+    -Dinstalled_tests=false \
+    -Dgnutls=true \
+    --default-library=both \
+    %{nil}
+
+%meson_build
+
+%install
+%meson_install
+# Since this is a generated .py file, set it to a known timestamp for
+# better reproducibility.
+# Also copy the timestamp for other .py files, because meson doesn't
+# do this, see https://github.com/mesonbuild/meson/issues/5027.
+touch -r gio/gdbus-2.0/codegen/config.py.in %{buildroot}%{_datadir}/glib-2.00/codegen/*.py
+chrpath --delete %{buildroot}%{_libdir}/*.so
+
+# Perform byte compilation manually to avoid issues with
+# irreproducibility of the default invalidation mode, see
+# https://www.python.org/dev/peps/pep-0552/ and
+# https://bugzilla.redhat.com/show_bug.cgi?id=1686078
+export PYTHONHASHSEED=0
+%py_byte_compile %{__python3} %{buildroot}%{_datadir}
+
+mv %{buildroot}%{_bindir}/gio-querymodules %{buildroot}%{_bindir}/gio-querymodules-%{__isa_bits}
+sed -i -e "/^gio_querymodules=/s/gio-querymodules/gio-querymodules-%{__isa_bits}/" %{buildroot}%{_libdir}/pkgconfig/gio-2.00.pc
+
+mkdir -p %{buildroot}%{_libdir}/gio/modules
+touch %{buildroot}%{_libdir}/gio/modules/giomodule.cache
+
+# remove unneeded
+rm -fr %{buildroot}%{_bindir}
+rm -fr %{buildroot}%{_datadir}/bash-completion
+
+%find_lang glib200
+
+%transfiletriggerin -- %{_libdir}/gio/modules
+gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || :
+
+%transfiletriggerpostun -- %{_libdir}/gio/modules
+gio-querymodules-%{__isa_bits} %{_libdir}/gio/modules &> /dev/null || :
+
+%transfiletriggerin -- %{_datadir}/glib-2.00/schemas
+glib-compile-schemas %{_datadir}/glib-2.00/schemas &> /dev/null || :
+
+%transfiletriggerpostun -- %{_datadir}/glib-2.00/schemas
+glib-compile-schemas %{_datadir}/glib-2.00/schemas &> /dev/null || :
+
+%files -f glib200.lang
+%license COPYING
+%doc AUTHORS NEWS README
+%{_libdir}/libglib-2.00.so.*
+%{_libdir}/libgthread-2.00.so.*
+%{_libdir}/libgmodule-2.00.so.*
+%{_libdir}/libgobject-2.00.so.*
+%{_libdir}/libgio-2.00.so.*
+%dir %{_datadir}/glib-2.00
+%dir %{_datadir}/glib-2.00/schemas
+%dir %{_libdir}/gio
+%dir %{_libdir}/gio/modules
+%ghost %{_libdir}/gio/modules/giomodule.cache
+
+
+%files devel
+%{_libdir}/lib*.so
+%{_libdir}/glib-2.00
+%{_includedir}/*
+%{_datadir}/aclocal/*
+%{_libdir}/pkgconfig/*
+%{_datadir}/glib-2.00/gdb
+%{_datadir}/glib-2.00/gettext
+%{_datadir}/glib-2.00/schemas/gschema.dtd
+%{_datadir}/glib-2.00/valgrind/glib.supp
+%{_datadir}/glib-2.00/codegen
+%{_datadir}/gdb/
+%{_datadir}/gettext/
+%{_datadir}/systemtap/
+
+
+%files static
+%{_libdir}/libgio-2.00.a
+%{_libdir}/libglib-2.00.a
+%{_libdir}/libgmodule-2.00.a
+%{_libdir}/libgobject-2.00.a
+%{_libdir}/libgthread-2.00.a
+
+
+%changelog
+* Mon May 13 2024 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-15
+- Fix CVE-2024-34397, signal subscription vulnerabilities
+- Resolves: RHEL-35775
+
+* Wed Feb 21 2024 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-14
+- Rebuild against newer util-linux for libmnt changes
+- Resolves: RHEL-23637
+
+* Thu Feb 01 2024 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-13
+- Backport GUnixMountMonitor port to libmnt_monitor
+- Resolves: RHEL-23637
+
+* Fri Nov 03 2023 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-12
+- Fix race with waitpid() and child watcher sources
+- Resolves: RHEL-14761
+
+* Wed Jul 19 2023 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-11
+- Really fix authentication failures when sd-bus clients connect to GDBus servers
+- Resolves: #2217771
+
+* Thu Jul 06 2023 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-10
+- Fix authentication failures when sd-bus clients connect to GDBus servers
+- Resolves: #2217771
+
+* Thu May 25 2023 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-9
+- Resolve s390x crashes introduced by fixes for CVE-2023-24593/CVE-2023-25180
+- Related: #2181196
+- Related: #2181200
+
+* Wed May 17 2023 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-8
+- Resolve use after free introduced by fixes for CVE-2023-24593/CVE-2023-25180
+- Related: #2181196
+- Related: #2181200
+
+* Fri Mar 24 2023 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-7
+- Fix CVE-2023-24593 and CVE-2023-25180
+- Resolves: #2181196
+- Resolves: #2181200
+
+* Fri Dec 02 2022 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-6
+- Drop gdesktopappinfo patchset
+- Resolves: #2150307
+
+* Fri Jan 21 2022 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-5
+- Add one more upstream patch to gspawn patchset
+- Related: #1910092
+
+* Fri Jan 21 2022 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-4
+- Add gspawn patchset
+- Resolves: #1910092
+
+* Wed Dec 01 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-3
+- Fix GNetworkMonitor after NetworkManager D-Bus API changes
+- Resolves: #2014624
+
+* Wed Sep 15 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-2
+- Fix g_get_user_database_entry() crash when used with nss-systemd
+- Resolves: #2004711
+
+* Sat Aug 21 2021 Kalev Lember <klember@redhat.com> - 2.68.4-1
+- Update to 2.68.4
+
+* Wed Aug 18 2021 DJ Delorie <dj@redhat.com> - 2.68.3-6
+- Rebuilt for libffi 3.4.2 SONAME transition.
+  Related: rhbz#1891914
+
+* Tue Aug 17 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.3-5
+- Backport GPowerProfileMonitor
+- Resolves: #1994466
+
+* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 2.68.3-4
+- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
+  Related: rhbz#1991688
+
+* Tue Jul 27 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.3-3
+- Fix build with glibc 2.34
+- Resolves: #1984626
+
+* Thu Jul 01 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.3-2
+- Refresh gnutls-hmac patchset to fix leaks in error path
+- Related: #1971823
+
+* Mon Jun 28 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.3-1
+- Update to 2.68.3
+- Resolves: #1976713
+- Remove Recommends: shared-mime-info
+- Resolves: #1947897
+
+* Wed Jun 23 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.2-2
+- Update GHmac patchset and reenable glib2-static
+- Resolves: #1971823
+
+* Wed May 19 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.2-1
+- Update to 2.68.2
+- Resolves: #1961039
+
+* Tue May 11 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.1-4
+- No changes, bump revision to retry gating
+- Related: #1951126
+
+* Fri May 07 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.1-3
+- Add missing bundled provides
+- Add rpminspect gating configuration
+- Consolidate GDesktopAppInfo patchset
+- Resolves: #1951126
+
+* Wed Apr 28 2021 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.1-2
+- Refresh GDesktopAppInfo patchset
+- Related: #1951126
+
+* Thu Apr 22 2021 Kalev Lember <klember@redhat.com> - 2.68.1-1
+- Update to 2.68.1
+
+* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 2.68.0-3
+- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
+
+* Fri Mar 26 2021 Kalev Lember <klember@redhat.com> - 2.68.0-2
+- Rebuild to fix sysprof-capture symbols leaking into libraries consuming it
+
+* Thu Mar 18 2021 Kalev Lember <klember@redhat.com> - 2.68.0-1
+- Update to 2.68.0
+
+* Thu Mar 18 2021 Petr Pisar <ppisar@redhat.com> - 2.67.6-2
+- Disable debugging glib (bug #1936339)
+
+* Thu Mar 11 2021 Kalev Lember <klember@redhat.com> - 2.67.6-1
+- Update to 2.67.6
+
+* Tue Mar 02 2021 Kalev Lember <klember@redhat.com> - 2.67.5-1
+- Update to 2.67.5
+
+* Wed Feb 24 2021 Kalev Lember <klember@redhat.com> - 2.67.4-3
+- Enable sysprof capture support
+
+* Fri Feb 19 2021 Kalev Lember <klember@redhat.com> - 2.67.4-2
+- Backport a fix for gsubprocesslauncher regression
+
+* Tue Feb 16 2021 Kalev Lember <klember@redhat.com> - 2.67.4-1
+- Update to 2.67.4
+
+* Tue Feb 09 2021 Benjamin Berg <bberg@redhat.com> - 2.67.3-2
+- Add patches to move applications into systemd scopes
+
+* Thu Feb 04 2021 Kalev Lember <klember@redhat.com> - 2.67.3-1
+- Update to 2.67.3
+- Fix gtk-doc directory ownership
+
+* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.67.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Fri Jan 22 2021 Peter Robinson <pbrobinson@fedoraproject.org> - 2.67.1-3
+- Drop dependency on gamin
+
+* Sat Dec 19 2020 Kevin Fenzi <kevin@scrye.com> - 2.67.1-2
+- Add already upstream patch to fix gdm crasher.
+
+* Sat Dec 19 2020 Kalev Lember <klember@redhat.com> - 2.67.1-1
+- Update to 2.67.1
+
+* Fri Dec 04 2020 Ondrej Holy <oholy@redhat.com> - 2.67.0-7
+- Explicitly create modules dir to fix ELN build
+
+* Tue Dec 01 2020 Ondrej Holy and Michael Catanzaro <mcatanzaro@redhat.com> - 2.67.0-6
+- Disable glib2-fam in RHEL
+
+* Tue Nov 24 2020 Kalev Lember <klember@redhat.com> - 2.67.0-5
+- Backport upstream patches to fix invalid use of volatile objects
+  (gcc 11 support)
+
+* Wed Nov 11 2020 Michael Catanzaro <mcatanzaro@redhat.com> - 2.67.0-4
+- Make GnuTLS patch RHEL-specific, and make glib2-static subpackage Fedora-specific
+
+* Tue Nov 10 2020 Michael Catanzaro <mcatanzaro@redhat.com> - 2.67.0-3
+- Use GnuTLS to implement GHmac (thanks to Colin Walters)
+
+* Wed Nov 04 2020 Michael Catanzaro <mcatanzaro@redhat.com> - 2.67.0-2
+- Backport fix for GSocketClient crash
+
+* Thu Oct 29 2020 Kalev Lember <klember@redhat.com> - 2.67.0-1
+- Update to 2.67.0
+
+* Mon Oct 19 2020 Kalev Lember <klember@redhat.com> - 2.66.2-1
+- Update to 2.66.2
+- Drop gtk-doc patch as we finally have a new enough gtk-doc
+
+* Wed Oct 14 2020 Michael Catanzaro <mcatanzaro@redhat.com> - 2.66.1-3
+- Fix yet another timezone bug
+
+* Wed Oct 14 2020 Michael Catanzaro <mcatanzaro@redhat.com> - 2.66.1-2
+- Fix timezone-related bugs in many applications caused by new glib timezone cache
+
+* Thu Oct  1 2020 Kalev Lember <klember@redhat.com> - 2.66.1-1
+- Update to 2.66.1
+
+* Thu Sep 10 2020 Kalev Lember <klember@redhat.com> - 2.66.0-1
+- Update to 2.66.0
+
+* Wed Sep 02 2020 Kalev Lember <klember@redhat.com> - 2.65.3-1
+- Update to 2.65.3
+
+* Tue Aug 25 2020 Adam Williamson <awilliam@redhat.com> - 2.65.2-3
+- Backport fix for GGO #2189 (error accessing some filesystems)
+
+* Thu Aug 20 2020 Jeff Law <law@redhat.com> - 2.65.2-2
+- Re-enable LTO
+
+* Tue Aug 18 2020 Kalev Lember <klember@redhat.com> - 2.65.2-1
+- Update to 2.65.2
+
+* Mon Aug 17 2020 Kalev Lember <klember@redhat.com> - 2.65.1-1
+- Update to 2.65.1
+
+* Sat Aug 01 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.65.0-5
+- Second attempt - Rebuilt for
+  https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Mon Jul 27 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.65.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Tue Jun 30 2020 Jeff Law <aw@redhat.com> - 2.65.0-3
+Disable LTO
+
+* Mon Jun 22 2020 Kalev Lember <klember@redhat.com> - 2.65.0-2
+- Update gio-2.0.pc with correct gio-querymodules name when renaming it
+  (#1849441)
+
+* Mon Jun 22 2020 Kalev Lember <klember@redhat.com> - 2.65.0-1
+- Update to 2.65.0
+
+* Wed May 20 2020 Kalev Lember <klember@redhat.com> - 2.64.3-1
+- Update to 2.64.3
+
+* Tue Apr 28 2020 Tomas Popela <tpopela@redhat.com> - 2.64.2-2
+- Backport fix for a race condition in GCancellable (rhbz#1825230)
+
+* Fri Apr 10 2020 Kalev Lember <klember@redhat.com> - 2.64.2-1
+- Update to 2.64.2
+
+* Wed Mar 11 2020 Kalev Lember <klember@redhat.com> - 2.64.1-1
+- Update to 2.64.1
+
+* Mon Mar 02 2020 Kalev Lember <klember@redhat.com> - 2.64.0-1
+- Update to 2.64.0
+
+* Mon Feb 24 2020 Kalev Lember <klember@redhat.com> - 2.63.6-1
+- Update to 2.63.6
+
+* Wed Feb 12 2020 Kalev Lember <klember@redhat.com> - 2.63.5-3
+- Backport a patch to work around SELinux policies not allowing
+  SYS_sched_setattr (#1795524)
+
+* Fri Feb 07 2020 Michael Catanzaro <mcatanzaro@redhat.com> - 2.63.5-2
+- Add patch for CVE-2020-6750 and related issues.
+
+* Mon Feb 03 2020 Kalev Lember <klember@redhat.com> - 2.63.5-1
+- Update to 2.63.5
+
+* Wed Jan 29 2020 Stephen Gallagher <sgallagh@redhat.com> - 2.63.4-3
+- Fix GThreadPool initialization that is breaking createrepo_c (BZ #1795052)
+
+* Tue Jan 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.63.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Fri Jan 24 2020 Kalev Lember <klember@redhat.com> - 2.63.4-1
+- Update to 2.63.4
+
+* Mon Dec 16 2019 Kalev Lember <klember@redhat.com> - 2.63.3-1
+- Update to 2.63.3
+
+* Mon Dec 02 2019 Kalev Lember <klember@redhat.com> - 2.63.2-1
+- Update to 2.63.2
+
+* Fri Oct 04 2019 Kalev Lember <klember@redhat.com> - 2.63.0-1
+- Update to 2.63.0
+
+* Fri Oct 04 2019 Kalev Lember <klember@redhat.com> - 2.62.1-1
+- Update to 2.62.1
+
+* Fri Sep 06 2019 Kalev Lember <klember@redhat.com> - 2.62.0-1
+- Update to 2.62.0
+
+* Tue Sep 03 2019 Kalev Lember <klember@redhat.com> - 2.61.3-1
+- Update to 2.61.3
+
+* Mon Aug 12 2019 Kalev Lember <klember@redhat.com> - 2.61.2-1
+- Update to 2.61.2
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.61.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Tue Jun 11 2019 David King <amigadave@amigadave.com> - 2.61.1-2
+- Fix CVE-2019-12450 (#1719142)
+- Consistently use buildroot macro
+
+* Fri May 24 2019 Kalev Lember <klember@redhat.com> - 2.61.1-1
+- Update to 2.61.1
+
+* Tue Apr 16 2019 Adam Williamson <awilliam@redhat.com> - 2.61.0-2
+- Rebuild with Meson fix for #1699099
+
+* Mon Apr 15 2019 Kalev Lember <klember@redhat.com> - 2.61.0-1
+- Update to 2.61.0
+
+* Mon Apr 15 2019 Kalev Lember <klember@redhat.com> - 2.60.1-1
+- Update to 2.60.1
+
+* Wed Mar 13 2019 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - 2.60.0-3
+- Switch back to timestamp-based pyc invalidation mode
+
+* Wed Mar  6 2019 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - 2.60.0-2
+- Make sure all .py files have fixed timestamps (fixes issue with
+  parallel installability of i686 and amd64 -devel packages)
+- Switch to explicit byte compilation to override invalidation mode
+
+* Mon Mar 04 2019 Kalev Lember <klember@redhat.com> - 2.60.0-1
+- Update to 2.60.0
+
+* Mon Feb 18 2019 Kalev Lember <klember@redhat.com> - 2.59.3-1
+- Update to 2.59.3
+
+* Mon Feb 04 2019 Kalev Lember <klember@redhat.com> - 2.59.2-1
+- Update to 2.59.2
+
+* Thu Jan 31 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.59.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Sat Jan 26 2019 Kalev Lember <klember@redhat.com> - 2.59.1-1
+- Update to 2.59.1
+
+* Thu Jan 03 2019 Kalev Lember <klember@redhat.com> - 2.59.0-1
+- Update to 2.59.0
+- Switch to the meson build system
+
+* Tue Dec 18 2018 Kalev Lember <klember@redhat.com> - 2.58.2-1
+- Update to 2.58.2
+
+* Fri Oct 05 2018 Kalev Lember <klember@redhat.com> - 2.58.1-2
+- Fix multilib -devel installs (#1634778)
+
+* Fri Sep 21 2018 Kalev Lember <klember@redhat.com> - 2.58.1-1
+- Update to 2.58.1
+
+* Wed Sep 05 2018 Kalev Lember <klember@redhat.com> - 2.58.0-1
+- Update to 2.58.0
+
+* Thu Aug 2 2018 Ondrej Holy <oholy@redhat.com> - 2.57.2-1
+- Update to 2.57.2
+
+* Fri Jul 20 2018 Ondrej Holy <oholy@redhat.com> - 2.57.1-1
+- Update to 2.57.1
+
+* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 2.56.1-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Tue Jun 19 2018 Miro Hrončok <mhroncok@redhat.com> - 2.56.1-5
+- Rebuilt for Python 3.7
+
+* Thu Jun 14 2018 Debarshi Ray <rishi@fedoraproject.org> - 2.56.1-4
+- Backport patch to fix possible invalid pointer in dbus callback in the FD.o
+  notification backend (RH #1584916)
+
+* Sun May 27 2018 Kalev Lember <klember@redhat.com> - 2.56.1-3
+- Fix multilib -devel installs (#1581067)
+
+* Sun May 13 2018 Fabio Valentini <decathorpe@gmail.com> - 2.56.1-2
+- Include upstream patch to fix gdbus-codegen with meson 0.46.
+
+* Sun Apr 08 2018 Kalev Lember <klember@redhat.com> - 2.56.1-1
+- Update to 2.56.1
+
+* Mon Mar 12 2018 Kalev Lember <klember@redhat.com> - 2.56.0-1
+- Update to 2.56.0
+
+* Wed Feb 07 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 2.55.2-3
+- Undo disabling mangling
+
+* Wed Feb 07 2018 Kalev Lember <klember@redhat.com> - 2.55.2-2
+- Disable brp-mangle-shebangs shebangs
+
+* Wed Feb 07 2018 Kalev Lember <klember@redhat.com> - 2.55.2-1
+- Update to 2.55.2
+- Drop ldconfig scriptlets
+
+* Wed Jan 31 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 2.55.1-3
+- Switch to %%ldconfig_scriptlets
+
+* Thu Jan 18 2018 Kalev Lember <klember@redhat.com> - 2.55.1-2
+- gmain: Partial revert of recent wakeup changes
+
+* Mon Jan 08 2018 Kalev Lember <klember@redhat.com> - 2.55.1-1
+- Update to 2.55.1
+- Drop upstreamed systemtap multilib fix
+
+* Tue Dec 19 2017 Kalev Lember <klember@redhat.com> - 2.55.0-1
+- Update to 2.55.0
+
+* Wed Nov 01 2017 Kalev Lember <klember@redhat.com> - 2.54.2-1
+- Update to 2.54.2
+
+* Fri Oct 06 2017 Kalev Lember <klember@redhat.com> - 2.54.1-1
+- Update to 2.54.1
+
+* Mon Sep 11 2017 Kalev Lember <klember@redhat.com> - 2.54.0-1
+- Update to 2.54.0
+
+* Tue Sep 05 2017 Kalev Lember <klember@redhat.com> - 2.53.7-1
+- Update to 2.53.7
+
+* Sat Aug 19 2017 Kalev Lember <klember@redhat.com> - 2.53.6-1
+- Update to 2.53.6
+
+* Mon Aug 07 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 2.53.5-1
+- Update to 2.53.5
+
+* Tue Aug 01 2017 Kalev Lember <klember@redhat.com> - 2.53.4-4
+- Backport glib-mkenums flags annotation parsing fixes
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 2.53.4-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Fri Jul 21 2017 Kalev Lember <klember@redhat.com> - 2.53.4-2
+- Revert a GKeyFile introspection ABI change
+
+* Tue Jul 18 2017 Kalev Lember <klember@redhat.com> - 2.53.4-1
+- Update to 2.53.4
+
+* Thu Jun 22 2017 Kalev Lember <klember@redhat.com> - 2.53.3-1
+- Update to 2.53.3
+
+* Thu Jun 8 2017 Owen Taylor <otaylor@redhat.com> - 2.53.2-2
+- Make triggers also compile schemas in /app/share/glib-2.0/schemas
+
+* Wed May 24 2017 Florian Müllner <fmuellner@redhat.com> - 2.53.2-1
+- Update to 2.53.2
+
+* Mon May 15 2017 Kalev Lember <klember@redhat.com> - 2.52.2-2
+- Backport a gmain GWakeup patch to fix timedatex high CPU usage (#1450628)
+
+* Tue May 09 2017 Kalev Lember <klember@redhat.com> - 2.52.2-1
+- Update to 2.52.2
+
+* Tue Apr 11 2017 Colin Walters <walters@verbum.org> - 2.52.1-3
+- Backport patches for gmain wakeup for qemu
+  See: https://bugzilla.gnome.org/show_bug.cgi?id=761102
+
+* Tue Apr 11 2017 Colin Walters <walters@verbum.org> - 2.52.1-2
+- Explictly remove PCRE sources
+- Related: https://bugzilla.redhat.com/show_bug.cgi?id=1324770
+
+* Tue Apr 11 2017 Kalev Lember <klember@redhat.com> - 2.52.1-1
+- Update to 2.52.1
+
+* Mon Mar 20 2017 Kalev Lember <klember@redhat.com> - 2.52.0-1
+- Update to 2.52.0
+
+* Thu Mar 16 2017 Kalev Lember <klember@redhat.com> - 2.51.5-1
+- Update to 2.51.5
+
+* Thu Mar 02 2017 Kalev Lember <klember@redhat.com> - 2.51.4-2
+- Remove the dependency on dbus-launch again (#927212)
+
+* Wed Mar 01 2017 David King <amigadave@amigadave.com> - 2.51.4-1
+- Update to 2.51.4
+- Add a Requires on dbus-launch (#927212)
+- Use pkgconfig for BuildRequires
+
+* Tue Feb 14 2017 Richard Hughes <rhughes@redhat.com> - 2.51.2-1
+- Update to 2.51.2
+
+* Mon Feb 13 2017 Richard Hughes <rhughes@redhat.com> - 2.51.1-1
+- Update to 2.51.1
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 2.51.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Mon Dec 19 2016 Miro Hrončok <mhroncok@redhat.com> - 2.51.0-2
+- Rebuild for Python 3.6
+
+* Sun Oct 30 2016 Kalev Lember <klember@redhat.com> - 2.51.0-1
+- Update to 2.51.0
+
+* Wed Oct 12 2016 Kalev Lember <klember@redhat.com> - 2.50.1-1
+- Update to 2.50.1
+
+* Mon Sep 19 2016 Kalev Lember <klember@redhat.com> - 2.50.0-1
+- Update to 2.50.0
+
+* Tue Sep 13 2016 Kalev Lember <klember@redhat.com> - 2.49.7-1
+- Update to 2.49.7
+- Don't set group tags
+
+* Sun Aug 28 2016 Kalev Lember <klember@redhat.com> - 2.49.6-1
+- Update to 2.49.6
+
+* Thu Aug 18 2016 Kalev Lember <klember@redhat.com> - 2.49.5-1
+- Update to 2.49.5
+- Own /usr/share/gdb and /usr/share/systemtap directories
+
+* Tue Aug 16 2016 Miro Hrončok <mhroncok@redhat.com> - 2.49.4-3
+- Use Python 3 for the RPM Python byte compilation
+
+* Wed Jul 27 2016 Ville Skyttä <ville.skytta@iki.fi> - 2.49.4-2
+- Switch to Python 3 (#1286284)
+
+* Thu Jul 21 2016 Kalev Lember <klember@redhat.com> - 2.49.4-1
+- Update to 2.49.4
+
+* Sun Jul 17 2016 Kalev Lember <klember@redhat.com> - 2.49.3-1
+- Update to 2.49.3
+
+* Wed Jun 22 2016 Richard Hughes <rhughes@redhat.com> - 2.49.2-1
+- Update to 2.49.2
+
+* Wed Jun 01 2016 Yaakov Selkowitz <yselkowi@redhat.com> - 2.49.1-2
+- Soften shared-mime-info dependency (#1266118)
+
+* Fri May 27 2016 Florian Müllner <fmuellner@redhat.com> - 2.49.1-1
+- Update to 2.49.1
+
+* Tue May 10 2016 Kalev Lember <klember@redhat.com> - 2.48.1-1
+- Update to 2.48.1
+
+* Wed Apr 06 2016 Colin Walters <walters@redhat.com> - 2.48.0-2
+- Explicitly require system pcre, though we happened to default to this now
+  anyways due to something else pulling PCRE into the buildroot
+  Closes rhbz#1287266
+
+* Tue Mar 22 2016 Kalev Lember <klember@redhat.com> - 2.48.0-1
+- Update to 2.48.0
+
+* Thu Mar 17 2016 Richard Hughes <rhughes@redhat.com> - 2.47.92-1
+- Update to 2.47.92
+
+* Wed Feb 24 2016 Colin Walters <walters@redhat.com> - 2.47.6.19.gad2092b-2
+- git snapshot to work around https://bugzilla.gnome.org/show_bug.cgi?id=762637
+- Add --with-python=/usr/bin/python explicitly to hopefully fix a weird
+  issue I am seeing where librepo fails to build in epel7 with this due to
+  us requiring /bin/python.
+
+* Wed Feb 17 2016 Richard Hughes <rhughes@redhat.com> - 2.47.6-1
+- Update to 2.47.6
+
+* Wed Feb 03 2016 Fedora Release Engineering <releng@fedoraproject.org> - 2.47.5-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Tue Jan 19 2016 David King <amigadave@amigadave.com> - 2.47.5-1
+- Update to 2.47.5
+
+* Wed Dec 16 2015 Kalev Lember <klember@redhat.com> - 2.47.4-1
+- Update to 2.47.4
+
+* Wed Nov 25 2015 Kalev Lember <klember@redhat.com> - 2.47.3-1
+- Update to 2.47.3
+
+* Wed Nov 25 2015 Kalev Lember <klember@redhat.com> - 2.47.2-1
+- Update to 2.47.2
+
+* Mon Nov 09 2015 Kevin Fenzi <kevin@scrye.com> - 2.47.1-2
+- Add full path redirect output to null and || : to triggers.
+
+* Wed Oct 28 2015 Kalev Lember <klember@redhat.com> - 2.47.1-1
+- Update to 2.47.1
+
+* Mon Oct 19 2015 Kalev Lember <klember@redhat.com> - 2.46.1-2
+- Backport an upstream fix for app launching under wayland (#1273146)
+
+* Wed Oct 14 2015 Kalev Lember <klember@redhat.com> - 2.46.1-1
+- Update to 2.46.1
+
+* Mon Sep 21 2015 Kalev Lember <klember@redhat.com> - 2.46.0-1
+- Update to 2.46.0
+
+* Mon Sep 14 2015 Kalev Lember <klember@redhat.com> - 2.45.8-1
+- Update to 2.45.8
+
+* Tue Sep 01 2015 Kalev Lember <klember@redhat.com> - 2.45.7-1
+- Update to 2.45.7
+
+* Wed Aug 19 2015 Kalev Lember <klember@redhat.com> - 2.45.6-1
+- Update to 2.45.6
+
+* Wed Aug 19 2015 Kalev Lember <klember@redhat.com> - 2.45.5-1
+- Update to 2.45.5
+
+* Fri Aug 14 2015 Matthias Clasen <mclasen@redhat.com> - 2.45.4-2
+- Add file triggers for gio modules and gsettings schemas
+
+* Tue Jul 21 2015 David King <amigadave@amigadave.com> - 2.45.4-1
+- Update to 2.45.4
+
+* Wed Jun 24 2015 Kalev Lember <klember@redhat.com> - 2.45.3-2
+- Backport a patch to fix notification withdrawing in gnome-software
+
+* Wed Jun 24 2015 David King <amigadave@amigadave.com> - 2.45.3-1
+- Update to 2.45.3
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.45.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Tue May 26 2015 David King <amigadave@amigadave.com> - 2.45.2-1
+- Update to 2.45.2
+
+* Thu Apr 30 2015 Kalev Lember <kalevlember@gmail.com> - 2.45.1-1
+- Update to 2.45.1
+
+* Mon Mar 23 2015 Kalev Lember <kalevlember@gmail.com> - 2.44.0-1
+- Update to 2.44.0
+
+* Tue Mar 17 2015 Kalev Lember <kalevlember@gmail.com> - 2.43.92-1
+- Update to 2.43.92
+
+* Mon Mar 02 2015 Kalev Lember <kalevlember@gmail.com> - 2.43.91-1
+- Update to 2.43.91
+
+* Sat Feb 21 2015 Till Maas <opensource@till.name> - 2.43.90-2
+- Rebuilt for Fedora 23 Change
+  https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code
+
+* Wed Feb 18 2015 David King <amigadave@amigadave.com> - 2.43.90-1
+- Update to 2.43.90
+- Update man pages glob in files section
+
+* Tue Feb 10 2015 Matthias Clasen <mclasen@redhat.com> - 2.43.4-1
+- Update to 2.43.4
+
+* Tue Jan 20 2015 David King <amigadave@amigadave.com> - 2.43.3-1
+- Update to 2.43.3
+
+* Wed Dec 17 2014 Kalev Lember <kalevlember@gmail.com> - 2.43.2-1
+- Update to 2.43.2
+
+* Tue Nov 25 2014 Kalev Lember <kalevlember@gmail.com> - 2.43.1-1
+- Update to 2.43.1
+
+* Thu Oct 30 2014 Florian Müllner <fmuellner@redhat.com> - 2.43.0-1
+- Update to 2.43.0
+
+* Mon Sep 22 2014 Kalev Lember <kalevlember@gmail.com> - 2.42.0-1
+- Update to 2.42.0
+
+* Tue Sep 16 2014 Kalev Lember <kalevlember@gmail.com> - 2.41.5-1
+- Update to 2.41.5
+
+* Thu Sep  4 2014 Matthias Clasen <mclasen@redhat.com> 2.41.4-3
+- Don't remove rpath from gdbus-peer test - it doesn't work without it
+
+* Thu Sep 04 2014 Bastien Nocera <bnocera@redhat.com> 2.41.4-2
+- Fix banshee getting selected as the default movie player
+
+* Tue Sep 02 2014 Kalev Lember <kalevlember@gmail.com> - 2.41.4-1
+- Update to 2.41.4
+
+* Sat Aug 16 2014 Kalev Lember <kalevlember@gmail.com> - 2.41.3-1
+- Update to 2.41.3
+
+* Sat Aug 16 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.41.2-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Wed Jul 23 2014 Stef Walter <stefw@redhat.com> - 2.41.2-2
+- Fix regression with GDBus array encoding rhbz#1122128
+
+* Mon Jul 14 2014 Kalev Lember <kalevlember@gmail.com> - 2.41.2-1
+- Update to 2.41.2
+
+* Sat Jul 12 2014 Tom Callaway <spot@fedoraproject.org> - 2.41.1-2
+- fix license handling
+
+* Tue Jun 24 2014 Richard Hughes <rhughes@redhat.com> - 2.41.1-1
+- Update to 2.41.1
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.41.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Tue May 27 2014 Kalev Lember <kalevlember@gmail.com> - 2.41.0-1
+- Update to 2.41.0
+
+* Mon Mar 24 2014 Richard Hughes <rhughes@redhat.com> - 2.40.0-1
+- Update to 2.40.0
+
+* Tue Mar 18 2014 Richard Hughes <rhughes@redhat.com> - 2.39.92-1
+- Update to 2.39.92
+
+* Tue Mar 04 2014 Richard Hughes <rhughes@redhat.com> - 2.39.91-1
+- Update to 2.39.91
+
+* Tue Feb 18 2014 Richard Hughes <rhughes@redhat.com> - 2.39.90-1
+- Update to 2.39.90
+
+* Tue Feb 04 2014 Richard Hughes <rhughes@redhat.com> - 2.39.4-1
+- Update to 2.39.4
+
+* Tue Jan 14 2014 Richard Hughes <rhughes@redhat.com> - 2.39.3-1
+- Update to 2.39.3
+
+* Sun Dec 22 2013 Richard W.M. Jones <rjones@redhat.com> - 2.39.2-2
+- Re-add static subpackage so that we can build static qemu as
+  an AArch64 binfmt.
+
+* Tue Dec 17 2013 Richard Hughes <rhughes@redhat.com> - 2.39.2-1
+- Update to 2.39.2
+
+* Mon Dec 09 2013 Richard Hughes <rhughes@redhat.com> - 2.39.1-2
+- Backport a patch from master to stop gnome-settings-daemon crashing.
+
+* Thu Nov 14 2013 Richard Hughes <rhughes@redhat.com> - 2.39.1-1
+- Update to 2.39.1
+
+* Mon Oct 28 2013 Richard Hughes <rhughes@redhat.com> - 2.39.0-1
+- Update to 2.39.0
+
+* Tue Sep 24 2013 Kalev Lember <kalevlember@gmail.com> - 2.38.0-1
+- Update to 2.38.0
+
+* Tue Sep 17 2013 Kalev Lember <kalevlember@gmail.com> - 2.37.93-1
+- Update to 2.37.93
+
+* Mon Sep 02 2013 Kalev Lember <kalevlember@gmail.com> - 2.37.7-1
+- Update to 2.37.7
+
+* Wed Aug 21 2013 Debarshi Ray <rishi@fedoraproject.org> - 2.37.6-1
+- Update to 2.37.6
+
+* Sat Aug 03 2013 Petr Pisar <ppisar@redhat.com> - 2.37.5-2
+- Perl 5.18 rebuild
+
+* Thu Aug  1 2013 Debarshi Ray <rishi@fedoraproject.org> - 2.37.5-1
+- Update to 2.37.5
+
+* Wed Jul 17 2013 Petr Pisar <ppisar@redhat.com> - 2.37.4-2
+- Perl 5.18 rebuild
+
+* Tue Jul  9 2013 Matthias Clasen <mclasen@redhat.com> - 2.37.4-1
+- Update to 2.37.4
+
+* Thu Jun 20 2013 Debarshi Ray <rishi@fedoraproject.org> - 2.37.2-1
+- Update to 2.37.2
+
+* Tue May 28 2013 Matthias Clasen <mclasen@redhat.com> - 2.37.1-1
+- Update to 2.37.1
+- Add a tests subpackage
+
+* Sat May 04 2013 Kalev Lember <kalevlember@gmail.com> - 2.37.0-1
+- Update to 2.37.0
+
+* Sat Apr 27 2013 Thorsten Leemhuis <fedora@leemhuis.info> - 2.36.1-2
+- Fix pidgin freezes by applying patch from master (#956872)
+
+* Mon Apr 15 2013 Kalev Lember <kalevlember@gmail.com> - 2.36.1-1
+- Update to 2.36.1
+
+* Mon Mar 25 2013 Kalev Lember <kalevlember@gmail.com> - 2.36.0-1
+- Update to 2.36.0
+
+* Tue Mar 19 2013 Matthias Clasen <mclasen@redhat.com> - 2.35.9-1
+- Update to 2.35.9
+
+* Thu Feb 21 2013 Kalev Lember <kalevlember@gmail.com> - 2.35.8-1
+- Update to 2.35.8
+
+* Tue Feb 05 2013 Kalev Lember <kalevlember@gmail.com> - 2.35.7-1
+- Update to 2.35.7
+
+* Tue Jan 15 2013 Matthias Clasen <mclasen@redhat.com> - 2.35.4-1
+- Update to 2.35.4
+
+* Thu Dec 20 2012 Kalev Lember <kalevlember@gmail.com> - 2.35.3-1
+- Update to 2.35.3
+
+* Sat Nov 24 2012 Kalev Lember <kalevlember@gmail.com> - 2.35.2-1
+- Update to 2.35.2
+
+* Thu Nov 08 2012 Kalev Lember <kalevlember@gmail.com> - 2.35.1-1
+- Update to 2.35.1
+- Drop upstreamed codegen-in-datadir.patch
diff --git a/base/rx/rx-glib2/gnutls-hmac.patch b/base/rx/rx-glib2/gnutls-hmac.patch
new file mode 100644
index 0000000..4b1ba0e
--- /dev/null
+++ b/base/rx/rx-glib2/gnutls-hmac.patch
@@ -0,0 +1,1086 @@
+From ff90bb8474b1e724727f4014b446e7c851e609bd Mon Sep 17 00:00:00 2001
+From: Colin Walters <walters@verbum.org>
+Date: Fri, 7 Jun 2019 18:44:43 +0000
+Subject: [PATCH 1/4] ghmac: Split off wrapper functions into ghmac-utils.c
+
+Prep for adding a GnuTLS HMAC implementation; these are just
+utility functions that call the "core" API.
+---
+ glib/ghmac-utils.c | 145 +++++++++++++++++++++++++++++++++++++++++++++
+ glib/ghmac.c       | 112 ----------------------------------
+ glib/meson.build   |   1 +
+ 3 files changed, 146 insertions(+), 112 deletions(-)
+ create mode 100644 glib/ghmac-utils.c
+
+diff --git a/glib/ghmac-utils.c b/glib/ghmac-utils.c
+new file mode 100644
+index 000000000..a17359ff1
+--- /dev/null
++++ b/glib/ghmac-utils.c
+@@ -0,0 +1,145 @@
++/* ghmac.h - data hashing functions
++ *
++ * Copyright (C) 2011  Collabora Ltd.
++ * Copyright (C) 2019  Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "config.h"
++
++#include <string.h>
++
++#include "ghmac.h"
++
++#include "glib/galloca.h"
++#include "gatomic.h"
++#include "gslice.h"
++#include "gmem.h"
++#include "gstrfuncs.h"
++#include "gtestutils.h"
++#include "gtypes.h"
++#include "glibintl.h"
++
++/**
++ * g_compute_hmac_for_data:
++ * @digest_type: a #GChecksumType to use for the HMAC
++ * @key: (array length=key_len): the key to use in the HMAC
++ * @key_len: the length of the key
++ * @data: (array length=length): binary blob to compute the HMAC of
++ * @length: length of @data
++ *
++ * Computes the HMAC for a binary @data of @length. This is a
++ * convenience wrapper for g_hmac_new(), g_hmac_get_string()
++ * and g_hmac_unref().
++ *
++ * The hexadecimal string returned will be in lower case.
++ *
++ * Returns: the HMAC of the binary data as a string in hexadecimal.
++ *   The returned string should be freed with g_free() when done using it.
++ *
++ * Since: 2.30
++ */
++gchar *
++g_compute_hmac_for_data (GChecksumType  digest_type,
++                         const guchar  *key,
++                         gsize          key_len,
++                         const guchar  *data,
++                         gsize          length)
++{
++  GHmac *hmac;
++  gchar *retval;
++
++  g_return_val_if_fail (length == 0 || data != NULL, NULL);
++
++  hmac = g_hmac_new (digest_type, key, key_len);
++  if (!hmac)
++    return NULL;
++
++  g_hmac_update (hmac, data, length);
++  retval = g_strdup (g_hmac_get_string (hmac));
++  g_hmac_unref (hmac);
++
++  return retval;
++}
++
++/**
++ * g_compute_hmac_for_bytes:
++ * @digest_type: a #GChecksumType to use for the HMAC
++ * @key: the key to use in the HMAC
++ * @data: binary blob to compute the HMAC of
++ *
++ * Computes the HMAC for a binary @data. This is a
++ * convenience wrapper for g_hmac_new(), g_hmac_get_string()
++ * and g_hmac_unref().
++ *
++ * The hexadecimal string returned will be in lower case.
++ *
++ * Returns: the HMAC of the binary data as a string in hexadecimal.
++ *   The returned string should be freed with g_free() when done using it.
++ *
++ * Since: 2.50
++ */
++gchar *
++g_compute_hmac_for_bytes (GChecksumType  digest_type,
++                          GBytes        *key,
++                          GBytes        *data)
++{
++  gconstpointer byte_data;
++  gsize length;
++  gconstpointer key_data;
++  gsize key_len;
++
++  g_return_val_if_fail (data != NULL, NULL);
++  g_return_val_if_fail (key != NULL, NULL);
++
++  byte_data = g_bytes_get_data (data, &length);
++  key_data = g_bytes_get_data (key, &key_len);
++  return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length);
++}
++
++
++/**
++ * g_compute_hmac_for_string:
++ * @digest_type: a #GChecksumType to use for the HMAC
++ * @key: (array length=key_len): the key to use in the HMAC
++ * @key_len: the length of the key
++ * @str: the string to compute the HMAC for
++ * @length: the length of the string, or -1 if the string is nul-terminated
++ *
++ * Computes the HMAC for a string.
++ *
++ * The hexadecimal string returned will be in lower case.
++ *
++ * Returns: the HMAC as a hexadecimal string.
++ *     The returned string should be freed with g_free()
++ *     when done using it.
++ *
++ * Since: 2.30
++ */
++gchar *
++g_compute_hmac_for_string (GChecksumType  digest_type,
++                           const guchar  *key,
++                           gsize          key_len,
++                           const gchar   *str,
++                           gssize         length)
++{
++  g_return_val_if_fail (length == 0 || str != NULL, NULL);
++
++  if (length < 0)
++    length = strlen (str);
++
++  return g_compute_hmac_for_data (digest_type, key, key_len,
++                                  (const guchar *) str, length);
++}
+diff --git a/glib/ghmac.c b/glib/ghmac.c
+index 49fd272f0..4f181f21f 100644
+--- a/glib/ghmac.c
++++ b/glib/ghmac.c
+@@ -329,115 +329,3 @@ g_hmac_get_digest (GHmac  *hmac,
+   g_checksum_update (hmac->digesto, buffer, len);
+   g_checksum_get_digest (hmac->digesto, buffer, digest_len);
+ }
+-
+-/**
+- * g_compute_hmac_for_data:
+- * @digest_type: a #GChecksumType to use for the HMAC
+- * @key: (array length=key_len): the key to use in the HMAC
+- * @key_len: the length of the key
+- * @data: (array length=length): binary blob to compute the HMAC of
+- * @length: length of @data
+- *
+- * Computes the HMAC for a binary @data of @length. This is a
+- * convenience wrapper for g_hmac_new(), g_hmac_get_string()
+- * and g_hmac_unref().
+- *
+- * The hexadecimal string returned will be in lower case.
+- *
+- * Returns: the HMAC of the binary data as a string in hexadecimal.
+- *   The returned string should be freed with g_free() when done using it.
+- *
+- * Since: 2.30
+- */
+-gchar *
+-g_compute_hmac_for_data (GChecksumType  digest_type,
+-                         const guchar  *key,
+-                         gsize          key_len,
+-                         const guchar  *data,
+-                         gsize          length)
+-{
+-  GHmac *hmac;
+-  gchar *retval;
+-
+-  g_return_val_if_fail (length == 0 || data != NULL, NULL);
+-
+-  hmac = g_hmac_new (digest_type, key, key_len);
+-  if (!hmac)
+-    return NULL;
+-
+-  g_hmac_update (hmac, data, length);
+-  retval = g_strdup (g_hmac_get_string (hmac));
+-  g_hmac_unref (hmac);
+-
+-  return retval;
+-}
+-
+-/**
+- * g_compute_hmac_for_bytes:
+- * @digest_type: a #GChecksumType to use for the HMAC
+- * @key: the key to use in the HMAC
+- * @data: binary blob to compute the HMAC of
+- *
+- * Computes the HMAC for a binary @data. This is a
+- * convenience wrapper for g_hmac_new(), g_hmac_get_string()
+- * and g_hmac_unref().
+- *
+- * The hexadecimal string returned will be in lower case.
+- *
+- * Returns: the HMAC of the binary data as a string in hexadecimal.
+- *   The returned string should be freed with g_free() when done using it.
+- *
+- * Since: 2.50
+- */
+-gchar *
+-g_compute_hmac_for_bytes (GChecksumType  digest_type,
+-                          GBytes        *key,
+-                          GBytes        *data)
+-{
+-  gconstpointer byte_data;
+-  gsize length;
+-  gconstpointer key_data;
+-  gsize key_len;
+-
+-  g_return_val_if_fail (data != NULL, NULL);
+-  g_return_val_if_fail (key != NULL, NULL);
+-
+-  byte_data = g_bytes_get_data (data, &length);
+-  key_data = g_bytes_get_data (key, &key_len);
+-  return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length);
+-}
+-
+-
+-/**
+- * g_compute_hmac_for_string:
+- * @digest_type: a #GChecksumType to use for the HMAC
+- * @key: (array length=key_len): the key to use in the HMAC
+- * @key_len: the length of the key
+- * @str: the string to compute the HMAC for
+- * @length: the length of the string, or -1 if the string is nul-terminated
+- *
+- * Computes the HMAC for a string.
+- *
+- * The hexadecimal string returned will be in lower case.
+- *
+- * Returns: the HMAC as a hexadecimal string.
+- *     The returned string should be freed with g_free()
+- *     when done using it.
+- *
+- * Since: 2.30
+- */
+-gchar *
+-g_compute_hmac_for_string (GChecksumType  digest_type,
+-                           const guchar  *key,
+-                           gsize          key_len,
+-                           const gchar   *str,
+-                           gssize         length)
+-{
+-  g_return_val_if_fail (length == 0 || str != NULL, NULL);
+-
+-  if (length < 0)
+-    length = strlen (str);
+-
+-  return g_compute_hmac_for_data (digest_type, key, key_len,
+-                                  (const guchar *) str, length);
+-}
+diff --git a/glib/meson.build b/glib/meson.build
+index 8c18e6de4..329b8d197 100644
+--- a/glib/meson.build
++++ b/glib/meson.build
+@@ -253,6 +253,7 @@ glib_sources = files(
+   'ggettext.c',
+   'ghash.c',
+   'ghmac.c',
++  'ghmac-utils.c',
+   'ghook.c',
+   'ghostutils.c',
+   'giochannel.c',
+-- 
+2.31.1
+
+From 5395d36e6685e0b7377794c59c5820970bb472ef Mon Sep 17 00:00:00 2001
+From: Colin Walters <walters@verbum.org>
+Date: Fri, 7 Jun 2019 19:36:54 +0000
+Subject: [PATCH 2/4] Add a gnutls backend for GHmac
+
+For RHEL we want apps to use FIPS-certified crypto libraries,
+and HMAC apparently counts as "keyed" and hence needs to
+be validated.
+
+Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1630260
+Replaces: https://gitlab.gnome.org/GNOME/glib/merge_requests/897
+
+This is a build-time option that backs the GHmac API with GnuTLS.
+Most distributors ship glib-networking built with GnuTLS, and
+most apps use glib-networking, so this isn't a net-new library
+in most cases.
+
+=======================================================================
+
+mcatanzaro note:
+
+I've updated Colin's original patch with several enhancements:
+
+Implement g_hmac_copy() using gnutls_hmac_copy(), which didn't exist
+when Colin developed this patch.
+
+Removed use of GSlice
+
+Better error checking in g_hmac_new(). It is possible for
+gnutls_hmac_init() to fail if running in FIPS mode and an MD5 digest is
+requested. In this case, we should return NULL rather than returning a
+broken GHmac with a NULL gnutls_hmac_hd_t. This was leading to a later
+null pointer dereference inside gnutls_hmac_update(). Applications are
+responsible for checking to ensure the return value of g_hmac_new() is
+not NULL since it is annotated as nullable. Added documentation to
+indicate this possibility.
+
+Properly handle length -1 in g_hmac_update(). This means we've been
+given a NUL-terminated string and should use strlen(). GnuTLS doesn't
+accept -1, so let's call strlen() ourselves.
+
+Crash the application with g_error() if gnutls_hmac() fails for any
+reason. This is necessary because g_hmac_update() is not fallible, so we
+have no way to indicate error. Crashing seems better than returning the
+wrong result later when g_hmac_get_string() or g_hmac_get_digest() is
+later called. (Those functions are also not fallible.) Fortunately, I
+don't think this error should actually be hit in practice.
+
+https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903
+---
+ glib/gchecksum.c        |   9 +-
+ glib/gchecksumprivate.h |  32 +++++++
+ glib/ghmac-gnutls.c     | 187 ++++++++++++++++++++++++++++++++++++++++
+ glib/ghmac.c            |  15 ++++
+ glib/meson.build        |  10 ++-
+ meson.build             |   7 ++
+ meson_options.txt       |   7 +-
+ 7 files changed, 260 insertions(+), 7 deletions(-)
+ create mode 100644 glib/gchecksumprivate.h
+ create mode 100644 glib/ghmac-gnutls.c
+
+diff --git a/glib/gchecksum.c b/glib/gchecksum.c
+index 29b479bc6..929958c3a 100644
+--- a/glib/gchecksum.c
++++ b/glib/gchecksum.c
+@@ -20,7 +20,7 @@
+ 
+ #include <string.h>
+ 
+-#include "gchecksum.h"
++#include "gchecksumprivate.h"
+ 
+ #include "gslice.h"
+ #include "gmem.h"
+@@ -173,9 +173,9 @@ sha_byte_reverse (guint32 *buffer,
+ }
+ #endif /* G_BYTE_ORDER == G_BIG_ENDIAN */
+ 
+-static gchar *
+-digest_to_string (guint8 *digest,
+-                  gsize   digest_len)
++gchar *
++gchecksum_digest_to_string (guint8 *digest,
++                            gsize   digest_len)
+ {
+   gsize i, len = digest_len * 2;
+   gchar *retval;
+@@ -194,6 +194,7 @@ digest_to_string (guint8 *digest,
+ 
+   return retval;
+ }
++#define digest_to_string gchecksum_digest_to_string
+ 
+ /*
+  * MD5 Checksum
+diff --git a/glib/gchecksumprivate.h b/glib/gchecksumprivate.h
+new file mode 100644
+index 000000000..86c7a3b61
+--- /dev/null
++++ b/glib/gchecksumprivate.h
+@@ -0,0 +1,32 @@
++/* gstdioprivate.h - Private GLib stdio functions
++ *
++ * Copyright 2017 Руслан Ижбулатов
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#ifndef __G_CHECKSUMPRIVATE_H__
++#define __G_CHECKSUMPRIVATE_H__
++
++#include "gchecksum.h"
++
++G_BEGIN_DECLS
++
++gchar *
++gchecksum_digest_to_string (guint8 *digest,
++                            gsize   digest_len);
++
++G_END_DECLS
++
++#endif
+\ No newline at end of file
+diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c
+new file mode 100644
+index 000000000..9fb775f89
+--- /dev/null
++++ b/glib/ghmac-gnutls.c
+@@ -0,0 +1,187 @@
++/* ghmac.h - data hashing functions
++ *
++ * Copyright (C) 2011  Collabora Ltd.
++ * Copyright (C) 2019  Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this library; if not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include "config.h"
++
++#include <string.h>
++#include <gnutls/crypto.h>
++
++#include "ghmac.h"
++
++#include "glib/galloca.h"
++#include "gatomic.h"
++#include "gslice.h"
++#include "gmem.h"
++#include "gstrfuncs.h"
++#include "gchecksumprivate.h"
++#include "gtestutils.h"
++#include "gtypes.h"
++#include "glibintl.h"
++
++#ifndef HAVE_GNUTLS
++#error "build configuration error"
++#endif
++
++struct _GHmac
++{
++  int ref_count;
++  GChecksumType digest_type;
++  gnutls_hmac_hd_t hmac;
++  gchar *digest_str;
++};
++
++GHmac *
++g_hmac_new (GChecksumType  digest_type,
++            const guchar  *key,
++            gsize          key_len)
++{
++  gnutls_mac_algorithm_t algo;
++  GHmac *hmac = g_new0 (GHmac, 1);
++  int ret;
++
++  hmac->ref_count = 1;
++  hmac->digest_type = digest_type;
++
++  switch (digest_type)
++    {
++    case G_CHECKSUM_MD5:
++      algo = GNUTLS_MAC_MD5;
++      break;
++    case G_CHECKSUM_SHA1:
++      algo = GNUTLS_MAC_SHA1;
++      break;
++    case G_CHECKSUM_SHA256:
++      algo = GNUTLS_MAC_SHA256;
++      break;
++    case G_CHECKSUM_SHA384:
++      algo = GNUTLS_MAC_SHA384;
++      break;
++    case G_CHECKSUM_SHA512:
++      algo = GNUTLS_MAC_SHA512;
++      break;
++    default:
++      g_free (hmac);
++      g_return_val_if_reached (NULL);
++    }
++
++  ret = gnutls_hmac_init (&hmac->hmac, algo, key, key_len);
++  if (ret != 0)
++    {
++      /* There is no way to report an error here, but one possible cause of
++       * failure is that the requested digest may be disabled by FIPS mode.
++       */
++      g_free (hmac);
++      return NULL;
++    }
++
++  return hmac;
++}
++
++GHmac *
++g_hmac_copy (const GHmac *hmac)
++{
++  GHmac *copy;
++
++  g_return_val_if_fail (hmac != NULL, NULL);
++
++  copy = g_new0 (GHmac, 1);
++  copy->ref_count = 1;
++  copy->digest_type = hmac->digest_type;
++  copy->hmac = gnutls_hmac_copy (hmac->hmac);
++
++  /* g_hmac_copy is not allowed to fail, so we'll have to crash on error. */
++  if (!copy->hmac)
++    g_error ("gnutls_hmac_copy failed");
++
++  return copy;
++}
++
++GHmac *
++g_hmac_ref (GHmac *hmac)
++{
++  g_return_val_if_fail (hmac != NULL, NULL);
++
++  g_atomic_int_inc (&hmac->ref_count);
++
++  return hmac;
++}
++
++void
++g_hmac_unref (GHmac *hmac)
++{
++  g_return_if_fail (hmac != NULL);
++
++  if (g_atomic_int_dec_and_test (&hmac->ref_count))
++    {
++      gnutls_hmac_deinit (hmac->hmac, NULL);
++      g_free (hmac->digest_str);
++      g_free (hmac);
++    }
++}
++
++
++void
++g_hmac_update (GHmac        *hmac,
++               const guchar *data,
++               gssize        length)
++{
++  int ret;
++
++  g_return_if_fail (hmac != NULL);
++  g_return_if_fail (length == 0 || data != NULL);
++
++  if (length == -1)
++    length = strlen ((const char *)data);
++
++  /* g_hmac_update is not allowed to fail, so we'll have to crash on error. */
++  ret = gnutls_hmac (hmac->hmac, data, length);
++  if (ret != 0)
++    g_error ("gnutls_hmac failed: %s", gnutls_strerror (ret));
++}
++
++const gchar *
++g_hmac_get_string (GHmac *hmac)
++{
++  guint8 *buffer;
++  gsize digest_len;
++
++  g_return_val_if_fail (hmac != NULL, NULL);
++
++  if (hmac->digest_str)
++    return hmac->digest_str;
++
++  digest_len = g_checksum_type_get_length (hmac->digest_type);
++  buffer = g_alloca (digest_len);
++
++  gnutls_hmac_output (hmac->hmac, buffer);
++  hmac->digest_str = gchecksum_digest_to_string (buffer, digest_len);
++  return hmac->digest_str;
++}
++
++
++void
++g_hmac_get_digest (GHmac  *hmac,
++                   guint8 *buffer,
++                   gsize  *digest_len)
++{
++  g_return_if_fail (hmac != NULL);
++
++  gnutls_hmac_output (hmac->hmac, buffer);
++  *digest_len = g_checksum_type_get_length (hmac->digest_type);
++}
+diff --git a/glib/ghmac.c b/glib/ghmac.c
+index 4f181f21f..0e39ea40a 100644
+--- a/glib/ghmac.c
++++ b/glib/ghmac.c
+@@ -33,6 +33,9 @@
+ #include "gtypes.h"
+ #include "glibintl.h"
+ 
++#ifdef HAVE_GNUTLS
++#error "build configuration error"
++#endif
+ 
+ /**
+  * SECTION:hmac
+@@ -84,6 +87,18 @@ struct _GHmac
+  * Support for digests of type %G_CHECKSUM_SHA512 has been added in GLib 2.42.
+  * Support for %G_CHECKSUM_SHA384 was added in GLib 2.52.
+  *
++ * Note that #GHmac creation may fail, in which case this function will
++ * return %NULL. Since there is no error parameter, it is not possible
++ * to indicate why.
++ *
++ * In Fedora, CentOS Stream, and Red Hat Enterprise Linux, GLib is
++ * configured to use GnuTLS to implement #GHmac in order to support FIPS
++ * compliance. This introduces additional failure possibilities that are
++ * not present in upstream GLib. For example, the creation of a #GHmac
++ * will fail if @digest_type is %G_CHECKSUM_MD5 and the system is
++ * running in FIPS mode. #GHmac creation may also fail if GLib is unable
++ * to load GnuTLS.
++ *
+  * Returns: the newly created #GHmac, or %NULL.
+  *   Use g_hmac_unref() to free the memory allocated by it.
+  *
+diff --git a/glib/meson.build b/glib/meson.build
+index 329b8d197..2417de53d 100644
+--- a/glib/meson.build
++++ b/glib/meson.build
+@@ -252,7 +252,6 @@ glib_sources = files(
+   'gfileutils.c',
+   'ggettext.c',
+   'ghash.c',
+-  'ghmac.c',
+   'ghmac-utils.c',
+   'ghook.c',
+   'ghostutils.c',
+@@ -308,6 +307,7 @@ glib_sources = files(
+   'guriprivate.h',
+   'gutils.c',
+   'gutilsprivate.h',
++  'gchecksumprivate.h',
+   'guuid.c',
+   'gvariant.c',
+   'gvariant-core.c',
+@@ -352,6 +352,12 @@ else
+   glib_dtrace_hdr = []
+ endif
+ 
++if get_option('gnutls')
++  glib_sources += files('ghmac-gnutls.c')
++else
++  glib_sources += files('ghmac.c')
++endif
++
+ pcre_static_args = []
+ 
+ if use_pcre_static_flag
+@@ -378,7 +384,7 @@ libglib = library('glib-2.0',
+   # intl.lib is not compatible with SAFESEH
+   link_args : [noseh_link_args, glib_link_flags, win32_ldflags],
+   include_directories : configinc,
+-  dependencies : pcre_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep],
++  dependencies : pcre_deps + [thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep],
+   c_args : glib_c_args,
+   objc_args : glib_c_args,
+ )
+diff --git a/meson.build b/meson.build
+index e2eba1871..cca15f653 100644
+--- a/meson.build
++++ b/meson.build
+@@ -2090,6 +2090,13 @@ if host_system == 'linux'
+   glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found())
+ endif
+ 
++# gnutls is used optionally by ghmac
++libgnutls_dep = []
++if get_option('gnutls')
++  libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)]
++  glib_conf.set('HAVE_GNUTLS', 1)
++endif
++
+ if host_system == 'windows'
+   winsock2 = cc.find_library('ws2_32')
+ endif
+diff --git a/meson_options.txt b/meson_options.txt
+index 072765361..c8f26ac02 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -39,6 +39,11 @@ option('internal_pcre',
+        value : false,
+        description : 'whether to use internal PCRE')
+ 
++option('gnutls',
++       type : 'boolean',
++       value : false,
++       description : 'build with gnutls support')
++
+ option('man',
+        type : 'boolean',
+        value : false,
+-- 
+2.31.1
+
+From 61c175277acb8d1e080305acd444201c5ad1fb81 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Wed, 16 Jun 2021 20:35:00 -0500
+Subject: [PATCH 3/4] dlopen GnuTLS instead of linking directly
+
+I'd like to enable our GnuTLS GHmac patchset in Fedora in order to
+ensure it is receiving sufficient real-world testing, since we've
+discovered several bugs thus far. Problem is Fedora has one requirement
+that RHEL does not: it needs to build glib as a static lib. This is
+needed by QEMU in Fedora for complicated technical reasons that I don't
+understand. However, nothing in RHEL needs it. This means we failed to
+notice that glib2-static is broken in RHEL, because there is no
+gnutls-static! We could fix this by adding a gnutls-static package, but
+that seems like overkill, and adding more static libraries where they're
+not truly necessary is not the direction we want to move in anyway. So
+instead, let's just dlopen GnuTLS to sidestep this problem entirely.
+
+This would not be a good solution for upstream, but upstream has made
+clear that this patchset is already non-upstreamable, so it will be fine
+for our purposes.
+---
+ glib/ghmac-gnutls.c | 101 ++++++++++++++++++++++++++++++++++++++++++--
+ glib/ghmac.c        |   2 +-
+ glib/meson.build    |   2 +-
+ meson.build         |   6 +--
+ 4 files changed, 102 insertions(+), 9 deletions(-)
+
+diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c
+index 9fb775f89..1800fc2e0 100644
+--- a/glib/ghmac-gnutls.c
++++ b/glib/ghmac-gnutls.c
+@@ -19,8 +19,8 @@
+ 
+ #include "config.h"
+ 
++#include <dlfcn.h>
+ #include <string.h>
+-#include <gnutls/crypto.h>
+ 
+ #include "ghmac.h"
+ 
+@@ -31,13 +31,16 @@
+ #include "gstrfuncs.h"
+ #include "gchecksumprivate.h"
+ #include "gtestutils.h"
++#include "gthread.h"
+ #include "gtypes.h"
+ #include "glibintl.h"
+ 
+-#ifndef HAVE_GNUTLS
++#ifndef USE_GNUTLS
+ #error "build configuration error"
+ #endif
+ 
++typedef gpointer gnutls_hmac_hd_t;
++
+ struct _GHmac
+ {
+   int ref_count;
+@@ -46,15 +49,107 @@ struct _GHmac
+   gchar *digest_str;
+ };
+ 
++typedef enum
++{
++  GNUTLS_MAC_MD5 = 2,
++  GNUTLS_MAC_SHA1 = 3,
++  GNUTLS_MAC_SHA256 = 6,
++  GNUTLS_MAC_SHA384 = 7,
++  GNUTLS_MAC_SHA512 = 8,
++} gnutls_mac_algorithm_t;
++
++/* Why are we dlopening GnuTLS instead of linking to it directly? Because we
++ * want to be able to build GLib as a static library without depending on a
++ * static build of GnuTLS. QEMU depends on static linking with GLib, but Fedora
++ * does not ship a static build of GnuTLS, and this allows us to avoid changing
++ * that.
++ */
++static int              (*gnutls_hmac_init)   (gnutls_hmac_hd_t *dig, gnutls_mac_algorithm_t algorithm, const void *key, size_t keylen);
++static gnutls_hmac_hd_t (*gnutls_hmac_copy)   (gnutls_hmac_hd_t handle);
++static void             (*gnutls_hmac_deinit) (gnutls_hmac_hd_t handle, void *digest);
++static int              (*gnutls_hmac)        (gnutls_hmac_hd_t handle, const void *ptext, size_t ptext_len);
++static void             (*gnutls_hmac_output) (gnutls_hmac_hd_t handle, void *digest);
++static const char *     (*gnutls_strerror)    (int error);
++
++static gsize gnutls_initialize_attempted = 0;
++static gboolean gnutls_initialize_successful = FALSE;
++
++static void
++initialize_gnutls (void)
++{
++  gpointer libgnutls;
++
++  libgnutls = dlopen ("libgnutls.so.30", RTLD_LAZY | RTLD_GLOBAL);
++  if (!libgnutls)
++    {
++      g_warning ("Cannot use GHmac: failed to load libgnutls.so.30: %s", dlerror ());
++      return;
++    }
++
++  gnutls_hmac_init = dlsym (libgnutls, "gnutls_hmac_init");
++  if (!gnutls_hmac_init)
++    {
++      g_warning ("Cannot use GHmac: failed to load gnutls_hmac_init: %s", dlerror ());
++      return;
++    }
++
++  gnutls_hmac_copy = dlsym (libgnutls, "gnutls_hmac_copy");
++  if (!gnutls_hmac_copy)
++    {
++      g_warning ("Cannot use GHmac: failed to load gnutls_hmac_copy: %s", dlerror ());
++      return;
++    }
++
++  gnutls_hmac_deinit = dlsym (libgnutls, "gnutls_hmac_deinit");
++  if (!gnutls_hmac_deinit)
++    {
++      g_warning ("Cannot use GHmac: failed to load gnutls_hmac_deinit: %s", dlerror ());
++      return;
++    }
++
++  gnutls_hmac = dlsym (libgnutls, "gnutls_hmac");
++  if (!gnutls_hmac)
++    {
++      g_warning ("Cannot use GHmac: failed to load gnutls_hmac: %s", dlerror ());
++      return;
++    }
++
++  gnutls_hmac_output = dlsym (libgnutls, "gnutls_hmac_output");
++  if (!gnutls_hmac_output)
++    {
++      g_warning ("Cannot use GHmac: failed to load gnutls_hmac_output: %s", dlerror ());
++      return;
++    }
++
++  gnutls_strerror = dlsym (libgnutls, "gnutls_strerror");
++  if (!gnutls_strerror)
++    {
++      g_warning ("Cannot use GHmac: failed to load gnutls_strerror: %s", dlerror ());
++      return;
++    }
++
++  gnutls_initialize_successful = TRUE;
++}
++
+ GHmac *
+ g_hmac_new (GChecksumType  digest_type,
+             const guchar  *key,
+             gsize          key_len)
+ {
+   gnutls_mac_algorithm_t algo;
+-  GHmac *hmac = g_new0 (GHmac, 1);
++  GHmac *hmac;
+   int ret;
+ 
++  if (g_once_init_enter (&gnutls_initialize_attempted))
++    {
++      initialize_gnutls ();
++      g_once_init_leave (&gnutls_initialize_attempted, 1);
++    }
++
++  if (!gnutls_initialize_successful)
++    return NULL;
++
++  hmac = g_new0 (GHmac, 1);
+   hmac->ref_count = 1;
+   hmac->digest_type = digest_type;
+ 
+diff --git a/glib/ghmac.c b/glib/ghmac.c
+index 0e39ea40a..2d9be91b8 100644
+--- a/glib/ghmac.c
++++ b/glib/ghmac.c
+@@ -33,7 +33,7 @@
+ #include "gtypes.h"
+ #include "glibintl.h"
+ 
+-#ifdef HAVE_GNUTLS
++#ifdef USE_GNUTLS
+ #error "build configuration error"
+ #endif
+ 
+diff --git a/glib/meson.build b/glib/meson.build
+index 2417de53d..ba42951aa 100644
+--- a/glib/meson.build
++++ b/glib/meson.build
+@@ -384,7 +384,7 @@ libglib = library('glib-2.0',
+   # intl.lib is not compatible with SAFESEH
+   link_args : [noseh_link_args, glib_link_flags, win32_ldflags],
+   include_directories : configinc,
+-  dependencies : pcre_deps + [thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep],
++  dependencies : pcre_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep] + [libdl_dep],
+   c_args : glib_c_args,
+   objc_args : glib_c_args,
+ )
+diff --git a/meson.build b/meson.build
+index cca15f653..404ef1790 100644
+--- a/meson.build
++++ b/meson.build
+@@ -2090,11 +2090,9 @@ if host_system == 'linux'
+   glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found())
+ endif
+ 
+-# gnutls is used optionally by ghmac
+-libgnutls_dep = []
++# gnutls is used optionally by GHmac
+ if get_option('gnutls')
+-  libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)]
+-  glib_conf.set('HAVE_GNUTLS', 1)
++  glib_conf.set('USE_GNUTLS', 1)
+ endif
+ 
+ if host_system == 'windows'
+-- 
+2.31.1
+
+From 7d1d96311b6ecd4f90ebbdd6fc58d28e06a86887 Mon Sep 17 00:00:00 2001
+From: Michael Catanzaro <mcatanzaro@redhat.com>
+Date: Wed, 16 Jun 2021 20:46:24 -0500
+Subject: [PATCH 4/4] Add test for GHmac in FIPS mode
+
+This will test a few problems that we hit recently:
+
+g_hmac_copy() is broken, https://bugzilla.redhat.com/show_bug.cgi?id=1786538
+
+Crash in g_hmac_update() in FIPS mode, https://bugzilla.redhat.com/show_bug.cgi?id=1971533
+
+Crash when passing -1 length to g_hmac_update() (discovered in #1971533)
+
+We'll also test to ensure MD5 fails, and stop compiling the other MD5
+tests.
+---
+ glib/tests/hmac.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/glib/tests/hmac.c b/glib/tests/hmac.c
+index 3ac3206df..2fa447984 100644
+--- a/glib/tests/hmac.c
++++ b/glib/tests/hmac.c
+@@ -1,7 +1,10 @@
++#include "config.h"
++
+ #include <glib.h>
+ #include <string.h>
+ #include <stdlib.h>
+ 
++#ifndef USE_GNUTLS
+ /* HMAC-MD5 test vectors as per RFC 2202 */
+ 
+ /* Test 1 */
+@@ -81,6 +84,7 @@ guint8 key_md5_test7[] = {
+ guint8 result_md5_test7[] = {
+     0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee, 0x1f, 0xb1,
+     0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e };
++#endif
+ 
+ /* HMAC-SHA1, HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512 test vectors
+  * as per RFCs 2202 and 4868.
+@@ -299,6 +303,7 @@ typedef struct {
+   gconstpointer result;
+ } HmacCase;
+ 
++#ifndef USE_GNUTLS
+ HmacCase hmac_md5_tests[] = {
+   { G_CHECKSUM_MD5, key_md5_test1, 16, "Hi There", 8, result_md5_test1 },
+   { G_CHECKSUM_MD5, "Jefe", 4, "what do ya want for nothing?", 28,
+@@ -317,6 +322,7 @@ HmacCase hmac_md5_tests[] = {
+       73, result_md5_test7 },
+   { -1, NULL, 0, NULL, 0, NULL },
+ };
++#endif
+ 
+ HmacCase hmac_sha1_tests[] = {
+   { G_CHECKSUM_SHA1, key_sha_test1, 20, "Hi There", 8, result_sha1_test1 },
+@@ -493,11 +499,45 @@ test_hmac_for_bytes (void)
+   g_bytes_unref (data);
+ }
+ 
++#ifdef USE_GNUTLS
++static void
++test_gnutls_fips_mode (void)
++{
++  GHmac *hmac;
++  GHmac *copy;
++
++  /* No MD5 in FIPS mode. */
++  hmac = g_hmac_new (G_CHECKSUM_MD5, "abc123", sizeof ("abc123"));
++  g_assert_null (hmac);
++
++  /* SHA-256 should be good. */
++  hmac = g_hmac_new (G_CHECKSUM_SHA256, "abc123", sizeof ("abc123"));
++  g_assert_nonnull (hmac);
++
++  /* Ensure g_hmac_update() does not crash when called with -1. */
++  g_hmac_update (hmac, "You win again, gravity!", -1);
++
++  /* Ensure g_hmac_copy() does not crash. */
++  copy = g_hmac_copy (hmac);
++  g_assert_nonnull (hmac);
++  g_hmac_unref (hmac);
++
++  g_assert_cmpstr (g_hmac_get_string (copy), ==, "795ba6900bcb22e8ce65c2ec02db4e85697da921deb960ee3143bf88a4a60f83");
++  g_hmac_unref (copy);
++}
++#endif
++
+ int
+ main (int argc,
+     char **argv)
+ {
+   int i;
++
++#ifdef USE_GNUTLS
++  /* This has to happen before GnuTLS is dlopened. */
++  g_setenv ("GNUTLS_FORCE_FIPS_MODE", "1", FALSE);
++#endif
++
+   g_test_init (&argc, &argv, NULL);
+ 
+   for (i = 0 ; hmac_sha1_tests[i].key_len > 0 ; i++)
+@@ -532,6 +572,7 @@ main (int argc,
+       g_free (name);
+     }
+ 
++#ifndef USE_GNUTLS
+   for (i = 0 ; hmac_md5_tests[i].key_len > 0 ; i++)
+     {
+       gchar *name = g_strdup_printf ("/hmac/md5-%d", i + 1);
+@@ -539,6 +580,7 @@ main (int argc,
+         (void (*)(const void *)) test_hmac);
+       g_free (name);
+     }
++#endif
+ 
+   g_test_add_func ("/hmac/ref-unref", test_hmac_ref_unref);
+   g_test_add_func ("/hmac/copy", test_hmac_copy);
+@@ -546,5 +588,9 @@ main (int argc,
+   g_test_add_func ("/hmac/for-string", test_hmac_for_string);
+   g_test_add_func ("/hmac/for-bytes", test_hmac_for_bytes);
+ 
++#ifdef USE_GNUTLS
++  g_test_add_func ("/hmac/gnutls-fips-mode", test_gnutls_fips_mode);
++#endif
++
+   return g_test_run ();
+ }
+-- 
+2.31.1
diff --git a/base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch b/base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch
new file mode 100644
index 0000000..39298db
--- /dev/null
+++ b/base/rx/rx-gpgme/0001-don-t-add-extra-libraries-for-linking.patch
@@ -0,0 +1,47 @@
+From 07a8ac908cbadb22c344895ebf9cc00c6a8fd3f7 Mon Sep 17 00:00:00 2001
+From: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+Date: Wed, 29 Mar 2017 07:05:41 +0200
+Subject: [PATCH] don't add extra libraries for linking
+
+Signed-off-by: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+---
+ lang/cpp/src/GpgmeppConfig.cmake.in.in | 2 +-
+ src/gpgme-config.in                    | 6 +++---
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in
+index 73f5eaad..3104d715 100644
+--- a/lang/cpp/src/GpgmeppConfig.cmake.in.in
++++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in
+@@ -63,7 +63,7 @@ add_library(Gpgmepp SHARED IMPORTED)
+ 
+ set_target_properties(Gpgmepp PROPERTIES
+   INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/gpgme++;@resolved_includedir@"
+-  INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@;@LIBASSUAN_LIBS@"
++  INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@"
+   IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp@libsuffix@"
+ )
+ 
+diff --git a/src/gpgme-config.in b/src/gpgme-config.in
+index a4d152e1..6a854e4a 100644
+--- a/src/gpgme-config.in
++++ b/src/gpgme-config.in
+@@ -22,12 +22,12 @@ cflags="-I@includedir@"
+ libs="-L@libdir@"
+ 
+ # Network libraries.
+-assuan_cflags="@LIBASSUAN_CFLAGS@"
+-assuan_libs="@LIBASSUAN_LIBS@"
++#assuan_cflags="@LIBASSUAN_CFLAGS@"
++#assuan_libs="@LIBASSUAN_LIBS@"
+ 
+ # Configure libgpg-error.
+ gpg_error_cflags="@GPG_ERROR_CFLAGS@"
+-gpg_error_libs="@GPG_ERROR_LIBS@"
++#gpg_error_libs="@GPG_ERROR_LIBS@"
+ 
+ # Configure thread packages.
+ thread_modules=""
+-- 
+2.15.1
+
diff --git a/base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch b/base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch
new file mode 100644
index 0000000..c4f87c5
--- /dev/null
+++ b/base/rx/rx-gpgme/0001-fix-stupid-ax_python_devel.patch
@@ -0,0 +1,112 @@
+From b0eabea4b1232ee7f45d13b8add928d463f37444 Mon Sep 17 00:00:00 2001
+From: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+Date: Wed, 29 Mar 2017 07:13:35 +0200
+Subject: [PATCH] fix stupid ax_python_devel
+
+References: https://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=commit;h=883a2abd5af5c96be894d5ef7ee6e9a2b8e64307
+Signed-off-by: Igor Gnatenko <ignatenkobrain@fedoraproject.org>
+---
+ m4/ax_python_devel.m4 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+diff -up gpgme-1.22.0/configure.stupid gpgme-1.22.0/configure
+--- gpgme-1.22.0/configure.stupid	2023-08-21 09:46:33.000000000 +0200
++++ gpgme-1.22.0/configure	2023-10-10 12:31:08.317796779 +0200
+@@ -23374,7 +23374,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -24146,7 +24146,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -24918,7 +24918,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -25690,7 +25690,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -26462,7 +26462,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -27234,7 +27234,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -28006,7 +28006,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -28778,7 +28778,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -29550,7 +29550,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -30322,7 +30322,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
+@@ -31094,7 +31094,7 @@ variable to configure. See \`\`configure
+ 	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5
+ printf %s "checking for the distutils Python package... " >&6; }
+ 	ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+-	if test -z "$ac_distutils_result"; then
++	if test $? -eq 0; then
+ 		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+ printf "%s\n" "yes" >&6; }
+ 	else
diff --git a/base/rx/rx-gpgme/0002-setup_py_extra_opts.patch b/base/rx/rx-gpgme/0002-setup_py_extra_opts.patch
new file mode 100644
index 0000000..dbcd4a7
--- /dev/null
+++ b/base/rx/rx-gpgme/0002-setup_py_extra_opts.patch
@@ -0,0 +1,12 @@
+diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in
+index c0fc091..d567ecb 100644
+--- a/lang/python/Makefile.in
++++ b/lang/python/Makefile.in
+@@ -802,6 +802,7 @@ install-exec-local:
+ 	  --build-base="$$(basename "$${PYTHON}")-gpg" \
+ 	  install \
+ 	  --prefix "$(DESTDIR)$(prefix)" \
++	  $${SETUP_PY_EXTRA_OPTS:-} \
+ 	  --verbose ; \
+ 	done
+ 
diff --git a/base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch b/base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch
new file mode 100644
index 0000000..5588205
--- /dev/null
+++ b/base/rx/rx-gpgme/1001-qt-skip-test-remarks-for-gnupg2-2.4.patch
@@ -0,0 +1,58 @@
+diff --git a/lang/qt/tests/t-remarks.cpp b/lang/qt/tests/t-remarks.cpp
+index f9a901a..4b2f2ab 100644
+--- a/lang/qt/tests/t-remarks.cpp
++++ b/lang/qt/tests/t-remarks.cpp
+@@ -47,6 +47,12 @@
+ 
+ #include "t-support.h"
+ 
++#define SKIP_ON_2_4() do { \
++    if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.4.0") { \
++        QSKIP("The test does not work well with gnupg 2.4+."); \
++    } \
++} while (false)
++
+ using namespace QGpgME;
+ using namespace GpgME;
+ 
+@@ -105,6 +111,7 @@ private Q_SLOTS:
+ 
+     void testRemarkReplaceSingleUIDExportable()
+     {
++        SKIP_ON_2_4();
+         if (!loopbackSupported()) {
+             return;
+         }
+@@ -187,6 +194,7 @@ private Q_SLOTS:
+ 
+     void testMultipleRemarks()
+     {
++        SKIP_ON_2_4();
+         if (!loopbackSupported()) {
+             return;
+         }
+@@ -269,6 +277,7 @@ private Q_SLOTS:
+ 
+     void testRemarkReplaceSingleUID()
+     {
++        SKIP_ON_2_4();
+         if (!loopbackSupported()) {
+             return;
+         }
+@@ -350,6 +359,7 @@ private Q_SLOTS:
+ 
+     void testRemarkReplaceMultiUID()
+     {
++        SKIP_ON_2_4();
+         if (!loopbackSupported()) {
+             return;
+         }
+@@ -466,6 +476,8 @@ private:
+     QTemporaryDir mDir;
+ };
+ 
++#undef SKIP_ON_2_4
++
+ QTEST_MAIN(TestRemarks)
+ 
+ #include "t-remarks.moc"
diff --git a/base/rx/rx-gpgme/changelog b/base/rx/rx-gpgme/changelog
new file mode 100644
index 0000000..5ab404d
--- /dev/null
+++ b/base/rx/rx-gpgme/changelog
@@ -0,0 +1,380 @@
+* Fri Jun 16 2023 Marie Loise Nolden <loise@kde.org> - 1.20.0-1
+- Update to 1.20.0 and enable/add qt6 build for qgpgme. The qt5 and qt6
+  rpms have been split out into separate libs and -devel rpms for compatibility.
+
+* Tue Nov 02 2021 Frantisek Sumsal <frantisek@sumsal.cz> - 1.15.1-6
+- Fix build with glibc >=2.34 (RHBZ#1984691, RHBZ#1987561)
+
+* Thu Jul 22 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.15.1-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
+
+* Thu Jun 03 2021 Python Maint <python-maint@redhat.com> - 1.15.1-4
+- Rebuilt for Python 3.10
+
+* Wed Jun 02 2021 Miro Hrončok <mhroncok@redhat.com> - 1.15.1-3
+- Also remove RPATH from /usr/bin/gpgme-json
+
+* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.15.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Sun Jan 24 2021 Igor Raits <ignatenkobrain@fedoraproject.org> - 1.15.1-1
+- Update to 1.15.1
+
+* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.14.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Sat Jul 18 2020 Igor Raits <ignatenkobrain@fedoraproject.org> - 1.14.0-1
+- Update to 1.14.0
+
+* Fri May 22 2020 Miro Hrončok <mhroncok@redhat.com> - 1.13.1-8
+- Rebuilt for Python 3.9
+
+* Thu Apr 30 2020 Tomáš Mráz <tmraz@redhat.com> - 1.13.1-7
+- Fix FTBFS with gnupg-2.2.19 and above
+
+* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.13.1-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Thu Oct 03 2019 Miro Hrončok <mhroncok@redhat.com> - 1.13.1-5
+- Rebuilt for Python 3.8.0rc1 (#1748018)
+
+* Thu Aug 15 2019 Miro Hrončok <mhroncok@redhat.com> - 1.13.1-4
+- Rebuilt for Python 3.8
+
+* Sat Aug 10 2019 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.13.1-3
+- Set real VERSION
+
+* Sat Aug  3 2019 Peter Robinson <pbrobinson@fedoraproject.org> 1.13.1-2
+- Move .pc files to devel so the base library doesn't pull in devel packages
+
+* Mon Jul 29 18:46:42 CEST 2019 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.13.1-1
+- Update to 1.13.1
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.12.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Wed Jun 05 2019 Miro Hrončok <mhroncok@redhat.com> - 1.12.0-2
+- Subpackage python2-gpg has been removed
+  See https://fedoraproject.org/wiki/Changes/Mass_Python_2_Package_Removal
+
+* Sat Feb 16 2019 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.12.0-1
+- Update to 1.12.0
+
+* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.11.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.11.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Fri Jun 15 2018 Miro Hrončok <mhroncok@redhat.com> - 1.11.1-2
+- Rebuilt for Python 3.7
+
+* Fri Apr 20 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.11.1-1
+- Update to 1.11.1
+
+* Thu Apr 19 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.11.0-1
+- Update to 1.11.0
+
+* Tue Apr 17 2018 Jonathan Lebon <jonathan@jlebon.com> - 1.10.0-4
+- Backport patch to tweak STATUS_FAILURE handling
+
+* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.10.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Tue Jan 30 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.10.0-2
+- Switch to %%ldconfig_scriptlets
+
+* Wed Dec 13 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.10.0-1
+- Update to 1.10.0
+
+* Tue Nov 07 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.9.0-8
+- Use better Obsoletes for platform-python
+
+* Fri Nov 03 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.9.0-7
+- Remove platform-python subpackages
+
+* Thu Aug 10 2017 Petr Viktorin <pviktori@redhat.com> - 1.9.0-6
+- Add subpackage for platform-python (https://fedoraproject.org/wiki/Changes/Platform_Python_Stack)
+
+* Mon Aug 07 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.9.0-5
+- Remove BuildRequires: pth-devel, it is not needed for long time
+
+* Mon Aug 07 2017 Björn Esser <besser82@fedoraproject.org> - 1.9.0-4
+- Rebuilt for AutoReq cmake-filesystem
+
+* Wed Aug 02 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.9.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.9.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Wed Mar 29 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.9.0-1
+- Update to 1.9.0
+
+* Sat Feb 11 2017 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.8.0-12
+- Fix FTBFS
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.8.0-11
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Wed Jan 18 2017 Rex Dieter <rdieter@fedoraproject.org> - 1.8.0-10
+- patch out LIBASSUAN_LIBRARIES in cmake too
+
+* Wed Jan 18 2017 Rex Dieter <rdieter@fedoraproject.org> - 1.8.0-9
+- gpgmepp-devel: Requires: libassuan-devel
+
+* Mon Jan 16 2017 Rex Dieter <rdieter@fedoraproject.org> - 1.8.0-8
+- qgpgme-devel: Conflicts: kdepimlibs-devel < 4.14.10-17
+
+* Sun Jan 01 2017 Rex Dieter <rdieter@math.unl.edu> - 1.8.0-7
+- rename gpgme-pp to gpgmepp, simplify -devel deps
+
+* Sun Jan 01 2017 Rex Dieter <rdieter@math.unl.edu> - 1.8.0-6
+- backport upstream cmake-related fix
+
+* Thu Dec 22 2016 Miro Hrončok <mhroncok@redhat.com> - 1.8.0-5
+- Rebuild for Python 3.6
+
+* Sun Dec 11 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 1.8.0-4
+- Rename pythonX-gpgme into pythonX-gpg
+
+* Sun Dec 11 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 1.8.0-3
+- Add Qt and C++ subpackages
+
+* Sat Dec 10 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 1.8.0-2
+- Enable tests
+
+* Sat Dec 10 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com> - 1.8.0-1
+- Update to 1.8.0
+
+* Wed Sep 21 2016 Igor Gnatenko <ignatenko@redhat.com> - 1.7.0-1
+- Update to 1.7.0
+
+* Mon Jul 25 2016 Igor Gnatenko <ignatenko@redhat.com> - 1.6.0-3
+- Set min ver for libgpg-error
+
+* Mon Jul 25 2016 Igor Gnatenko <ignatenko@redhat.com> - 1.6.0-2
+- Backport patch for STATUS_KEY_CONSIDERED (RHBZ #1359521)
+
+* Wed Jul 13 2016 Igor Gnatenko <ignatenko@redhat.com> - 1.6.0-1
+- Update to 1.6.0 (RHBZ #1167656)
+
+* Wed Feb 03 2016 Fedora Release Engineering <releng@fedoraproject.org> - 1.4.3-7
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.3-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Sat Dec 06 2014 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.4.3-5
+- CVE-2014-3564, rhbz#1125170, gpgme-1.3.2-bufferoverflow.patch
+
+* Sat Aug 16 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.3-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Sat Jul 12 2014 Tom Callaway <spot@fedoraproject.org> - 1.4.3-3
+- fix license handling
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.4.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Wed Oct 09 2013 Rex Dieter <rdieter@fedoraproject.org> - 1.4.3-1
+- gpgme-1.4.3
+- cleanup .spec, trim changelog
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.2-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Tue Jul 09 2013 Karsten Hopp <karsten@redhat.com> 1.3.2-3
+- rebuild to fix some f20 dependency issues on PPC
+
+* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.2-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Tue Nov 20 2012 Frantisek Kluknavsky <fkluknav@redhat.com> - 1.3.2-2
+- minor spec cleanup
+
+* Wed Sep 26 2012 Tomas Mraz <tmraz@redhat.com> - 1.3.2-1
+- new upstream version
+- re-enable gpg tests (original patch by John Morris <john@zultron.com>)
+- quiet configure warning 'could not find g13'
+- there is no libgpgme-pth anymore
+
+* Thu Jul 19 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.0-9
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Sun Apr 22 2012 Rex Dieter <rdieter@fedoraproject.org> 1.3.0-8
+- -devel: make Requires: libgpg-error-devel arch'd
+- ensure gpgme-config wrapper is executable
+
+* Sun Apr 22 2012 Rex Dieter <rdieter@fedoraproject.org> 1.3.0-7
+- gpgme.h: fatal error: gpgme-i386.h: No such file or directory compilation terminated (#815116)
+
+* Wed Feb 15 2012 Simon Lukasik <slukasik@redhat.com> - 1.3.0-6
+- Resolve multilib conflict of gpgme-config (#341351)
+- Resolve multilib conflict of gpgme.h (#341351)
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.0-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Thu Mar 17 2011 Rex Dieter <rdieter@fedoraproject.org> - 1.3.0-4
+- gpgme-config: remove libassuan-related flags as threatened (#676954) 
+\
+* Sun Feb 13 2011 Rex Dieter <rdieter@fedoraproject.org> - 1.3.0-3
+- -devel: fix typo (broken dep)
+
+* Sat Feb 12 2011 Rex Dieter <rdieter@fedoraproject.org> - 1.3.0-2
+- BR: libassuan2-devel
+- gpgme-config outputs -lassuan (#676954)
+
+* Fri Feb 11 2011 Tomas Mraz <tmraz@redhat.com> - 1.3.0-1
+- new upstream version
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.2.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Wed Aug 18 2010 Tomas Mraz <tmraz@redhat.com> - 1.2.0-3
+- fix the condition for adding the -D_FILE_OFFSET_BITS...
+
+* Wed Aug 11 2010 Tomas Mraz <tmraz@redhat.com> - 1.2.0-2
+- add -D_FILE_OFFSET_BITS... to gpgme-config as appropriate (#621698)
+
+* Fri Jul 02 2010 Rex Dieter <rdieter@fedoraproject.org> - 1.2.0-1
+- gpgme-1.2.0 (#610984)
+
+* Sun Feb 14 2010 Rex Dieter <rdieter@fedoraproject.org> - 1.1.8-4
+- FTBFS gpgme-1.1.8-3.fc13: ImplicitDSOLinking (#564605)
+
+* Thu Nov 19 2009 Tomas Mraz <tmraz@redhat.com> - 1.1.8-3
+- Add buildrequires gnupg2-smime for the gpgsm
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.8-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Sat Jun 20 2009 Rex Dieter <rdieter@fedoraproject.org> - 1.1.8-1
+- gpgme-1.1.8
+- -devel: s/postun/preun/ info scriptlet
+
+* Wed Mar 11 2009 Rex Dieter <rdieter@fedoraproject.org> - 1.1.7-3
+- track shlib sonames closer, to highlight future abi/soname changes
+- _with_gpg macro, to potentially conditionalize gnupg vs gnupg2 defaults
+  for various os/releases (ie, fedora vs rhel)
+
+* Tue Feb 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.7-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Sat Oct 18 2008 Rex Dieter <rdieter@fedoraproject.org> 1.1.7-1
+- gpgme-1.1.7
+
+* Sun Feb 17 2008 Rex Dieter <rdieter@fedoraproject.org> 1.1.6-3
+- --with-gpg=%%_bindir/gpg2 (#432445)
+- drop Requires: gnupg (#432445)
+
+* Fri Feb 08 2008 Rex Dieter <rdieter@fedoraproject.org> 1.1.6-2 
+- respin (gcc43)
+
+* Fri Jan 04 2008 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.6-1
+- gpgme-1.1.6
+- multiarch conflicts in gpgme (#341351)
+
+* Sat Aug 25 2007 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.5-4
+- BR: gawk
+
+* Sat Aug 25 2007 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.5-3
+- respin (BuildID)
+
+* Thu Aug 09 2007 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.5-2
+- License: LGPLv2+
+
+* Mon Jul 09 2007 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.5-1
+- gpgme-1.1.5
+
+* Mon Mar 05 2007 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.4-1
+- gpgme-1.1.4
+
+* Sat Feb 03 2007 Rex Dieter <rdieter[AT]fedoraproject.org> 1.1.3-1
+- gpgme-1.1.3
+
+* Tue Oct 03 2006 Rex Dieter <rexdieter[AT]users.sf.net>
+- respin
+
+* Mon Sep 18 2006 Rex Dieter <rexdieter[AT]users.sf.net> 1.1.2-6
+- fix gpgme-config --thread=pthread --cflags
+
+* Tue Aug 29 2006 Rex Dieter <rexdieter[AT]users.sf.net> 1.1.2-5
+- fc6 respin
+
+* Mon Mar 6 2006 Rex Dieter <rexdieter[AT]users.sf.net> 1.1.2-4
+- add back support for gpgme-config --thread=pthread
+
+* Mon Mar 6 2006 Rex Dieter <rexdieter[AT]users.sf.net> 1.1.2-2
+- drop extraneous libs from gpgme-config
+
+* Fri Mar 3 2006 Rex Dieter <rexdieter[AT]users.sf.net> 1.1.2-1
+- 1.1.2
+- drop upstreamed gpgme-1.1.0-tests.patch
+
+* Wed Mar 1 2006 Rex Dieter <rexdieter[AT]users.sf.net>
+- fc5: gcc/glibc respin
+
+* Wed Nov 30 2005 Rex Dieter <rexdieter[AT]users.sf.net> - 1.1.0-3
+- (re)build against (newer) libksba/gnupg2
+
+* Thu Oct 06 2005 Rex Dieter <rexdieter[AT]users.sf.net> - 1.1.0-2
+- 1.1.0
+
+* Mon Aug  8 2005 Rex Dieter <rexdieter[AT]users.sf.net> - 1.0.3-1
+- 1.0.3
+- --disable-static
+
+* Thu May 12 2005 Michael Schwendt <mschwendt[AT]users.sf.net> - 1.0.2-3
+- rebuilt
+
+* Fri Mar 18 2005 Ville Skyttä <ville.skytta at iki.fi> - 1.0.2-2
+- Fix FC4 build.
+
+* Tue Feb  1 2005 Michael Schwendt <mschwendt[AT]users.sf.net> - 0:1.0.2-1
+- LGPL used here, and made summary more explicit.
+- Remove dirmngr dependency (gpgsm interfaces with it).
+- Obsolete cryptplug as gpgme >= 0.4.5 provides what we used cryptplug for.
+
+* Thu Jan 06 2005 Rex Dieter <rexdieter[AT]users.sf.net> 0:1.0.2-0.fdr.1
+- 1.0.2
+
+* Thu Oct 21 2004 Rex Dieter <rexdieter at sf.net> 0:1.0.0-0.fdr.1
+- 1.0.0
+- Requires: dirmngr
+
+* Tue Oct 19 2004 Rex Dieter <rexdieter at sf.net> 0:0.4.7-0.fdr.1
+- 0.4.7
+
+* Sun May  2 2004 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.3-0.fdr.3
+- Require %%{_bindir}/gpgsm instead of newpg.
+- Cosmetic spec file improvements.
+
+* Thu Oct 23 2003 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.3-0.fdr.2
+- Update description.
+
+* Tue Oct  7 2003 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.3-0.fdr.1
+- Update to 0.4.3.
+
+* Fri Aug 15 2003 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.2-0.fdr.1
+- Update to 0.4.2.
+- make check in the %%check section.
+
+* Thu Jul 10 2003 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.1-0.fdr.1
+- Update to 0.4.1.
+- Make -devel cooperate with --excludedocs.
+
+* Sat Apr 19 2003 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.0-0.fdr.2
+- BuildRequire pth-devel, fix missing epoch in -devel Requires (#169).
+- Save .spec in UTF-8.
+
+* Sat Mar 22 2003 Ville Skyttä <ville.skytta at iki.fi> - 0:0.4.0-0.fdr.1
+- Update to current Fedora guidelines.
+- Exclude %%{_libdir}/*.la.
+
+* Tue Feb 12 2003 Warren Togami <warren@togami.com> 0.4.0-1.fedora.3
+- info/dir temporary workaround
+
+* Sat Feb  8 2003 Ville Skyttä <ville.skytta at iki.fi> - 0.4.0-1.fedora.1
+- First Fedora release.
diff --git a/base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch b/base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch
new file mode 100644
index 0000000..bbf88de
--- /dev/null
+++ b/base/rx/rx-gpgme/gpgme-1.3.2-largefile.patch
@@ -0,0 +1,24 @@
+diff -up gpgme-1.3.2/src/gpgme-config.in.largefile gpgme-1.3.2/src/gpgme-config.in
+--- gpgme-1.3.2/src/gpgme-config.in.largefile	2012-09-26 10:10:37.882744198 +0200
++++ gpgme-1.3.2/src/gpgme-config.in	2012-09-26 10:16:02.558762827 +0200
+@@ -41,6 +41,10 @@ cflags_pthread=""
+ cflags_glib="@GLIB_CFLAGS@"
+ with_glib=
+ 
++if test "0@NEED__FILE_OFFSET_BITS@" -gt "0" ; then
++    cflags_lfs="-D_FILE_OFFSET_BITS=@NEED__FILE_OFFSET_BITS@"
++fi
++
+ output=""
+ 
+ usage()
+@@ -105,6 +109,9 @@ while test $# -gt 0; do
+ 	    exit 0
+ 	    ;;
+         --cflags)
++	    if test "x$cflags_lfs" != "x"; then
++		output="$output $cflags_lfs"
++	    fi
+             result=
+             tmp_c=
+             tmp_g=
diff --git a/base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch b/base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch
new file mode 100644
index 0000000..6188066
--- /dev/null
+++ b/base/rx/rx-gpgme/gpgme-downgrade-libgpg-error-version.patch
@@ -0,0 +1,24 @@
+diff -Naur a/configure b/configure
+--- a/configure	2025-02-10 16:01:00.000000000 +0600
++++ b/configure	2025-02-18 10:34:48.662943905 +0600
+@@ -3570,7 +3570,7 @@
+ GPGME_CONFIG_API_VERSION=1
+ ##############################################
+ 
+-NEED_GPG_ERROR_VERSION=1.47
++NEED_GPG_ERROR_VERSION=1.31
+ NEED_LIBASSUAN_API=2
+ NEED_LIBASSUAN_VERSION=2.4.2
+ 
+diff -Naur a/configure.ac b/configure.ac
+--- a/configure.ac	2025-02-10 15:57:10.000000000 +0600
++++ b/configure.ac	2025-02-18 10:34:59.988883253 +0600
+@@ -87,7 +87,7 @@
+ GPGME_CONFIG_API_VERSION=1
+ ##############################################
+ 
+-NEED_GPG_ERROR_VERSION=1.47
++NEED_GPG_ERROR_VERSION=1.31
+ NEED_LIBASSUAN_API=2
+ NEED_LIBASSUAN_VERSION=2.4.2
+ 
diff --git a/base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch b/base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch
new file mode 100644
index 0000000..0b7cacb
--- /dev/null
+++ b/base/rx/rx-gpgme/gpgme-exclude-extra-libraries-from-linking.patch
@@ -0,0 +1,31 @@
+diff -Naur a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in
+--- a/lang/cpp/src/GpgmeppConfig.cmake.in.in	2025-02-10 15:52:23.000000000 +0600
++++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in	2025-02-18 10:36:13.788488048 +0600
+@@ -63,7 +63,7 @@
+ 
+ set_target_properties(Gpgmepp PROPERTIES
+   INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/gpgme++;@resolved_includedir@"
+-  INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@;@LIBASSUAN_LIBS@"
++  INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme@libsuffix@"
+   IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp@libsuffix@"
+ )
+ 
+diff -Naur a/src/gpgme-config.in b/src/gpgme-config.in
+--- a/src/gpgme-config.in	2018-12-03 15:37:25.000000000 +0600
++++ b/src/gpgme-config.in	2025-02-18 10:36:59.981240681 +0600
+@@ -25,12 +25,12 @@
+ libs="-L@libdir@"
+ 
+ # Network libraries.
+-assuan_cflags="@LIBASSUAN_CFLAGS@"
+-assuan_libs="@LIBASSUAN_LIBS@"
++#assuan_cflags="@LIBASSUAN_CFLAGS@"
++#assuan_libs="@LIBASSUAN_LIBS@"
+ 
+ # Configure libgpg-error.
+ gpg_error_cflags="@GPG_ERROR_CFLAGS@"
+-gpg_error_libs="@GPG_ERROR_LIBS@"
++#gpg_error_libs="@GPG_ERROR_LIBS@"
+ 
+ # Configure thread packages.
+ thread_modules=""
diff --git a/base/rx/rx-gpgme/gpgme-multilib.h b/base/rx/rx-gpgme/gpgme-multilib.h
new file mode 100644
index 0000000..574f09d
--- /dev/null
+++ b/base/rx/rx-gpgme/gpgme-multilib.h
@@ -0,0 +1,20 @@
+/* gpgme-multilib.h */
+/* This file is here to prevent a file conflict on multiarch systems.  A
+ * conflict will occur because gpgme.h has arch-specific definitions.
+ *
+ * DO NOT INCLUDE THE NEW FILE DIRECTLY -- ALWAYS INCLUDE THIS ONE INSTEAD. */
+
+#ifndef GPGME_MULTILIB_H
+#define GPGME_MULTILIB_H
+#include <bits/wordsize.h>
+
+#if __WORDSIZE == 32
+#include "gpgme-32.h"
+#elif __WORDSIZE == 64
+#include "gpgme-64.h"
+#else
+#error "unexpected value for __WORDSIZE macro"
+#endif
+
+#endif
+
diff --git a/base/rx/rx-gpgme/gpgme.spec b/base/rx/rx-gpgme/gpgme.spec
new file mode 100644
index 0000000..3bb8fa8
--- /dev/null
+++ b/base/rx/rx-gpgme/gpgme.spec
@@ -0,0 +1,412 @@
+%bcond_with check
+# No Qt5 on RHEL 10 and higher
+%bcond_without qt5
+%bcond_without qt6
+%bcond_with python
+
+%global base_name gpgme
+%global gnupg2_min_ver 2.2.24
+%global libgpg_error_min_ver 1.47
+
+# we are doing out of source build
+%global _configure ../configure
+
+# brpldconfig creates links that conflict with original gpgme
+%undefine __brp_ldconfig
+
+# We are using python3.11, make sure sitearch is correct
+%global python3_pkgversion 3.11
+
+%global _syslibdir  %{_libdir}
+%global _sysincludedir  %{_includedir}
+
+%global _prefix     /opt/rx
+%global _sysconfdir %{_prefix}/etc
+%global _includedir %{_prefix}/include
+%global _libdir     %{_prefix}/%{_lib}
+%global _sbindir    %{_prefix}/sbin
+%global _datadir    %{_prefix}/share
+%global _mandir     %{_datadir}/man
+%global _docdir     %{_datadir}/doc
+%global _infodir    %{_datadir}/info
+
+Name:           rx-gpgme
+Summary:        GnuPG Made Easy - high level crypto API - version 1.23
+Version:        1.24.2
+Release:        1%{?dist}
+
+# MIT: src/cJSON.{c,h} (used by gpgme-json)
+License:        LGPL-2.1-or-later AND MIT
+URL:            https://gnupg.org/related_software/gpgme/
+Source0:        https://gnupg.org/ftp/gcrypt/gpgme/gpgme-%{version}.tar.bz2
+Source2:        gpgme-multilib.h
+
+## downstream patches
+# Don't add extra libs/cflags in gpgme-config/cmake equivalent
+Patch1001:      0001-don-t-add-extra-libraries-for-linking.patch
+# add -D_FILE_OFFSET_BITS... to gpgme-config, upstreamable
+Patch1002:      gpgme-1.3.2-largefile.patch
+# Let's fix stupid AX_PYTHON_DEVEL
+#Patch1003:      0001-fix-stupid-ax_python_devel.patch
+# Allow extra options to be passed to setup.py during installation
+Patch1004:      0002-setup_py_extra_opts.patch
+
+%if 0%{?rhel} < 9
+# Adjust libgpg-error version
+Patch1100:      gpgme-downgrade-libgpg-error-version.patch
+%endif
+ 
+## temporary downstream fixes
+# Skip lang/qt/tests/t-remarks on gnupg 2.4+
+Patch3001:      1001-qt-skip-test-remarks-for-gnupg2-2.4.patch
+
+BuildRequires:  make
+BuildRequires:  cmake
+#BuildRequires:  gcc
+BuildRequires:  clang
+BuildRequires:  gawk
+BuildRequires:  texinfo
+BuildRequires:  rx-gnupg2 >= %{gnupg2_min_ver}
+BuildRequires:  rx-gnupg2-smime
+BuildRequires:  pkgconfig(gpg-error) >= %{libgpg_error_min_ver}
+BuildRequires:  libassuan-devel >= 2.4.2
+
+
+# For python bindings
+BuildRequires:  swig
+
+# to remove RPATH
+BuildRequires:  chrpath
+
+# For AutoReq cmake-filesystem
+BuildRequires:  cmake
+
+Requires:       rx-gnupg2 >= %{gnupg2_min_ver}
+
+Provides:       %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+# On the following architectures workaround multiarch conflict of -devel packages:
+%define multilib_arches %{ix86} x86_64 ia64 ppc ppc64 s390 s390x %{sparc}
+
+%description
+GnuPG Made Easy (GPGME) is a library designed to make access to GnuPG
+easier for applications.  It provides a high-level crypto API for
+encryption, decryption, signing, signature verification and key
+management.
+
+%package devel
+Summary:        Development headers and libraries for %{name}
+Requires:       %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Requires:       pkgconfig(gpg-error) >= %{libgpg_error_min_ver}
+
+# Conflicts with files in gpgme devel packages
+Conflicts:      gpgme-devel
+Obsoletes:      gpgme1.22-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+
+
+%description devel
+%{summary}.
+
+
+%package -n %{name}pp
+Summary:        C++ bindings/wrapper for GPGME
+Provides:       %{name}-pp = %{?epoch:%{epoch}:}%{version}-%{release}
+Provides:       %{name}-pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Provides:       %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Requires:       %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+%description -n %{name}pp
+%{summary}.
+
+%package -n %{name}pp-devel
+Summary:        Development libraries and header files for %{name}-pp
+Provides:       %{name}-pp-devel = %{?epoch:%{epoch}:}%{version}-%{release}
+Provides:       %{name}-pp-devel%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Requires:       %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Requires:       %{name}-devel%{?_isa}
+
+# Conflicts with files in gpgme devel packages
+Conflicts:      gpgmepp-devel <= 1.16
+Obsoletes:      gpgme1.22pp-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+
+%description -n %{name}pp-devel
+%{summary}
+
+%if %{with qt5}
+%package -n rx-q%{base_name}-qt5
+Summary:        Qt5 API bindings/wrapper for GPGME
+Requires:       %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+BuildRequires:  pkgconfig(Qt5Core)
+BuildRequires:  pkgconfig(Qt5Test)
+Obsoletes:      rx-q%{base_name} < %{?epoch:%{epoch}:}%{version}-%{release}
+Provides:       rx-q%{base_name} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+
+%description -n rx-q%{base_name}-qt5
+%{summary}.
+%endif
+
+%if %{with qt6}
+%package -n rx-q%{base_name}-qt6
+Summary:        Qt6 API bindings/wrapper for GPGME
+Requires:       %{name}pp%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+BuildRequires:  pkgconfig(Qt6Core)
+BuildRequires:  pkgconfig(Qt6Test)
+
+%description -n rx-q%{base_name}-qt6
+%{summary}.
+%endif
+
+
+%if %{with qt5}
+%package -n rx-q%{base_name}-qt5-devel
+Summary:        Development libraries and header files for %{name}-qt5
+# before libqgpgme.so symlink was moved to avoid conflict
+Conflicts:      kdepimlibs-devel < 4.14.10-17
+Requires:       rx-q%{base_name}-qt5%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Provides:       rx-q%{base_name}-devel = %{?epoch:%{epoch}:}%{version}-%{release}
+Provides:       rx-q%{base_name}-devel%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+# Conflicts with files in gpgme devel packages
+Conflicts:      qgpgme-devel
+Obsoletes:      qgpgme1.22-qt5-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+
+Obsoletes:      rx-q%{base_name}-common-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes:      q%{base_name}1.22-common-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+
+%description -n rx-q%{base_name}-qt5-devel
+%{summary}.
+%endif
+
+%if %{with qt6}
+%package -n rx-q%{base_name}-qt6-devel
+Summary:        Development libraries and header files for %{name}-qt6
+Requires:       rx-q%{base_name}-qt6%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+Obsoletes:      qgpgme1.22-qt6-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes:      rx-q%{base_name}-common-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes:      q%{base_name}1.22-common-devel < %{?epoch:%{epoch}:}%{version}-%{release}
+
+%description -n rx-q%{base_name}-qt6-devel
+%{summary}.
+%endif
+
+%if %{with python}
+%package -n python%{python3_pkgversion}-gpg
+Summary:        %{name} bindings for Python 3
+BuildRequires:  python%{python3_pkgversion}-devel
+# Needed since Python 3.12+ drops distutils
+BuildRequires:  python%{python3_pkgversion}-setuptools
+Requires:       %{name}%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+
+%description -n python%{python3_pkgversion}-gpg
+%{summary}.
+%endif
+
+%prep
+%autosetup -p1 -n gpgme-%{version}
+
+## HACK ALERT
+# The config script already suppresses the -L if it's /usr/lib, so cheat and
+# set it to a value which we know will be suppressed.
+sed -i -e 's|^libdir=@libdir@$|libdir=@exec_prefix@/lib|g' src/gpgme-config.in
+
+# The build machinery does not support Python 3.9+ yet
+# https://github.com/gpg/gpgme/pull/4
+sed -i 's/3.8/%{python3_pkgversion}/g' configure
+
+%build
+export CC=clang
+export CXX=clang++
+# People neeed to learn that you can't run autogen.sh anymore
+#./autogen.sh
+
+# Since 1.16.0, we need to explicitly pass -D_LARGEFILE_SOURCE and
+# -D_FILE_OFFSET_BITS=64 for the QT binding to build successfully on 32-bit
+# platforms.
+export CFLAGS='-I%{_sysincludedir} -I%{_sysincludedir}/Qt6 %{optflags} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -Qunused-arguments'
+export CXXFLAGS='-I%{_sysincludedir} -I%{_sysincludedir}/Qt6 %{optflags} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -Qunused-arguments'
+# Explicit new lines in C(XX)FLAGS can break naive build scripts
+export CFLAGS="$(echo ${CFLAGS} | tr '\n\\' '  ')"
+export CXXFLAGS="$(echo ${CXXFLAGS} | tr '\n\\' '  ')"
+export SETUPTOOLS_USE_DISTUTILS=local
+
+export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
+# Link statically
+export LDFLAGS="-L%{_libdir} -L%{_syslibdir} -Wl,-rpath=%{_libdir} -lgpg-error $LDFLAGS"
+export PATH="%{_bindir}:$PATH"
+
+# Force to use rx-gnupg2
+sed 's@GPG_DEFAULT=no@GPG_DEFAULT="/opt/rx/bin/gpg"@g' -i configure
+sed 's@GPGSM_DEFAULT=no@GPGSM_DEFAULT="/opt/rx/bin/gpgsm"@g' -i configure
+sed 's@GPGCONF_DEFAULT=no@GPGCONF_DEFAULT="/opt/rx/bin/gpgconf"@g' -i configure
+sed 's@G13_DEFAULT=no@G13_DEFAULT="/opt/rx/bin/g13"@g' -i configure
+
+# Also build either qt5 or qt6
+mkdir build
+pushd build
+%configure --disable-static --disable-silent-rules --enable-languages=cpp,%{?with_python:python,}%{?with_qt5:qt,}%{!?with_qt5:%{?with_qt6:qt6,}}
+%make_build
+popd
+
+# Build qt6 in extra step if qt5 has been build
+%if %{with qt5} && %{with qt6}
+mkdir build-qt6
+pushd build-qt6
+%configure --disable-static --disable-silent-rules --enable-languages=cpp,qt6%{?with_python:,python}
+%make_build
+popd
+%endif
+
+%install
+# When using distutils from setuptools 60+, ./setup.py install use
+# the .egg format. This forces setuptools to use .egg-info format.
+# SETUP_PY_EXTRA_OPTS is introduced by the Patch1004 above.
+export SETUPTOOLS_USE_DISTUTILS=local
+export SETUP_PY_EXTRA_OPTS="--single-version-externally-managed --root=/"
+# Aliso install either qt5 or qt6
+pushd build
+%make_install
+popd
+# Install qt6 in extra step if qt5 has been installed
+%if %{with qt5} && %{with qt6}
+pushd build-qt6
+%make_install
+popd
+%endif
+
+# unpackaged files
+rm -fv %{buildroot}%{_infodir}/dir
+rm -fv %{buildroot}%{_libdir}/lib*.la
+
+# Hack to resolve multiarch conflict (#341351)
+%ifarch %{multilib_arches}
+mv %{buildroot}%{_bindir}/gpgme-config{,.%{_target_cpu}}
+cat > gpgme-config-multilib.sh <<__END__
+#!/bin/sh
+exec %{_bindir}/gpgme-config.\$(arch) \$@
+__END__
+install -D -p gpgme-config-multilib.sh %{buildroot}%{_bindir}/gpgme-config
+mv %{buildroot}%{_includedir}/gpgme.h \
+   %{buildroot}%{_includedir}/gpgme-%{__isa_bits}.h
+install -m644 -p -D %{SOURCE2} %{buildroot}%{_includedir}/gpgme.h
+%endif
+chrpath -d %{buildroot}%{_bindir}/%{base_name}-tool
+chrpath -d %{buildroot}%{_bindir}/%{base_name}-json
+chrpath -d %{buildroot}%{_libdir}/lib%{base_name}pp.so*
+# qt5
+%if %{with qt5}
+chrpath -d %{buildroot}%{_libdir}/libq%{base_name}.so*
+%endif
+# qt6
+%if %{with qt6}
+chrpath -d %{buildroot}%{_libdir}/libq%{base_name}qt6.so*
+%endif
+
+# autofoo installs useless stuff for uninstall
+rm -vf %{buildroot}%{python2_sitelib}/gpg/install_files.txt
+rm -vf %{buildroot}%{python3_sitelib}/gpg/install_files.txt
+
+# Remove duplicate stuff
+rm -rf %{buildroot}%{_bindir}/%{base_name}-json
+rm -rf %{buildroot}%{_datadir}/man/man1/gpgme-json*
+#rm -fv %{buildroot}%{_libdir}/lib%{base_name}.so.11
+#rm -fv %{buildroot}%{_libdir}/lib%{base_name}pp.so.6
+
+mkdir -p %{buildroot}%{_syslibdir}/cmake
+ln -sf %{_libdir}/cmake/Gpgmepp %{buildroot}%{_syslibdir}/cmake/Gpgmepp
+%if %{with qt5}
+ln -sf %{_libdir}/cmake/QGpgme %{buildroot}%{_syslibdir}/cmake/QGpgme
+%endif
+%if %{with qt6}
+ln -sf %{_libdir}/cmake/QGpgmeQt6 %{buildroot}%{_syslibdir}/cmake/QGpgmeQt6
+%endif
+
+%if %{with check}
+%check
+pushd build
+make check
+popd
+%endif
+
+%files
+%license COPYING* LICENSES
+%doc AUTHORS NEWS README*
+%{_libdir}/lib%{base_name}.so.11*
+
+%files devel
+%{_bindir}/%{base_name}-config
+%{_bindir}/%{base_name}-tool
+%ifarch %{multilib_arches}
+%{_bindir}/%{base_name}-config.%{_target_cpu}
+%{_includedir}/%{base_name}-%{__isa_bits}.h
+%endif
+%{_includedir}/%{base_name}.h
+%{_libdir}/lib%{base_name}.so
+%{_datadir}/aclocal/%{base_name}.m4
+%{_infodir}/%{base_name}.info*
+%{_libdir}/pkgconfig/%{base_name}*.pc
+
+%files -n %{name}pp
+%doc lang/cpp/README
+%{_libdir}/lib%{base_name}pp.so.6*
+
+%files -n %{name}pp-devel
+%{_includedir}/%{base_name}++/
+%{_libdir}/lib%{base_name}pp.so
+%{_libdir}/cmake/Gpgmepp/
+%{_syslibdir}/cmake/Gpgmepp
+
+%if %{with qt5}
+%files -n rx-q%{base_name}-qt5
+%doc lang/qt/README
+%{_libdir}/libq%{base_name}.so.15*
+%endif
+
+%if %{with qt6}
+%files -n rx-q%{base_name}-qt6
+%{_libdir}/libq%{base_name}qt6.so.15*
+%endif
+
+%if %{with qt5}
+%files -n rx-q%{base_name}-qt5-devel
+%{_libdir}/libq%{base_name}.so
+%{_libdir}/cmake/QGpgme/
+%{_syslibdir}/cmake/QGpgme
+%{_includedir}/q%{base_name}-qt5/
+%endif
+
+%if %{with qt6}
+%files -n rx-q%{base_name}-qt6-devel
+%{_libdir}/libq%{base_name}qt6.so
+%{_libdir}/cmake/QGpgmeQt6/
+%{_syslibdir}/cmake/QGpgmeQt6
+%{_includedir}/q%{base_name}-qt6/
+%endif
+
+%if %{with python}
+%files -n python%{python3_pkgversion}-gpg
+%doc lang/python/README
+%{python3_sitearch}/gpg-*.egg-info/
+%{python3_sitearch}/gpg/
+%endif
+
+%changelog
+* Tue Feb 18 2025 Raven <raven@sysadmins.ws> - 1.24.2-1
+- update to 1.24.2
+
+* Tue Feb 18 2025 Raven <raven@sysadmins.ws> - 1.23.2-4
+- rebuild (qt6)
+
+* Tue Oct  1 2024 Raven <raven@sysadmins.ws> - 1.23.2-3
+- downgrade gpg-error version
+- obsolete *1.22-devel packages from epel
+
+* Mon Jun  3 2024 Raven <raven@sysadmins.ws> - 1.23.2-2
+- rebuild (qt6)
+
+* Fri Mar 15 2024 Raven <raven@sysadmins.ws> - 1.23.2-1
+- update to 1.23.2
+
+* Fri Mar 15 2024 Raven <raven@sysadmins.ws> - 1.22.0-1
+- converted from el9 epel package
diff --git a/base/rx/rx-harfbuzz/harfbuzz.spec b/base/rx/rx-harfbuzz/harfbuzz.spec
index c21a5a9..4818c32 100644
--- a/base/rx/rx-harfbuzz/harfbuzz.spec
+++ b/base/rx/rx-harfbuzz/harfbuzz.spec
@@ -2,7 +2,7 @@
 
 Name:           rx-harfbuzz
 Version:        7.0.0
-Release:        2%{?dist}%{?_trivial}%{?_buildid}
+Release:        4%{?dist}%{?_trivial}%{?_buildid}
 Summary:        Text shaping library
 
 License:        MIT
@@ -101,6 +101,9 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
 %{_libdir}/libharfbuzz-icu.so.*
 
 %changelog
+* Tue Oct 29 2024 Raven <raven@sysadmins.ws> - 7.0.0-4
+- rebuilt with new rx-freetype
+
 * Fri Feb 17 2023 Lucas Meneghel Rodrigues <lmr@amazon.com> 7.0.0-2.amzn2023.0.1
 - Update to upstream 7.0.0
 
diff --git a/base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch b/base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch
new file mode 100644
index 0000000..2ad09bc
--- /dev/null
+++ b/base/rx/rx-libgpg-error/libgpg-error-1.29-multilib.patch
@@ -0,0 +1,122 @@
+diff -up libgpg-error-1.29/configure.ac.multilib libgpg-error-1.29/configure.ac
+--- libgpg-error-1.29/configure.ac.multilib	2018-04-11 14:41:10.479019981 +0200
++++ libgpg-error-1.29/configure.ac	2018-04-11 14:43:31.288394113 +0200
+@@ -215,13 +215,13 @@ GNUPG_FUNC_MKDIR_TAKES_ONE_ARG
+ 
+ 
+ # Find a 64 bit integer type to be used instead of off_t.  We prefer
+-# the standard integer types over int64_t and finally try long long.
+-if test "$ac_cv_sizeof_int" = "8"; then
++# int64_t and finally try long long.
++if test "$ac_cv_header_stdint_h" = yes; then
++   replacement_for_off_t="int64_t"
++elif test "$ac_cv_sizeof_int" = "8"; then
+    replacement_for_off_t="int"
+ elif test "$ac_cv_sizeof_long" = "8"; then
+    replacement_for_off_t="long"
+-elif test "$ac_cv_header_stdint_h" = yes; then
+-   replacement_for_off_t="int64_t"
+ elif test "$ac_cv_sizeof_long_long" = "8"; then
+    replacement_for_off_t="long long"
+ else
+diff -up libgpg-error-1.29/configure.multilib libgpg-error-1.29/configure
+--- libgpg-error-1.29/configure.multilib	2018-04-11 09:34:30.000000000 +0200
++++ libgpg-error-1.29/configure	2018-04-11 14:41:10.481020028 +0200
+@@ -11301,7 +11301,7 @@ shlibpath_var=
+ shlibpath_overrides_runpath=unknown
+ version_type=none
+ dynamic_linker="$host_os ld.so"
+-sys_lib_dlsearch_path_spec="/lib /usr/lib"
++sys_lib_dlsearch_path_spec="/lib /usr/lib /usr/lib64 /usr/lib64"
+ need_lib_prefix=unknown
+ hardcode_into_libs=no
+ 
+@@ -11775,7 +11775,7 @@ fi
+   # appending ld.so.conf contents (and includes) to the search path.
+   if test -f /etc/ld.so.conf; then
+     lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+-    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
++    sys_lib_dlsearch_path_spec="/lib /usr/lib /usr/lib64 /usr/lib64 $lt_ld_extra"
+   fi
+ 
+   # We used to test for /lib/ld.so.1 and disable shared libraries on
+@@ -15879,12 +15879,12 @@ fi
+ 
+ # Find a 64 bit integer type to be used instead of off_t.  We prefer
+ # the standard integer types over int64_t and finally try long long.
+-if test "$ac_cv_sizeof_int" = "8"; then
++if test "$ac_cv_header_stdint_h" = yes; then
++   replacement_for_off_t="int64_t"
++elif test "$ac_cv_sizeof_int" = "8"; then
+    replacement_for_off_t="int"
+ elif test "$ac_cv_sizeof_long" = "8"; then
+    replacement_for_off_t="long"
+-elif test "$ac_cv_header_stdint_h" = yes; then
+-   replacement_for_off_t="int64_t"
+ elif test "$ac_cv_sizeof_long_long" = "8"; then
+    replacement_for_off_t="long long"
+ else
+diff -up libgpg-error-1.29/src/gen-posix-lock-obj.c.multilib libgpg-error-1.29/src/gen-posix-lock-obj.c
+--- libgpg-error-1.29/src/gen-posix-lock-obj.c.multilib	2016-11-16 13:22:03.000000000 +0100
++++ libgpg-error-1.29/src/gen-posix-lock-obj.c	2018-04-11 14:41:10.481020028 +0200
+@@ -72,6 +72,7 @@ main (void)
+ #ifdef USE_POSIX_THREADS
+   unsigned char *p;
+   int i;
++  int initidx = 0;
+ #endif
+   struct {
+     long vers;
+@@ -111,11 +112,12 @@ main (void)
+ 
+   /* To force a probably suitable alignment of the structure we use a
+      union and include a long and a pointer to a long.  */
+-  printf ("typedef struct\n"
++  printf ("#include <pthread.h>\n"
++          "typedef struct\n"
+           "{\n"
+           "  long _vers;\n"
+           "  union {\n"
+-          "    volatile char _priv[%d];\n"
++          "    volatile char _priv[sizeof(pthread_mutex_t)];\n"
+           "%s"
+           "    long _x_align;\n"
+           "    long *_xp_align;\n"
+@@ -123,7 +125,6 @@ main (void)
+           "} gpgrt_lock_t;\n"
+           "\n"
+           "#define GPGRT_LOCK_INITIALIZER {%d,{{",
+-          SIZEOF_PTHREAD_MUTEX_T,
+ # if USE_16BYTE_ALIGNMENT
+           "    int _x16_align __attribute__ ((aligned (16)));\n",
+ # elif USE_DOUBLE_FOR_ALIGNMENT
+@@ -137,10 +138,16 @@ main (void)
+   p = (unsigned char *)&mtx;
+   for (i=0; i < sizeof mtx; i++)
+     {
++      if (p[i] != 0)
++        initidx = i;
++    }
++
++  for (i=0; i <= initidx; i++)
++    {
+       if (i && !(i % 8))
+         printf (" \\\n%*s", 36, "");
+       printf ("%u", p[i]);
+-      if (i < sizeof mtx - 1)
++      if (i < initidx)
+         putchar (',');
+     }
+   fputs ("}}}\n", stdout);
+diff -up libgpg-error-1.29/src/gpg-error.h.in.multilib libgpg-error-1.29/src/gpg-error.h.in
+--- libgpg-error-1.29/src/gpg-error.h.in.multilib	2018-04-11 14:41:10.481020028 +0200
++++ libgpg-error-1.29/src/gpg-error.h.in	2018-04-11 14:45:28.184203566 +0200
+@@ -17,7 +17,7 @@
+  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
+  * SPDX-License-Identifier: LGPL-2.1-or-later
+  *
+- * @configure_input@
++ * Do not edit.  Generated from gpg-error.h.in.
+  */
+ 
+ /* The GnuPG project consists of many components.  Error codes are
diff --git a/base/rx/rx-libgpg-error/libgpg-error.spec b/base/rx/rx-libgpg-error/libgpg-error.spec
new file mode 100644
index 0000000..bb60db9
--- /dev/null
+++ b/base/rx/rx-libgpg-error/libgpg-error.spec
@@ -0,0 +1,361 @@
+%global realname libgpg-error
+%global _syslibdir  %{_libdir}
+%global _prefix     /opt/rx
+%global _bindir     %{_prefix}/bin
+%global _libdir     %{_prefix}/%{_lib}
+%global _includedir %{_prefix}/include
+%global _datadir    %{_prefix}/share
+%global _docdir     %{_datadir}/doc
+%global _mandir     %{_datadir}/man
+%global _infodir    %{_datadir}/info
+
+Name: rx-libgpg-error
+Version: 1.51
+Release: 1%{?dist}
+Summary: Library for error values used by GnuPG components
+URL: https://www.gnupg.org/related_software/libgpg-error/
+License: LGPLv2+
+
+Source0: https://www.gnupg.org/ftp/gcrypt/libgpg-error/%{realname}-%{version}.tar.bz2
+Source1: https://www.gnupg.org/ftp/gcrypt/libgpg-error/%{realname}-%{version}.tar.bz2.sig
+Patch1: libgpg-error-1.29-multilib.patch
+Patch2: set-proper-version-suffix.patch
+
+BuildRequires: gcc
+BuildRequires: gawk, gettext, autoconf, automake, gettext-devel, libtool
+BuildRequires: texinfo
+BuildRequires: gettext-autopoint
+BuildRequires: make
+
+%description
+This is a library that defines common error values for all GnuPG
+components.  Among these are GPG, GPGSM, GPGME, GPG-Agent, libgcrypt,
+pinentry, SmartCard Daemon and possibly more in the future.
+
+%package devel
+Summary: Development files for the %{name} package
+Requires: %{name}%{?_isa} = %{version}-%{release}
+Requires: pkgconfig
+
+%description devel
+This is a library that defines common error values for all GnuPG
+components.  Among these are GPG, GPGSM, GPGME, GPG-Agent, libgcrypt,
+pinentry, SmartCard Daemon and possibly more in the future. This package
+contains files necessary to develop applications using libgpg-error.
+
+%prep
+%autosetup -p1 -n %{realname}-%{version}
+
+autoreconf -f
+
+# The config script already suppresses the -L if it's /usr/lib, so cheat and
+# set it to a value which we know will be suppressed.
+sed -i -e 's|^libdir=@libdir@$|libdir=@exec_prefix@/lib64|g;s|@GPG_ERROR_CONFIG_HOST@|none|g' src/gpg-error-config.in
+sed -i -e '/--variable=host/d' src/gpg-error-config-test.sh.in
+
+# Modify configure to drop rpath for /usr/lib64
+sed -i -e 's|sys_lib_dlsearch_path_spec="/lib /usr/lib|sys_lib_dlsearch_path_spec="/lib /usr/lib %{_libdir} %{_syslibdir}|g' configure
+
+%build
+export LDFLAGS="-L%{_libdir} -L%{_syslibdir} -Wl,-rpath=%{_libdir} $LDFLAGS"
+
+%configure \
+    --disable-static\
+    --disable-rpath \
+    --disable-languages \
+    --enable-install-gpg-error-config
+%make_build
+
+%install
+%make_install
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+rm -f $RPM_BUILD_ROOT%{_infodir}/dir
+
+%find_lang %{realname}
+
+%check
+make check
+
+%ldconfig_scriptlets
+
+%files -f %{realname}.lang
+%license COPYING COPYING.LIB
+%doc AUTHORS README NEWS
+%{_bindir}/gpg-error
+%{_libdir}/libgpg-error.so.0*
+%{_datadir}/libgpg-error
+
+%files devel
+%{_bindir}/gpg-error-config
+%{_bindir}/gpgrt-config
+%{_bindir}/yat2m
+%{_libdir}/libgpg-error.so
+%{_libdir}/pkgconfig/gpg-error.pc
+%{_includedir}/gpg-error.h
+%{_includedir}/gpgrt.h
+%{_datadir}/aclocal/gpg-error.m4
+%{_datadir}/aclocal/gpgrt.m4
+%{_infodir}/gpgrt.info*
+%{_mandir}/man1/gpgrt-config.*
+%{_mandir}/man1/gpg-error-config.*
+
+%changelog
+* Tue Feb 18 2025 Raven <raven@sysadmins.ws> - 1.51-1
+- New upstream release
+
+* Wed Aug 24 2022 Raven <raven@sysadmins.ws> - 1.42-5
+- change installation root to /opt/rx
+
+* Mon Dec 06 2021 Jakub Jelen <jjelen@redhat.com> - 1.42-5
+- Avoid using bad function inet_addr
+
+* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 1.42-4
+- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
+  Related: rhbz#1991688
+
+* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 1.42-3
+- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
+
+* Mon Apr 12 2021 Jakub Jelen <jjelen@redhat.com> - 1.42-2
+- Address coverity reported issues
+
+* Mon Mar 22 2021 Jakub Jelen <jjelen@redhat.com> - 1.42-1
+- New upstream release (#1941582)
+
+* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.41-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Mon Jan 04 2021 Jakub Jelen <jjelen@redhat.com> - 1.41-1
+- New upstream release (#1909749)
+
+* Tue Dec 01 2020 Jakub Jelen <jjelen@redhat.com> - 1.39-1
+- New upstream release (#1800640)
+
+* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.37-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Fri Feb 28 2020 Tomáš Mráz <tmraz@redhat.com> 1.37-1
+- new upstream release 1.37
+
+* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.36-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Wed Aug 14 2019 Tomáš Mráz <tmraz@redhat.com> 1.36-2
+- fix FTBFS in rawhide due to new gawk
+
+* Sat Aug  3 2019 Peter Robinson <pbrobinson@fedoraproject.org> 1.36-1
+- new upstream release 1.36
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.33-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.33-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Tue Jan 08 2019 Tomáš Mráz <tmraz@redhat.com> 1.33-1
+- new upstream release 1.33
+- dropped obsolete install-info scriptlets
+
+* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.31-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Mon Jun 11 2018 Tomáš Mráz <tmraz@redhat.com> 1.31-1
+- new upstream release 1.31
+
+* Wed Apr 11 2018 Tomáš Mráz <tmraz@redhat.com> 1.29-1
+- new upstream release 1.29
+
+* Wed Feb 28 2018 Richard W.M. Jones <rjones@redhat.com> - 1.27-6
+- Backport patch which adds riscv64 support.
+
+* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.27-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Sat Feb 03 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.27-4
+- Switch to %%ldconfig_scriptlets
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.27-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.27-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Wed Mar 15 2017 Tomáš Mráz <tmraz@redhat.com> 1.27-1
+- new upstream release 1.27
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.25-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Tue Nov 15 2016 Tomáš Mráz <tmraz@redhat.com> 1.25-1
+- new upstream release 1.25
+
+* Thu Jul 14 2016 Tomáš Mráz <tmraz@redhat.com> 1.24-1
+- new upstream release
+
+* Fri Mar 18 2016 Rex Dieter <rdieter@fedoraproject.org> - 1.21-3
+- drop explicit /sbin/ldconfig scriptlet deps (#1319144)
+
+* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 1.21-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Tue Dec 22 2015 Tomáš Mráz <tmraz@redhat.com> 1.21-1
+- new upstream release
+
+* Tue Sep  1 2015 Tomáš Mráz <tmraz@redhat.com> 1.20-1
+- new upstream release
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.19-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Mon Apr 13 2015 Tomáš Mráz <tmraz@redhat.com> 1.19-1
+- new upstream release
+
+* Sat Feb 21 2015 Till Maas <opensource@till.name> - 1.17-3
+- Rebuilt for Fedora 23 Change
+  https://fedoraproject.org/wiki/Changes/Harden_all_packages_with_position-independent_code
+
+* Fri Jan 30 2015 Tomáš Mráz <tmraz@redhat.com> 1.17-2
+- do not conflict on header file between architectures (#1180857)
+
+* Thu Jan 29 2015 Tomáš Mráz <tmraz@redhat.com> 1.17-1
+- new upstream release
+
+* Fri Sep 19 2014 Tomáš Mráz <tmraz@redhat.com> 1.16-1
+- new upstream release
+- move from /lib to /usr/lib
+
+* Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.13-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Fri Jul 18 2014 Tom Callaway <spot@fedoraproject.org> 1.13-2
+- fix license handling
+
+* Wed Jun 25 2014 Tomáš Mráz <tmraz@redhat.com> 1.13-1
+- new upstream release
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.12-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Fri Aug 23 2013 Tomáš Mráz <tmraz@redhat.com> 1.12-1
+- new upstream release
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.11-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Fri Apr  5 2013 Tomáš Mráz <tmraz@redhat.com> 1.11-1
+- new upstream release
+
+* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.10-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Thu Jul 19 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.10-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.10-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Fri Jul 15 2011 Tomáš Mráz <tmraz@redhat.com> 1.10-1
+- new upstream release
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.9-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Fri Jul 23 2010 Rex Dieter <rdieter@fedoraproject.org> 1.9-1
+- libgpg-error-1.9
+
+* Thu Feb 25 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-3
+- turn off common lisp bindings the right way
+- drop finger output
+- recode the changelog into UTF-8 if it isn't UTF-8 (rpmlint)
+
+* Mon Jan 11 2010 Nalin Dahyabhai <nalin@redhat.com> - 1.7-2
+- fix use of macro in changelog (rpmlint)
+- build with --disable-rpath (rpmlint)
+- build with %%{?_smp_mflags}
+
+* Thu Oct 15 2009 Nalin Dahyabhai <nalin@redhat.com> - 1.7-1
+- long-overdue update to 1.7
+- add a disttag
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.6-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Wed Feb 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.6-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Tue Feb 19 2008 Fedora Release Engineering <rel-eng@fedoraproject.org> - 1.6-2
+- Autorebuild for GCC 4.3
+
+* Fri Dec  7 2007 Nalin Dahyabhai <nalin@redhat.com>
+- remove the generic install docs (#226021)
+
+* Fri Dec  7 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.6-1
+- update to 1.6
+- add suggested summary, buildrequires, and modify install call as suggested
+  by package review (#226021)
+
+* Mon Oct 15 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-6
+- use ldconfig to make the soname symlink so that it gets packaged (#331241)
+
+* Wed Aug 22 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-5
+- add missing gawk buildrequirement
+
+* Thu Aug 16 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-4
+- clarify license
+
+* Mon Jul 30 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-3
+- disable static libraries (part of #249815)
+
+* Fri Jul 27 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-2
+- move libgpg-error shared library to /%%{_lib} (#249816)
+
+* Thu Jul 19 2007 Nalin Dahyabhai <nalin@redhat.com> - 1.5-1
+- update to 1.5
+
+* Sun Oct 01 2006 Jesse Keating <jkeating@redhat.com> - 1.4-2
+- rebuilt for unwind info generation, broken in gcc-4.1.1-21
+
+* Mon Sep 18 2006 Bill Nottngham <notting@redhat.com> - 1.4-1
+- update to 1.4
+- don't ship lisp bindings
+
+* Wed Jul 12 2006 Jesse Keating <jkeating@redhat.com> - 1.3-3.1
+- rebuild
+
+* Mon Jun  5 2006 Nalin Dahyabhai <nalin@redhat.com> 1.3-3
+- give gpg-error-config libdir=@exec_prefix@/lib instead of @libdir@, so that
+  it agrees on 32- and 64-bit arches (it suppresses the -L argument if @libdir@
+  is /usr/lib, so this should be cleaner than adding a non-standard .pc file
+  which upstream developers might inadvertently think they can depend to be on
+  every system which provides this library)
+
+* Mon May 15 2006 Karsten Hopp <karsten@redhat.de> 1.3-2
+- switch to pkgconfig so that gpg-error-config can be the same on 
+  32bit and 64bit archs
+
+* Tue May  2 2006 Nalin Dahyabhai <nalin@redhat.com> - 1.3-1
+- update to version 1.3
+
+* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 1.1-1.2.1
+- bump again for double-long bug on ppc(64)
+
+* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 1.1-1.2
+- rebuilt for new gcc4.1 snapshot and glibc changes
+
+* Fri Dec 09 2005 Jesse Keating <jkeating@redhat.com>
+- rebuilt
+
+* Wed Nov 30 2005 Karsten Hopp <karsten@redhat.de> 1.1-1
+- update
+
+* Wed Mar  2 2005 Bill Nottingham <notting@redhat.com> - 1.0-2
+- we can rebuild it. we have the technology.
+
+* Tue Aug 31 2004 Bill Nottingham <notting@redhat.com> - 1.0-1
+- update to 1.0
+
+* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Fri Apr 16 2004 Bill Nottingham <notting@redhat.com> - 0.7-1
+- adapt upstream specfile
diff --git a/base/rx/rx-libgpg-error/set-proper-version-suffix.patch b/base/rx/rx-libgpg-error/set-proper-version-suffix.patch
new file mode 100644
index 0000000..fcbb55d
--- /dev/null
+++ b/base/rx/rx-libgpg-error/set-proper-version-suffix.patch
@@ -0,0 +1,11 @@
+--- a/autogen.sh	2024-07-04 21:14:53.000000000 +0600
++++ b/autogen.sh	2025-02-18 11:23:30.572654260 +0600
+@@ -271,7 +271,7 @@
+     else
+       ingit=no
+       beta=yes
+-      tmp="-unknown"
++      tmp="-rx"
+       cid="0000000"
+       rev="0000000"
+       rvd="0"
diff --git a/base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch
new file mode 100644
index 0000000..75e0cea
--- /dev/null
+++ b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-CET.patch
@@ -0,0 +1,1248 @@
+From db1afe88c361ceea22c6ac8abdb9b0ff41a39aa2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Nikola=20Forr=C3=B3?= <nforro@redhat.com>
+Date: Mon, 15 Apr 2019 16:56:45 +0200
+Subject: [PATCH] x86 SIMD: Add endbr32/endbr64 instructions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Allow for indirect branch tracking with Intel CET (Control-Flow
+Enforcement Technology) [1], by making all exported routines a possible
+target for an indirect jump.
+
+Signed-off-by: Nikola Forró <nforro@redhat.com>
+
+[1] https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf
+---
+ simd/i386/jccolext-avx2.asm   | 1 +
+ simd/i386/jccolext-mmx.asm    | 1 +
+ simd/i386/jccolext-sse2.asm   | 1 +
+ simd/i386/jcgryext-avx2.asm   | 1 +
+ simd/i386/jcgryext-mmx.asm    | 1 +
+ simd/i386/jcgryext-sse2.asm   | 1 +
+ simd/i386/jchuff-sse2.asm     | 1 +
+ simd/i386/jcphuff-sse2.asm    | 2 ++
+ simd/i386/jcsample-avx2.asm   | 2 ++
+ simd/i386/jcsample-mmx.asm    | 2 ++
+ simd/i386/jcsample-sse2.asm   | 2 ++
+ simd/i386/jdcolext-avx2.asm   | 1 +
+ simd/i386/jdcolext-mmx.asm    | 1 +
+ simd/i386/jdcolext-sse2.asm   | 1 +
+ simd/i386/jdmrgext-avx2.asm   | 2 ++
+ simd/i386/jdmrgext-mmx.asm    | 2 ++
+ simd/i386/jdmrgext-sse2.asm   | 2 ++
+ simd/i386/jdsample-avx2.asm   | 4 ++++
+ simd/i386/jdsample-mmx.asm    | 4 ++++
+ simd/i386/jdsample-sse2.asm   | 4 ++++
+ simd/i386/jfdctflt-3dn.asm    | 1 +
+ simd/i386/jfdctflt-sse.asm    | 1 +
+ simd/i386/jfdctfst-mmx.asm    | 1 +
+ simd/i386/jfdctfst-sse2.asm   | 1 +
+ simd/i386/jfdctint-avx2.asm   | 1 +
+ simd/i386/jfdctint-mmx.asm    | 1 +
+ simd/i386/jfdctint-sse2.asm   | 1 +
+ simd/i386/jidctflt-3dn.asm    | 1 +
+ simd/i386/jidctflt-sse.asm    | 1 +
+ simd/i386/jidctflt-sse2.asm   | 1 +
+ simd/i386/jidctfst-mmx.asm    | 1 +
+ simd/i386/jidctfst-sse2.asm   | 1 +
+ simd/i386/jidctint-avx2.asm   | 1 +
+ simd/i386/jidctint-mmx.asm    | 1 +
+ simd/i386/jidctint-sse2.asm   | 1 +
+ simd/i386/jidctred-mmx.asm    | 2 ++
+ simd/i386/jidctred-sse2.asm   | 2 ++
+ simd/i386/jquant-3dn.asm      | 2 ++
+ simd/i386/jquant-mmx.asm      | 2 ++
+ simd/i386/jquant-sse.asm      | 2 ++
+ simd/i386/jquantf-sse2.asm    | 2 ++
+ simd/i386/jquanti-avx2.asm    | 2 ++
+ simd/i386/jquanti-sse2.asm    | 2 ++
+ simd/nasm/jsimdext.inc        | 8 ++++++++
+ simd/x86_64/jccolext-avx2.asm | 1 +
+ simd/x86_64/jccolext-sse2.asm | 1 +
+ simd/x86_64/jcgryext-avx2.asm | 1 +
+ simd/x86_64/jcgryext-sse2.asm | 1 +
+ simd/x86_64/jchuff-sse2.asm   | 1 +
+ simd/x86_64/jcphuff-sse2.asm  | 2 ++
+ simd/x86_64/jcsample-avx2.asm | 2 ++
+ simd/x86_64/jcsample-sse2.asm | 2 ++
+ simd/x86_64/jdcolext-avx2.asm | 1 +
+ simd/x86_64/jdcolext-sse2.asm | 1 +
+ simd/x86_64/jdmrgext-avx2.asm | 2 ++
+ simd/x86_64/jdmrgext-sse2.asm | 2 ++
+ simd/x86_64/jdsample-avx2.asm | 4 ++++
+ simd/x86_64/jdsample-sse2.asm | 4 ++++
+ simd/x86_64/jfdctflt-sse.asm  | 1 +
+ simd/x86_64/jfdctfst-sse2.asm | 1 +
+ simd/x86_64/jfdctint-avx2.asm | 1 +
+ simd/x86_64/jfdctint-sse2.asm | 1 +
+ simd/x86_64/jidctflt-sse2.asm | 1 +
+ simd/x86_64/jidctfst-sse2.asm | 1 +
+ simd/x86_64/jidctint-avx2.asm | 1 +
+ simd/x86_64/jidctint-sse2.asm | 1 +
+ simd/x86_64/jidctred-sse2.asm | 2 ++
+ simd/x86_64/jquantf-sse2.asm  | 2 ++
+ simd/x86_64/jquanti-avx2.asm  | 2 ++
+ simd/x86_64/jquanti-sse2.asm  | 2 ++
+ 70 files changed, 116 insertions(+)
+
+diff --git a/simd/i386/jccolext-avx2.asm b/simd/i386/jccolext-avx2.asm
+index c46d684..7dc6e08 100644
+--- a/simd/i386/jccolext-avx2.asm
++++ b/simd/i386/jccolext-avx2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_avx2)
+
+ EXTN(jsimd_rgb_ycc_convert_avx2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jccolext-mmx.asm b/simd/i386/jccolext-mmx.asm
+index 6357a42..8048abb 100644
+--- a/simd/i386/jccolext-mmx.asm
++++ b/simd/i386/jccolext-mmx.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_mmx)
+
+ EXTN(jsimd_rgb_ycc_convert_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jccolext-sse2.asm b/simd/i386/jccolext-sse2.asm
+index c6c8085..5307ddc 100644
+--- a/simd/i386/jccolext-sse2.asm
++++ b/simd/i386/jccolext-sse2.asm
+@@ -41,6 +41,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_sse2)
+
+ EXTN(jsimd_rgb_ycc_convert_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jcgryext-avx2.asm b/simd/i386/jcgryext-avx2.asm
+index 3fa7973..27a0e11 100644
+--- a/simd/i386/jcgryext-avx2.asm
++++ b/simd/i386/jcgryext-avx2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_gray_convert_avx2)
+
+ EXTN(jsimd_rgb_gray_convert_avx2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jcgryext-mmx.asm b/simd/i386/jcgryext-mmx.asm
+index 8af42e5..dda0e05 100644
+--- a/simd/i386/jcgryext-mmx.asm
++++ b/simd/i386/jcgryext-mmx.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_gray_convert_mmx)
+
+ EXTN(jsimd_rgb_gray_convert_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jcgryext-sse2.asm b/simd/i386/jcgryext-sse2.asm
+index c9d6ff1..f8835bb 100644
+--- a/simd/i386/jcgryext-sse2.asm
++++ b/simd/i386/jcgryext-sse2.asm
+@@ -41,6 +41,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_gray_convert_sse2)
+
+ EXTN(jsimd_rgb_gray_convert_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jchuff-sse2.asm b/simd/i386/jchuff-sse2.asm
+index 76cc85f..0217480 100644
+--- a/simd/i386/jchuff-sse2.asm
++++ b/simd/i386/jchuff-sse2.asm
+@@ -350,6 +350,7 @@ times 1 << 14 db 15
+     GLOBAL_FUNCTION(jsimd_huff_encode_one_block_sse2)
+
+ EXTN(jsimd_huff_encode_one_block_sse2):
++    _endbr32
+
+ %assign stack_offset      0
+ %define arg_state         4 + stack_offset
+diff --git a/simd/i386/jcphuff-sse2.asm b/simd/i386/jcphuff-sse2.asm
+index c26b48a..7fb01e5 100644
+--- a/simd/i386/jcphuff-sse2.asm
++++ b/simd/i386/jcphuff-sse2.asm
+@@ -281,6 +281,7 @@
+     GLOBAL_FUNCTION(jsimd_encode_mcu_AC_first_prepare_sse2)
+
+ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+@@ -460,6 +461,7 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2):
+     GLOBAL_FUNCTION(jsimd_encode_mcu_AC_refine_prepare_sse2)
+
+ EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jcsample-avx2.asm b/simd/i386/jcsample-avx2.asm
+index 0a20802..46eba8c 100644
+--- a/simd/i386/jcsample-avx2.asm
++++ b/simd/i386/jcsample-avx2.asm
+@@ -43,6 +43,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_downsample_avx2)
+
+ EXTN(jsimd_h2v1_downsample_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+@@ -216,6 +217,7 @@ EXTN(jsimd_h2v1_downsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_downsample_avx2)
+
+ EXTN(jsimd_h2v2_downsample_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jcsample-mmx.asm b/simd/i386/jcsample-mmx.asm
+index 2c223ee..b2b8ded 100644
+--- a/simd/i386/jcsample-mmx.asm
++++ b/simd/i386/jcsample-mmx.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_downsample_mmx)
+
+ EXTN(jsimd_h2v1_downsample_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+@@ -185,6 +186,7 @@ EXTN(jsimd_h2v1_downsample_mmx):
+     GLOBAL_FUNCTION(jsimd_h2v2_downsample_mmx)
+
+ EXTN(jsimd_h2v2_downsample_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jcsample-sse2.asm b/simd/i386/jcsample-sse2.asm
+index 4fea60d..4c22b40 100644
+--- a/simd/i386/jcsample-sse2.asm
++++ b/simd/i386/jcsample-sse2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_downsample_sse2)
+
+ EXTN(jsimd_h2v1_downsample_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+@@ -198,6 +199,7 @@ EXTN(jsimd_h2v1_downsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_downsample_sse2)
+
+ EXTN(jsimd_h2v2_downsample_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jdcolext-avx2.asm b/simd/i386/jdcolext-avx2.asm
+index 015be04..b076765 100644
+--- a/simd/i386/jdcolext-avx2.asm
++++ b/simd/i386/jdcolext-avx2.asm
+@@ -43,6 +43,7 @@
+     GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_avx2)
+
+ EXTN(jsimd_ycc_rgb_convert_avx2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jdcolext-mmx.asm b/simd/i386/jdcolext-mmx.asm
+index 5813cfc..150f5b6 100644
+--- a/simd/i386/jdcolext-mmx.asm
++++ b/simd/i386/jdcolext-mmx.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_mmx)
+
+ EXTN(jsimd_ycc_rgb_convert_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jdcolext-sse2.asm b/simd/i386/jdcolext-sse2.asm
+index d5572b3..cd3ac70 100644
+--- a/simd/i386/jdcolext-sse2.asm
++++ b/simd/i386/jdcolext-sse2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_sse2)
+
+ EXTN(jsimd_ycc_rgb_convert_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jdmrgext-avx2.asm b/simd/i386/jdmrgext-avx2.asm
+index e35f728..0db0aa4 100644
+--- a/simd/i386/jdmrgext-avx2.asm
++++ b/simd/i386/jdmrgext-avx2.asm
+@@ -43,6 +43,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_avx2)
+
+ EXTN(jsimd_h2v1_merged_upsample_avx2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+@@ -523,6 +524,7 @@ EXTN(jsimd_h2v1_merged_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_avx2)
+
+ EXTN(jsimd_h2v2_merged_upsample_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jdmrgext-mmx.asm b/simd/i386/jdmrgext-mmx.asm
+index eb3e36b..6427a1a 100644
+--- a/simd/i386/jdmrgext-mmx.asm
++++ b/simd/i386/jdmrgext-mmx.asm
+@@ -40,6 +40,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_mmx)
+
+ EXTN(jsimd_h2v1_merged_upsample_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+@@ -408,6 +409,7 @@ EXTN(jsimd_h2v1_merged_upsample_mmx):
+     GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_mmx)
+
+ EXTN(jsimd_h2v2_merged_upsample_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jdmrgext-sse2.asm b/simd/i386/jdmrgext-sse2.asm
+index c113dc4..6897fa1 100644
+--- a/simd/i386/jdmrgext-sse2.asm
++++ b/simd/i386/jdmrgext-sse2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_sse2)
+
+ EXTN(jsimd_h2v1_merged_upsample_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+@@ -465,6 +466,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_sse2)
+
+ EXTN(jsimd_h2v2_merged_upsample_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jdsample-avx2.asm b/simd/i386/jdsample-avx2.asm
+index a800c35..7d52708 100644
+--- a/simd/i386/jdsample-avx2.asm
++++ b/simd/i386/jdsample-avx2.asm
+@@ -60,6 +60,7 @@ PW_EIGHT times 16 dw 8
+     GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_avx2)
+
+ EXTN(jsimd_h2v1_fancy_upsample_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     pushpic     ebx
+@@ -227,6 +228,7 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_avx2)
+
+ EXTN(jsimd_h2v2_fancy_upsample_avx2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+@@ -570,6 +572,7 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v1_upsample_avx2)
+
+ EXTN(jsimd_h2v1_upsample_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+@@ -669,6 +672,7 @@ EXTN(jsimd_h2v1_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_upsample_avx2)
+
+ EXTN(jsimd_h2v2_upsample_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jdsample-mmx.asm b/simd/i386/jdsample-mmx.asm
+index 12c49f0..7f2ab40 100644
+--- a/simd/i386/jdsample-mmx.asm
++++ b/simd/i386/jdsample-mmx.asm
+@@ -59,6 +59,7 @@ PW_EIGHT times 4 dw 8
+     GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_mmx)
+
+ EXTN(jsimd_h2v1_fancy_upsample_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     pushpic     ebx
+@@ -217,6 +218,7 @@ EXTN(jsimd_h2v1_fancy_upsample_mmx):
+     GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_mmx)
+
+ EXTN(jsimd_h2v2_fancy_upsample_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+@@ -541,6 +543,7 @@ EXTN(jsimd_h2v2_fancy_upsample_mmx):
+     GLOBAL_FUNCTION(jsimd_h2v1_upsample_mmx)
+
+ EXTN(jsimd_h2v1_upsample_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+@@ -640,6 +643,7 @@ EXTN(jsimd_h2v1_upsample_mmx):
+     GLOBAL_FUNCTION(jsimd_h2v2_upsample_mmx)
+
+ EXTN(jsimd_h2v2_upsample_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jdsample-sse2.asm b/simd/i386/jdsample-sse2.asm
+index 4e28d2f..3311b25 100644
+--- a/simd/i386/jdsample-sse2.asm
++++ b/simd/i386/jdsample-sse2.asm
+@@ -59,6 +59,7 @@ PW_EIGHT times 8 dw 8
+     GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_sse2)
+
+ EXTN(jsimd_h2v1_fancy_upsample_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     pushpic     ebx
+@@ -216,6 +217,7 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_sse2)
+
+ EXTN(jsimd_h2v2_fancy_upsample_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+@@ -538,6 +540,7 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v1_upsample_sse2)
+
+ EXTN(jsimd_h2v1_upsample_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+@@ -635,6 +638,7 @@ EXTN(jsimd_h2v1_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_upsample_sse2)
+
+ EXTN(jsimd_h2v2_upsample_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jfdctflt-3dn.asm b/simd/i386/jfdctflt-3dn.asm
+index 322ab16..109e36e 100644
+--- a/simd/i386/jfdctflt-3dn.asm
++++ b/simd/i386/jfdctflt-3dn.asm
+@@ -56,6 +56,7 @@ PD_1_306 times 2 dd 1.306562964876376527856643
+     GLOBAL_FUNCTION(jsimd_fdct_float_3dnow)
+
+ EXTN(jsimd_fdct_float_3dnow):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jfdctflt-sse.asm b/simd/i386/jfdctflt-sse.asm
+index 86952c6..b1e0576 100644
+--- a/simd/i386/jfdctflt-sse.asm
++++ b/simd/i386/jfdctflt-sse.asm
+@@ -67,6 +67,7 @@ PD_1_306 times 4 dd 1.306562964876376527856643
+     GLOBAL_FUNCTION(jsimd_fdct_float_sse)
+
+ EXTN(jsimd_fdct_float_sse):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jfdctfst-mmx.asm b/simd/i386/jfdctfst-mmx.asm
+index 80645a5..be84fdb 100644
+--- a/simd/i386/jfdctfst-mmx.asm
++++ b/simd/i386/jfdctfst-mmx.asm
+@@ -81,6 +81,7 @@ PW_F1306 times 4 dw F_1_306 << CONST_SHIFT
+     GLOBAL_FUNCTION(jsimd_fdct_ifast_mmx)
+
+ EXTN(jsimd_fdct_ifast_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jfdctfst-sse2.asm b/simd/i386/jfdctfst-sse2.asm
+index 446fa7a..945f9cf 100644
+--- a/simd/i386/jfdctfst-sse2.asm
++++ b/simd/i386/jfdctfst-sse2.asm
+@@ -82,6 +82,7 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT
+     GLOBAL_FUNCTION(jsimd_fdct_ifast_sse2)
+
+ EXTN(jsimd_fdct_ifast_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jfdctint-avx2.asm b/simd/i386/jfdctint-avx2.asm
+index 23cf733..56acb63 100644
+--- a/simd/i386/jfdctint-avx2.asm
++++ b/simd/i386/jfdctint-avx2.asm
+@@ -260,6 +260,7 @@ PW_1_NEG1                  times 8  dw  1
+     GLOBAL_FUNCTION(jsimd_fdct_islow_avx2)
+
+ EXTN(jsimd_fdct_islow_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     pushpic     ebx
+diff --git a/simd/i386/jfdctint-mmx.asm b/simd/i386/jfdctint-mmx.asm
+index 34a43b9..4d1e773 100644
+--- a/simd/i386/jfdctint-mmx.asm
++++ b/simd/i386/jfdctint-mmx.asm
+@@ -102,6 +102,7 @@ PW_DESCALE_P2X times 4 dw  1 << (PASS1_BITS - 1)
+     GLOBAL_FUNCTION(jsimd_fdct_islow_mmx)
+
+ EXTN(jsimd_fdct_islow_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jfdctint-sse2.asm b/simd/i386/jfdctint-sse2.asm
+index 6f8e18c..3954c8f 100644
+--- a/simd/i386/jfdctint-sse2.asm
++++ b/simd/i386/jfdctint-sse2.asm
+@@ -103,6 +103,7 @@ PW_DESCALE_P2X times 8 dw  1 << (PASS1_BITS - 1)
+     GLOBAL_FUNCTION(jsimd_fdct_islow_sse2)
+
+ EXTN(jsimd_fdct_islow_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctflt-3dn.asm b/simd/i386/jidctflt-3dn.asm
+index 8795191..eb49902 100644
+--- a/simd/i386/jidctflt-3dn.asm
++++ b/simd/i386/jidctflt-3dn.asm
+@@ -65,6 +65,7 @@ PB_CENTERJSAMP  times 8 db CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_float_3dnow)
+
+ EXTN(jsimd_idct_float_3dnow):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctflt-sse.asm b/simd/i386/jidctflt-sse.asm
+index b27ecfd..ffe54f8 100644
+--- a/simd/i386/jidctflt-sse.asm
++++ b/simd/i386/jidctflt-sse.asm
+@@ -75,6 +75,7 @@ PB_CENTERJSAMP times 8 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_float_sse)
+
+ EXTN(jsimd_idct_float_sse):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctflt-sse2.asm b/simd/i386/jidctflt-sse2.asm
+index c646eae..fd1fe35 100644
+--- a/simd/i386/jidctflt-sse2.asm
++++ b/simd/i386/jidctflt-sse2.asm
+@@ -75,6 +75,7 @@ PB_CENTERJSAMP  times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_float_sse2)
+
+ EXTN(jsimd_idct_float_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctfst-mmx.asm b/simd/i386/jidctfst-mmx.asm
+index 24622d4..00940b8 100644
+--- a/simd/i386/jidctfst-mmx.asm
++++ b/simd/i386/jidctfst-mmx.asm
+@@ -96,6 +96,7 @@ PB_CENTERJSAMP times 8 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_ifast_mmx)
+
+ EXTN(jsimd_idct_ifast_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctfst-sse2.asm b/simd/i386/jidctfst-sse2.asm
+index 19704ff..1f4af33 100644
+--- a/simd/i386/jidctfst-sse2.asm
++++ b/simd/i386/jidctfst-sse2.asm
+@@ -94,6 +94,7 @@ PB_CENTERJSAMP times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_ifast_sse2)
+
+ EXTN(jsimd_idct_ifast_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctint-avx2.asm b/simd/i386/jidctint-avx2.asm
+index 199c7df..2eb606a 100644
+--- a/simd/i386/jidctint-avx2.asm
++++ b/simd/i386/jidctint-avx2.asm
+@@ -296,6 +296,7 @@ PW_1_NEG1                  times 8  dw  1
+     GLOBAL_FUNCTION(jsimd_idct_islow_avx2)
+
+ EXTN(jsimd_idct_islow_avx2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctint-mmx.asm b/simd/i386/jidctint-mmx.asm
+index f15c8d3..2d91b7e 100644
+--- a/simd/i386/jidctint-mmx.asm
++++ b/simd/i386/jidctint-mmx.asm
+@@ -109,6 +109,7 @@ PB_CENTERJSAMP times 8 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_islow_mmx)
+
+ EXTN(jsimd_idct_islow_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctint-sse2.asm b/simd/i386/jidctint-sse2.asm
+index 43e3201..804be19 100644
+--- a/simd/i386/jidctint-sse2.asm
++++ b/simd/i386/jidctint-sse2.asm
+@@ -107,6 +107,7 @@ PB_CENTERJSAMP times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_islow_sse2)
+
+ EXTN(jsimd_idct_islow_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+diff --git a/simd/i386/jidctred-mmx.asm b/simd/i386/jidctred-mmx.asm
+index e2307e1..cb43106 100644
+--- a/simd/i386/jidctred-mmx.asm
++++ b/simd/i386/jidctred-mmx.asm
+@@ -117,6 +117,7 @@ PB_CENTERJSAMP  times 8 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_4x4_mmx)
+
+ EXTN(jsimd_idct_4x4_mmx):
++    _endbr32
+     push        ebp
+     mov         eax, esp                    ; eax = original ebp
+     sub         esp, byte 4
+@@ -504,6 +505,7 @@ EXTN(jsimd_idct_4x4_mmx):
+     GLOBAL_FUNCTION(jsimd_idct_2x2_mmx)
+
+ EXTN(jsimd_idct_2x2_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jidctred-sse2.asm b/simd/i386/jidctred-sse2.asm
+index 6e56494..2a61b9e 100644
+--- a/simd/i386/jidctred-sse2.asm
++++ b/simd/i386/jidctred-sse2.asm
+@@ -115,6 +115,7 @@ PB_CENTERJSAMP  times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_4x4_sse2)
+
+ EXTN(jsimd_idct_4x4_sse2):
++    _endbr32
+     push        ebp
+     mov         eax, esp                     ; eax = original ebp
+     sub         esp, byte 4
+@@ -425,6 +426,7 @@ EXTN(jsimd_idct_4x4_sse2):
+     GLOBAL_FUNCTION(jsimd_idct_2x2_sse2)
+
+ EXTN(jsimd_idct_2x2_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+diff --git a/simd/i386/jquant-3dn.asm b/simd/i386/jquant-3dn.asm
+index 5cb60ca..a0599eb 100644
+--- a/simd/i386/jquant-3dn.asm
++++ b/simd/i386/jquant-3dn.asm
+@@ -36,6 +36,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_float_3dnow)
+
+ EXTN(jsimd_convsamp_float_3dnow):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+@@ -138,6 +139,7 @@ EXTN(jsimd_convsamp_float_3dnow):
+     GLOBAL_FUNCTION(jsimd_quantize_float_3dnow)
+
+ EXTN(jsimd_quantize_float_3dnow):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jquant-mmx.asm b/simd/i386/jquant-mmx.asm
+index 61305c6..080021b 100644
+--- a/simd/i386/jquant-mmx.asm
++++ b/simd/i386/jquant-mmx.asm
+@@ -36,6 +36,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_mmx)
+
+ EXTN(jsimd_convsamp_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+@@ -145,6 +146,7 @@ EXTN(jsimd_convsamp_mmx):
+     GLOBAL_FUNCTION(jsimd_quantize_mmx)
+
+ EXTN(jsimd_quantize_mmx):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jquant-sse.asm b/simd/i386/jquant-sse.asm
+index 218adc9..cacd2a9 100644
+--- a/simd/i386/jquant-sse.asm
++++ b/simd/i386/jquant-sse.asm
+@@ -36,6 +36,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_float_sse)
+
+ EXTN(jsimd_convsamp_float_sse):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+@@ -138,6 +139,7 @@ EXTN(jsimd_convsamp_float_sse):
+     GLOBAL_FUNCTION(jsimd_quantize_float_sse)
+
+ EXTN(jsimd_quantize_float_sse):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jquantf-sse2.asm b/simd/i386/jquantf-sse2.asm
+index a881ab5..6f4789c 100644
+--- a/simd/i386/jquantf-sse2.asm
++++ b/simd/i386/jquantf-sse2.asm
+@@ -36,6 +36,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_float_sse2)
+
+ EXTN(jsimd_convsamp_float_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+@@ -115,6 +116,7 @@ EXTN(jsimd_convsamp_float_sse2):
+     GLOBAL_FUNCTION(jsimd_quantize_float_sse2)
+
+ EXTN(jsimd_quantize_float_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jquanti-avx2.asm b/simd/i386/jquanti-avx2.asm
+index 5ed6bec..efcddd2 100644
+--- a/simd/i386/jquanti-avx2.asm
++++ b/simd/i386/jquanti-avx2.asm
+@@ -37,6 +37,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_avx2)
+
+ EXTN(jsimd_convsamp_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+@@ -130,6 +131,7 @@ EXTN(jsimd_convsamp_avx2):
+     GLOBAL_FUNCTION(jsimd_quantize_avx2)
+
+ EXTN(jsimd_quantize_avx2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/i386/jquanti-sse2.asm b/simd/i386/jquanti-sse2.asm
+index 0a50940..98d39e0 100644
+--- a/simd/i386/jquanti-sse2.asm
++++ b/simd/i386/jquanti-sse2.asm
+@@ -36,6 +36,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_sse2)
+
+ EXTN(jsimd_convsamp_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+     push        ebx
+@@ -121,6 +122,7 @@ EXTN(jsimd_convsamp_sse2):
+     GLOBAL_FUNCTION(jsimd_quantize_sse2)
+
+ EXTN(jsimd_quantize_sse2):
++    _endbr32
+     push        ebp
+     mov         ebp, esp
+ ;   push        ebx                     ; unused
+diff --git a/simd/nasm/jsimdext.inc b/simd/nasm/jsimdext.inc
+index bebcb20..4b0cd69 100644
+--- a/simd/nasm/jsimdext.inc
++++ b/simd/nasm/jsimdext.inc
+@@ -513,6 +513,14 @@ const_base:
+
+ %endif
+
++%imacro _endbr32 0
++    dd 0xfb1e0ff3
++%endmacro
++
++%imacro _endbr64 0
++    dd 0xfa1e0ff3
++%endmacro
++
+ ; --------------------------------------------------------------------------
+ ;  Defines picked up from the C headers
+ ;
+diff --git a/simd/x86_64/jccolext-avx2.asm b/simd/x86_64/jccolext-avx2.asm
+index dd7ea39..e6c8283 100644
+--- a/simd/x86_64/jccolext-avx2.asm
++++ b/simd/x86_64/jccolext-avx2.asm
+@@ -41,6 +41,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_avx2)
+
+ EXTN(jsimd_rgb_ycc_convert_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jccolext-sse2.asm b/simd/x86_64/jccolext-sse2.asm
+index bc1e817..4bb1af6 100644
+--- a/simd/x86_64/jccolext-sse2.asm
++++ b/simd/x86_64/jccolext-sse2.asm
+@@ -40,6 +40,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_ycc_convert_sse2)
+
+ EXTN(jsimd_rgb_ycc_convert_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jcgryext-avx2.asm b/simd/x86_64/jcgryext-avx2.asm
+index c8c8d12..12ecb7e 100644
+--- a/simd/x86_64/jcgryext-avx2.asm
++++ b/simd/x86_64/jcgryext-avx2.asm
+@@ -41,6 +41,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_gray_convert_avx2)
+
+ EXTN(jsimd_rgb_gray_convert_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jcgryext-sse2.asm b/simd/x86_64/jcgryext-sse2.asm
+index 7e5a0f2..e3a2413 100644
+--- a/simd/x86_64/jcgryext-sse2.asm
++++ b/simd/x86_64/jcgryext-sse2.asm
+@@ -40,6 +40,7 @@
+     GLOBAL_FUNCTION(jsimd_rgb_gray_convert_sse2)
+
+ EXTN(jsimd_rgb_gray_convert_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jchuff-sse2.asm b/simd/x86_64/jchuff-sse2.asm
+index 0c2cdd6..44ea81d 100644
+--- a/simd/x86_64/jchuff-sse2.asm
++++ b/simd/x86_64/jchuff-sse2.asm
+@@ -261,6 +261,7 @@ times 1 << 15 db 16
+     GLOBAL_FUNCTION(jsimd_huff_encode_one_block_sse2)
+
+ EXTN(jsimd_huff_encode_one_block_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+
+diff --git a/simd/x86_64/jcphuff-sse2.asm b/simd/x86_64/jcphuff-sse2.asm
+index 11db4b2..2157e97 100644
+--- a/simd/x86_64/jcphuff-sse2.asm
++++ b/simd/x86_64/jcphuff-sse2.asm
+@@ -282,6 +282,7 @@
+     GLOBAL_FUNCTION(jsimd_encode_mcu_AC_first_prepare_sse2)
+
+ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     and         rsp, byte (-SIZEOF_XMMWORD)  ; align to 128 bits
+@@ -445,6 +446,7 @@ EXTN(jsimd_encode_mcu_AC_first_prepare_sse2):
+     GLOBAL_FUNCTION(jsimd_encode_mcu_AC_refine_prepare_sse2)
+
+ EXTN(jsimd_encode_mcu_AC_refine_prepare_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     and         rsp, byte (-SIZEOF_XMMWORD)  ; align to 128 bits
+diff --git a/simd/x86_64/jcsample-avx2.asm b/simd/x86_64/jcsample-avx2.asm
+index 589c52b..7d8d4e0 100644
+--- a/simd/x86_64/jcsample-avx2.asm
++++ b/simd/x86_64/jcsample-avx2.asm
+@@ -44,6 +44,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_downsample_avx2)
+
+ EXTN(jsimd_h2v1_downsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 6
+@@ -205,6 +206,7 @@ EXTN(jsimd_h2v1_downsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_downsample_avx2)
+
+ EXTN(jsimd_h2v2_downsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 6
+diff --git a/simd/x86_64/jcsample-sse2.asm b/simd/x86_64/jcsample-sse2.asm
+index 7a4f1bc..8932b94 100644
+--- a/simd/x86_64/jcsample-sse2.asm
++++ b/simd/x86_64/jcsample-sse2.asm
+@@ -43,6 +43,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_downsample_sse2)
+
+ EXTN(jsimd_h2v1_downsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 6
+@@ -187,6 +188,7 @@ EXTN(jsimd_h2v1_downsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_downsample_sse2)
+
+ EXTN(jsimd_h2v2_downsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 6
+diff --git a/simd/x86_64/jdcolext-avx2.asm b/simd/x86_64/jdcolext-avx2.asm
+index 070436c..6c0a212 100644
+--- a/simd/x86_64/jdcolext-avx2.asm
++++ b/simd/x86_64/jdcolext-avx2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_avx2)
+
+ EXTN(jsimd_ycc_rgb_convert_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jdcolext-sse2.asm b/simd/x86_64/jdcolext-sse2.asm
+index bba3a30..28d05e4 100644
+--- a/simd/x86_64/jdcolext-sse2.asm
++++ b/simd/x86_64/jdcolext-sse2.asm
+@@ -41,6 +41,7 @@
+     GLOBAL_FUNCTION(jsimd_ycc_rgb_convert_sse2)
+
+ EXTN(jsimd_ycc_rgb_convert_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jdmrgext-avx2.asm b/simd/x86_64/jdmrgext-avx2.asm
+index 1191645..f58384c 100644
+--- a/simd/x86_64/jdmrgext-avx2.asm
++++ b/simd/x86_64/jdmrgext-avx2.asm
+@@ -42,6 +42,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_avx2)
+
+ EXTN(jsimd_h2v1_merged_upsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+@@ -506,6 +507,7 @@ EXTN(jsimd_h2v1_merged_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_avx2)
+
+ EXTN(jsimd_h2v2_merged_upsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+diff --git a/simd/x86_64/jdmrgext-sse2.asm b/simd/x86_64/jdmrgext-sse2.asm
+index 8988dd0..8641aa3 100644
+--- a/simd/x86_64/jdmrgext-sse2.asm
++++ b/simd/x86_64/jdmrgext-sse2.asm
+@@ -41,6 +41,7 @@
+     GLOBAL_FUNCTION(jsimd_h2v1_merged_upsample_sse2)
+
+ EXTN(jsimd_h2v1_merged_upsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+@@ -448,6 +449,7 @@ EXTN(jsimd_h2v1_merged_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_merged_upsample_sse2)
+
+ EXTN(jsimd_h2v2_merged_upsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+diff --git a/simd/x86_64/jdsample-avx2.asm b/simd/x86_64/jdsample-avx2.asm
+index c6ddbb5..c5594ed 100644
+--- a/simd/x86_64/jdsample-avx2.asm
++++ b/simd/x86_64/jdsample-avx2.asm
+@@ -62,6 +62,7 @@ PW_EIGHT times 16 dw 8
+     GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_avx2)
+
+ EXTN(jsimd_h2v1_fancy_upsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push_xmm    3
+@@ -215,6 +216,7 @@ EXTN(jsimd_h2v1_fancy_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_avx2)
+
+ EXTN(jsimd_h2v2_fancy_upsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+@@ -524,6 +526,7 @@ EXTN(jsimd_h2v2_fancy_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v1_upsample_avx2)
+
+ EXTN(jsimd_h2v1_upsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+@@ -612,6 +615,7 @@ EXTN(jsimd_h2v1_upsample_avx2):
+     GLOBAL_FUNCTION(jsimd_h2v2_upsample_avx2)
+
+ EXTN(jsimd_h2v2_upsample_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+diff --git a/simd/x86_64/jdsample-sse2.asm b/simd/x86_64/jdsample-sse2.asm
+index 24cd389..27bf771 100644
+--- a/simd/x86_64/jdsample-sse2.asm
++++ b/simd/x86_64/jdsample-sse2.asm
+@@ -61,6 +61,7 @@ PW_EIGHT times 8 dw 8
+     GLOBAL_FUNCTION(jsimd_h2v1_fancy_upsample_sse2)
+
+ EXTN(jsimd_h2v1_fancy_upsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+@@ -202,6 +203,7 @@ EXTN(jsimd_h2v1_fancy_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_fancy_upsample_sse2)
+
+ EXTN(jsimd_h2v2_fancy_upsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+@@ -497,6 +499,7 @@ EXTN(jsimd_h2v2_fancy_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v1_upsample_sse2)
+
+ EXTN(jsimd_h2v1_upsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+@@ -583,6 +586,7 @@ EXTN(jsimd_h2v1_upsample_sse2):
+     GLOBAL_FUNCTION(jsimd_h2v2_upsample_sse2)
+
+ EXTN(jsimd_h2v2_upsample_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+diff --git a/simd/x86_64/jfdctflt-sse.asm b/simd/x86_64/jfdctflt-sse.asm
+index 3595496..b5abeee 100644
+--- a/simd/x86_64/jfdctflt-sse.asm
++++ b/simd/x86_64/jfdctflt-sse.asm
+@@ -66,6 +66,7 @@ PD_1_306 times 4 dd 1.306562964876376527856643
+     GLOBAL_FUNCTION(jsimd_fdct_float_sse)
+
+ EXTN(jsimd_fdct_float_sse):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jfdctfst-sse2.asm b/simd/x86_64/jfdctfst-sse2.asm
+index d33c58a..78af5e4 100644
+--- a/simd/x86_64/jfdctfst-sse2.asm
++++ b/simd/x86_64/jfdctfst-sse2.asm
+@@ -81,6 +81,7 @@ PW_F1306 times 8 dw F_1_306 << CONST_SHIFT
+     GLOBAL_FUNCTION(jsimd_fdct_ifast_sse2)
+
+ EXTN(jsimd_fdct_ifast_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jfdctint-avx2.asm b/simd/x86_64/jfdctint-avx2.asm
+index d0afe5e..b083fbb 100644
+--- a/simd/x86_64/jfdctint-avx2.asm
++++ b/simd/x86_64/jfdctint-avx2.asm
+@@ -260,6 +260,7 @@ PW_1_NEG1                  times 8  dw  1
+     GLOBAL_FUNCTION(jsimd_fdct_islow_avx2)
+
+ EXTN(jsimd_fdct_islow_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 1
+diff --git a/simd/x86_64/jfdctint-sse2.asm b/simd/x86_64/jfdctint-sse2.asm
+index 024ce90..88ea491 100644
+--- a/simd/x86_64/jfdctint-sse2.asm
++++ b/simd/x86_64/jfdctint-sse2.asm
+@@ -102,6 +102,7 @@ PW_DESCALE_P2X times 8 dw  1 << (PASS1_BITS - 1)
+     GLOBAL_FUNCTION(jsimd_fdct_islow_sse2)
+
+ EXTN(jsimd_fdct_islow_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jidctflt-sse2.asm b/simd/x86_64/jidctflt-sse2.asm
+index 952fbe3..8610710 100644
+--- a/simd/x86_64/jidctflt-sse2.asm
++++ b/simd/x86_64/jidctflt-sse2.asm
+@@ -76,6 +76,7 @@ PB_CENTERJSAMP  times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_float_sse2)
+
+ EXTN(jsimd_idct_float_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jidctfst-sse2.asm b/simd/x86_64/jidctfst-sse2.asm
+index a3da8d8..351ee06 100644
+--- a/simd/x86_64/jidctfst-sse2.asm
++++ b/simd/x86_64/jidctfst-sse2.asm
+@@ -95,6 +95,7 @@ PB_CENTERJSAMP times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_ifast_sse2)
+
+ EXTN(jsimd_idct_ifast_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jidctint-avx2.asm b/simd/x86_64/jidctint-avx2.asm
+index 528da01..00087c7 100644
+--- a/simd/x86_64/jidctint-avx2.asm
++++ b/simd/x86_64/jidctint-avx2.asm
+@@ -282,6 +282,7 @@ PW_1_NEG1                  times 8  dw  1
+     GLOBAL_FUNCTION(jsimd_idct_islow_avx2)
+
+ EXTN(jsimd_idct_islow_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp                     ; rbp = aligned rbp
+     push_xmm    4
+diff --git a/simd/x86_64/jidctint-sse2.asm b/simd/x86_64/jidctint-sse2.asm
+index 92f633e..9301e81 100644
+--- a/simd/x86_64/jidctint-sse2.asm
++++ b/simd/x86_64/jidctint-sse2.asm
+@@ -108,6 +108,7 @@ PB_CENTERJSAMP times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_islow_sse2)
+
+ EXTN(jsimd_idct_islow_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+diff --git a/simd/x86_64/jidctred-sse2.asm b/simd/x86_64/jidctred-sse2.asm
+index 1ec500c..e74162d 100644
+--- a/simd/x86_64/jidctred-sse2.asm
++++ b/simd/x86_64/jidctred-sse2.asm
+@@ -116,6 +116,7 @@ PB_CENTERJSAMP  times 16 db  CENTERJSAMPLE
+     GLOBAL_FUNCTION(jsimd_idct_4x4_sse2)
+
+ EXTN(jsimd_idct_4x4_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     push        r15
+@@ -413,6 +414,7 @@ EXTN(jsimd_idct_4x4_sse2):
+     GLOBAL_FUNCTION(jsimd_idct_2x2_sse2)
+
+ EXTN(jsimd_idct_2x2_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 4
+diff --git a/simd/x86_64/jquantf-sse2.asm b/simd/x86_64/jquantf-sse2.asm
+index 232bbb2..2d65986 100644
+--- a/simd/x86_64/jquantf-sse2.asm
++++ b/simd/x86_64/jquantf-sse2.asm
+@@ -37,6 +37,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_float_sse2)
+
+ EXTN(jsimd_convsamp_float_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 3
+@@ -109,6 +110,7 @@ EXTN(jsimd_convsamp_float_sse2):
+     GLOBAL_FUNCTION(jsimd_quantize_float_sse2)
+
+ EXTN(jsimd_quantize_float_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 3
+diff --git a/simd/x86_64/jquanti-avx2.asm b/simd/x86_64/jquanti-avx2.asm
+index 66104d7..a7ea496 100644
+--- a/simd/x86_64/jquanti-avx2.asm
++++ b/simd/x86_64/jquanti-avx2.asm
+@@ -38,6 +38,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_avx2)
+
+ EXTN(jsimd_convsamp_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 3
+@@ -115,6 +116,7 @@ EXTN(jsimd_convsamp_avx2):
+     GLOBAL_FUNCTION(jsimd_quantize_avx2)
+
+ EXTN(jsimd_quantize_avx2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 3
+diff --git a/simd/x86_64/jquanti-sse2.asm b/simd/x86_64/jquanti-sse2.asm
+index 11e9f4c..24cb1cf 100644
+--- a/simd/x86_64/jquanti-sse2.asm
++++ b/simd/x86_64/jquanti-sse2.asm
+@@ -37,6 +37,7 @@
+     GLOBAL_FUNCTION(jsimd_convsamp_sse2)
+
+ EXTN(jsimd_convsamp_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 3
+@@ -115,6 +116,7 @@ EXTN(jsimd_convsamp_sse2):
+     GLOBAL_FUNCTION(jsimd_quantize_sse2)
+
+ EXTN(jsimd_quantize_sse2):
++    _endbr64
+     push        rbp
+     mov         rbp, rsp
+     collect_args 3
+--
diff --git a/base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch
new file mode 100644
index 0000000..ef4c14d
--- /dev/null
+++ b/base/rx/rx-libjpeg-turbo/libjpeg-turbo-cmake.patch
@@ -0,0 +1,33 @@
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index adb0ca4..902c271 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -1768,18 +1768,6 @@ endif()
+
+ install(TARGETS rdjpgcom wrjpgcom RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+-install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.ijg
+-  ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/example.c
+-  ${CMAKE_CURRENT_SOURCE_DIR}/tjexample.c
+-  ${CMAKE_CURRENT_SOURCE_DIR}/libjpeg.txt
+-  ${CMAKE_CURRENT_SOURCE_DIR}/structure.txt
+-  ${CMAKE_CURRENT_SOURCE_DIR}/usage.txt ${CMAKE_CURRENT_SOURCE_DIR}/wizard.txt
+-  ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md DESTINATION ${CMAKE_INSTALL_DOCDIR})
+-if(WITH_JAVA)
+-  install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/java/TJExample.java
+-    DESTINATION ${CMAKE_INSTALL_DOCDIR})
+-endif()
+-
+ if(UNIX OR MINGW)
+   install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cjpeg.1
+     ${CMAKE_CURRENT_SOURCE_DIR}/djpeg.1 ${CMAKE_CURRENT_SOURCE_DIR}/jpegtran.1
+@@ -1803,7 +1791,7 @@ install(EXPORT ${CMAKE_PROJECT_NAME}Targets
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jconfig.h
+   ${CMAKE_CURRENT_SOURCE_DIR}/jerror.h ${CMAKE_CURRENT_SOURCE_DIR}/jmorecfg.h
+-  ${CMAKE_CURRENT_SOURCE_DIR}/jpeglib.h
++  ${CMAKE_CURRENT_SOURCE_DIR}/jpeglib.h ${CMAKE_CURRENT_SOURCE_DIR}/jpegint.h
+   DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+ include(cmakescripts/BuildPackages.cmake)
+--
diff --git a/base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec b/base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec
new file mode 100644
index 0000000..6354a9a
--- /dev/null
+++ b/base/rx/rx-libjpeg-turbo/libjpeg-turbo.spec
@@ -0,0 +1,508 @@
+%global realname    libjpeg-turbo
+%global _sys_bindir %_bindir
+%global _prefix     /opt/rx
+%global _bindir     %{_prefix}/bin
+%global _libdir     %{_prefix}/%{_lib}
+%global _datadir    %{_prefix}/share
+%global _includedir %{_prefix}/include
+%global _mandir     %{_datadir}/man
+
+Name:           rx-libjpeg-turbo
+Version:        3.0.2
+Release:        1%{?dist}
+Summary:        A MMX/SSE2/SIMD accelerated library for manipulating JPEG image files
+License:        IJG
+URL:            http://sourceforge.net/projects/libjpeg-turbo
+
+Source0:        http://downloads.sourceforge.net/%{realname}/%{realname}-%{version}.tar.gz
+Patch0:         libjpeg-turbo-cmake.patch
+Patch1:         libjpeg-turbo-CET.patch
+
+BuildRequires:  gcc
+BuildRequires:  cmake
+BuildRequires:  libtool
+BuildRequires:  nasm
+
+
+%description
+The libjpeg-turbo package contains a library of functions for manipulating JPEG
+images.
+
+%package devel
+Summary:        Headers for the libjpeg-turbo library
+Requires:       rx-libjpeg-turbo%{?_isa} = %{version}-%{release}
+
+
+%description devel
+This package contains header files necessary for developing programs which will
+manipulate JPEG files using the libjpeg-turbo library.
+
+%package utils
+Summary:        Utilities for manipulating JPEG images
+Requires:       rx-libjpeg-turbo%{?_isa} = %{version}-%{release}
+
+%description utils
+The libjpeg-turbo-utils package contains simple client programs for accessing
+the libjpeg functions. It contains cjpeg, djpeg, jpegtran, rdjpgcom and
+wrjpgcom. Cjpeg compresses an image file into JPEG format. Djpeg decompresses a
+JPEG file into a regular image file. Jpegtran can perform various useful
+transformations on JPEG files. Rdjpgcom displays any text comments included in a
+JPEG file. Wrjpgcom inserts text comments into a JPEG file.
+
+%package -n rx-turbojpeg
+Summary:        TurboJPEG library
+
+%description -n rx-turbojpeg
+The turbojpeg package contains the TurboJPEG shared library.
+
+%package -n rx-turbojpeg-devel
+Summary:        Headers for the TurboJPEG library
+Requires:       rx-turbojpeg%{?_isa} = %{version}-%{release}
+
+%description -n rx-turbojpeg-devel
+This package contains header files necessary for developing programs which will
+manipulate JPEG files using the TurboJPEG library.
+
+%prep
+%autosetup -p1 -n %{realname}-%{version}
+
+%build
+export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
+export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} $LDFLAGS"
+# NASM object files are missing GNU Property note for Intel CET,
+# force it on the resulting library
+%ifarch %{ix86} x86_64
+export LDFLAGS="$RPM_LD_FLAGS -Wl,-z,ibt -Wl,-z,shstk"
+%endif
+
+%{cmake} -DCMAKE_SKIP_RPATH:BOOL=YES \
+         -DCMAKE_SKIP_INSTALL_RPATH:BOOL=YES \
+%ifarch s390x
+         -DFLOATTEST:STRING="fp-contract" \
+%endif
+         -DENABLE_STATIC:BOOL=NO
+
+%cmake_build
+
+%install
+%cmake_install
+find %{buildroot} -name "*.la" -delete
+
+# Fix perms
+chmod -x README.md
+
+# Remove tjbench
+rm -f %{buildroot}/%{_bindir}/tjbench
+
+# multilib header hack
+# we only apply this to known Red Hat multilib arches, per bug #1264675
+case `uname -i` in
+  i386 | ppc | s390 | sparc )
+    wordsize="32"
+    ;;
+  x86_64 | ppc64 | s390x | sparc64 )
+    wordsize="64"
+    ;;
+  *)
+    wordsize=""
+    ;;
+esac
+
+if test -n "$wordsize"
+then
+  mv $RPM_BUILD_ROOT%{_includedir}/jconfig.h \
+     $RPM_BUILD_ROOT%{_includedir}/jconfig-$wordsize.h
+
+  cat >$RPM_BUILD_ROOT%{_includedir}/jconfig.h <<EOF
+#ifndef JCONFIG_H_MULTILIB
+#define JCONFIG_H_MULTILIB
+
+#include <bits/wordsize.h>
+
+#if __WORDSIZE == 32
+# include "jconfig-32.h"
+#elif __WORDSIZE == 64
+# include "jconfig-64.h"
+#else
+# error "unexpected value for __WORDSIZE macro"
+#endif
+
+#endif
+EOF
+
+fi
+
+mkdir -p %{buildroot}%{_sys_bindir} 
+for bin in cjpeg djpeg jpegtran rdjpgcom wrjpgcom
+do
+  ln -sf %{_bindir}/${bin} %{buildroot}/%{_sys_bindir}/${bin}-3
+done
+
+%check
+export LD_LIBRARY_PATH=%{buildroot}%{_libdir}
+%ctest
+
+%ldconfig_scriptlets
+%ldconfig_scriptlets -n rx-turbojpeg
+
+%files
+%license LICENSE.md
+%doc README.md README.ijg ChangeLog.md
+%{_libdir}/libjpeg.so.62*
+
+%files devel
+%doc coderules.txt jconfig.txt libjpeg.txt structure.txt
+%{_includedir}/jconfig*.h
+%{_includedir}/jerror.h
+%{_includedir}/jmorecfg.h
+%{_includedir}/jpegint.h
+%{_includedir}/jpeglib.h
+%{_libdir}/libjpeg.so
+%{_libdir}/pkgconfig/libjpeg.pc
+%{_libdir}/cmake/%{realname}/%{realname}*.cmake
+
+%files utils
+%doc usage.txt wizard.txt
+%{_sys_bindir}/*-3
+%{_bindir}/cjpeg
+%{_bindir}/djpeg
+%{_bindir}/jpegtran
+%{_bindir}/rdjpgcom
+%{_bindir}/wrjpgcom
+%{_mandir}/man1/cjpeg.1*
+%{_mandir}/man1/djpeg.1*
+%{_mandir}/man1/jpegtran.1*
+%{_mandir}/man1/rdjpgcom.1*
+%{_mandir}/man1/wrjpgcom.1*
+
+%files -n rx-turbojpeg
+%license LICENSE.md
+%doc README.md README.ijg ChangeLog.md
+%{_libdir}/libturbojpeg.so.0*
+
+%files -n rx-turbojpeg-devel
+%doc tjexample.c
+%{_includedir}/turbojpeg.h
+%{_libdir}/libturbojpeg.so
+%{_libdir}/pkgconfig/libturbojpeg.pc
+
+%changelog
+* Tue May  7 2024 Raven <raven@sysadmins.ws> - 3.0.2-1
+- New upstream release 3.0.2
+
+* Mon Aug 15 2022 Nikola Forró <nforro@redhat.com> - 2.1.4-1
+- New upstream release 2.1.4 (#2118023)
+
+* Thu Jul 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 2.1.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
+
+* Sun Feb 27 2022 Nikola Forró <nforro@redhat.com> - 2.1.3-1
+- New upstream release 2.1.3 (#2058898)
+
+* Thu Jan 20 2022 Fedora Release Engineering <releng@fedoraproject.org> - 2.1.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
+
+* Mon Nov 22 2021 Matej Mužila <mmuzila@redhat.com> - 2.1.2-1
+- New upstream release 2.1.2 (#2025141)
+
+* Wed Aug 11 2021 Nikola Forró <nforro@redhat.com> - 2.1.1-1
+- New upstream release 2.1.1 (#1991844)
+
+* Thu Jul 22 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.1.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
+
+* Mon Apr 26 2021 Nikola Forró <nforro@redhat.com> - 2.1.0-1
+- New upstream release 2.1.0 (#1953074)
+
+* Thu Mar 25 2021 Nikola Forró <nforro@redhat.com> - 2.0.90-2
+- Fix CVE-2021-20205 (#1937387)
+
+* Thu Jan 28 2021 Nikola Forró <nforro@redhat.com> - 2.0.90-1
+- New upstream release 2.0.90 (#1898427)
+
+* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.5-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Tue Aug 04 2020 Nikola Forró <nforro@redhat.com> - 2.0.5-5
+- Fix FTBFS (#1864007)
+
+* Sat Aug 01 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.5-4
+- Second attempt - Rebuilt for
+  https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.5-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Mon Jul 13 2020 Tom Stellard <tstellar@redhat.com> - 2.0.5-2
+- Use make macros
+- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro
+
+* Fri Jul 03 2020 Nikola Forró <nforro@redhat.com> - 2.0.5-1
+- New upstream release 2.0.5 (#1850293)
+
+* Tue Jun 16 2020 Nikola Forró <nforro@redhat.com> - 2.0.4-3
+- Fix CVE-2020-13790 (#1847159)
+
+* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.4-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Wed Jan 08 2020 Nikola Forró <nforro@redhat.com> - 2.0.4-1
+- New upstream release 2.0.4 (#1787793)
+
+* Thu Sep 05 2019 Nikola Forró <nforro@redhat.com> - 2.0.3-1
+- New upstream release 2.0.3 (#1749130)
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.2-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Tue Jun 04 2019 Nikola Forró <nforro@redhat.com> - 2.0.2-3
+- Fix LDFLAGS
+
+* Mon Apr 29 2019 Nikola Forró <nforro@redhat.com> - 2.0.2-2
+- Support running with Intel CET
+
+* Wed Feb 27 2019 Nikola Forró <nforro@redhat.com> - 2.0.2-1
+- New upstream release 2.0.2
+
+* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.0.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Fri Jan 11 2019 Nikola Forró <nforro@redhat.com> - 2.0.0-3
+- Fix CVE-2018-19664 (#1656219)
+
+* Fri Jan 11 2019 Nikola Forró <nforro@redhat.com> - 2.0.0-2
+- Fix CVE-2018-20330 (#1665224)
+
+* Mon Jul 30 2018 Nikola Forró <nforro@redhat.com> - 2.0.0-1
+- New upstream release 2.0.0 (#1609439)
+
+* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.90-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Fri Jun 29 2018 Nikola Forró <nforro@redhat.com> - 1.5.90-3
+- Fix CVE-2018-1152 (#1593555)
+
+* Fri Jun 15 2018 Nikola Forró <nforro@redhat.com> - 1.5.90-2
+- Fix CVE-2018-11813 (#1588804)
+
+* Wed Mar 28 2018 Nikola Forró <nforro@redhat.com> - 1.5.90-1
+- New upstream release 1.5.90 (#1560219)
+
+* Tue Feb 20 2018 Nikola Forró <nforro@redhat.com> - 1.5.3-4
+- Add missing gcc build dependency
+
+* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.3-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Sat Feb 03 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 1.5.3-2
+- Switch to %%ldconfig_scriptlets
+
+* Tue Dec 19 2017 Nikola Forró <nforro@redhat.com> - 1.5.3-1
+- New upstream release 1.5.3 (#1468783)
+
+* Tue Dec 19 2017 Nikola Forró <nforro@redhat.com> - 1.5.1-5
+- re-enable check on ppc64(le)
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Thu May 18 2017 Karsten Hopp <karsten@redhat.com> - 1.5.1-2
+- disable check on ppc64(le)
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 1.5.1-1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Thu Oct 06 2016 Petr Hracek <phracek@redhat.com> - 1.5.1-1
+- New upstream relelase 1.5.1 (#1377903)
+
+* Wed Sep 21 2016 Peter Robinson <pbrobinson@fedoraproject.org> 1.5.0-4
+- Add upstream aarch64 NEON fix, re-enable SIMD on aarch64
+
+* Mon Sep 19 2016 Peter Robinson <pbrobinson@fedoraproject.org> 1.5.0-3
+- Temporarily disable SIMD on aarch64 until upstream #97 is fixed
+- Add NEON fix for ARMv7
+
+* Tue Sep 13 2016 Peter Robinson <pbrobinson@fedoraproject.org> 1.5.0-2
+- Add upstream fix to fix SIMD crash on aarch64 (rhbz #1368569)
+
+* Tue Jun 21 2016 Petr Hracek <phracek@redhat.com> - 1.5.0-1
+- New upstream release 1.5.0 (#1343786)
+
+* Thu Mar 10 2016 Petr Hracek <phracek@redhat.com> - 1.4.90-1
+- New upstream release 1.4.90 (#1313111)
+
+* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 1.4.2-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Wed Oct 07 2015 Petr Hracek <phracek@redhat.com> - 1.4.2-2
+- Fix problem with multilibs like jconfig.h (#1264675)
+
+* Wed Oct 07 2015 Petr Hracek <phracek@redhat.com> - 1.4.2-1
+- New upstream release 1.4.2 (#1265034)
+
+* Tue Jun 16 2015 Peter Robinson <pbrobinson@fedoraproject.org> 1.4.1-1
+- new upstream version 1.4.1
+- nasm available on all arches
+- run tests with SMP
+
+* Tue Jan 20 2015 Petr Hracek <phracek@redhat.com> - 1.4.0-1
+- new upstream version 1.4.0 (#1180442)
+
+* Wed Nov 26 2014 Petr Hracek <phracek@redhat.com> - 1.3.90-3
+- libjpeg-turbo no longer defined macros like JPP (#1164815)
+
+* Wed Nov 19 2014 Petr Hracek <phracek@redhat.com> - 1.3.90-2
+- Resolves #1161585 Add suport for secondary arches
+
+* Mon Oct 27 2014 Petr Hracek <phracek@redhat.com> - 1.3.90-1
+- new upstream version 1.3.90
+Resolves #1135375
+
+* Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.3.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Thu Apr 17 2014 Simone Caronni <negativo17@gmail.com> - 1.3.1-2
+- Re-add libjpeg-devel requirements for broken packages since Fedora 13.
+
+* Wed Apr 16 2014 Petr Hracek <phracek@redhat.com> - 1.3.1-1
+- New upstream version
+- Remove upstreamed patches, add missing jpegint.h
+- Clean up SPEC file
+- Disable --static subpackage
+- Remove libjpeg obsolency, removed in Fedora 13
+
+* Thu Dec 19 2013 Petr Hracek <phracek@redhat.com> - 1.3.0-2
+- Apply fixes CVE-2013-6629, CVE-2013-6630 (#20131737)
+
+* Thu Jul 25 2013 Petr Hracek <phracek@redhat.com> - 1.3.0-1
+- new upstream version
+- no soname bump change
+
+* Tue Mar 26 2013 Adam Tkac <atkac redhat com> - 1.2.90-2
+- rebuild for ARM64 support
+
+* Fri Feb 08 2013 Adam Tkac <atkac redhat com> 1.2.90-1
+- update to 1.2.90
+
+* Mon Feb 04 2013 Adam Tkac <atkac redhat com> 1.2.90-0.1.20130204svn922
+- update to 1.2.80 snapshot (#854695)
+- run `make test` during build
+
+* Fri Jan 18 2013 Adam Tkac <atkac redhat com> 1.2.1-6
+- build with jpeg6 API/ABI (jpeg8-ABI feature was dropped)
+
+* Tue Dec 04 2012 Adam Tkac <atkac redhat com> 1.2.1-5
+- change license to IJG (#877517)
+
+* Wed Oct 24 2012 Adam Tkac <atkac redhat com> 1.2.1-4
+- build with jpeg8 API/ABI (#854695)
+
+* Thu Oct 18 2012 Adam Tkac <atkac redhat com> 1.2.1-3
+- minor provides tuning (#863231)
+
+* Thu Jul 19 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.2.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Mon Jul 16 2012 Adam Tkac <atkac redhat com> 1.2.1-1
+- update to 1.2.1
+
+* Thu Mar 08 2012 Adam Tkac <atkac redhat com> 1.2.0-1
+- update to 1.2.0
+
+* Fri Jan 13 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.1.1-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Mon Nov 21 2011 Orion Poplawski <orion cora nwra com> 1.1.1-3
+- Make turobojpeg-devel depend on turbojpeg
+
+* Fri Oct 7 2011 Orion Poplawski <orion cora nwra com> 1.1.1-2
+- Ship the turbojpeg library (#744258)
+
+* Mon Jul 11 2011 Adam Tkac <atkac redhat com> 1.1.1-1
+- update to 1.1.1
+  - ljt11-rh688712.patch merged
+
+* Tue Mar 22 2011 Adam Tkac <atkac redhat com> 1.1.0-2
+- handle broken JPEGs better (#688712)
+
+* Tue Mar 01 2011 Adam Tkac <atkac redhat com> 1.1.0-1
+- update to 1.1.0
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.0.90-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Mon Jan 17 2011 Adam Tkac <atkac redhat com> 1.0.90-1
+- update to 1.0.90
+- libjpeg-turbo10-rh639672.patch merged
+
+* Fri Oct 29 2010 Adam Tkac <atkac redhat com> 1.0.1-3
+- add support for arithmetic coded files into decoder (#639672)
+
+* Wed Sep 29 2010 jkeating - 1.0.1-2
+- Rebuilt for gcc bug 634757
+
+* Mon Sep 13 2010 Adam Tkac <atkac redhat com> 1.0.1-1
+- update to 1.0.1
+  - libjpeg-turbo10-rh617469.patch merged
+- add -static subpkg (#632859)
+
+* Wed Aug 04 2010 Adam Tkac <atkac redhat com> 1.0.0-3
+- fix huffman decoder to handle broken JPEGs well (#617469)
+
+* Fri Jul 02 2010 Adam Tkac <atkac redhat com> 1.0.0-2
+- add libjpeg-devel%%{_isa} provides to -devel subpkg to satisfy imlib-devel
+  deps
+
+* Fri Jul 02 2010 Adam Tkac <atkac redhat com> 1.0.0-1
+- update to 1.0.0
+- patches merged
+  - libjpeg-turbo-programs.patch
+  - libjpeg-turbo-nosimd.patch
+- add libjpeg provides to the main package to workaround problems with broken
+  java-1.6.0-openjdk package
+
+* Fri Jul 02 2010 Adam Tkac <atkac redhat com> 0.0.93-13
+- remove libjpeg provides from -utils subpkg
+
+* Wed Jun 30 2010 Rex Dieter <rdieter@fedoraproject.org> 0.0.93-12
+- move Obsoletes: libjpeg to main pkg
+
+* Wed Jun 30 2010 Rex Dieter <rdieter@fedoraproject.org> 0.0.93-11
+- -utils: Requires: %%name ...
+
+* Wed Jun 30 2010 Adam Tkac <atkac redhat com> 0.0.93-10
+- add Provides = libjpeg to -utils subpackage
+
+* Mon Jun 28 2010 Adam Tkac <atkac redhat com> 0.0.93-9
+- merge review related fixes (#600243)
+
+* Wed Jun 16 2010 Adam Tkac <atkac redhat com> 0.0.93-8
+- merge review related fixes (#600243)
+
+* Mon Jun 14 2010 Adam Tkac <atkac redhat com> 0.0.93-7
+- obsolete -static libjpeg subpackage (#600243)
+
+* Mon Jun 14 2010 Adam Tkac <atkac redhat com> 0.0.93-6
+- improve package description a little (#600243)
+- include example.c as %%doc in the -devel subpackage
+
+* Fri Jun 11 2010 Adam Tkac <atkac redhat com> 0.0.93-5
+- don't use "fc12" disttag in obsoletes/provides (#600243)
+
+* Thu Jun 10 2010 Adam Tkac <atkac redhat com> 0.0.93-4
+- fix compilation on platforms without MMX/SSE (#600243)
+
+* Thu Jun 10 2010 Adam Tkac <atkac redhat com> 0.0.93-3
+- package review related fixes (#600243)
+
+* Wed Jun 09 2010 Adam Tkac <atkac redhat com> 0.0.93-2
+- package review related fixes (#600243)
+
+* Fri Jun 04 2010 Adam Tkac <atkac redhat com> 0.0.93-1
+- initial package
diff --git a/base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch b/base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch
new file mode 100644
index 0000000..01ca0dd
--- /dev/null
+++ b/base/rx/rx-libwebp/95ea5226c870449522240ccff26f0b006037c520.patch
@@ -0,0 +1,26 @@
+diff -rupN --no-dereference libwebp-1.3.2/src/dec/vp8l_dec.c libwebp-1.3.2-new/src/dec/vp8l_dec.c
+--- libwebp-1.3.2/src/dec/vp8l_dec.c	2023-09-14 00:11:07.000000000 +0200
++++ libwebp-1.3.2-new/src/dec/vp8l_dec.c	2023-09-28 20:47:39.648154201 +0200
+@@ -1241,9 +1241,20 @@ static int DecodeImageData(VP8LDecoder*
+   }
+ 
+   br->eos_ = VP8LIsEndOfStream(br);
+-  if (dec->incremental_ && br->eos_ && src < src_end) {
++  // In incremental decoding:
++  // br->eos_ && src < src_last: if 'br' reached the end of the buffer and
++  // 'src_last' has not been reached yet, there is not enough data. 'dec' has to
++  // be reset until there is more data.
++  // !br->eos_ && src < src_last: this cannot happen as either the buffer is
++  // fully read, either enough has been read to reach 'src_last'.
++  // src >= src_last: 'src_last' is reached, all is fine. 'src' can actually go
++  // beyond 'src_last' in case the image is cropped and an LZ77 goes further.
++  // The buffer might have been enough or there is some left. 'br->eos_' does
++  // not matter.
++  assert(!dec->incremental_ || (br->eos_ && src < src_last) || src >= src_last);
++  if (dec->incremental_ && br->eos_ && src < src_last) {
+     RestoreState(dec);
+-  } else if (!br->eos_) {
++  } else if ((dec->incremental_ && src >= src_last) || !br->eos_) {
+     // Process the remaining rows corresponding to last row-block.
+     if (process_func != NULL) {
+       process_func(dec, row > last_row ? last_row : row);
diff --git a/base/rx/rx-libwebp/fix-cmake-files-location.patch b/base/rx/rx-libwebp/fix-cmake-files-location.patch
new file mode 100644
index 0000000..d9fe5c5
--- /dev/null
+++ b/base/rx/rx-libwebp/fix-cmake-files-location.patch
@@ -0,0 +1,11 @@
+--- a/CMakeLists.txt	2024-04-13 02:48:48.000000000 +0600
++++ b/CMakeLists.txt	2024-05-13 10:06:57.159443901 +0600
+@@ -781,7 +781,7 @@
+   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+-set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/)
++set(ConfigPackageLocation ${CMAKE_CONFIG_PREFIX}/cmake/${PROJECT_NAME}/)
+ install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}::
+         DESTINATION ${ConfigPackageLocation})
+ 
diff --git a/base/rx/rx-libwebp/libwebp-cmakedir.patch b/base/rx/rx-libwebp/libwebp-cmakedir.patch
new file mode 100644
index 0000000..811794a
--- /dev/null
+++ b/base/rx/rx-libwebp/libwebp-cmakedir.patch
@@ -0,0 +1,12 @@
+diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt
+--- libwebp-1.4.0/CMakeLists.txt	2024-04-14 15:30:20.053730120 +0200
++++ libwebp-1.4.0-new/CMakeLists.txt	2024-04-14 15:30:20.056730140 +0200
+@@ -784,7 +784,7 @@ install(
+   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+   LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+-set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/)
++set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/)
+ install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}::
+         DESTINATION ${ConfigPackageLocation})
+ 
diff --git a/base/rx/rx-libwebp/libwebp-freeglut.patch b/base/rx/rx-libwebp/libwebp-freeglut.patch
new file mode 100644
index 0000000..676e71d
--- /dev/null
+++ b/base/rx/rx-libwebp/libwebp-freeglut.patch
@@ -0,0 +1,35 @@
+diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt
+--- libwebp-1.4.0/CMakeLists.txt	2024-04-12 22:48:48.000000000 +0200
++++ libwebp-1.4.0-new/CMakeLists.txt	2024-04-14 15:30:19.981729641 +0200
+@@ -582,8 +582,8 @@ endif()
+ 
+ if(WEBP_BUILD_VWEBP)
+   # vwebp
+-  find_package(GLUT)
+-  if(GLUT_FOUND)
++  find_package(FreeGLUT)
++  if(FreeGLUT_FOUND)
+     include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+     parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "VWEBP_SRCS" "vwebp")
+     add_executable(vwebp ${VWEBP_SRCS})
+@@ -591,7 +591,7 @@ if(WEBP_BUILD_VWEBP)
+       vwebp
+       ${OPENGL_LIBRARIES}
+       exampleutil
+-      GLUT::GLUT
++      glut
+       imageioutil
+       webp
+       webpdemux)
+diff -rupN --no-dereference libwebp-1.4.0/examples/vwebp.c libwebp-1.4.0-new/examples/vwebp.c
+--- libwebp-1.4.0/examples/vwebp.c	2024-04-12 22:48:48.000000000 +0200
++++ libwebp-1.4.0-new/examples/vwebp.c	2024-04-14 15:30:19.981729641 +0200
+@@ -28,7 +28,7 @@
+ #if defined(HAVE_GLUT_GLUT_H)
+ #include <GLUT/glut.h>
+ #else
+-#include <GL/glut.h>
++#include <GL/freeglut.h>
+ #ifdef FREEGLUT
+ #include <GL/freeglut.h>
+ #endif
diff --git a/base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch b/base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch
new file mode 100644
index 0000000..9cda88c
--- /dev/null
+++ b/base/rx/rx-libwebp/libwebp-mingw-libsuffix.patch
@@ -0,0 +1,13 @@
+diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt
+--- libwebp-1.4.0/CMakeLists.txt	2024-04-14 15:30:20.015729868 +0200
++++ libwebp-1.4.0-new/CMakeLists.txt	2024-04-14 15:30:20.020729901 +0200
+@@ -301,6 +301,9 @@ macro(set_version FILE TARGET_NAME NAME_
+                  MACHO_CURRENT_VERSION
+                  ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}.${LT_REVISION})
+   endif()
++  if(WIN32)
++    set_target_properties(${TARGET_NAME} PROPERTIES SUFFIX "-${LT_CURRENT_MINUS_AGE}${CMAKE_SHARED_LIBRARY_SUFFIX}")
++  endif(WIN32)
+ endmacro()
+ 
+ # ##############################################################################
diff --git a/base/rx/rx-libwebp/libwebp-rpath.patch b/base/rx/rx-libwebp/libwebp-rpath.patch
new file mode 100644
index 0000000..e066270
--- /dev/null
+++ b/base/rx/rx-libwebp/libwebp-rpath.patch
@@ -0,0 +1,20 @@
+diff -rupN --no-dereference libwebp-1.4.0/CMakeLists.txt libwebp-1.4.0-new/CMakeLists.txt
+--- libwebp-1.4.0/CMakeLists.txt	2024-04-14 15:30:20.089730360 +0200
++++ libwebp-1.4.0-new/CMakeLists.txt	2024-04-14 15:30:20.093730386 +0200
+@@ -114,11 +114,11 @@ endif()
+ include(cmake/deps.cmake)
+ include(GNUInstallDirs)
+ 
+-if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH)
+-  # Set the rpath to match autoconf/libtool behavior. Note this must be set
+-  # before target creation.
+-  set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+-endif()
++# if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH)
++#   # Set the rpath to match autoconf/libtool behavior. Note this must be set
++#   # before target creation.
++#   set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
++# endif()
+ 
+ # ##############################################################################
+ # Options.
diff --git a/base/rx/rx-libwebp/libwebp.spec b/base/rx/rx-libwebp/libwebp.spec
new file mode 100644
index 0000000..b84282f
--- /dev/null
+++ b/base/rx/rx-libwebp/libwebp.spec
@@ -0,0 +1,365 @@
+%global _hardened_build 1
+%global realname libwebp
+
+%global _root_prefix  %{_prefix}
+%global _root_libdir  %{_root_prefix}/%{_lib}
+%global _root_bindir  %{_root_prefix}/bin
+
+%global _prefix /opt/rx
+%global _libdir %{_prefix}/%{_lib}
+%global _bindir %{_prefix}/bin
+%global _datadir %{_prefix}/share
+%global _mandir %{_prefix}/share/man
+%global _includedir %{_prefix}/include
+
+
+Name:          rx-libwebp
+Version:       1.4.0
+Release:       1%{?dist}
+URL:           http://webmproject.org/
+Summary:       Library and tools for the WebP graphics format
+# Additional IPR is licensed as well. See PATENTS file for details
+License:       BSD
+Source0:       http://downloads.webmproject.org/releases/webp/%{realname}-%{version}.tar.gz
+Source1:       libwebp_jni_example.java
+
+# Fix build with freeglut
+Patch0:        libwebp-freeglut.patch
+# Fix cmake module install location
+Patch2:        fix-cmake-files-location.patch
+# Kill rpath
+#Patch3:        libwebp-rpath.patch
+
+#BuildRequires: rx-libjpeg-turbo-devel
+BuildRequires: libjpeg-devel
+BuildRequires: libpng-devel
+BuildRequires: giflib-devel
+BuildRequires: libtiff-devel
+BuildRequires: java-devel
+BuildRequires: jpackage-utils
+BuildRequires: swig
+BuildRequires: ninja-build
+BuildRequires: freeglut-devel
+BuildRequires: cmake
+BuildRequires: clang
+
+#Requires: rx-libjpeg-turbo
+
+%description
+WebP is an image format that does lossy compression of digital
+photographic images. WebP consists of a codec based on VP8, and a
+container based on RIFF. Webmasters, web developers and browser
+developers can use WebP to compress, archive and distribute digital
+images more efficiently.
+
+
+%package tools
+Summary:       The WebP command line tools
+
+%description tools
+WebP is an image format that does lossy compression of digital
+photographic images. WebP consists of a codec based on VP8, and a
+container based on RIFF. Webmasters, web developers and browser
+developers can use WebP to compress, archive and distribute digital
+images more efficiently.
+
+
+%package devel
+Summary:       Development files for libwebp, a library for the WebP format
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+Provides:      pkgconfig(libsharpyuv) = %{version}-%{release}
+Provides:      pkgconfig(libwebp) = %{version}-%{release}
+Provides:      pkgconfig(libwebpdecoder) = %{version}-%{release}
+Provides:      pkgconfig(libwebpdemux) = %{version}-%{release}
+Provides:      pkgconfig(libwebpmux) = %{version}-%{release}
+
+
+%description devel
+WebP is an image format that does lossy compression of digital
+photographic images. WebP consists of a codec based on VP8, and a
+container based on RIFF. Webmasters, web developers and browser
+developers can use WebP to compress, archive and distribute digital
+images more efficiently.
+
+
+%package java
+Summary:       Java bindings for libwebp, a library for the WebP format
+Requires:      %{name}%{?_isa} = %{version}-%{release}
+Requires:      java-headless
+Requires:      jpackage-utils
+
+%description java
+Java bindings for libwebp.
+
+
+%prep
+%autosetup -p1 -n %{realname}-%{version}
+
+
+%build
+%ifarch aarch64
+export CFLAGS="%{optflags} -frename-registers"
+%endif
+export LDFLAGS="%{build_ldflags} -L%{_libdir} -Wl,-rpath=%{_libdir}"
+
+export CC=clang
+export CXX=clang++
+
+# Native build
+%cmake -DCMAKE_CONFIG_PREFIX=%{_libdir}
+%cmake_build
+
+# swig generated Java bindings
+cp %{SOURCE1} .
+cd swig
+rm -rf libwebp.jar libwebp_java_wrap.c
+mkdir -p java/com/google/webp
+swig -ignoremissing -I../src -java \
+    -package com.google.webp  \
+    -outdir java/com/google/webp \
+    -o libwebp_java_wrap.c libwebp.swig
+
+clang %{__global_ldflags} %(echo %{optflags} | sed 's/-fcf-protection//')-shared -fPIC \
+    -I/usr/lib/jvm/java/include \
+    -I/usr/lib/jvm/java/include/linux \
+    -I../src \
+    -L.. \
+    -L../src/.libs -lwebp libwebp_java_wrap.c \
+    -o libwebp_jni.so
+
+cd java
+javac com/google/webp/libwebp.java
+jar cvf ../libwebp.jar com/google/webp/*.class
+
+
+%install
+%cmake_install
+
+find "%{buildroot}/%{_libdir}" -type f -name "*.la" -delete
+
+# swig generated Java bindings
+mkdir -p %{buildroot}/%{_libdir}/%{realname}-java
+cp swig/*.jar swig/*.so %{buildroot}/%{_libdir}/%{realname}-java/
+
+#mkdir -p %{buildroot}%{_root_libdir}/
+#mv %{buildroot}%{_libdir}/pkgconfig %{buildroot}%{_root_libdir}/
+
+mkdir -p %{buildroot}%{_root_bindir}
+
+for b in cwebp dwebp gif2webp img2webp webpinfo webpmux
+do
+    ln -sf %{_bindir}/$b %{buildroot}%{_root_bindir}/${b}-1.3
+done
+
+
+%ldconfig_scriptlets
+
+
+%files tools
+%{_bindir}/cwebp
+%{_bindir}/dwebp
+%{_bindir}/gif2webp
+%{_bindir}/img2webp
+%{_bindir}/webpinfo
+%{_bindir}/webpmux
+%{_root_bindir}/*-1.3
+%{_mandir}/man*/*
+
+%files -n %{name}
+%doc README.md PATENTS NEWS AUTHORS
+%license COPYING
+%{_libdir}/%{realname}.so.7*
+%{_libdir}/%{realname}decoder.so.3*
+%{_libdir}/%{realname}demux.so.2*
+%{_libdir}/%{realname}mux.so.3*
+%{_libdir}/libsharpyuv.so.0*
+
+%files devel
+%{_libdir}/%{realname}*.so
+%{_libdir}/libsharpyuv.so
+%{_includedir}/*
+%{_libdir}/pkgconfig/*.pc
+%{_libdir}/cmake/WebP/
+
+
+%files java
+%doc libwebp_jni_example.java
+%{_libdir}/%{realname}-java/
+
+
+%changelog
+* Tue May  7 2024 Raven <raven@sysadmins.ws> - 1.4.0-1
+- Update to 1.4.0
+
+* Wed Dec 20 2023 Raven <raven@sysadmins.ws> - 1.3.2-1
+- Update to 1.3.2
+
+* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 1.2.0-2
+- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
+
+* Mon Feb 01 2021 Sandro Mani <manisandro@gmail.com> - 1.2.0-1
+- Update to 1.2.0
+
+* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 1.1.0-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
+
+* Tue Jul 28 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.1.0-5
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
+* Sat Jul 11 2020 Jiri Vanek <jvanek@redhat.com> - 1.1.0-4
+- Rebuilt for JDK-11, see https://fedoraproject.org/wiki/Changes/Java11
+
+* Mon May 18 2020 Sandro Mani <manisandro@gmail.com> - 1.1.0-3
+- Don't manually and incorrectly install vwebp, Makefile already does it correctly (#1836640)
+
+* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 1.1.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
+
+* Tue Jan 07 2020 Sandro Mani <manisandro@gmail.com> - 1.1.0-1
+- Update to 1.1.0
+
+* Tue Sep 17 2019 Gwyn Ciesla <gwync@protonmail.com> - 1.0.3-3
+- Rebuilt for new freeglut
+
+* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
+
+* Mon Jul 15 2019 Sandro Mani <manisandro@gmail.com> - 1.0.3-1
+- Update to 1.0.3
+
+* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.2-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Tue Jan 22 2019 Sandro Mani <manisandro@gmail.com> - 1.0.2-1
+- Update to 1.0.2
+
+* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.0.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Thu Apr 26 2018 Sandro Mani <manisandro@gmail.com> - 1.0.0-1
+- Update to 1.0.0
+
+* Tue Feb 27 2018 Sandro Mani <manisandro@gmail.com> - 0.6.1-8
+- Fix LDFLAGS not passed when building libwebp_jni.so (#1548718)
+
+* Mon Feb 26 2018 Sandro Mani <manisandro@gmail.com> - 0.6.1-7
+- More big-endian fixes
+
+* Fri Feb 16 2018 Sandro Mani <manisandro@gmail.com> - 0.6.1-6
+- Backport another big-endian fix
+
+* Fri Feb 16 2018 Sandro Mani <manisandro@gmail.com> - 0.6.1-5
+- Backport upstream big-endian fix
+
+* Tue Feb 13 2018 Sandro Mani <manisandro@gmail.com> - 0.6.1-4
+- Rebuild (giflib)
+
+* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Sat Feb 03 2018 Igor Gnatenko <ignatenkobrain@fedoraproject.org> - 0.6.1-2
+- Switch to %%ldconfig_scriptlets
+
+* Thu Nov 30 2017 Sandro Mani <manisandro@gmail.com> - 0.6.1-1
+- Update to 0.6.1
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Wed Feb 01 2017 Sandro Mani <manisandro@gmail.com> - 0.6.0-1
+- Update to 0.6.0
+
+* Thu Dec 22 2016 Sandro Mani <manisandro@gmail.com> - 0.5.2-1
+- Update to 0.5.2
+
+* Sat Oct 29 2016 Sandro Mani <manisandro@gmail.com> - 0.5.1-2
+- Backport e2affacc35f1df6cc3b1a9fa0ceff5ce2d0cce83 (CVE-2016-9085, rhbz#1389338)
+
+* Fri Aug 12 2016 Sandro Mani <manisandro@gmail.com> - 0.5.1-1
+- upstream release 0.5.1
+
+* Thu Feb 04 2016 Fedora Release Engineering <releng@fedoraproject.org> - 0.5.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Mon Dec 28 2015 Sandro Mani <manisandro@gmail.com> - 0.5.0-1
+- upstream release 0.5.0
+
+* Fri Oct 30 2015 Sandro Mani <manisandro@gmail.com> - 0.4.4-1
+- upstream release 0.4.4
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.4.3-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Fri Mar 27 2015 Sandro Mani <manisandro@gmail.com> - 0.4.3-2
+- Add BuildRequires: freeglut-devel to build vwebp
+
+* Thu Mar 12 2015 Sandro Mani <manisandro@gmail.com> - 0.4.3-1
+- upstream release 0.4.3
+
+* Fri Oct 17 2014 Sandro Mani <manisandro@gmail.com> - 0.4.2-1
+- upstream release 0.4.2
+
+* Sun Aug 17 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.4.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Wed Aug 13 2014 Peter Robinson <pbrobinson@fedoraproject.org> 0.4.1-2
+- Use frename-registers cflag to fix FTBFS on aarch64
+
+* Tue Aug 05 2014 Sandro Mani <manisandro@gmail.com> - 0.4.1-1
+- upstream release 0.4.1
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.4.0-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Tue Apr 08 2014 Jaromir Capik <jcapik@redhat.com> - 0.4.0-3
+- Fixing endian checks (#962091)
+- Fixing FTPBS caused by rpath presence
+
+* Fri Mar 28 2014 Michael Simacek <msimacek@redhat.com> - 0.4.0-2
+- Use Requires: java-headless rebuild (#1067528)
+
+* Thu Jan 02 2014 Sandro Mani <manisandro@gmail.com> - 0.4.0-1
+- upstream release 0.4.0
+
+* Wed Oct 02 2013 Sandro Mani <manisandro@gmail.com> - 0.3.1-2
+- enable webpdemux
+
+* Sun Aug 04 2013 Sandro Mani <manisandro@gmail.com> - 0.3.1-1
+- upstream release 0.3.1
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.3.0-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Mon May 13 2013 Rahul Sundaram <sundaram@fedoraproject.org> - 0.3.0-1
+- upstream release 0.3.0
+- enable gif2webp
+- add build requires on giflib-devel and libtiff-devel
+- use make_install and hardened macros
+- list binaries explicitly
+
+* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.2.1-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
+* Fri Jan 18 2013 Adam Tkac <atkac redhat com> - 0.2.1-2
+- rebuild due to "jpeg8-ABI" feature drop
+
+* Thu Dec 27 2012 Rahul Sundaram <sundaram@fedoraproject.org> - 0.2.1-1
+- new upstream release 0.2.1
+
+* Fri Dec 21 2012 Adam Tkac <atkac redhat com> - 0.1.3-3
+- rebuild against new libjpeg
+
+* Thu Jul 19 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.1.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Thu Feb 02 2012 Rahul Sundaram <sundaram@fedoraproject.org> - 0.1.3-1
+- Several spec improvements by Scott Tsai <scottt.tw@gmail.com>
+
+* Wed May 25 2011 Rahul Sundaram <sundaram@fedoraproject.org> - 0.1.2-1
+- Initial spec. Based on openSUSE one
diff --git a/base/rx/rx-libwebp/libwebp_jni_example.java b/base/rx/rx-libwebp/libwebp_jni_example.java
new file mode 100644
index 0000000..d28d9b8
--- /dev/null
+++ b/base/rx/rx-libwebp/libwebp_jni_example.java
@@ -0,0 +1,27 @@
+import com.google.webp.libwebp;
+
+import java.lang.reflect.Method;
+
+public class libwebp_jni_example {
+  static {
+    try {
+      System.load("/usr/lib64/libwebp-java/libwebp_jni.so");
+    } catch (UnsatisfiedLinkError e) {
+      System.load("/usr/lib/libwebp-java/libwebp_jni.so");
+    }
+  }
+
+  /**
+   * usage: java -cp libwebp.jar:. libwebp_jni_example
+   */
+  public static void main(String argv[]) {
+    final int version = libwebp.WebPGetDecoderVersion();
+    System.out.println("libwebp version: " + Integer.toHexString(version));
+
+    System.out.println("libwebp methods:");
+    final Method[] libwebpMethods = libwebp.class.getDeclaredMethods();
+    for (int i = 0; i < libwebpMethods.length; i++) {
+      System.out.println(libwebpMethods[i]);
+    }
+  }
+}
diff --git a/base/rx/rx-poppler/poppler.spec b/base/rx/rx-poppler/poppler.spec
index 8e1b86e..703b8bd 100644
--- a/base/rx/rx-poppler/poppler.spec
+++ b/base/rx/rx-poppler/poppler.spec
@@ -19,7 +19,7 @@
 Summary: PDF rendering library
 Name:    rx-poppler
 Version: 23.02.0
-Release: 2%{?dist}
+Release: 5%{?dist}
 License: (GPLv2 or GPLv3) and GPLv2+ and LGPLv2+ and MIT
 URL:     http://poppler.freedesktop.org/
 Source0: http://poppler.freedesktop.org/poppler-%{version}.tar.xz
@@ -32,7 +32,7 @@ Patch3:  poppler-21.01.0-glib-introspection.patch
 
 BuildRequires: make
 BuildRequires: cmake
-BuildRequires: gcc-c++
+BuildRequires: gcc-toolset-14-gcc-c++
 BuildRequires: gettext-devel
 BuildRequires: pkgconfig(cairo)
 BuildRequires: pkgconfig(cairo-ft)
@@ -171,6 +171,7 @@ Requires: %{name}-devel%{?_isa} = %{version}-%{release}
 chmod -x poppler/CairoFontEngine.cc
 
 %build
+%enable_devtoolset14
 export PATH=%{_bindir}${PATH:+:${PATH}}
 export LDFLAGS="-L%{_libdir} -Wl,-rpath=%{_libdir} -L%{_prefix}/glib2/%{_lib} -Wl,-rpath=%{_prefix}/glib2/%{_lib} ${LDFLAGS}"
 export PKG_CONFIG_PATH=%{_libdir}/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}
@@ -288,6 +289,9 @@ test "$(pkg-config --modversion poppler-qt6)" = "%{version}"
 %{_includedir}/poppler/cpp
 
 %changelog
+* Tue Oct 29 2024 Raven <raven@sysadmins.ws> - 23.02.0-4
+- rebuilt with new rx-freetype
+
 * Fri Feb  3 2023 Marek Kasik <mkasik@redhat.com> - 23.02.0-1
 - Update to 23.02.0
 - Resolves: #2123190