Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kitinerary for openSUSE:Factory 
checked in at 2023-05-12 20:33:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kitinerary (Old)
 and      /work/SRC/openSUSE:Factory/.kitinerary.new.1533 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kitinerary"

Fri May 12 20:33:25 2023 rev:61 rq:1086341 version:23.04.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes    2023-04-23 
22:47:08.898604579 +0200
+++ /work/SRC/openSUSE:Factory/.kitinerary.new.1533/kitinerary.changes  
2023-05-12 20:33:55.904864334 +0200
@@ -1,0 +2,9 @@
+Tue May  9 10:47:06 UTC 2023 - Christophe Marin <christo...@krop.fr>
+
+- Update to 23.04.1
+  * New bugfix release
+  * For more details please see:
+  * https://kde.org/announcements/gear/23.04.1/
+- Too many changes to list here.
+
+-------------------------------------------------------------------

Old:
----
  kitinerary-23.04.0.tar.xz
  kitinerary-23.04.0.tar.xz.sig

New:
----
  kitinerary-23.04.1.tar.xz
  kitinerary-23.04.1.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kitinerary.spec ++++++
--- /var/tmp/diff_new_pack.DWs2CA/_old  2023-05-12 20:33:56.504866340 +0200
+++ /var/tmp/diff_new_pack.DWs2CA/_new  2023-05-12 20:33:56.508866353 +0200
@@ -19,7 +19,7 @@
 %define libname libKPimItinerary5
 %bcond_without released
 Name:           kitinerary
-Version:        23.04.0
+Version:        23.04.1
 Release:        0
 Summary:        Data model and extraction system for travel reservations
 License:        LGPL-2.1-or-later


++++++ kitinerary-23.04.0.tar.xz -> kitinerary-23.04.1.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/CMakeLists.txt 
new/kitinerary-23.04.1/CMakeLists.txt
--- old/kitinerary-23.04.0/CMakeLists.txt       2023-04-13 17:08:26.000000000 
+0200
+++ new/kitinerary-23.04.1/CMakeLists.txt       2023-05-06 10:59:18.000000000 
+0200
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 
 cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-set(PIM_VERSION "5.23.0")
+set(PIM_VERSION "5.23.1")
 project(KItinerary VERSION ${PIM_VERSION})
 
 set(KF_MIN_VERSION "5.91.0")
@@ -48,8 +48,8 @@
     find_package(SharedMimeInfo 1.3 REQUIRED)
 endif()
 
-set(KMIME_VERSION "5.23.0")
-set(PIM_PKPASS "5.23.0")
+set(KMIME_VERSION "5.23.1")
+set(PIM_PKPASS "5.23.1")
 
 find_package(KPim${KF_MAJOR_VERSION}Mime ${KMIME_VERSION} CONFIG REQUIRED)
 find_package(KPimPkPass ${PIM_PKPASS} CONFIG REQUIRED)
Binary files 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin
 and 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json
 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json
--- 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-PLAI-v1.01.bin.json
  2023-05-06 10:59:18.000000000 +0200
@@ -0,0 +1,26 @@
+[
+    {
+        "@context": "http://schema.org";,
+        "@type": "Ticket",
+        "issuedBy": {
+            "@type": "Organization",
+            "identifier": "uic:1080",
+            "name": "DB AG"
+        },
+        "name": "Deutschlandticket",
+        "ticketNumber": "J7EJ5KNN",
+        "ticketToken": 
"aztecbin:I1VUMDExMDgwVFQwMDUwLAIUaF474Q5HKwSfP4bJ3CWga/66E1ACFD9cK3OOzpglLvERyv9+ycn9TpbqAAAAADAzODd4nAuN93B1dDEwNDAwNTY0sDDwMnf1MvX282NAAkBZEyMDI2OgtIGJi6uLa2h8iI9jJFDYyMwgwMfR0wCk38QASBkZgICpX2JuqpWBKULE3DsnsbgYKGYG5BhBxMwCUouK9awMjODKDI3cD+/JKclMz07NLAFKGFqC1ILMNgzLz1MwMNQzMNUDuUTBwMDKwIDLKbMYLGgGFTQGCgLVWoKMA+oyNPRJzUxU8C9KT8xLNDCFi4MoM7jZQGVg5xhagHmWbokZRdmJRSWpEEmwqKG5S2ppSXFyRk5iXkpJZnJ2aklovJuPa4ShsYGhqVHSJoa2h7wHT7s3iApyNLLKtXjliwVHFi9X4Gps4Jhx6tIhnnlPzh26c2jGxIYmNYZ4hhOCSns5QrIjs+bYqJnJymx9+PSyBksDC5dKQA2DCX+3gxATQ5ey9urFs6Wdk7mLlRd7S0drLxCYqcC5hYHBNWPqzJ0JZ7sKPhhwtjs4bPut1SCk4FB1Uds04WADBwcHAAcxge4=",
+        "ticketedSeat": {
+            "@type": "Seat",
+            "seatingType": "2"
+        },
+        "underName": {
+            "@type": "Person",
+            "familyName": "Organa",
+            "givenName": "Leia",
+            "name": "Leia Organa"
+        },
+        "validFrom": "2023-05-01T00:00:00+02:00",
+        "validUntil": "2023-06-01T00:03:00+02:00"
+    }
+]
Binary files 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin
 and 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json
 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json
--- 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-TLB-RCT2-v1.01.bin.json
  2023-05-06 10:59:18.000000000 +0200
@@ -0,0 +1,26 @@
+[
+    {
+        "@context": "http://schema.org";,
+        "@type": "Ticket",
+        "issuedBy": {
+            "@type": "Organization",
+            "identifier": "uic:1080",
+            "name": "DB AG"
+        },
+        "name": "Deutschlandticket",
+        "ticketNumber": "J7EJ5KNN",
+        "ticketToken": 
"aztecbin:I1VUMDExMDgwVFQwMDUwLAIUP/mxp2V39hvTz76OYofP0x8Ijx0CFCupd/BViXc/b5hrwemuH/WXMnXRAAAAADA0MjV4nAuN93B1dDEwNDAwNTY0sDDwMnf1MvX282NAAkBZEyMDI2OgtIGJi6uLa2h8iI9jJFDY2NgoyDnEyMDA0BSILQwMgQIGBgaWbokZRdmJRSWpQGONgOosgYIGZv5F6Yl5iUAGXMjEJzUz0cAQohPoBkNzl9TSkuLkjJzEvJSSzOTs1BKg0SDlIGOBCoE8UyBlBuIYBKQWFefnaaTmaRoYA7lAV4LNBDvVDCxgChIAatADEmYG5ggRAz2Qg8AmI9SYAUUsECLGYDVmZjARQyNDsEtMQY4xNnc/vCenJDNdoSw/VwFsgx7IYoWkzGIw1wzMNTQG22EM0m/sGhoE5APNM4TYYGKpY2AQGu/m4xphaAzyZ9ImhraHvAdPuzeICnI0ssq1eOWLBUcWL1fgamzgmHHq0iGeeU/OHbpzaMbEhiY1hniGE4JKezlCsiOz5tiomcnKbH349LIGSwMLl0pADYMJf7eDEBNDl7L26sWzpZ2TuYuVF3tLR2svEJipwLmFgcE1Y+rMnQlnuwo+GHC2Ozhs+63VIKTgUHVR2zThYAMHBwcAqg6Oow==",
+        "ticketedSeat": {
+            "@type": "Seat",
+            "seatingType": "2"
+        },
+        "underName": {
+            "@type": "Person",
+            "familyName": "Organa",
+            "givenName": "Leia",
+            "name": "Leia Organa"
+        },
+        "validFrom": "2023-05-01T00:00:00+02:00",
+        "validUntil": "2023-06-01T00:03:00+02:00"
+    }
+]
Binary files 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin
 and 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json
 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json
--- 
old/kitinerary-23.04.0/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/kitinerary-23.04.1/autotests/extractordata/deutschebahn/20230501-Deutschlandticket-FCB-v1.01.bin.json
   2023-05-06 10:59:18.000000000 +0200
@@ -0,0 +1,26 @@
+[
+    {
+        "@context": "http://schema.org";,
+        "@type": "Ticket",
+        "issuedBy": {
+            "@type": "Organization",
+            "identifier": "uic:1080",
+            "name": "DB AG"
+        },
+        "name": "Deutschlandticket",
+        "ticketNumber": "J7EJ5KNN",
+        "ticketToken": 
"aztecbin:I1VUMDExMDgwVFQwMDUwLAIUVhuRhpHjgkMGCpfK1dUqgGCqBNQCFE+Wjm/DOV5pQuW2XCnZ1gmaC1GQAAAAADAxNjN4nAGYAGf/VV9GTEVYMTMwMTUyYrIAhuENwctHgBURCIEFHoRKbxZTWXOnIAqBgAiYytLCDJ7kzsLcwpiRgIImAF8AyBEivQhUa1lqnDwmNh0cteHl0ygEgAQKJFB8ADQPi0ASAgCKIyuro5sbQ2MLcyOjSxtbK6AQmSAJtAAARWiVmblgzYpw8DAJh0BAtvsqgBIgQHrRKzVgwYAICAiTHjco",
+        "ticketedSeat": {
+            "@type": "Seat",
+            "seatingType": "2"
+        },
+        "underName": {
+            "@type": "Person",
+            "familyName": "Organa",
+            "givenName": "Leia",
+            "name": "Leia Organa"
+        },
+        "validFrom": "2023-05-01T00:00:00+02:00",
+        "validUntil": "2023-06-01T00:03:00+02:00"
+    }
+]
Binary files 
old/kitinerary-23.04.0/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin
 and 
new/kitinerary-23.04.1/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json
 
