Subject: bug#79167: [PATCH] nnmbox-read-mbox: fix Xref header parsing

From: Jakub Ječmínek <[email-masked]>
Date: Sun, 03 Aug 2025 15:48:37 +0200

Tags: patch

When opening an mbox file using the nnmbox backend, the `nnmbox-read-mbox'
function parses it and, if the article is new, adds an 'X-Gnus-Newsgroup'
header. This header is populated from the Xref header if the article includes
one.

However, the search for Xref is broken. Steps to reproduce:

0. Save the attached mbox file named '2016-11'
1. emacs -Q
2. (setq gnus-select-method '(nnmbox "" (nnmbox-mbox-file "/path/to/2016-11")))
3. M-x gnus RET
4. M-: (switch-to-buffer nnmbox-mbox-buffer) RET
5. Search for message with ID
<[email-masked]>

You'll see this article contains two 'X-Gnus-Newsgroup' headers:

X-Gnus-Newsgroup: Wed, 02 Nov 2016 17:25   Sun Aug  3 15:15:26 2025  
X-Gnus-Newsgroup: gnu.emacs.help:219001   Sun Aug  3 15:15:26 2025

The first line shouldn't be there because the Xref header contains just one
group:

Xref: usenet.stanford.edu gnu.emacs.help:219001

The attached patch fixes this issue.

In GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.48,
 cairo version 1.18.4) of 2025-05-04 built on localhost
Repository revision: 815dea7b59392d83ca8f9e98d1569484aea605ad
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12401008
System Description: Gentoo Linux

Configured using:
 'configure --prefix=/usr --build=x86_64-pc-linux-gnu
 --host=x86_64-pc-linux-gnu --mandir=/usr/share/man --infodir=/usr/share/info
 --datadir=/usr/share --sysconfdir=/etc --localstatedir=/var/lib
 --datarootdir=/usr/share --disable-silent-rules
 --docdir=/usr/share/doc/emacs-31.0.9999
 --htmldir=/usr/share/doc/emacs-31.0.9999/html --libdir=/usr/lib64
 --program-suffix=-emacs-31-vcs --includedir=/usr/include/emacs-31-vcs
 --infodir=/usr/share/info/emacs-31-vcs --localstatedir=/var
 --enable-locallisppath=/etc/emacs:/usr/share/emacs/site-lisp
 --without-compress-install --without-hesiod --without-pop
 --without-systemduserunitdir --with-file-notification=inotify --with-pdumper
 --enable-acl --enable-xattr --with-dbus --without-modules --without-gameuser
 --with-libgmp --with-gpm --without-native-compilation --without-kerberos
 --without-kerberos5 --with-lcms2 --with-xml2 --with-mailutils
 --without-selinux --with-sqlite3 --with-gnutls --with-libsystemd
 --with-threads --with-tree-sitter --without-wide-int --with-sound=alsa
 --with-zlib --with-x --without-pgtk --without-ns --without-gconf
 --without-gsettings --without-toolkit-scroll-bars --with-xpm --with-xft
 --with-cairo --without-harfbuzz --without-libotf --without-m17n-flt
 --with-x-toolkit=gtk3 --without-xwidgets --with-gif --with-jpeg --with-png
 --with-rsvg --with-tiff --without-webp --with-imagemagick
 --with-dumping=pdumper 'CFLAGS=-march=native -O2 -pipe -fno-fast-math
 -ffp-contract=off' CPPFLAGS= 'LDFLAGS=-Wl,-O1 -Wl,--as-needed
 -Wl,-z,pack-relative-relocs''

>From 1bc7ecf0f12faeca067486e704d290413d5acbca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20Je=C4=8Dm=C3=ADnek?= <[email-masked]>
Date: Sun, 3 Aug 2025 14:58:14 +0200
Subject: [PATCH] nnmbox-read-mbox: fix Xref header parsing

* lisp/gnus/nnmbox.el (nnmbox-read-mbox): Use the 'mail-fetch-field'
function instead of a broken regexp.
---
 lisp/gnus/nnmbox.el | 31 ++++++++++++++++++-------------
 1 file changed, 18 insertions(+), 13 deletions(-)

diff --git a/lisp/gnus/nnmbox.el b/lisp/gnus/nnmbox.el
index fb024bf9ef8..f8575445b65 100644
--- a/lisp/gnus/nnmbox.el
+++ b/lisp/gnus/nnmbox.el
@@ -683,19 +683,24 @@ nnmbox-read-mbox
 	    ;; headers).  In this case, there is an Xref line which
 	    ;; provides the relevant information to construct the
 	    ;; missing header(s).
-	    (save-excursion
-	      (save-restriction
-		(narrow-to-region start end)
-		(if (re-search-forward "\nXref: [^ ]+" end-header t)
-		    ;; generate headers from Xref:
-		    (let (alist)
-		      (while (re-search-forward " \\([^:]+\\):\\([0-9]+\\)" end-header t)
-			(push (cons (match-string 1)
-				    (string-to-number (match-string 2))) alist))
-		      (nnmbox-insert-newsgroup-line alist))
-		  ;; this is really a new article
-		  (nnmbox-save-mail
-		   (nnmail-article-group 'nnmbox-active-number))))))
+            (save-excursion
+              (save-restriction
+                (narrow-to-region start end)
+                (let ((xref (with-restriction start end-header
+                              (mail-fetch-field "Xref")))
+                      alist)
+                  (if (null xref)
+                      ;; this is a new article
+                      (nnmbox-save-mail
+                       (nnmail-article-group 'nnmbox-active-number))
+                    ;; generate headers from Xref
+                    ;; the first element is hostname so we skip it
+                    (dolist (xref-entry (cdr (split-string xref " ")))
+                      (push (cons
+                             (car (setq xref-entry (split-string xref-entry ":")))
+                             (string-to-number (cadr xref-entry)))
+                            alist))
+                    (nnmbox-insert-newsgroup-line alist))))))
 	  (goto-char end))
 	;; put article lists in order
 	(setq alist nnmbox-group-active-articles)
-- 
2.49.1