11#pragma once
2- #include < ctype.h> // isascii
32#include " AudioToolsConfig.h"
43#include " AbstractMetaData.h"
54#include " AudioTools/CoreAudio/AudioBasic/StrView.h"
65#include " AudioTools/Communication/HTTP/AbstractURLStream.h"
76
7+ #ifndef AUDIOTOOLS_METADATA_ICY_ASCII_ONLY
8+ #define AUDIOTOOLS_METADATA_ICY_ASCII_ONLY true
9+ #endif
10+
811namespace audio_tools {
912
1013/* *
@@ -109,12 +112,7 @@ class MetaDataICY : public AbstractMetaData {
109112 metaDataLen = metaSize (ch);
110113 LOGI (" metaDataLen: %d" , metaDataLen);
111114 if (metaDataLen > 0 ) {
112- // Enhanced validation: reject suspiciously large metadata (>4080 bytes = 255*16)
113- // Also reject extremely small metadata that's unlikely to be valid
114- if (metaDataLen > 4080 || metaDataLen < 16 ) {
115- LOGW (" Suspicious metaDataLen %d -> skipping metadata block" , metaDataLen);
116- nextStatus = ProcessData;
117- } else if (metaDataLen > 200 ) {
115+ if (metaDataLen > 200 ) {
118116 LOGI (" Unexpected metaDataLen -> processed as data" );
119117 nextStatus = ProcessData;
120118 } else {
@@ -170,46 +168,28 @@ class MetaDataICY : public AbstractMetaData {
170168 // / determines the meta data size from the size byte
171169 virtual int metaSize (uint8_t metaSize) { return metaSize * 16 ; }
172170
173- inline bool isAscii (uint8_t ch){ return ch < 128 ;}
174-
175- // / Make sure that the result is a valid ASCII string with printable characters
176- // / Enhanced validation to reject corrupted metadata before it affects audio stream
177- virtual bool isAscii (char * result, int l) {
178- if (l < 1 ) return false ;
179-
180- // Check entire metadata string, not just first 10 characters
181- int printable_count = 0 ;
182- int control_count = 0 ;
183-
171+ // / Make sure that the result is a printable string
172+ virtual bool isPrintable (const char * str, int l) {
173+ int remain = 0 ;
184174 for (int j = 0 ; j < l; j++) {
185- uint8_t ch = (uint8_t )result[j];
186-
187- // Reject non-ASCII bytes (>= 128)
188- if (ch >= 128 ) return false ;
189-
190- // Count printable vs control characters
191- if (ch >= 32 && ch <= 126 ) {
192- printable_count++;
193- } else if (ch == ' \n ' || ch == ' \r ' || ch == ' \t ' || ch == 0 ) {
194- // Allow common control characters
195- continue ;
175+ uint8_t ch = str[j];
176+ if (remain) {
177+ if (ch < 0x80 || ch > 0xbf ) return false ;
178+ remain--;
196179 } else {
197- // Unusual control character
198- control_count++;
180+ if (ch < 0x80 ) { // ASCII
181+ if (ch != ' \n ' && ch != ' \r ' && ch != ' \t ' && ch < 32 || ch == 127 )
182+ return false ; // control chars
183+ }
184+ #if !AUDIOTOOLS_METADATA_ICY_ASCII_ONLY
185+ else if (ch >= 0xc2 && ch <= 0xdf ) remain = 1 ;
186+ else if (ch >= 0xe0 && ch <= 0xef ) remain = 2 ;
187+ else if (ch >= 0xf0 && ch <= 0xf4 ) remain = 3 ;
188+ #endif
189+ else return false ;
199190 }
200191 }
201-
202- // Require at least 50% printable characters to reject binary garbage
203- // 50% threshold is the absolute minimum - accepts any ICY padding strategy
204- // Binary garbage typically has < 30% printable, so 50% provides good separation
205- // Super CFL: 68.8% (33/48) easily passes
206- if (printable_count < (l * 0.50 )) {
207- LOGW (" Metadata validation failed: only %d/%d printable (%.1f%%)" ,
208- printable_count, l, (printable_count * 100.0 ) / l);
209- return false ;
210- }
211-
212- return true ;
192+ return remain == 0 ;
213193 }
214194
215195 // / allocates the memory to store the metadata / we support changing sizes
@@ -226,8 +206,7 @@ class MetaDataICY : public AbstractMetaData {
226206 // CHECK_MEMORY();
227207 TRACED ();
228208 metaData[len] = 0 ;
229- // Use full validation on entire metadata string, not just first 12 bytes
230- if (isAscii (metaData, len)) {
209+ if (isPrintable (metaData, len)) {
231210 LOGI (" %s" , metaData);
232211 StrView meta (metaData, len + 1 , len);
233212 int start = meta.indexOf (" StreamTitle=" );
@@ -247,7 +226,6 @@ class MetaDataICY : public AbstractMetaData {
247226 // Don't print corrupted binary data - could contain terminal control codes
248227 LOGW (" Unexpected Data: corrupted metadata block rejected (len=%d)" , len);
249228 // Signal corruption to application so it can disconnect/reconnect
250- // This is critical: metaint boundary is now desynchronized and audio will glitch
251229 if (callback != nullptr ) {
252230 callback (Corrupted, nullptr , len);
253231 }
0 commit comments