commit 32a1c1c59b43b01bed97aab2cc18315fe0a39a41
Author: Eric Bollengier <eric@baculasystems.com>
Date:   Wed Jan 24 12:04:40 2024 +0100

    Fix org#2704 about old FD compatibility
    
    Old client (prior to 11.0.0) job messages are silently dropped from
    the director logs. The Job status is correct, but the messages are
    not displayed in the catalog or the bacula-dir.log.
    
    The workaround is to enable the local log on each impacted FD,
    or upgrade them to 13.0.x.
    
    This fix will detect the client version and apply a fix dynamically
    when getting a message.

diff --git a/src/dird/fd_cmds.c b/src/dird/fd_cmds.c
index a42b172321..185c1c33be 100644
--- a/src/dird/fd_cmds.c
+++ b/src/dird/fd_cmds.c
@@ -72,6 +72,34 @@ static void delete_bsock_end_cb(JCR *jcr, void *ctx)
    free_bsock(socket);
 }
 
+/* 16.0.10 (12Jan24) x86_64-pc-linux-gnu,ubuntu,20.04 -> 160010 */
+static uint64_t scan_version(char *str)
+{
+   Enter(0);
+   uint64_t version = 0;
+
+   regex_t r1;
+   regmatch_t pmatch[16];
+   regcomp(&r1, "^([0-9]+)\\.([0-9]+)\\.([0-9]+)", REG_EXTENDED);
+   if (regexec(&r1, str, 4, pmatch, 0) == 0 &&
+       pmatch[1].rm_so == 0 && pmatch[1].rm_eo > 0 && pmatch[1].rm_eo < 50 &&
+       pmatch[2].rm_so > 0 && pmatch[2].rm_eo > 0 && (pmatch[2].rm_eo - pmatch[2].rm_so) < 50 &&
+       pmatch[3].rm_so > 0 && pmatch[3].rm_eo > 0 && (pmatch[3].rm_eo - pmatch[3].rm_so) < 50)
+   {
+      char buf[50];
+      bstrncpy(buf, str + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so + 1);
+      version = str_to_uint64(buf) * 10000;
+
+      bstrncpy(buf, str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so + 1);
+      version += str_to_uint64(buf) * 100;
+
+      bstrncpy(buf, str + pmatch[3].rm_so, pmatch[3].rm_eo - pmatch[3].rm_so + 1);
+      version += str_to_uint64(buf);
+   }
+   regfree(&r1);
+   return version;
+}
+
 /*
  * Open connection with File daemon.
  * Try connecting every retry_interval (default 10 sec), and
@@ -195,6 +223,8 @@ int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
           cr.FileRetention = jcr->client->FileRetention;
           cr.JobRetention = jcr->client->JobRetention;
           bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
+          jcr->client_version = scan_version(cr.Uname);
+
           if (!db_update_client_record(jcr, jcr->db, &cr)) {
              Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
                 db_strerror(jcr->db));
diff --git a/src/dird/getmsg.c b/src/dird/getmsg.c
index 89e6b97785..c441fcf65e 100644
--- a/src/dird/getmsg.c
+++ b/src/dird/getmsg.c
@@ -251,6 +251,14 @@ int bget_dirmsg(JCR *jcr, BSOCK *bs, BSOCK_CLIENT_TYPE role)
          if (*msg == ' ') {
             msg++;                    /* skip leading space */
          }
+         /* Fix to support old FDs */
+         if (role == BSOCK_TYPE_FD && jcr->client_version > 0 && jcr->client_version < 130000) {
+            type = type + 1; /* Adding M_EVENTS pushed all old events by 1, we fix it automatically here */
+         }
+         if (type == M_ABORT) { // not allowed here
+            Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg);
+            continue;
+         }
          Dmsg1(900, "Dispatch msg: %s", msg);
          dispatch_message(jcr, type, mtime, msg);
          continue;
diff --git a/src/jcr.h b/src/jcr.h
index 53038cf587..a0c5c12106 100644
--- a/src/jcr.h
+++ b/src/jcr.h
@@ -399,6 +399,7 @@ public:
    int32_t FDVersion;                 /* File daemon version number */
    int32_t SDVersion;                 /* Storage daemon version number */
    int64_t spool_size;                /* Spool size for this job */
+   uint64_t client_version;           /* Client version as a number */
    utime_t snapshot_retention;        /* Snapshot retention (from Client/Job resource) */
    volatile bool sd_msg_thread_done;  /* Set when Storage message thread done */
    bool wasVirtualFull;               /* set if job was VirtualFull */