new/kitinerary-23.04.1/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json
--- 
old/kitinerary-23.04.0/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/kitinerary-23.04.1/autotests/extractordata/vrr/20230301-deutschlandticket-vdv.bin.json
  2023-05-06 10:59:18.000000000 +0200
@@ -0,0 +1,17 @@
+[
+    {
+        "@context": "http://schema.org";,
+        "@type": "Ticket",
+        "name": "Deutschlandticket",
+        "ticketNumber": "2099643",
+        "ticketToken": 
"aztecbin:noGAcCGqbL4sBzo3/kOD7wj1EhQvyK2NciRfyGSyr10XpjR/kGwTlKxLfXpmRnDS6vft4BmcB+iiccTNWkQBqOyD/OGi8lEP2KA5vsu6fPVzVp4dpLhocyUSBfekTORtnFkZAp7srho5vOQFGWsDmZbFKs4X/WBylo9/KKh4WeoYBc+aDwAAAAAAAAAAAABWRFYRCX8hgchfN4HAOfLABJIEWvFRilfF13NDSKABrFDMrUDcfzcaNKY828sW63tyxXfYpVHFn89CBrRAy54q04/zDpd9C44NShkBAfJ18d+GMOECTHS1qOp8AD1lteIBFOzalpJeuhRYSc4m/XSERlk94RpuFWo4TLvHq57lz8fV/GlaHzwUSrdPKRUSUHnr60OBKipr75sIyT3HKLShbyHyvJ4G33KAHsCtKurbzVbeU1k4RNAVont2Pxzlpt3M/d3DuAkGsIDC0LmOXzgBgUIIREVWRFYRAhY=",
+        "underName": {
+            "@type": "Person",
+            "familyName": "Mustermann",
+            "givenName": "Max",
+            "name": "Max Mustermann"
+        },
+        "validFrom": "2023-03-01T00:00:00",
+        "validUntil": "2023-03-31T23:59:58"
+    }
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/autotests/nameoptimizertest.cpp 
new/kitinerary-23.04.1/autotests/nameoptimizertest.cpp
--- old/kitinerary-23.04.0/autotests/nameoptimizertest.cpp      2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/autotests/nameoptimizertest.cpp      2023-05-06 
10:59:18.000000000 +0200
@@ -131,6 +131,21 @@
 
         QCOMPARE(p.givenName(), expected);
     }
+
+    void testSpaceExpansion()
+    {
+        Person p;
+        p.setFamilyName(s("THEDRAGON"));
+        p.setGivenName(s("KONQI"));
+        p = NameOptimizer::optimizeName(s("KONQI THE DRAGON"), p);
+        QCOMPARE(p.familyName(), s("THE DRAGON"));
+
+        p.setFamilyName(s("THEDRAGON"));
+        p.setGivenName(s("KONQIDR"));
+        p = NameOptimizer::optimizeName(s("KONQI DR THE DRAGON"), p);
+        QCOMPARE(p.familyName(), s("THE DRAGON"));
+        QCOMPARE(p.givenName(), s("KONQI"));
+    }
 };
 
 QTEST_GUILESS_MAIN(NameOptimizerTest)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/autotests/terminalfindertest.cpp 
new/kitinerary-23.04.1/autotests/terminalfindertest.cpp
--- old/kitinerary-23.04.0/autotests/terminalfindertest.cpp     2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/autotests/terminalfindertest.cpp     2023-05-06 
10:59:18.000000000 +0200
@@ -43,6 +43,7 @@
         QTest::newRow("DEL3") << s(" T2 some other text") << 3 << s("2");
         QTest::newRow("DEL4") << s(" (T3) more information") << 5 << s("3");
         QTest::newRow("DEL5") << s(" (T1C) ") << 6 << s("1C");
+        QTest::newRow("DEL6") << s(" T-3 (DEL)") << 4 << s("3");
         QTest::newRow("FRA") << s(", Terminal 2 (FRA)\nMadrid (MAD)") << 12 << 
s("2");
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/po/de/kitinerary.po 
new/kitinerary-23.04.1/po/de/kitinerary.po
--- old/kitinerary-23.04.0/po/de/kitinerary.po  2023-04-13 17:08:26.000000000 
+0200
+++ new/kitinerary-23.04.1/po/de/kitinerary.po  2023-05-06 10:59:18.000000000 
+0200
@@ -13,7 +13,7 @@
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Lokalize 22.12.1\n"
 
 #: calendarhandler.cpp:160
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/po/gl/kitinerary.po 
new/kitinerary-23.04.1/po/gl/kitinerary.po
--- old/kitinerary-23.04.0/po/gl/kitinerary.po  2023-04-13 17:08:26.000000000 
+0200
+++ new/kitinerary-23.04.1/po/gl/kitinerary.po  2023-05-06 10:59:18.000000000 
+0200
@@ -1,13 +1,13 @@
 # Copyright (C) YEAR This_file_is_part_of_KDE
 # This file is distributed under the same license as the PACKAGE package.
-# Adrian Chaves <adr...@chaves.io>, 2018, 2019, 2020.
+# Adrian Chaves <adr...@chaves.io>, 2018, 2019, 2020, 2023.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n";
 "POT-Creation-Date: 2022-07-20 00:46+0000\n"
-"PO-Revision-Date: 2020-02-16 23:04+0100\n"
+"PO-Revision-Date: 2023-04-30 11:00+0200\n"
 "Last-Translator: Adrián Chaves (Gallaecio) <adr...@chaves.io>\n"
 "Language-Team: Galician <proxe...@trasno.gal>\n"
 "Language: gl\n"
@@ -15,14 +15,13 @@
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 19.08.3\n"
+"X-Generator: Lokalize 23.04.0\n"
 
 #: calendarhandler.cpp:160
-#, fuzzy, kde-format
-#| msgid "Coach: %1"
+#, kde-format
 msgctxt "canceled train/flight/loading reservation"
 msgid "Canceled: %1"
-msgstr "Coche: %1"
+msgstr "Cancelada: %1"
 
 #: calendarhandler.cpp:186
 #, kde-format
@@ -45,8 +44,7 @@
 msgstr "Hora de embarque: %1"
 
 #: calendarhandler.cpp:219
-#, fuzzy, kde-format
-#| msgid "Departure gate: %1"
+#, kde-format
 msgctxt "flight departure gate"
 msgid "Departure gate: %1"
 msgstr "Porta de saída: %1"
@@ -94,15 +92,14 @@
 msgstr "Bus %1 de %2 a %3"
 
 #: calendarhandler.cpp:306
-#, fuzzy, kde-format
-#| msgid "Train %1 from %2 to %3"
+#, kde-format
 msgid "Ferry from %1 to %2"
-msgstr "Tren %1 de %2 a %3"
+msgstr "Transbordador de %1 a %2"
 
 #: calendarhandler.cpp:319
 #, kde-format
 msgid "Ticket number: %1"
-msgstr ""
+msgstr "Número de tícket: %1"
 
 #: calendarhandler.cpp:329
 #, kde-format
@@ -112,28 +109,27 @@
 #: calendarhandler.cpp:340
 #, kde-format
 msgid "Check-in: %1"
-msgstr ""
+msgstr "Entrada: %1"
 
 #: calendarhandler.cpp:343
 #, kde-format
 msgid "Check-out: %1"
-msgstr ""
+msgstr "Saída: %1"
 
 #: calendarhandler.cpp:346
 #, kde-format
 msgid "Phone: %1"
-msgstr ""
+msgstr "Teléfono: %1"
 
 #: calendarhandler.cpp:349
 #, kde-format
 msgid "Email: %1"
-msgstr ""
+msgstr "Correo electrónico: %1"
 
 #: calendarhandler.cpp:352
-#, fuzzy, kde-format
-#| msgid "Seat: %1"
+#, kde-format
 msgid "Website: %1"
-msgstr "Asento: %1"
+msgstr "Sitio web: %1"
 
 #: calendarhandler.cpp:395
 #, kde-format
@@ -146,8 +142,7 @@
 msgstr "Reserva de restaurante: %1"
 
 #: calendarhandler.cpp:456
-#, fuzzy, kde-format
-#| msgid "Number Of People: %1"
+#, kde-format
 msgid "Number of people: %1"
 msgstr "Número de persoas: %1"
 
@@ -157,8 +152,7 @@
 msgstr "A nome de: %1"
 
 #: calendarhandler.cpp:473
-#, fuzzy, kde-format
-#| msgid "Rental Car reservation: %1"
+#, kde-format
 msgid "Rental car reservation: %1"
 msgstr "Reserva de coche de alugamento: %1"
 
@@ -168,6 +162,8 @@
 "Pickup location: %1\n"
 "%2\n"
 msgstr ""
+"Lugar de recollida: %1\n"
+"%2\n"
 
 #: calendarhandler.cpp:488
 #, kde-format
@@ -175,13 +171,11 @@
 "Dropoff location: %1\n"
 "%2\n"
 msgstr ""
+"Lugar de chegada: %1\n"
+"%2\n"
 
 #: calendarhandler.cpp:517
-#, fuzzy, kde-format
-#| msgid ""
-#| "Reservation reference: %1\n"
-#| "Under name: %2\n"
-#| "PickUp location: %3"
+#, kde-format
 msgid ""
 "Reservation reference: %1\n"
 "Under name: %2\n"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/po/zh_CN/kitinerary.po 
new/kitinerary-23.04.1/po/zh_CN/kitinerary.po
--- old/kitinerary-23.04.0/po/zh_CN/kitinerary.po       2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/po/zh_CN/kitinerary.po       2023-05-06 
10:59:18.000000000 +0200
@@ -3,7 +3,7 @@
 "Project-Id-Version: kdeorg\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n";
 "POT-Creation-Date: 2022-07-20 00:46+0000\n"
