From 109adf8368c4fca7af4e1bfc1ff9e8648ed1c73f Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 22:12:18 +0000
Subject: [PATCH 01/13] Cleanup: Codestyle

---
 src/format_flac.c | 44 ++++++++++++++++++++++++--------------------
 1 file changed, 24 insertions(+), 20 deletions(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index d804463d..a4401dd3 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -34,9 +34,9 @@
 static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
 {
     ICECAST_LOG_DEBUG("freeing FLAC codec");
-    stats_event (ogg_info->mount, "FLAC_version", NULL);
-    ogg_stream_clear (&codec->os);
-    free (codec);
+    stats_event(ogg_info->mount, "FLAC_version", NULL);
+    ogg_stream_clear(&codec->os);
+    free(codec);
 }
 
 
@@ -45,36 +45,40 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
 {
     refbuf_t * refbuf;
 
-    if (codec->headers)
-    {
+    if (codec->headers) {
         ogg_packet packet;
-        if (ogg_stream_pagein (&codec->os, page) < 0)
-        {
+
+        if (ogg_stream_pagein(&codec->os, page) < 0) {
             ogg_info->error = 1;
             return NULL;
         }
-        while (ogg_stream_packetout (&codec->os, &packet))
-        {
+
+        while (ogg_stream_packetout(&codec->os, &packet)) {
             int type = packet.packet[0];
-            if (type == 0xFF)
-            {
+
+            if (type == 0xFF) {
                 codec->headers = 0;
                 break;
             }
+
             if (type >= 1 && type <= 0x7E)
                 continue;
             if (type >= 0x81 && type <= 0xFE)
                 continue;
+
             ogg_info->error = 1;
+
             return NULL;
         }
-        if (codec->headers)
-        {
-            format_ogg_attach_header (ogg_info, page);
+
+        if (codec->headers) {
+            format_ogg_attach_header(ogg_info, page);
             return NULL;
         }
     }
-    refbuf = make_refbuf_with_page (page);
+
+    refbuf = make_refbuf_with_page(page);
+
     return refbuf;
 }
 
@@ -84,13 +88,13 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
 ogg_codec_t *initial_flac_page (format_plugin_t *plugin, ogg_page *page)
 {
     ogg_state_t *ogg_info = plugin->_state;
-    ogg_codec_t *codec = calloc (1, sizeof (ogg_codec_t));
+    ogg_codec_t *codec = calloc(1, sizeof(ogg_codec_t));
     ogg_packet packet;
 
-    ogg_stream_init (&codec->os, ogg_page_serialno (page));
-    ogg_stream_pagein (&codec->os, page);
+    ogg_stream_init(&codec->os, ogg_page_serialno(page));
+    ogg_stream_pagein(&codec->os, page);
 
-    ogg_stream_packetout (&codec->os, &packet);
+    ogg_stream_packetout(&codec->os, &packet);
 
     ICECAST_LOG_DEBUG("checking for FLAC codec");
     do
@@ -108,7 +112,7 @@ ogg_codec_t *initial_flac_page (format_plugin_t *plugin, ogg_page *page)
         ICECAST_LOG_INFO("seen initial FLAC header");
 
         parse += 4;
-        stats_event_args (ogg_info->mount, "FLAC_version", "%d.%d",  parse[0], parse[1]);
+        stats_event_args(ogg_info->mount, "FLAC_version", "%d.%d",  parse[0], parse[1]);
         codec->process_page = process_flac_page;
         codec->codec_free = flac_codec_free;
         codec->headers = 1;

From 65f7f559e4d102fc718c832062849378890ce4b7 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 22:14:12 +0000
Subject: [PATCH 02/13] Fix: Check for packet.bytes >= 1

---
 src/format_flac.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index a4401dd3..c09a5c10 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -54,18 +54,20 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
         }
 
         while (ogg_stream_packetout(&codec->os, &packet)) {
-            int type = packet.packet[0];
+            if (packet.bytes >= 1) {
+                int type = packet.packet[0];
 
-            if (type == 0xFF) {
-                codec->headers = 0;
-                break;
+                if (type == 0xFF) {
+                    codec->headers = 0;
+                    break;
+                }
+
+                if (type >= 1 && type <= 0x7E)
+                    continue;
+                if (type >= 0x81 && type <= 0xFE)
+                    continue;
             }
 
-            if (type >= 1 && type <= 0x7E)
-                continue;
-            if (type >= 0x81 && type <= 0xFE)
-                continue;
-
             ogg_info->error = 1;
 
             return NULL;

From 756e3b7d22fb6819d79b06b5b3cf72924dd82799 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 22:33:28 +0000
Subject: [PATCH 03/13] Update: Parse FLAC block types and report via
 ICECAST_LOG_DEBUG()

---
 src/format_flac.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index c09a5c10..8b058d6f 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -19,6 +19,7 @@
 #endif
 
 #include <stdlib.h>
+#include <stdint.h>
 #include <ogg/ogg.h>
 #include <string.h>
 
@@ -30,6 +31,64 @@
 #define CATMODULE "format-flac"
 #include "logging.h"
 
+typedef enum {
+    FLAC_BLOCK_TYPE__ERROR = -1,
+    FLAC_BLOCK_TYPE_STREAMINFO = 0,
+    FLAC_BLOCK_TYPE_PADDING = 1,
+    FLAC_BLOCK_TYPE_APPLICATION = 2,
+    FLAC_BLOCK_TYPE_SEEKTABLE = 3,
+    FLAC_BLOCK_TYPE_VORBIS_COMMENT = 4,
+    FLAC_BLOCK_TYPE_CUESHEET = 5,
+    FLAC_BLOCK_TYPE_PICTURE = 6
+} flac_block_type_t;
+
+static flac_block_type_t flac_blocktype(const ogg_packet * packet)
+{
+    uint8_t type;
+
+    if (packet->bytes <= 4)
+        return FLAC_BLOCK_TYPE__ERROR;
+
+    type = packet->packet[0] & 0x7F;
+
+    if (type <= FLAC_BLOCK_TYPE_PICTURE) {
+        return type;
+    } else {
+        return FLAC_BLOCK_TYPE__ERROR;
+    }
+}
+
+static const char * flac_block_type_to_name(flac_block_type_t type)
+{
+    switch (type) {
+        case FLAC_BLOCK_TYPE__ERROR:
+            return "<error>";
+            break;
+        case FLAC_BLOCK_TYPE_STREAMINFO:
+            return "STREAMINFO";
+            break;
+        case FLAC_BLOCK_TYPE_PADDING:
+            return "PADDING";
+            break;
+        case FLAC_BLOCK_TYPE_APPLICATION:
+            return "APPLICATION";
+            break;
+        case FLAC_BLOCK_TYPE_SEEKTABLE:
+            return "SEEKTABLE";
+            break;
+        case FLAC_BLOCK_TYPE_VORBIS_COMMENT:
+            return "VORBIS_COMMENT";
+            break;
+        case FLAC_BLOCK_TYPE_CUESHEET:
+            return "CUESHEET";
+            break;
+        case FLAC_BLOCK_TYPE_PICTURE:
+            return "PICTURE";
+            break;
+    }
+
+    return "<unknown>";
+}
 
 static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
 {
@@ -55,13 +114,18 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
 
         while (ogg_stream_packetout(&codec->os, &packet)) {
             if (packet.bytes >= 1) {
-                int type = packet.packet[0];
+                uint8_t type = packet.packet[0];
+                flac_block_type_t blocktype;
 
                 if (type == 0xFF) {
                     codec->headers = 0;
                     break;
                 }
 
+                blocktype = flac_blocktype(&packet);
+
+                ICECAST_LOG_DEBUG("Found header of type %s%s", flac_block_type_to_name(blocktype), (type & 0x80) ? "|0x80" : "");
+
                 if (type >= 1 && type <= 0x7E)
                     continue;
                 if (type >= 0x81 && type <= 0xFE)

From c80fd692b2e300f1b2642efaffe52794fb8300b8 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 22:53:49 +0000
Subject: [PATCH 04/13] Feature: Added basic FLAC METADATA_BLOCK_HEADER parser

---
 src/format_flac.c | 68 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 49 insertions(+), 19 deletions(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index 8b058d6f..18b9569b 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -20,6 +20,7 @@
 
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdbool.h>
 #include <ogg/ogg.h>
 #include <string.h>
 
@@ -42,20 +43,55 @@ typedef enum {
     FLAC_BLOCK_TYPE_PICTURE = 6
 } flac_block_type_t;
 
-static flac_block_type_t flac_blocktype(const ogg_packet * packet)
+typedef struct {
+    flac_block_type_t type;
+    bool last;
+    size_t len;
+    const void *data;
+} flac_block_t;
+
+/* returns true if parse was ok, false on error */
+static bool flac_parse_block(flac_block_t *block, const ogg_packet * packet)
 {
     uint8_t type;
+    uint32_t len;
 
+    /* check header length */
     if (packet->bytes <= 4)
-        return FLAC_BLOCK_TYPE__ERROR;
+        return false;
 
-    type = packet->packet[0] & 0x7F;
+    type = packet->packet[0];
 
-    if (type <= FLAC_BLOCK_TYPE_PICTURE) {
-        return type;
-    } else {
-        return FLAC_BLOCK_TYPE__ERROR;
+    /* 0xFF is the sync code for a FRAME not a block */
+    if (type == 0xFF)
+        return false;
+
+    memset(block, 0, sizeof(*block));
+
+    if (type & 0x80) {
+        block->last = true;
+        type &= 0x7F;
     }
+
+    if (type > FLAC_BLOCK_TYPE_PICTURE)
+        return false;
+
+    block->type = type;
+
+    len  = (unsigned char)packet->packet[1];
+    len <<= 8;
+    len |= (unsigned char)packet->packet[2];
+    len <<= 8;
+    len |= (unsigned char)packet->packet[3];
+
+    /* check Ogg packet size vs. self-sync size */
+    if (packet->bytes != (len + 4))
+        return false;
+
+    block->len = len;
+    block->data = packet->packet + 4;
+
+    return true;
 }
 
 static const char * flac_block_type_to_name(flac_block_type_t type)
@@ -113,23 +149,17 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
         }
 
         while (ogg_stream_packetout(&codec->os, &packet)) {
-            if (packet.bytes >= 1) {
-                uint8_t type = packet.packet[0];
-                flac_block_type_t blocktype;
+            flac_block_t block;
 
-                if (type == 0xFF) {
+            if (flac_parse_block(&block, &packet)) {
+                ICECAST_LOG_DEBUG("Found header of type %s%s with %zu bytes of data", flac_block_type_to_name(block.type), block.last ? "(last)" : "", block.len);
+
+                if (block.last) {
                     codec->headers = 0;
                     break;
                 }
 
-                blocktype = flac_blocktype(&packet);
-
-                ICECAST_LOG_DEBUG("Found header of type %s%s", flac_block_type_to_name(blocktype), (type & 0x80) ? "|0x80" : "");
-
-                if (type >= 1 && type <= 0x7E)
-                    continue;
-                if (type >= 0x81 && type <= 0xFE)
-                    continue;
+                continue;
             }
 
             ogg_info->error = 1;

From e0ed27bc4f798be4bd37600a83b5db92f4636259 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 23:50:28 +0000
Subject: [PATCH 05/13] Update: Created a stub function to handle all FLAC
 BLOCKs

---
 src/format_flac.c | 30 +++++++++++++++++++-----------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index 18b9569b..e8fd8988 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -51,16 +51,16 @@ typedef struct {
 } flac_block_t;
 
 /* returns true if parse was ok, false on error */
-static bool flac_parse_block(flac_block_t *block, const ogg_packet * packet)
+static bool flac_parse_block(flac_block_t *block, const ogg_packet * packet, size_t offset)
 {
     uint8_t type;
     uint32_t len;
 
     /* check header length */
-    if (packet->bytes <= 4)
+    if ((size_t)packet->bytes <= (4 + offset))
         return false;
 
-    type = packet->packet[0];
+    type = packet->packet[offset];
 
     /* 0xFF is the sync code for a FRAME not a block */
     if (type == 0xFF)
@@ -78,18 +78,18 @@ static bool flac_parse_block(flac_block_t *block, const ogg_packet * packet)
 
     block->type = type;
 
-    len  = (unsigned char)packet->packet[1];
+    len  = (unsigned char)packet->packet[1 + offset];
     len <<= 8;
-    len |= (unsigned char)packet->packet[2];
+    len |= (unsigned char)packet->packet[2 + offset];
     len <<= 8;
-    len |= (unsigned char)packet->packet[3];
+    len |= (unsigned char)packet->packet[3 + offset];
 
     /* check Ogg packet size vs. self-sync size */
-    if (packet->bytes != (len + 4))
+    if ((size_t)packet->bytes != (len + 4 + offset))
         return false;
 
     block->len = len;
-    block->data = packet->packet + 4;
+    block->data = packet->packet + 4 + offset;
 
     return true;
 }
@@ -126,6 +126,11 @@ static const char * flac_block_type_to_name(flac_block_type_t type)
     return "<unknown>";
 }
 
+static void flac_handle_block(ogg_state_t *ogg_info, ogg_codec_t *codec, const flac_block_t *block)
+{
+    ICECAST_LOG_DEBUG("Found header of type %s%s with %zu bytes of data", flac_block_type_to_name(block->type), block->last ? "(last)" : "", block->len);
+}
+
 static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
 {
     ICECAST_LOG_DEBUG("freeing FLAC codec");
@@ -134,7 +139,6 @@ static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
     free(codec);
 }
 
-
 /* Here, we just verify the page is ok and then add it to the queue */
 static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin)
 {
@@ -151,8 +155,8 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
         while (ogg_stream_packetout(&codec->os, &packet)) {
             flac_block_t block;
 
-            if (flac_parse_block(&block, &packet)) {
-                ICECAST_LOG_DEBUG("Found header of type %s%s with %zu bytes of data", flac_block_type_to_name(block.type), block.last ? "(last)" : "", block.len);
+            if (flac_parse_block(&block, &packet, 0)) {
+                flac_handle_block(ogg_info, codec, &block);
 
                 if (block.last) {
                     codec->headers = 0;
@@ -196,6 +200,7 @@ ogg_codec_t *initial_flac_page (format_plugin_t *plugin, ogg_page *page)
     do
     {
         unsigned char *parse = packet.packet;
+        flac_block_t block;
 
         if (page->header_len + page->body_len != 79)
             break;
@@ -214,6 +219,9 @@ ogg_codec_t *initial_flac_page (format_plugin_t *plugin, ogg_page *page)
         codec->headers = 1;
         codec->name = "FLAC";
 
+        if (flac_parse_block(&block, &packet, 13))
+            flac_handle_block(ogg_info, codec, &block);
+
         format_ogg_attach_header(ogg_info, page);
         return codec;
     } while (0);

From 488f767b9eef0960642b604b5af65ea953187d7a Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 23:52:27 +0000
Subject: [PATCH 06/13] Update: Updated copyright

---
 src/format_flac.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index e8fd8988..04bd0a7d 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -8,7 +8,7 @@
  *                      oddsock <oddsock@xiph.org>,
  *                      Karl Heyes <karl@xiph.org>
  *                      and others (see AUTHORS for details).
- * Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
+ * Copyright 2014-2022, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
  */
 
 

From b23ef22eaf783360ec70b98f6e3c5dbfc89a0a90 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Tue, 15 Mar 2022 23:55:45 +0000
Subject: [PATCH 07/13] Update: Added stub for Xiph metadata (vorbis comments,
 ...)

---
 src/Makefile.am     |  2 ++
 src/metadata_xiph.c | 17 +++++++++++++++++
 src/metadata_xiph.h | 12 ++++++++++++
 3 files changed, 31 insertions(+)
 create mode 100644 src/metadata_xiph.c
 create mode 100644 src/metadata_xiph.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 5796bcc0..e58b7016 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ noinst_HEADERS = \
     event_exec.h \
     event_url.h \
     acl.h auth.h \
+    metadata_xiph.h \
     format.h \
     format_ogg.h \
     format_mp3.h \
@@ -95,6 +96,7 @@ icecast_SOURCES = \
     listensocket.c \
     fastevent.c \
     navigation.c \
+    metadata_xiph.c \
     format.c \
     format_ogg.c \
     format_mp3.c \
diff --git a/src/metadata_xiph.c b/src/metadata_xiph.c
new file mode 100644
index 00000000..40ef6ece
--- /dev/null
+++ b/src/metadata_xiph.c
@@ -0,0 +1,17 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2022,      Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "metadata_xiph.h"
+
+#include "logging.h"
+#define CATMODULE "metadata-xiph"
+
diff --git a/src/metadata_xiph.h b/src/metadata_xiph.h
new file mode 100644
index 00000000..5386c1fc
--- /dev/null
+++ b/src/metadata_xiph.h
@@ -0,0 +1,12 @@
+/* Icecast
+ *
+ * This program is distributed under the GNU General Public License, version 2.
+ * A copy of this license is included with this source.
+ *
+ * Copyright 2022,      Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
+ */
+
+#ifndef __METADATA_XIPH_H__
+#define __METADATA_XIPH_H__
+
+#endif

From e169a7d020487bd94219ef5eac2341821d993d5a Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Wed, 16 Mar 2022 00:43:40 +0000
Subject: [PATCH 08/13] Feature: Added functions to parse vorbis comments

---
 src/metadata_xiph.c | 85 +++++++++++++++++++++++++++++++++++++++++++++
 src/metadata_xiph.h | 11 ++++++
 2 files changed, 96 insertions(+)

diff --git a/src/metadata_xiph.c b/src/metadata_xiph.c
index 40ef6ece..608177c0 100644
--- a/src/metadata_xiph.c
+++ b/src/metadata_xiph.c
@@ -10,8 +10,93 @@
 #include <config.h>
 #endif
 
+#include <stdlib.h>
+#include <string.h>
+
 #include "metadata_xiph.h"
 
 #include "logging.h"
 #define CATMODULE "metadata-xiph"
 
+uint32_t metadata_xiph_read_u32be_unaligned(const unsigned char *in)
+{
+    uint32_t ret = 0;
+    ret += in[3];
+    ret <<= 8;
+    ret += in[2];
+    ret <<= 8;
+    ret += in[1];
+    ret <<= 8;
+    ret += in[0];
+    return ret;
+}
+
+bool     metadata_xiph_read_vorbis_comments(vorbis_comment *vc, const void *buffer, size_t len)
+{
+    bool ret = true;
+    size_t expected_len = 8;
+    uint32_t vendor_len;
+    uint32_t count;
+    uint32_t i;
+    char *out_buffer = NULL;
+    size_t out_buffer_len = 0;
+
+    if (!vc || !buffer || len < expected_len)
+        return false;
+
+    /* reading vendor tag and discarding it */
+    vendor_len = metadata_xiph_read_u32be_unaligned(buffer);
+    expected_len += vendor_len;
+
+    if (len < expected_len)
+        return false;
+
+    buffer += 4 + vendor_len;
+
+    count = metadata_xiph_read_u32be_unaligned(buffer);
+
+    expected_len += count * 4;
+
+    if (len < expected_len)
+        return false;
+
+    buffer += 4;
+
+    for (i = 0; i < count; i++) {
+        uint32_t comment_len = metadata_xiph_read_u32be_unaligned(buffer);
+
+        buffer += 4;
+
+        expected_len += comment_len;
+
+        if (len < expected_len) {
+            ret = false;
+            break;
+        }
+
+        if (out_buffer_len < comment_len || !out_buffer) {
+            char *n_out_buffer = realloc(out_buffer, comment_len + 1);
+            if (!n_out_buffer) {
+                ret = false;
+                break;
+            }
+
+            out_buffer = n_out_buffer;
+            out_buffer_len = comment_len;
+        }
+
+        memcpy(out_buffer, buffer, comment_len);
+        out_buffer[comment_len] = 0;
+        buffer += comment_len;
+
+        vorbis_comment_add(vc, out_buffer);
+    }
+
+    if (!ret) {
+        free(out_buffer);
+        vorbis_comment_clear(vc);
+        vorbis_comment_init(vc);
+    }
+
+    return ret;
+}
diff --git a/src/metadata_xiph.h b/src/metadata_xiph.h
index 5386c1fc..a9c60806 100644
--- a/src/metadata_xiph.h
+++ b/src/metadata_xiph.h
@@ -9,4 +9,15 @@
 #ifndef __METADATA_XIPH_H__
 #define __METADATA_XIPH_H__
 
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h> /* for size_t */
+
+#include <vorbis/codec.h>
+
+uint32_t metadata_xiph_read_u32be_unaligned(const unsigned char *in);
+
+/* returns true if parsing was successful, *vc must be in inited state before and will be in inited state after (even when false is returned) */
+bool     metadata_xiph_read_vorbis_comments(vorbis_comment *vc, const void *buffer, size_t len);
+
 #endif

From 873e462035d4708ca7f023ac877af379a307faaa Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Wed, 16 Mar 2022 01:00:39 +0000
Subject: [PATCH 09/13] Cleanup: Migrated Opus support to new metadata_xiph

---
 src/format_opus.c | 93 +++--------------------------------------------
 1 file changed, 5 insertions(+), 88 deletions(-)

diff --git a/src/format_opus.c b/src/format_opus.c
index a1d3d27a..9d12b677 100644
--- a/src/format_opus.c
+++ b/src/format_opus.c
@@ -7,7 +7,7 @@
  *
  * Copyright 2012,      David Richards, Mozilla Foundation,
  *                      and others (see AUTHORS for details).
- * Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
+ * Copyright 2014-2022, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
  */
 
 
@@ -21,6 +21,7 @@
 #include <string.h>
 #include <ogg/ogg.h>
 
+#include "metadata_xiph.h"
 #include "format_opus.h"
 #include "stats.h"
 #include "refbuf.h"
@@ -37,19 +38,6 @@ static void opus_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
     free(codec);
 }
 
-static uint32_t __read_header_u32be_unaligned(const unsigned char *in)
-{
-    uint32_t ret = 0;
-    ret += in[3];
-    ret <<= 8;
-    ret += in[2];
-    ret <<= 8;
-    ret += in[1];
-    ret <<= 8;
-    ret += in[0];
-    return ret;
-}
-
 static void __handle_header_opushead(ogg_state_t *ogg_info, ogg_packet *packet)
 {
     if (packet->bytes < 19) {
@@ -63,17 +51,12 @@ static void __handle_header_opushead(ogg_state_t *ogg_info, ogg_packet *packet)
     }
 
     stats_event_args(ogg_info->mount, "audio_channels", "%ld", (long int)packet->packet[9]);
-    stats_event_args(ogg_info->mount, "audio_samplerate", "%ld", (long int)__read_header_u32be_unaligned(packet->packet+12));
+    stats_event_args(ogg_info->mount, "audio_samplerate", "%ld", (long int)metadata_xiph_read_u32be_unaligned(packet->packet+12));
 }
 
 static void __handle_header_opustags(ogg_state_t *ogg_info, ogg_packet *packet, format_plugin_t *plugin) 
 {
-    size_t comments;
-    size_t next;
     size_t left = packet->bytes;
-    size_t buflen = 0;
-    char *buf = NULL;
-    char *buf_new;
     const void *p = packet->packet;
 
     if (packet->bytes < 16) {
@@ -85,77 +68,11 @@ static void __handle_header_opustags(ogg_state_t *ogg_info, ogg_packet *packet,
     p += 8;
     left -= 8;
 
-    /* Now the vendor string follows. We just skip it. */
-    next = __read_header_u32be_unaligned(p);
-    p += 4;
-    left -= 4;
-
-    if (left < (next + 4)) {
-        ICECAST_LOG_WARN("Bad Opus header: corrupted OpusTags header.");
-        return;
-    }
-    p += next;
-    left -= next;
-
-    /* Next is the comment counter. */
-    comments = __read_header_u32be_unaligned(p);
-    p += 4;
-    left -= 4;
-
-    /* Ok, next (comments) blocks follows, each composed of 4 byte length followed by the data */
-    if (left < (comments * 4)) {
-        ICECAST_LOG_WARN("Bad Opus header: corrupted OpusTags header.");
-        return;
-    }
-
     vorbis_comment_clear(&plugin->vc);
     vorbis_comment_init(&plugin->vc);
-
-    while (comments) {
-        next = __read_header_u32be_unaligned(p);
-        p += 4;
-        left -= 4;
-
-        if (left < next) {
-            if (buf)
-                free(buf);
-            vorbis_comment_clear(&plugin->vc);
-            vorbis_comment_init(&plugin->vc);
-            ICECAST_LOG_WARN("Bad Opus header: corrupted OpusTags header.");
-            return;
-        }
-
-        if ((next + 1) > buflen) {
-            buf_new = realloc(buf, next + 1);
-            if (buf_new) {
-                buf = buf_new;
-                buflen = next + 1;
-            }
-        }
-
-        if (buflen >= (next + 1)) {
-            memcpy(buf, p, next);
-            buf[next] = 0;
-            vorbis_comment_add(&plugin->vc, buf);
-        }
-
-        p += next;
-        left -= next;
-
-        comments--;
-        if (comments && left < 4) {
-            if (buf)
-                free(buf);
-            vorbis_comment_clear(&plugin->vc);
-            vorbis_comment_init(&plugin->vc);
-            ICECAST_LOG_WARN("Bad Opus header: corrupted OpusTags header.");
-            return;
-        }
+    if (!metadata_xiph_read_vorbis_comments(&plugin->vc, p, left)) {
+        ICECAST_LOG_WARN("Bad Opus header: corrupted OpusTags header.");
     }
-
-    if (buf)
-        free(buf);
-
     ogg_info->log_metadata = 1;
 }
 

From 595179a248b032ffc4f33b8f98afb88a472093dc Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Wed, 16 Mar 2022 01:01:03 +0000
Subject: [PATCH 10/13] Feature: Added support for FLAC VORBIS_COMMENT

---
 src/format_flac.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/format_flac.c b/src/format_flac.c
index 04bd0a7d..436c28cf 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include "refbuf.h"
+#include "metadata_xiph.h"
 #include "format_ogg.h"
 #include "client.h"
 #include "stats.h"
@@ -126,9 +127,20 @@ static const char * flac_block_type_to_name(flac_block_type_t type)
     return "<unknown>";
 }
 
-static void flac_handle_block(ogg_state_t *ogg_info, ogg_codec_t *codec, const flac_block_t *block)
+static void flac_handle_block(format_plugin_t *plugin, ogg_state_t *ogg_info, ogg_codec_t *codec, const flac_block_t *block)
 {
     ICECAST_LOG_DEBUG("Found header of type %s%s with %zu bytes of data", flac_block_type_to_name(block->type), block->last ? "(last)" : "", block->len);
+
+    switch (block->type) {
+        case FLAC_BLOCK_TYPE_VORBIS_COMMENT:
+            vorbis_comment_clear(&plugin->vc);
+            vorbis_comment_init(&plugin->vc);
+            if (!metadata_xiph_read_vorbis_comments(&plugin->vc, block->data, block->len)) {
+                ICECAST_LOG_ERROR("Can not parse FLAC header block VORBIS_COMMENT");
+            }
+            ogg_info->log_metadata = 1;
+            break;
+    }
 }
 
 static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec)
@@ -156,7 +168,7 @@ static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, o
             flac_block_t block;
 
             if (flac_parse_block(&block, &packet, 0)) {
-                flac_handle_block(ogg_info, codec, &block);
+                flac_handle_block(plugin, ogg_info, codec, &block);
 
                 if (block.last) {
                     codec->headers = 0;
@@ -220,7 +232,7 @@ ogg_codec_t *initial_flac_page (format_plugin_t *plugin, ogg_page *page)
         codec->name = "FLAC";
 
         if (flac_parse_block(&block, &packet, 13))
-            flac_handle_block(ogg_info, codec, &block);
+            flac_handle_block(plugin, ogg_info, codec, &block);
 
         format_ogg_attach_header(ogg_info, page);
         return codec;

From 7108aab18bd4a0db0e642e8adc1b7fc7339938e8 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Wed, 16 Mar 2022 01:26:38 +0000
Subject: [PATCH 11/13] Fix: Actually use the correct name for the function

---
 src/format_opus.c   | 2 +-
 src/metadata_xiph.c | 8 ++++----
 src/metadata_xiph.h | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/format_opus.c b/src/format_opus.c
index 9d12b677..94c7e0a6 100644
--- a/src/format_opus.c
+++ b/src/format_opus.c
@@ -51,7 +51,7 @@ static void __handle_header_opushead(ogg_state_t *ogg_info, ogg_packet *packet)
     }
 
     stats_event_args(ogg_info->mount, "audio_channels", "%ld", (long int)packet->packet[9]);
-    stats_event_args(ogg_info->mount, "audio_samplerate", "%ld", (long int)metadata_xiph_read_u32be_unaligned(packet->packet+12));
+    stats_event_args(ogg_info->mount, "audio_samplerate", "%ld", (long int)metadata_xiph_read_u32le_unaligned(packet->packet+12));
 }
 
 static void __handle_header_opustags(ogg_state_t *ogg_info, ogg_packet *packet, format_plugin_t *plugin) 
diff --git a/src/metadata_xiph.c b/src/metadata_xiph.c
index 608177c0..beeec5b0 100644
--- a/src/metadata_xiph.c
+++ b/src/metadata_xiph.c
@@ -18,7 +18,7 @@
 #include "logging.h"
 #define CATMODULE "metadata-xiph"
 
-uint32_t metadata_xiph_read_u32be_unaligned(const unsigned char *in)
+uint32_t metadata_xiph_read_u32le_unaligned(const unsigned char *in)
 {
     uint32_t ret = 0;
     ret += in[3];
@@ -45,7 +45,7 @@ bool     metadata_xiph_read_vorbis_comments(vorbis_comment *vc, const void *buff
         return false;
 
     /* reading vendor tag and discarding it */
-    vendor_len = metadata_xiph_read_u32be_unaligned(buffer);
+    vendor_len = metadata_xiph_read_u32le_unaligned(buffer);
     expected_len += vendor_len;
 
     if (len < expected_len)
@@ -53,7 +53,7 @@ bool     metadata_xiph_read_vorbis_comments(vorbis_comment *vc, const void *buff
 
     buffer += 4 + vendor_len;
 
-    count = metadata_xiph_read_u32be_unaligned(buffer);
+    count = metadata_xiph_read_u32le_unaligned(buffer);
 
     expected_len += count * 4;
 
@@ -63,7 +63,7 @@ bool     metadata_xiph_read_vorbis_comments(vorbis_comment *vc, const void *buff
     buffer += 4;
 
     for (i = 0; i < count; i++) {
-        uint32_t comment_len = metadata_xiph_read_u32be_unaligned(buffer);
+        uint32_t comment_len = metadata_xiph_read_u32le_unaligned(buffer);
 
         buffer += 4;
 
diff --git a/src/metadata_xiph.h b/src/metadata_xiph.h
index a9c60806..3c1056b4 100644
--- a/src/metadata_xiph.h
+++ b/src/metadata_xiph.h
@@ -15,7 +15,7 @@
 
 #include <vorbis/codec.h>
 
-uint32_t metadata_xiph_read_u32be_unaligned(const unsigned char *in);
+uint32_t metadata_xiph_read_u32le_unaligned(const unsigned char *in);
 
 /* returns true if parsing was successful, *vc must be in inited state before and will be in inited state after (even when false is returned) */
 bool     metadata_xiph_read_vorbis_comments(vorbis_comment *vc, const void *buffer, size_t len);

From 5d93c6b97f4fc933c020fa3b2d441ba0881b4599 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Wed, 16 Mar 2022 01:28:27 +0000
Subject: [PATCH 12/13] Update: Do not warn about blocks we do not handle

---
 src/format_flac.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/format_flac.c b/src/format_flac.c
index 436c28cf..c88344bd 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -140,6 +140,9 @@ static void flac_handle_block(format_plugin_t *plugin, ogg_state_t *ogg_info, og
             }
             ogg_info->log_metadata = 1;
             break;
+        default:
+            /* no-op */
+            break;
     }
 }
 

From 04f16811e1de827a0c90fabc0cae366ab9810035 Mon Sep 17 00:00:00 2001
From: Philipp Schafft <lion@lion.leolix.org>
Date: Wed, 16 Mar 2022 01:29:02 +0000
Subject: [PATCH 13/13] Feature: Report data from FLAC block STREAMINFO

---
 src/format_flac.c   | 25 +++++++++++++++++++++++++
 src/metadata_xiph.c | 13 +++++++++++++
 src/metadata_xiph.h |  1 +
 3 files changed, 39 insertions(+)

diff --git a/src/format_flac.c b/src/format_flac.c
index c88344bd..e8205420 100644
--- a/src/format_flac.c
+++ b/src/format_flac.c
@@ -127,11 +127,36 @@ static const char * flac_block_type_to_name(flac_block_type_t type)
     return "<unknown>";
 }
 
+static void flac_handle_block_streaminfo(format_plugin_t *plugin, ogg_state_t *ogg_info, ogg_codec_t *codec, const flac_block_t *block)
+{
+    uint32_t raw;
+    uint32_t sample_rate;
+    uint32_t channels;
+    uint32_t bits;
+
+    if (block->len != 34) {
+        ICECAST_LOG_ERROR("Can not parse FLAC header block STREAMINFO");
+        return;
+    }
+
+    raw = metadata_xiph_read_u32be_unaligned(block->data + 10);
+    sample_rate = ((raw >> 12) & 0xfffff) + 0;
+    channels    = ((raw >>  9) & 0x7    ) + 1;
+    bits        = ((raw >>  4) & 0x1F   ) + 1;
+
+    stats_event_args(ogg_info->mount, "audio_samplerate", "%ld", (long int)sample_rate);
+    stats_event_args(ogg_info->mount, "audio_channels", "%ld", (long int)channels);
+    stats_event_args(ogg_info->mount, "audio_bits", "%ld", (long int)bits);
+}
+
 static void flac_handle_block(format_plugin_t *plugin, ogg_state_t *ogg_info, ogg_codec_t *codec, const flac_block_t *block)
 {
     ICECAST_LOG_DEBUG("Found header of type %s%s with %zu bytes of data", flac_block_type_to_name(block->type), block->last ? "(last)" : "", block->len);
 
     switch (block->type) {
+        case FLAC_BLOCK_TYPE_STREAMINFO:
+            flac_handle_block_streaminfo(plugin, ogg_info, codec, block);
+            break;
         case FLAC_BLOCK_TYPE_VORBIS_COMMENT:
             vorbis_comment_clear(&plugin->vc);
             vorbis_comment_init(&plugin->vc);
diff --git a/src/metadata_xiph.c b/src/metadata_xiph.c
index beeec5b0..395f6542 100644
--- a/src/metadata_xiph.c
+++ b/src/metadata_xiph.c
@@ -18,6 +18,19 @@
 #include "logging.h"
 #define CATMODULE "metadata-xiph"
 
+uint32_t metadata_xiph_read_u32be_unaligned(const unsigned char *in)
+{
+    uint32_t ret = 0;
+    ret += in[0];
+    ret <<= 8;
+    ret += in[1];
+    ret <<= 8;
+    ret += in[2];
+    ret <<= 8;
+    ret += in[3];
+    return ret;
+}
+
 uint32_t metadata_xiph_read_u32le_unaligned(const unsigned char *in)
 {
     uint32_t ret = 0;
diff --git a/src/metadata_xiph.h b/src/metadata_xiph.h
index 3c1056b4..7127fdca 100644
--- a/src/metadata_xiph.h
+++ b/src/metadata_xiph.h
@@ -15,6 +15,7 @@
 
 #include <vorbis/codec.h>
 
+uint32_t metadata_xiph_read_u32be_unaligned(const unsigned char *in);
 uint32_t metadata_xiph_read_u32le_unaligned(const unsigned char *in);
 
 /* returns true if parsing was successful, *vc must be in inited state before and will be in inited state after (even when false is returned) */