-"PO-Revision-Date: 2023-04-10 14:12\n"
+"PO-Revision-Date: 2023-04-29 08:36\n"
 "Last-Translator: \n"
 "Language-Team: Chinese Simplified\n"
 "Language: zh_CN\n"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/cli/org.kde.kitinerary-extractor.appdata.xml 
new/kitinerary-23.04.1/src/cli/org.kde.kitinerary-extractor.appdata.xml
--- old/kitinerary-23.04.0/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2023-04-13 17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2023-05-06 10:59:18.000000000 +0200
@@ -116,9 +116,9 @@
     <binary>kitinerary-extractor</binary>
   </provides>
   <releases>
+    <release version="5.23.1" date="2023-05-11"/>
     <release version="5.23.0" date="2023-04-20"/>
     <release version="5.22.3" date="2023-03-02"/>
     <release version="5.22.2" date="2023-02-02"/>
-    <release version="5.22.1" date="2023-01-05"/>
   </releases>
 </component>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.cpp 
new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.cpp
--- old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.cpp  2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.cpp  2023-05-06 
10:59:18.000000000 +0200
@@ -79,6 +79,24 @@
     return d.addDays(type1DepartureDate());
 }
 
+QDate SSBv3Ticket::type2ValidFrom(const QDateTime &contextDate) const
+{
+    if (ticketTypeCode() != SSBv3Ticket::NRT) {
+        return {};
+    }
+
+    return issueDate(contextDate).addDays(type2FirstDayOfValidity());
+}
+
+QDate SSBv3Ticket::type2ValidUntil(const QDateTime& contextDate) const
+{
+    if (ticketTypeCode() != SSBv3Ticket::NRT) {
+        return {};
+    }
+
+    return issueDate(contextDate).addDays(type2LastDayOfValidity());
+}
+
 QByteArray SSBv3Ticket::rawData() const
 {
     return m_data;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.h 
new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.h
--- old/kitinerary-23.04.0/src/lib/era/ssbv3ticket.h    2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/era/ssbv3ticket.h    2023-05-06 
10:59:18.000000000 +0200
@@ -115,6 +115,10 @@
     Q_INVOKABLE QDate issueDate(const QDateTime &contextDate = 
QDateTime::currentDateTime()) const;
     /** Departure day for type 1 (IRT/RES/BOA) tickets. */
     Q_INVOKABLE QDate type1DepartureDay(const QDateTime &contextDate = 
QDateTime::currentDateTime()) const;
+    /** First day of validity for type 2 (NRT) tickets. */
+    Q_INVOKABLE QDate type2ValidFrom(const QDateTime &contextDate = 
QDateTime::currentDateTime()) const;
+    /** Last day of validity for type 2 (NRT) tickets. */
+    Q_INVOKABLE QDate type2ValidUntil(const QDateTime &contextDate = 
QDateTime::currentDateTime()) const;
 
     /** Raw barcode data. */
     QByteArray rawData() const;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/extractorpostprocessor.cpp 
new/kitinerary-23.04.1/src/lib/extractorpostprocessor.cpp
--- old/kitinerary-23.04.0/src/lib/extractorpostprocessor.cpp   2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/extractorpostprocessor.cpp   2023-05-06 
10:59:18.000000000 +0200
@@ -266,6 +266,13 @@
     trip.setArrivalTime(processTrainTripTime(trip.arrivalTime(), 
trip.departureDay(), trip.arrivalStation()));
     trip.setTrainNumber(trip.trainNumber().simplified());
     trip.setTrainName(trip.trainName().simplified());
+
+    // arrival less than a day before departure is an indication of the 
extractor failing to detect day rollover
+    const auto duration = trip.departureTime().secsTo(trip.arrivalTime());
+    if (duration < 0 && duration > -3600*24 && trip.departureTime().timeSpec() 
== trip.arrivalTime().timeSpec()) {
+        trip.setArrivalTime(trip.arrivalTime().addDays(1));
+    }
+
     return trip;
 }
 
@@ -379,7 +386,7 @@
     if (res.reservationFor().isValid()) {
         
res.setReservationFor(processBoatTrip(res.reservationFor().value<BoatTrip>()));
     }
-    return res;
+    return processReservation(res);
 }
 
 BoatTrip ExtractorPostprocessorPrivate::processBoatTrip(BoatTrip trip) const
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/flightpostprocessor.cpp 
new/kitinerary-23.04.1/src/lib/flightpostprocessor.cpp
--- old/kitinerary-23.04.0/src/lib/flightpostprocessor.cpp      2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/flightpostprocessor.cpp      2023-05-06 
10:59:18.000000000 +0200
@@ -98,7 +98,7 @@
         dt.setDate(flight.departureDay());
     }
 
-    if (dt.timeSpec() == Qt::TimeZone || codes.empty()) {
+    if ((dt.timeSpec() == Qt::TimeZone && dt.timeZone() != QTimeZone::utc()) 
|| codes.empty()) {
         return dt;
     }
 
@@ -115,7 +115,7 @@
     if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) {
         dt.setTimeSpec(Qt::TimeZone);
         dt.setTimeZone(tz);
-    } else if (dt.timeSpec() == Qt::UTC) {
+    } else if (dt.timeSpec() == Qt::UTC || (dt.timeSpec() == Qt::TimeZone && 
dt.timeZone() == QTimeZone::utc())) {
         dt = dt.toTimeZone(tz);
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/pdf/pdfbarcodeutil.cpp 
new/kitinerary-23.04.1/src/lib/pdf/pdfbarcodeutil.cpp
--- old/kitinerary-23.04.0/src/lib/pdf/pdfbarcodeutil.cpp       2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/pdf/pdfbarcodeutil.cpp       2023-05-06 
10:59:18.000000000 +0200
@@ -20,7 +20,7 @@
     // vector path complexity limits
     MinPathElementCount2D = 400,
     MaxPathElementCount2D = 5400,
-    MinPathElementCount1D = 200,
+    MinPathElementCount1D = 150,
     MaxPathElementCount1D = 400,
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.cpp 
new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.cpp
--- old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.cpp  
2023-04-13 17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.cpp  
2023-05-06 10:59:18.000000000 +0200
@@ -5,6 +5,7 @@
 */
 
 #include "eradocumentprocessor.h"
+#include "logging.h"
 
 #include "era/elbticket.h"
 #include "era/ssbticketreader.h"
@@ -12,6 +13,12 @@
 #include "era/ssbv2ticket.h"
 #include "era/ssbv3ticket.h"
 
+#include <KItinerary/ExtractorResult>
+
+#include <KItinerary/Reservation>
+#include <KItinerary/Ticket>
+#include <KItinerary/TrainTrip>
+
 using namespace KItinerary;
 
 bool ElbDocumentProcessor::canHandleData(const QByteArray &encodedData, 
[[maybe_unused]] QStringView fileName) const
@@ -39,3 +46,64 @@
     node.setContent(SSBTicketReader::read(encodedData));
     return node;
 }
+
+TrainStation makeStation(int idType, const QString &alphaId, int numericId)
+{
+    TrainStation station;
+    if (idType == 0 && numericId > 10'00000 && numericId < 99'99999) {
+        station.setIdentifier(QLatin1String("uic:") + 
QString::number(numericId));
+        station.setName(QString::number(numericId));
+    } else if (idType == 1 && alphaId.size() == 5 && 
std::all_of(alphaId.begin(), alphaId.end(), [](QChar c) { return c.isUpper(); 
})) {
+        // TODO is the identifier type defined in that case??
+        station.setName(alphaId);
+    }
+    return station;
+}
+
+void SsbDocumentProcessor::preExtract(ExtractorDocumentNode &node, 
[[maybe_unused]] const ExtractorEngine *engine) const
+{
+    if (node.isA<SSBv3Ticket>()) {
+        const auto ssb = node.content<SSBv3Ticket>();
+
+        Seat seat;
+        seat.setSeatingType(QString::number(ssb.classOfTravel()));
+        Ticket ticket;
+        ticket.setTicketToken(QLatin1String("aztecbin:") + 
QString::fromLatin1(ssb.rawData().toBase64()));
+
+        Organization issuer;
+        issuer.setIdentifier(QLatin1String("uic:") + 
QString::number(ssb.issuerCode())); // left pad with 0 to 4 digets?
+        TrainTrip trip;
+        trip.setProvider(issuer);
+
+        TrainReservation res;
+        res.setReservationNumber(ssb.tcn());
+        ticket.setTicketNumber(ssb.tcn());
+
+        switch (ssb.ticketTypeCode()) {
+            case SSBv3Ticket::IRT_RES_BOA:
+                
trip.setDepartureDay(ssb.type1DepartureDay(node.contextDateTime()));
+                trip.setTrainNumber(ssb.type1TrainNumber());
+                seat.setSeatSection(QString::number(ssb.type1CoachNumber()));
+                seat.setSeatNumber(ssb.type1SeatNumber());
+                
trip.setDepartureStation(makeStation(ssb.type1StationCodeNumericOrAlpha(), 
ssb.type1DepartureStationAlpha(), ssb.type1DepartureStationNum()));
+                
trip.setDepartureStation(makeStation(ssb.type1StationCodeNumericOrAlpha(), 
ssb.type1ArrivalStationAlpha(), ssb.type1ArrivalStationNum()));
+                break;
+            case SSBv3Ticket::NRT:
+                
trip.setDepartureStation(makeStation(ssb.type2StationCodeNumericOrAlpha(), 
ssb.type2DepartureStationAlpha(), ssb.type2DepartureStationNum()));
+                
trip.setArrivalStation(makeStation(ssb.type2StationCodeNumericOrAlpha(), 
ssb.type2ArrivalStationAlpha(), ssb.type2ArrivalStationNum()));
+                
ticket.setValidFrom(ssb.type2ValidFrom(node.contextDateTime()).startOfDay());
+                
ticket.setValidUntil({ssb.type2ValidUntil(node.contextDateTime()), {23, 59, 
59}});
+                
trip.setDepartureDay(ssb.type2ValidFrom(node.contextDateTime()));
+                break;
+            case SSBv3Ticket::GRT:
+            case SSBv3Ticket::RPT:
+                qCWarning(Log) << "Unsupported SSB v3 ticket type:" << 
ssb.ticketTypeCode();
+                return;
+        }
+
+        res.setReservationFor(trip);
+        ticket.setTicketedSeat(seat);
+        res.setReservedTicket(ticket);
+        node.addResult(QVector<QVariant>{QVariant::fromValue(res)});
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.h 
new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.h
--- old/kitinerary-23.04.0/src/lib/processors/eradocumentprocessor.h    
2023-04-13 17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/processors/eradocumentprocessor.h    
2023-05-06 10:59:18.000000000 +0200
@@ -24,6 +24,7 @@
 public:
     bool canHandleData(const QByteArray &encodedData, QStringView fileName) 
const override;
     ExtractorDocumentNode createNodeFromData(const QByteArray &encodedData) 
const override;
+    void preExtract(ExtractorDocumentNode &node, const ExtractorEngine 
*engine) const override;
 };
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/processors/uic9183documentprocessor.cpp 
new/kitinerary-23.04.1/src/lib/processors/uic9183documentprocessor.cpp
--- old/kitinerary-23.04.0/src/lib/processors/uic9183documentprocessor.cpp      
2023-04-13 17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/processors/uic9183documentprocessor.cpp      
2023-05-06 10:59:18.000000000 +0200
@@ -9,6 +9,7 @@
 #include <KItinerary/ExtractorResult>
 #include <KItinerary/JsonLdDocument>
 #include <KItinerary/Uic9183Parser>
+#include <KItinerary/Uic9183TicketLayout>
 #include <KItinerary/Rct2Ticket>
 #include <era/fcbticket.h>
 #include <uic9183/uic9183head.h>
@@ -24,6 +25,7 @@
 
 Uic9183DocumentProcessor::Uic9183DocumentProcessor()
 {
+    qRegisterMetaType<KItinerary::Uic9183TicketLayoutField>();
     qRegisterMetaType<KItinerary::Vendor0080BLOrderBlock>();
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/processors/vdvdocumentprocessor.cpp 
new/kitinerary-23.04.1/src/lib/processors/vdvdocumentprocessor.cpp
--- old/kitinerary-23.04.0/src/lib/processors/vdvdocumentprocessor.cpp  
2023-04-13 17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/processors/vdvdocumentprocessor.cpp  
2023-05-06 10:59:18.000000000 +0200
@@ -7,15 +7,14 @@
 #include "vdvdocumentprocessor.h"
 
 #include <KItinerary/ExtractorResult>
-#include <KItinerary/JsonLdDocument>
+#include <KItinerary/Ticket>
 #include <KItinerary/VdvTicket>
+#include <KItinerary/VdvTicketContent>
 #include <KItinerary/VdvTicketParser>
 
 #include <KLocalizedString>
 
 #include <QByteArray>
-#include <QJsonArray>
-#include <QJsonObject>
 
 using namespace KItinerary;
 
@@ -40,32 +39,34 @@
 {
     const auto vdv = node.content<VdvTicket>();
 
-    QJsonObject seat;
-    seat.insert(QStringLiteral("@type"), QLatin1String("Seat"));
+    Seat seat;
     switch (vdv.serviceClass()) {
+        case VdvTicket::UnknownClass:
+            break;
         case VdvTicket::FirstClass:
         case VdvTicket::FirstClassUpgrade:
-            seat.insert(QStringLiteral("seatingType"), QStringLiteral("1"));
+            seat.setSeatingType(QStringLiteral("1"));
             break;
         case VdvTicket::SecondClass:
-            seat.insert(QStringLiteral("seatingType"), QStringLiteral("2"));
+            seat.setSeatingType(QStringLiteral("2"));
             break;
         default:
             break;
     }
 
-    QJsonObject ticket;
-    ticket.insert(QStringLiteral("@type"), QLatin1String("Ticket"));
-    ticket.insert(QStringLiteral("ticketToken"), 
QString(QLatin1String("aztecbin:") + 
QString::fromLatin1(vdv.rawData().toBase64())));
-    ticket.insert(QStringLiteral("ticketedSeat"), seat);
+    Ticket ticket;
+    ticket.setTicketToken(QLatin1String("aztecbin:") + 
QString::fromLatin1(vdv.rawData().toBase64()));
+    ticket.setTicketedSeat(seat);
     if (vdv.serviceClass() == VdvTicket::FirstClassUpgrade) {
-        ticket.insert(QStringLiteral("name"), i18n("Upgrade"));
+        ticket.setName(i18n("Upgrade"));
+    } else if (const auto hdr = vdv.header(); hdr && hdr->productId == 9999) {
+        ticket.setName(QLatin1String("Deutschlandticket"));
     } else {
-        ticket.insert(QStringLiteral("name"), i18n("Ticket"));
+        ticket.setName(i18n("Ticket"));
     }
-    ticket.insert(QStringLiteral("ticketNumber"), vdv.ticketNumber());
-    ticket.insert(QStringLiteral("validFrom"), 
JsonLdDocument::toJsonValue(vdv.beginDateTime()));
-    ticket.insert(QStringLiteral("validUntil"), 
JsonLdDocument::toJsonValue(vdv.endDateTime()));
-    ticket.insert(QStringLiteral("underName"), 
JsonLdDocument::toJson(vdv.person()));
-    node.addResult(QJsonArray({ticket}));
+    ticket.setTicketNumber(vdv.ticketNumber());
+    ticket.setValidFrom(vdv.beginDateTime());
+    ticket.setValidUntil(vdv.endDateTime());
+    ticket.setUnderName(vdv.person());
+    node.addResult(QVector<QVariant>({ticket}));
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/atpi.js 
new/kitinerary-23.04.1/src/lib/scripts/atpi.js
--- old/kitinerary-23.04.0/src/lib/scripts/atpi.js      2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/atpi.js      2023-05-06 
10:59:18.000000000 +0200
@@ -3,6 +3,20 @@
    SPDX-License-Identifier: LGPL-2.0-or-later
 */
 
+function parseTravellerFromEvent(event, res)
+{
+    let reservations = [];
+    const travData = event.description.match(/Traveller\n\n(\d\. .* \/ 
.*\n)+/);
+    for (trav of travData[1].trim().split(/\n/)) {
+        const name = trav.match(/\d\. (.*) \/ (.*)/);
+        let r = JsonLd.clone(res);
+        r.underName.familyName = name[1];
+        r.underName.givenName = name[2];
+        reservations.push(r);
+    }
+    return reservations;
+}
+
 function parseFlightEvent(event) {
     let res = JsonLd.newFlightReservation();
     const summary = event.summary.match(/([A-Z0-9]{2}) +(\d{1,4}) 
([A-Z]{3})\/([A-Z]{3})/);
@@ -20,7 +34,7 @@
     res.reservationFor.arrivalAirport.name = arr[1];
     res.reservationFor.arrivalTerminal = arr[2];
     res.reservationNumber = event.description.match(/Airline reference: 
(.*)/)[1];
-    return res;
+    return parseTravellerFromEvent(event, res);
 }
 
 function parseHotelEvent(event) {
@@ -34,6 +48,5 @@
     res.reservationFor.address.postalCode = addr[4].match(/[A-Z]{2}/) ? 
addr[3] : addr[4];
     res.reservationFor.address.addressCountry = addr[4].match(/[A-Z]{2}/) ? 
addr[4] : addr[3];
     res.reservationFor.telephone = '+' + addr[5];
-    console.log(event);
-    return res;
+    return parseTravellerFromEvent(event, res);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.js 
new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.js
--- old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.js  1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.js  2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,37 @@
+/*
+   SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parseConfirmation(html) {
+    const text = html.root.recursiveContent;
+
+    let res = JsonLd.newLodgingReservation();
+    res.reservationNumber = text.match(/Buchungsnummer: (.*)/)[1];
+    const hotel = text.match(/Hotel\n(.*)\n(.*)\n(.*) - (.*) - (.*)/i);
+    res.reservationFor.name = hotel[1];
+    res.reservationFor.address.streetAddress = hotel[2];
+    res.reservationFor.address.postalCode = hotel[3];
+    res.reservationFor.address.addressLocality = hotel[4];
+    res.reservationFor.address.addressCountry = hotel[5];
+    const dates = text.match(/(\d\d\/\d\d\/\d{4}) \(.* 
(\d\d).*\)\n[\s\S]+\n(\d\d\/\d\d\/\d{4}) \(.* (\d\d).*\)/);
+    res.checkinTime = JsonLd.toDateTime(dates[1] + ' ' + dates[2], 'dd/MM/yyyy 
hh', 'de');
+    res.checkoutTime = JsonLd.toDateTime(dates[3] + ' ' + dates[4], 
'dd/MM/yyyy hh', 'de');
+    res.reservationFor.telephone = html.root.eval('//a[starts-with(@href, 
"tel:")]')[0].content;
+    res.reservationFor.email = html.root.eval('//a[starts-with(@href, 
"mailto:";)]')[0].content;
+    res.modifyReservationUrl = html.root.eval('//a[contains(@href, 
"my-booking")]')[0].attribute('href');
+
+    let reservations = [];
+    let idx = 0;
+    while (true) {
+        const p = text.substr(idx).match(/(.*)\n.*\n.* x \d+\n.*€\n/);
+        if (!p)
+            break;
+        idx += p.index + p[0].length;
+        let r = JsonLd.clone(res);
+        r.underName.name = p[1];
+        reservations.push(r);
+    }
+
+    return reservations.length ? reservations : res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.json 
new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.json
--- old/kitinerary-23.04.0/src/lib/scripts/bb-hotel.json        1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/bb-hotel.json        2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,13 @@
+{
+    "filter": [
+        {
+            "field": "From",
+            "match": "nore...@email.hotel-bb.com",
+            "mimeType": "message/rfc822",
+            "scope": "Ancestors"
+        }
+    ],
+    "function": "parseConfirmation",
+    "mimeType": "text/html",
+    "script": "bb-hotel.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bestwestern.js 
new/kitinerary-23.04.1/src/lib/scripts/bestwestern.js
--- old/kitinerary-23.04.0/src/lib/scripts/bestwestern.js       1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/bestwestern.js       2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,20 @@
+/*
+   SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parseConfirmation(html) {
+    let res = JsonLd.newLodgingReservation();
+    const text = html.root.recursiveContent;
+    const addr = text.match(/Hoteladresse:\n(.*)\n(.*)\n(.*)\n\s+(.*)\n/);
+    res.reservationFor.name = addr[1];
+    res.reservationFor.address.streetAddress = addr[2];
+    res.reservationFor.address.postalCode = addr[3];
+    res.reservationFor.address.addressLocality = addr[4];
+    const dt = text.match(/Anreise\/Abreise: .* 
(\d\d\.\d\d\.\d{4})\n\s+\/\s*\n.* (\d\d\.\d\d\.\d{4})/);
+    res.checkinTime = JsonLd.toDateTime(dt[1], "dd.MM.yyyy", "de");
+    res.checkoutTime = JsonLd.toDateTime(dt[2], "dd.MM.yyyy", "de");
+    res.reservationNumber = text.match(/Buchungsnummer: (.*)\n/)[1];
+    res.cancelReservationUrl = 
html.eval('//a[contains(@href,"showBookingCancelPage")]')[0].attribute('href');
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/bestwestern.json 
new/kitinerary-23.04.1/src/lib/scripts/bestwestern.json
--- old/kitinerary-23.04.0/src/lib/scripts/bestwestern.json     1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/bestwestern.json     2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,13 @@
+{
+    "filter": [
+        {
+            "field": "From",
+            "match": "@bestwestern\\.",
+            "mimeType": "message/rfc822",
+            "scope": "Ancestors"
+        }
+    ],
+    "function": "parseConfirmation",
+    "mimeType": "text/html",
+    "script": "bestwestern.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/czechrailways.js 
new/kitinerary-23.04.1/src/lib/scripts/czechrailways.js
--- old/kitinerary-23.04.0/src/lib/scripts/czechrailways.js     2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/czechrailways.js     2023-05-06 
10:59:18.000000000 +0200
@@ -157,17 +157,20 @@
     let idx = 0;
     let reservations = [];
     while (true) {
-        // TODO this doesn't consider seat reservations yet, lacking 
corresponding samples
-        let leg = text.substr(idx).match(/\n(.*?)  +(\d\d\.\d\d\.) 
+(\d\d:\d\d) +(.*?)\n(.*?)  +(\d\d.\d\d.) +(\d\d:\d\d)/);
+        let leg = text.substr(idx).match(/\n(.*?)  +(\d\d\.\d\d\.) 
+(\d\d:\d\d) +(.*?)(?:  +(\d+) +(\d+))?\n(.*?)  +(\d\d.\d\d.) +(\d\d:\d\d)/);
         if (!leg) {
             break;
         }
         let res = JsonLd.newTrainReservation();
         res.reservationFor.departureStation.name = leg[1];
         res.reservationFor.departureTime = JsonLd.toDateTime(leg[2] + leg[3], 
'dd.MM.hh:mm', 'cz');
-        res.reservationFor.arrivalStation.name = leg[5];
-        res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[6] + leg[7], 
'dd.MM.hh:mm', 'cz');
+        res.reservationFor.arrivalStation.name = leg[7];
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[8] + leg[9], 
'dd.MM.hh:mm', 'cz');
         res.reservationFor.trainNumber = leg[4];
+        if (leg[5] && leg[6]) {
+            res.reservedTicket.ticketedSeat.seatSection = leg[5];
+            res.reservedTicket.ticketedSeat.seatNumber = leg[6];
+        }
 
         if (triggerNode.result[0]['@type'] == 'TrainReservation') {
             res = JsonLd.apply(triggerNode.result[0], res);
@@ -183,3 +186,27 @@
 
     return reservations;
 }
+
+function parseEvent(event) {
+    let reservations = [];
+    let idx = 0;
+    while (true) {
+        const leg = event.description.substr(idx).match(/(.*)\n(.*) 
(\d\d):(\d\d)\n(.*) (\d\d):(\d\d)\n.* (\d+),.* (\d+)/);
+        if (!leg)
+            break;
+        idx += leg.index + leg[0].length;
+
+        let res = JsonLd.newTrainReservation();
+        res.reservationFor.trainNumber = leg[1];
+        res.reservationFor.departureStation.name = leg[2];
+        res.reservationFor.departureTime = new Date(event.dtStart);
+        res.reservationFor.departureTime.setHours(leg[3], leg[4]);
+        res.reservationFor.arrivalStation.name = leg[5];
+        res.reservationFor.arrivalTime = new Date(event.dtStart);
+        res.reservationFor.arrivalTime.setHours(leg[6], leg[7]);
+        res.reservedTicket.ticketedSeat.seatSection = leg[8];
+        res.reservedTicket.ticketedSeat.seatNumber = leg[9];
+        reservations.push(res);
+    }
+    return reservations;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/scripts/czechrailways.json 
new/kitinerary-23.04.1/src/lib/scripts/czechrailways.json
--- old/kitinerary-23.04.0/src/lib/scripts/czechrailways.json   2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/czechrailways.json   2023-05-06 
10:59:18.000000000 +0200
@@ -18,4 +18,17 @@
     "function": "parsePdfTicket",
     "mimeType": "application/pdf",
     "script": "czechrailways.js"
+},
+{
+    "filter": [
+        {
+            "field": "uid",
+            "match": "@www\\.cd\\.cz$",
+            "mimeType": "internal/event",
+            "scope": "Current"
+        }
+    ],
+    "function": "parseEvent",
+    "mimeType": "internal/event",
+    "script": "czechrailways.js"
 }]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.js 
new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.js
--- old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.js      2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.js      2023-05-06 
10:59:18.000000000 +0200
@@ -367,3 +367,24 @@
 
     return res;
 }
+
+function parseDBRegioBusUic(uic, node)
+{
+    let ticket = node.result[0];
+    if (uic.ticketLayout.type != 'PLAI' || ticket.name !== '')
+        return;
+
+    ticket.name = uic.ticketLayout.firstField.text;
+    for (let f = uic.ticketLayout.firstField; f && !f.isNull; f = f.next) {
+        const validFrom = f.text.match(/Erster Gültigkeitstag: (.*)/);
+        if (validFrom) {
+            ticket.validFrom = JsonLd.toDateTime(validFrom[1], 'dd.MM.yyyy', 
'de');
+        }
+        const name = f.text.match(/Name Fahrtberechtigter: (.*)/);
+        if (name) {
+            ticket.underName = JsonLd.newObject('Person');
+            ticket.underName.name = name[1];
+        }
+    }
+    return ticket;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.json 
new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.json
--- old/kitinerary-23.04.0/src/lib/scripts/deutschebahn.json    2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/deutschebahn.json    2023-05-06 
10:59:18.000000000 +0200
@@ -94,5 +94,18 @@
         "function": "parseEvent",
         "mimeType": "internal/event",
         "script": "deutschebahn.js"
+    },
+    {
+        "filter": [
+            {
+                "field": "carrierId",
+                "match": "2080",
+                "mimeType": "internal/uic9183",
+                "scope": "Current"
+            }
+        ],
+        "function": "parseDBRegioBusUic",
+        "mimeType": "internal/uic9183",
+        "script": "deutschebahn.js"
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/extractors.qrc 
new/kitinerary-23.04.1/src/lib/scripts/extractors.qrc
--- old/kitinerary-23.04.0/src/lib/scripts/extractors.qrc       2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/extractors.qrc       2023-05-06 
10:59:18.000000000 +0200
@@ -34,6 +34,10 @@
         <file>baeder-suite.js</file>
         <file>bateliers-arcachon.json</file>
         <file>bateliers-arcachon.js</file>
+        <file>bb-hotel.json</file>
+        <file>bb-hotel.js</file>
+        <file>bestwestern.json</file>
+        <file>bestwestern.js</file>
         <file>blablacar-bus.json</file>
         <file>blablacar-bus.js</file>
         <file>booking.json</file>
@@ -82,10 +86,14 @@
         <file>fjordline.js</file>
         <file>flixbus.json</file>
         <file>flixbus.js</file>
+        <file>gepard-express.json</file>
+        <file>gepard-express.js</file>
         <file>gnv.it.json</file>
         <file>gnv.it.js</file>
         <file>gomus.json</file>
         <file>gomus.js</file>
+        <file>grimaldi-lines.json</file>
+        <file>grimaldi-lines.js</file>
         <file>gwr.json</file>
         <file>gwr.js</file>
         <file>hertz.js</file>
@@ -203,6 +211,8 @@
         <file>vr.fi.js</file>
         <file>vueling.json</file>
         <file>vueling.js</file>
+        <file>westbahn.json</file>
+        <file>westbahn.js</file>
         <file>whyline.json</file>
         <file>whyline.js</file>
         <file>zssk.json</file>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/flixbus.js 
new/kitinerary-23.04.1/src/lib/scripts/flixbus.js
--- old/kitinerary-23.04.0/src/lib/scripts/flixbus.js   2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/flixbus.js   2023-05-06 
10:59:18.000000000 +0200
@@ -22,8 +22,11 @@
 
 function parseDate(year, baseDate, overrideDate, time)
 {
+    baseDate = baseDate.replace('.', '');
+    if (overrideDate)
+        overrideDate = overrideDate.replace('.', '');
     const s = (overrideDate ? overrideDate.trim() : baseDate) + ' ' + year + ' 
' + time;
-    return JsonLd.toDateTime(s, 'd MMM yyyy hh:mm', ['en', 'fr', 'pl', 'nl']);
+    return JsonLd.toDateTime(s, 'd MMM yyyy hh:mm', ['en', 'fr', 'pl', 'nl', 
'de']);
 }
 
 function parseLocation(place, addr1, addr2, links)
@@ -44,7 +47,7 @@
     const text = page.textInRect(0.0, 0.05, 0.5, 0.5);
     const links = page.linksInRect(0.0, 0.0, 0.5, 0.5);
     const resNum = triggerNode.content.match(/pdfqr\/(\d+)\//)[1];
-    const date = text.match(/^\S+,? (\d+ \S+) (\d{4})\n/);
+    const date = text.match(/^\S+,? (\d+\.? \S+) (\d{4})\n/);
 
     const timeColumn = page.textInRect(0.0, 0.1, 0.125, 0.5);
     const stationColumn = page.textInRect(0.125, 0.1, 0.5, 0.5);
@@ -54,7 +57,7 @@
     let reservations = [];
     while (true) {
         const times = 
timeColumn.substr(idxTime).match(/(\d\d:\d\d)\n([^:]*?\n)?([^:]*?\n)?(\d\d:\d\d)/);
-        const stations = 
stationColumn.substr(idxStations).match(/(.*)\n[ ]+(.*)(?:\n|,\n  
+(.*)\n).*(?:Bus|Autobus) +(.*)\n.*(?:Direction|à destination 
de|Kierunek|richting) (.*)\n(.*)\n(?:[ ]+(.*?)(?:\n|,\n +(.*)\n))?/);
+        const stations = 
stationColumn.substr(idxStations).match(/(.*)\n[ ]+(.*)(?:\n|,\n  
+(.*)\n).*(?:Bus|Autobus) +(.*)\n.*(?:Direction|à destination 
de|Kierunek|richting|Richtung) (.*)\n(.*)\n(?:[ ]+(.*?)(?:\n|,\n 
+(.*)\n))?/);
         if (!times || !stations) {
             break;
         }
@@ -69,7 +72,7 @@
         res.reservationFor.departureBusStop.name = stations[1];
         parseLocation(res.reservationFor.departureBusStop, stations[2], 
stations[3], links);
 
-        res.reservationFor.busNumber = stations[4];
+        res.reservationFor.busNumber = stations[4].match(/(.*?)(?:  |$)/)[1];
         res.reservationFor.busName = stations[5];
 
         res.reservationFor.arrivalTime = parseDate(date[2], date[1], times[3] 
? times[3] : times[2], times[4]);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/gepard-express.js 
new/kitinerary-23.04.1/src/lib/scripts/gepard-express.js
--- old/kitinerary-23.04.0/src/lib/scripts/gepard-express.js    1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/gepard-express.js    2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,28 @@
+/*
+   SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parsePdf(pdf, node, triggerNode) {
+    const text = pdf.pages[triggerNode.location].text;
+    const legs = text.split(/  +(?=.* ->)/).slice(1);
+    let reservations = [];
+    for (leg of legs) {
+        let res = JsonLd.newBusReservation();
+        const stops = leg.match(/(.*) -> (.*)/);
+        res.reservationFor.departureBusStop.name = stops[1];
+        res.reservationFor.arrivalBusStop.name = stops[2];
+        const times = leg.match(/ (\d{1,2}\.\d{1,2}\.\d{4} \d\d:\d\d)\n[\s\S]+ 
(\d{1,2}\.\d{1,2}\.\d{4} \d\d:\d\d)\n/);
+        res.reservationFor.departureTime = JsonLd.toDateTime(times[1], 
'dd.M.yyyy hh:mm', 'cz');
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(times[2], 
'dd.M.yyyy hh:mm', 'cz');
+        res.reservationFor.departurePlatform = leg.match(/Platform +(.*)/)[1];
+        res.reservationNumber = 
triggerNode.content.match(/s:4:"code";s:\d+:"(.*?)"/)[1];
+        res.underName.name = 
triggerNode.content.match(/s:8:"fullName";s:\d+:"(.*?)"/)[1];
+        res.reservedTicket.ticketToken = 'qrcode:' + triggerNode.content;
+        reservations.push(res);
+        const seat = leg.match(/(?:Seat|sedadla:) +(.*?) \(/);
+        if (seat)
+            res.reservedTicket.ticketedSeat.seatNumber = seat[1];
+    }
+    return reservations;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/scripts/gepard-express.json 
new/kitinerary-23.04.1/src/lib/scripts/gepard-express.json
--- old/kitinerary-23.04.0/src/lib/scripts/gepard-express.json  1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/gepard-express.json  2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,12 @@
+{
+    "filter": [
+        {
+            "match": "^a:7:{s:",
+            "mimeType": "text/plain",
+            "scope": "Descendants"
+        }
+    ],
+    "function": "parsePdf",
+    "mimeType": "application/pdf",
+    "script": "gepard-express.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.js 
new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.js
--- old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.js    1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.js    2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,33 @@
+/*
+   SPDX-FileCopyrightText: 2023 Volker Krause <vkra...@kde.org>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parsePdfTicket(pdf, node, triggerNode) {
+    const text = pdf.pages[triggerNode.location].text;
+    let res = JsonLd.newBoatReservation();
+    const dep = text.match(/Departure: (.*)  +(\d{2}\/\d{2}\/\d{2} 
\d{2}:\d{2})/);
+    res.reservationFor.departureBoatTerminal.name = dep[1];
+    res.reservationFor.departureTime = JsonLd.toDateTime(dep[2], 'dd/MM/yy 
hh:mm', 'it');
+    const arr = text.match(/Arrival: (.*)  +(\d{2}\/\d{2}\/\d{2} 
\d{2}:\d{2})/);
+    res.reservationFor.arrivalBoatTerminal.name = arr[1];
+    res.reservationFor.arrivalTime = JsonLd.toDateTime(arr[2], 'dd/MM/yy 
hh:mm', 'it');
+    res.reservationNumber = triggerNode.content;
+    res.reservedTicket.ticketToken = 'barcode128:' + triggerNode.content;
+
+    let idx = 0;
+    let reservations = [res];
+    while (true) {
+        const pas = text.substr(idx).match(/  +(.*?)  +[MF]  
+\d\d-\d\d-\d{4}\n/);
+        if (!pas)
+            break;
+        idx += pas.index + pas[0].length;
+        let r = JsonLd.clone(res);
+        const name = pas[1].split(/  +/);
+        r.underName.familyName = name[0];
+        r.underName.givenName = name[1];
+        // TODO: per-passenger ticket tokens
+        reservations.push(r);
+    }
+    return reservations;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.json 
new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.json
--- old/kitinerary-23.04.0/src/lib/scripts/grimaldi-lines.json  1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/grimaldi-lines.json  2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,12 @@
+{
+    "filter": [
+        {
+            "match": "^G\\d{9}$",
+            "mimeType": "text/plain",
+            "scope": "Descendants"
+        }
+    ],
+    "function": "parsePdfTicket",
+    "mimeType": "application/pdf",
+    "script": "grimaldi-lines.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/sncb.js 
new/kitinerary-23.04.1/src/lib/scripts/sncb.js
--- old/kitinerary-23.04.0/src/lib/scripts/sncb.js      2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/sncb.js      2023-05-06 
10:59:18.000000000 +0200
@@ -6,14 +6,15 @@
 
 function parsePage(page) {
     var res = JsonLd.newTrainReservation();
-    res.reservationFor.departureStation.name = 
page.text.match(/De:\s+(.*)\n/)[1];
-    res.reservationFor.arrivalStation.name = page.text.match(/A:\s+(.*)\n/)[1];
-    var date = page.text.match(/Date du voyage:\s+(\d\d)\/(\d\d)\/(\d{4})\n/);
+    res.reservationFor.departureStation.name = 
page.text.match(/(?:De|From):\s+(.*)\n/)[1];
+    res.reservationFor.arrivalStation.name = 
page.text.match(/(?:A|TO):\s+(.*)\n/)[1];
+    var date = page.text.match(/(?:Date du voyage|Travel 
date):\s+(\d\d)\/(\d\d)\/(\d{4})\n/);
     res.reservationFor.departureDay = date[3] + "-" + date[2] + "-" + date[1];
-    res.underName.givenName = page.text.match(/Prénom:\s+(.*)\n/)[1];
-    res.underName.familyName = page.text.match(/Nom:\s+(.*)\n/)[1];
-    res.reservedTicket.ticketedSeat.seatingType = 
page.text.match(/Classe:\s+(.*)\n/)[1];
+    res.underName.givenName = 
page.text.match(/(?:Prénom|Firstname):\s+(.*)\n/)[1];
+    res.underName.familyName = 
page.text.match(/(?:Nom|Lastname):\s+(.*)\n/)[1];
+    res.reservedTicket.ticketedSeat.seatingType = 
page.text.match(/Classe?:\s+(.*)\n/)[1];
     res.reservedTicket.ticketToken = "barcode128:" + 
page.text.match(/\s+([A-Z\d-]{15})\n/)[1];
+    res.reservedTicket.name = page.text.match(/(?:Type de billet|Ticket 
type):\s+(.*)\n/)[1];
     return res;
 }
 
@@ -25,3 +26,27 @@
     }
     return reservations;
 }
+
+function parseInternationalPdf(pdf, node, triggerNode) {
+    const page = pdf.pages[triggerNode.location].text;
+    let reservations = [];
+    let idx = 0;
+    while (true) {
+        const leg = page.substr(idx).match(/(\d\d\/\d\d\/\d{4}) (\d\d:\d\d) 
(.*?) +-> +(\d\d:\d\d) (.*?)  +(.*?)  +(.*?)  +(.*)\n/);
+        if (!leg) {
+            break;
+        }
+        idx += leg.index + leg[0].length;
+
+        let res = JsonLd.clone(triggerNode.result[0]);
+        res.reservationFor.departureTime = JsonLd.toDateTime(leg[1] + ' ' + 
leg[2], 'dd/MM/yyyy hh:mm', 'nl');
+        res.reservationFor.departureStation.name = leg[3];
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[1] + ' ' + 
leg[4], 'dd/MM/yyyy hh:mm', 'nl');
+        res.reservationFor.arrivalStation.name = leg[5];
+        res.reservationFor.trainNumber = leg[6];
+        res.reservedTicket.ticketedSeat.seatSection = leg[7];
+        res.reservedTicket.ticketedSeat.seatNumber = leg[8];
+        reservations.push(res);
+    }
+    return reservations;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/sncb.json 
new/kitinerary-23.04.1/src/lib/scripts/sncb.json
--- old/kitinerary-23.04.0/src/lib/scripts/sncb.json    2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/sncb.json    2023-05-06 
10:59:18.000000000 +0200
@@ -1,13 +1,33 @@
-{
-    "filter": [
-        {
-            "field": "From",
-            "match": "ticketonl...@b-rail.be",
-            "mimeType": "message/rfc822",
-            "scope": "Ancestors"
-        }
-    ],
-    "function": "parsePdf",
-    "script": "sncb.js",
-    "mimeType": "application/pdf"
-}
+[
+    {
+        "filter": [
+            {
+                "field": "From",
+                "match": "ticketonl...@b-rail.be",
+                "mimeType": "message/rfc822",
+                "scope": "Ancestors"
+            },
+            {
+                "match": "^[A-Z][A-Z0-9]{14}$",
+                "mimeType": "text/plain",
+                "scope": "Descendants"
+            }
+        ],
+        "function": "parsePdf",
+        "script": "sncb.js",
+        "mimeType": "application/pdf"
+    },
+    {
+        "filter": [
+            {
+                "field": "carrierId",
+                "match": "1088",
+                "mimeType": "internal/uic9183",
+                "scope": "Descendants"
+            }
+        ],
+        "function": "parseInternationalPdf",
+        "mimeType": "application/pdf",
+        "script": "sncb.js"
+    }
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/vueling.js 
new/kitinerary-23.04.1/src/lib/scripts/vueling.js
--- old/kitinerary-23.04.0/src/lib/scripts/vueling.js   2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/scripts/vueling.js   2023-05-06 
10:59:18.000000000 +0200
@@ -49,5 +49,10 @@
     const times = topRight.match(/(\d{2}:\d{2}) (?:H|Uhr) +(\d{2}:\d{2}) 
(?:H|Uhr)/);
     res.reservationFor.departureTime = JsonLd.toDateTime(times[1], "hh:mm", 
"en");
     res.reservationFor.arrivalTime = JsonLd.toDateTime(times[2], "hh:mm", 
"en");
+    const topLeft = page.textInRect(0.0, 0.0, 0.5, 1.0);
+    const grp = topLeft.match(/(?:Group|Grupo|Gruppe) +(\d)/i);
+    if (grp) {
+        res.boardingGroup = grp[1];
+    }
     return res;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/westbahn.js 
new/kitinerary-23.04.1/src/lib/scripts/westbahn.js
--- old/kitinerary-23.04.0/src/lib/scripts/westbahn.js  1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/westbahn.js  2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,32 @@
+/*
+   SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parseTicket(pdf, node, triggerNode) {
+    const page = pdf.pages[triggerNode.location];
+    const leftCol = page.textInRect(0.0, 0.0, 0.5, 1.0);
+    const rightCol = page.textInRect(0.5, 0.0, 1.0, 1.0);
+    let res = JsonLd.newTrainReservation();
+    res.reservedTicket.name = rightCol.match(/TICKET\n(.*)/)[1];
+    res.reservedTicket.ticketToken = 'qrcode:' + triggerNode.content;
+    res.reservedTicket.ticketNumber = rightCol.match(/TICKET NUMMER\n(.*)/)[1];
+    res.reservationNumber = rightCol.match(/TICKET CODE\n(.*)/)[1];
+    const leg = rightCol.match(/VERBINDUNG\n(.*) . (.*)/);
+    res.reservationFor.departureStation.name = leg[1];
+    res.reservationFor.arrivalStation.name = leg[2];
+    const date = rightCol.match(/GÜLTIGKEIT\n.*(\d\d\.\d\d.\d{4})/)[1];
+    res.reservationFor.departureDay = JsonLd.toDateTime(date, 'dd.MM.yyyy', 
'de');
+    const seat = rightCol.match(/Wagen (\d+) .* Sitz (.*)/);
+    if (seat) {
+        res.reservedTicket.ticketedSeat.seatSection = seat[1];
+        res.reservedTicket.ticketedSeat.seatNumber = seat[2];
+    }
+    const train = leftCol.match(/(\d\d:\d\d) +(\d\d:\d\d)\n.*\n.*  +(\S.*?)  
+/);
+    if (train) {
+        res.reservationFor.trainNumber = train[3];
+        res.reservationFor.departureTime = JsonLd.toDateTime(date + ' ' + 
train[1], 'dd.MM.yyyy hh:mm', 'de');
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(date + ' ' + 
train[2], 'dd.MM.yyyy hh:mm', 'de');
+    }
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/scripts/westbahn.json 
new/kitinerary-23.04.1/src/lib/scripts/westbahn.json
--- old/kitinerary-23.04.0/src/lib/scripts/westbahn.json        1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-23.04.1/src/lib/scripts/westbahn.json        2023-05-06 
10:59:18.000000000 +0200
@@ -0,0 +1,12 @@
+{
+    "filter": [
+        {
+            "match": "^https://westbahn.at/t/";,
+            "mimeType": "text/plain",
+            "scope": "Descendants"
+        }
+    ],
+    "function": "parseTicket",
+    "mimeType": "application/pdf",
+    "script": "westbahn.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/sortutil.cpp 
new/kitinerary-23.04.1/src/lib/sortutil.cpp
--- old/kitinerary-23.04.0/src/lib/sortutil.cpp 2023-04-13 17:08:26.000000000 
+0200
+++ new/kitinerary-23.04.1/src/lib/sortutil.cpp 2023-05-06 10:59:18.000000000 
+0200
@@ -151,6 +151,13 @@
 bool SortUtil::isBefore(const QVariant &lhs, const QVariant &rhs)
 {
     if (startDateTime(lhs) == startDateTime(rhs) && lhs.userType() == 
rhs.userType() && JsonLd::canConvert<Reservation>(lhs)) {
+        // sort by end date if available next, e.g. for overlapping hotel 
bookings
+        const auto lhsEndDt = endDateTime(lhs);
+        const auto rhsEndDt = endDateTime(rhs);
+        if (JsonLd::isA<LodgingReservation>(lhs) && lhsEndDt.isValid() && 
rhsEndDt.isValid() && lhsEndDt != rhsEndDt) {
+            return lhsEndDt < rhsEndDt;
+        }
+
         // for multi-traveler reservations, sort by traveler name to achieve a 
stable result
         const auto lhsRes = JsonLd::convert<Reservation>(lhs);
         const auto rhsRes = JsonLd::convert<Reservation>(rhs);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/text/nameoptimizer.cpp 
new/kitinerary-23.04.1/src/lib/text/nameoptimizer.cpp
--- old/kitinerary-23.04.0/src/lib/text/nameoptimizer.cpp       2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/text/nameoptimizer.cpp       2023-05-06 
10:59:18.000000000 +0200
@@ -57,6 +57,37 @@
     return std::any_of(std::begin(name_prefixes), std::end(name_prefixes), 
[s](const char *prefix) { return s == QLatin1String(prefix); });
 }
 
+static QStringView stripNamePrefix(QStringView s)
+{
+    for (auto prefix : name_prefixes) {
+        QLatin1String p(prefix);
+        if (s.endsWith(p) && s.size() > p.size() && s[s.size() - p.size() - 1] 
== QLatin1Char(' ')) {
+            return s.left(s.size() - p.size() - 1);
+        }
+    }
+
+    return s;
+}
+
+static bool isSameChar(QChar c1, QChar c2)
+{
+    if (c1 == c2) {
+        return true;
+    }
+
+    if (c1.decompositionTag() != c2.decompositionTag()) {
+        if (c1.decompositionTag() == QChar::Canonical) {
+            c1 = c1.decomposition().at(0);
+        }
+        if (c2.decompositionTag() == QChar::Canonical) {
+            c2 = c2.decomposition().at(0);
+        }
+        return c1 == c2;
+    }
+
+    return false;
+}
+
 QString NameOptimizer::optimizeNameString(const QString &text, const QString 
&name)
 {
     if (name.size() < 2) {
@@ -65,41 +96,33 @@
 
     for (int i = 0; i < text.size(); ++i) {
         bool mismatch = false;
-        auto nameLen = name.size();
-        for (int j = 0; j < name.size(); ++j) {
+        int nameLen = 0;
+        for (int j = 0; j < name.size(); ++j, ++nameLen) {
             // reached the end of text
-            if (i + j >= text.size()) {
+            if (i + nameLen >= text.size()) {
                 // remainder is either a prefix placed as suffix (see below), 
or we are unsuccessful
-                if (isNamePrefix(QStringView(name).mid(j))) {
-                    nameLen = j;
-                } else {
-                    return name;
+                if (!isNamePrefix(QStringView(name).mid(j))) {
+                    mismatch = true;
                 }
                 break;
             }
 
-            auto c1 = text.at(i+j).toCaseFolded();
+            auto c1 = text.at(i+nameLen).toCaseFolded();
             auto c2 = name.at(j).toCaseFolded();
 
-            if (c1 == c2) {
+            if (isSameChar(c1, c2)) {
                 continue;
             }
 
-            if (c1.decompositionTag() != c2.decompositionTag()) {
-                if (c1.decompositionTag() == QChar::Canonical) {
-                    c1 = c1.decomposition().at(0);
-                }
-                if (c2.decompositionTag() == QChar::Canonical) {
-                    c2 = c2.decomposition().at(0);
-                }
-                if (c1 == c2) {
-                    continue;
-                }
+            // expand spaces missing in name
+            if (nameLen > 0 && c1 == QLatin1Char(' ') && (i + nameLen + 1) < 
text.size() && isSameChar(text.at(i+nameLen+1).toCaseFolded(), c2)) {
+                ++nameLen;
+                continue;
             }
 
             // mismatch: check if the remainder is a name prefix (yes, those 
also occur frequently as suffixes of name parts in IATA BCBP for example)
             if (isNamePrefix(QStringView(name).mid(j))) {
-                nameLen = QStringView(name).left(j).trimmed().size();
+                nameLen = QStringView(name).left(nameLen).trimmed().size();
                 break;
             }
 
@@ -118,8 +141,9 @@
             continue;
         }
 
-        if (StringUtil::betterString(QStringView(text).mid(i, nameLen), name) 
!= name) {
-            return text.mid(i, nameLen);
+        const auto betterName = QStringView(text).mid(i, nameLen).trimmed();
+        if (StringUtil::betterString(betterName, name) != name) {
+            return stripNamePrefix(betterName).toString();
         }
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/text/terminalfinder.cpp 
new/kitinerary-23.04.1/src/lib/text/terminalfinder.cpp
--- old/kitinerary-23.04.0/src/lib/text/terminalfinder.cpp      2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/text/terminalfinder.cpp      2023-05-06 
10:59:18.000000000 +0200
@@ -17,7 +17,7 @@
     { " ?\\((?:terminal|aerogare) (?<name>\\w.*)\\)", 
QRegularExpression::CaseInsensitiveOption },
     { " ?\\((?<name>\\w.*) (?:terminal|aerogare)\\)", 
QRegularExpression::CaseInsensitiveOption },
     { "(?:, | ?- ?| )(?:terminal|aerogare) (?<name>\\w.*?)", 
QRegularExpression::CaseInsensitiveOption },
-    { " T(?<name>\\d[A-Z]?)", QRegularExpression::NoPatternOption },
+    { " T-?(?<name>\\d[A-Z]?)", QRegularExpression::NoPatternOption },
     { " ?\\(T(?<name>\\d[A-Z]?)\\)", QRegularExpression::NoPatternOption },
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/uic9183/rct2ticket.cpp 
new/kitinerary-23.04.1/src/lib/uic9183/rct2ticket.cpp
--- old/kitinerary-23.04.0/src/lib/uic9183/rct2ticket.cpp       2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/uic9183/rct2ticket.cpp       2023-05-06 
10:59:18.000000000 +0200
@@ -72,7 +72,8 @@
 }
 
 static constexpr const char* res_patterns[] = {
-    "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN 
+(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)"
+    "ZUG +(?P<train_number>\\d+) +(?P<train_category>[A-Z][A-Z0-9]+) +WAGEN 
+(?P<coach>\\d+) +PLATZ +(?P<seat>\\d[\\d, ]+)",
+    "ZUG +(?P<train_number>\\d+) +WAGEN +(?P<coach>\\d+) +PLATZ 
+(?P<seat>\\d[\\d, ]+)",
 };
 
 QString Rct2TicketPrivate::reservationPatternCapture(QStringView name) const
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/uic9183/uic9183parser.cpp 
new/kitinerary-23.04.1/src/lib/uic9183/uic9183parser.cpp
--- old/kitinerary-23.04.0/src/lib/uic9183/uic9183parser.cpp    2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/uic9183/uic9183parser.cpp    2023-05-06 
10:59:18.000000000 +0200
@@ -158,8 +158,8 @@
         if (issuerId == 80 && key.size() == 13 && 
key.endsWith(QLatin1String("0101"))) {
             return key.left(9); // DB domestic part of an international order
         }
-        if (issuerId == 1184 && key.size() == 9 && key.at(7) == 
QLatin1Char('_') && key.at(8).isDigit()) {
-            return key.left(7); // NS
+        if ((issuerId == 1088 || issuerId == 1184) && key.size() == 9 && 
key.at(7) == QLatin1Char('_') && key.at(8).isDigit()) {
+            return key.left(7); // SNCB and NS
         }
 
         return key;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.04.0/src/lib/uic9183/uic9183ticketlayout.h 
new/kitinerary-23.04.1/src/lib/uic9183/uic9183ticketlayout.h
--- old/kitinerary-23.04.0/src/lib/uic9183/uic9183ticketlayout.h        
2023-04-13 17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/uic9183/uic9183ticketlayout.h        
2023-05-06 10:59:18.000000000 +0200
@@ -36,6 +36,8 @@
     UIC_NUM_PROPERTY(size, 9 + m_offset, 4)
     UIC_STR_PROPERTY(text, 13 + m_offset, size())
 
+    Q_PROPERTY(bool isNull READ isNull STORED false)
+    Q_PROPERTY(KItinerary::Uic9183TicketLayoutField next READ next STORED 
false)
 public:
     Uic9183TicketLayoutField();
     /** Create a new U_TLAY field starting at @p offset in @p block.
@@ -64,7 +66,7 @@
     /** Ticket type (e.g. RCT2). */
     Q_PROPERTY(QString type READ type)
     Q_PROPERTY(QSize size READ size)
-
+    Q_PROPERTY(KItinerary::Uic9183TicketLayoutField firstField READ firstField 
STORED false)
 public:
     Uic9183TicketLayout();
     /** Parse U_TLAY ticket layout block in @p data.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/lib/vdv/vdvticket.cpp 
new/kitinerary-23.04.1/src/lib/vdv/vdvticket.cpp
--- old/kitinerary-23.04.0/src/lib/vdv/vdvticket.cpp    2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/lib/vdv/vdvticket.cpp    2023-05-06 
10:59:18.000000000 +0200
@@ -78,8 +78,14 @@
     offset += prodTransactionBlock.size();
     offset += sizeof(VdvTicketIssueData);
 
-    // 0 padding to reach at least 111 bytes
-    offset += std::max(111 - offset - (int)sizeof(VdvTicketTrailer), 0);
+    // 0 padding to reach the minimum size
+    // 111 in the specs known to us, but seems to vary in practice, so be 
flexible and
+    // just align with the input end
+    if (data.size() < offset + (int)sizeof(VdvTicketTrailer)) {
+        qCWarning(Log) << "Ticket data too small for VDV ticket trailer" << 
offset;
+        return;
+    }
+    offset = data.size() - (int)sizeof(VdvTicketTrailer);
 
     const auto trailer = reinterpret_cast<const 
VdvTicketTrailer*>(data.constData() + offset);
     if (memcmp(trailer->identifier, "VDV", 3) != 0) {
@@ -216,10 +222,8 @@
 
 const VdvTicketTrailer* VdvTicket::trailer() const
 {
-    auto offset = sizeof(VdvTicketHeader) + productData().size()
-                      + sizeof(VdvTicketCommonTransactionData) + 
productSpecificTransactionData().size()
-                      + sizeof(VdvTicketIssueData);
-    offset += std::max<int>(111 - offset - sizeof(VdvTicketTrailer), 0); // 
padding to 111 bytes
+    // see above
+    const auto offset = d->m_data.size() - (int)sizeof(VdvTicketTrailer);
     return d->m_data.isEmpty() ? nullptr : reinterpret_cast<const 
VdvTicketTrailer*>(d->m_data.constData() + offset);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.04.0/src/tools/ticket-barcode-dump.cpp 
new/kitinerary-23.04.1/src/tools/ticket-barcode-dump.cpp
--- old/kitinerary-23.04.0/src/tools/ticket-barcode-dump.cpp    2023-04-13 
17:08:26.000000000 +0200
+++ new/kitinerary-23.04.1/src/tools/ticket-barcode-dump.cpp    2023-05-06 
10:59:18.000000000 +0200
@@ -95,10 +95,19 @@
         std::cout << prop.name() << ": " << qPrintable(value.toString()) << 
std::endl;
     }
 
-    if (ticket.ticketTypeCode() == SSBv3Ticket::IRT_RES_BOA) {
-        std::cout << std::endl;
-        std::cout << "Issuing day: " << 
qPrintable(ticket.issueDate().toString(Qt::ISODate)) << std::endl;
-        std::cout << "Departure day: " << 
qPrintable(ticket.type1DepartureDay().toString(Qt::ISODate)) << std::endl;
+    std::cout << std::endl;
+    std::cout << "Issuing day: " << 
qPrintable(ticket.issueDate().toString(Qt::ISODate)) << std::endl;
+    switch (ticket.ticketTypeCode()) {
+        case SSBv3Ticket::IRT_RES_BOA:
+            std::cout << "Departure day: " << 
qPrintable(ticket.type1DepartureDay().toString(Qt::ISODate)) << std::endl;
+            break;
+        case SSBv3Ticket::NRT:
+            std::cout << "Valid from: " << 
qPrintable(ticket.type2ValidFrom().toString(Qt::ISODate)) << std::endl;
+            std::cout << "Valid until: " << 
qPrintable(ticket.type2ValidUntil().toString(Qt::ISODate)) << std::endl;
+            break;
+        case SSBv3Ticket::GRT:
+        case SSBv3Ticket::RPT:
+            break;
     }
 }
 

Reply via email to