Define bounded typed attachment metadata for PM and GUI
This commit is contained in:
1 parent
adff64e483
commit
7357a63fdf
2 files changed
+117
No files matched your search
@@ -0,0 +1,87 @@
|
||||
/* Ограниченный бинарный формат: kind:1, name_len:2, duration_ms:4, width:2,
|
||||
* height:2, UTF-8 name, waveform:100 для voice. Числа big-endian. */
|
||||
#include <string.h>
|
||||
#include "../../lib/debug_config.h"
|
||||
#include "attachment.h"
|
||||
|
||||
/* Имя должно быть корректным UTF-8 basename без управляющих символов. */
|
||||
static int valid_name(const char* name, size_t n) {
|
||||
if (!n || n > 255 || (n == 1 && name[0] == '.') || (n == 2 && !memcmp(name, "..", 2))) return 0;
|
||||
for (size_t i = 0; i < n;) {
|
||||
unsigned c = (unsigned char)name[i++];
|
||||
if (c < 0x20 || c == 0x7f || c == '/' || c == '\\') return 0;
|
||||
if (c < 0x80) continue;
|
||||
unsigned count, scalar, minimum;
|
||||
if (c >= 0xc2 && c <= 0xdf) { count = 1; scalar = c & 31; minimum = 0x80; }
|
||||
else if (c >= 0xe0 && c <= 0xef) { count = 2; scalar = c & 15; minimum = 0x800; }
|
||||
else if (c >= 0xf0 && c <= 0xf4) { count = 3; scalar = c & 7; minimum = 0x10000; }
|
||||
else return 0;
|
||||
if (count > n - i) return 0;
|
||||
while (count--) {
|
||||
c = (unsigned char)name[i++];
|
||||
if ((c & 0xc0) != 0x80) return 0;
|
||||
scalar = (scalar << 6) | (c & 63);
|
||||
}
|
||||
if (scalar < minimum || scalar > 0x10ffff || (scalar >= 0xd800 && scalar <= 0xdfff)) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Один набор правил для отправки, приёма и снимка истории. */
|
||||
int attachment_validate(const struct attachment_info* info) {
|
||||
if (!info || !valid_name(info->name, strnlen(info->name, sizeof(info->name)))) goto invalid;
|
||||
size_t wave = strnlen(info->waveform, sizeof(info->waveform));
|
||||
if (info->kind == ATTACHMENT_VOICE) {
|
||||
if (!info->duration_ms || info->duration_ms > 86400000 || info->width || info->height || wave != 100) goto invalid;
|
||||
if (strspn(info->waveform, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") != wave) goto invalid;
|
||||
} else {
|
||||
if (wave) goto invalid;
|
||||
if (info->kind == ATTACHMENT_FILE) {
|
||||
if (info->duration_ms || info->width || info->height) goto invalid;
|
||||
} else if (info->kind == ATTACHMENT_VIDEO || info->kind == ATTACHMENT_IMAGE) {
|
||||
if (!info->width || !info->height || info->width > 16384 || info->height > 16384) goto invalid;
|
||||
if (info->kind == ATTACHMENT_VIDEO ? (!info->duration_ms || info->duration_ms > 86400000) : info->duration_ms) goto invalid;
|
||||
} else goto invalid;
|
||||
}
|
||||
return 0;
|
||||
invalid:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment: invalid metadata kind=%d", info ? (int)info->kind : 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int attachment_encode(const struct attachment_info* info, uint8_t* out, size_t capacity) {
|
||||
if (attachment_validate(info)) return -1;
|
||||
size_t n = strlen(info->name), length = 11 + n + (info->kind == ATTACHMENT_VOICE ? 100 : 0);
|
||||
if (!out || capacity < length) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment: encode buffer too small"); return -1; }
|
||||
out[0] = (uint8_t)info->kind; out[1] = (uint8_t)(n >> 8); out[2] = (uint8_t)n;
|
||||
for (unsigned i = 0; i < 4; i++) out[3 + i] = (uint8_t)(info->duration_ms >> (24 - 8 * i));
|
||||
out[7] = (uint8_t)(info->width >> 8); out[8] = (uint8_t)info->width;
|
||||
out[9] = (uint8_t)(info->height >> 8); out[10] = (uint8_t)info->height;
|
||||
memcpy(out + 11, info->name, n);
|
||||
if (info->kind == ATTACHMENT_VOICE) memcpy(out + 11 + n, info->waveform, 100);
|
||||
return (int)length;
|
||||
}
|
||||
|
||||
int attachment_decode(const uint8_t* data, size_t length, struct attachment_info* info) {
|
||||
if (!data || !info || length < 11) goto invalid;
|
||||
size_t n = ((size_t)data[1] << 8) | data[2];
|
||||
if (!n || n > 255 || length != 11 + n + (data[0] == ATTACHMENT_VOICE ? 100 : 0) || memchr(data + 11, 0, n)) goto invalid;
|
||||
memset(info, 0, sizeof(*info)); info->kind = (enum attachment_kind)data[0];
|
||||
for (unsigned i = 0; i < 4; i++) info->duration_ms = (info->duration_ms << 8) | data[3 + i];
|
||||
info->width = (uint16_t)((data[7] << 8) | data[8]); info->height = (uint16_t)((data[9] << 8) | data[10]);
|
||||
memcpy(info->name, data + 11, n);
|
||||
if (info->kind == ATTACHMENT_VOICE) memcpy(info->waveform, data + 11 + n, 100);
|
||||
return attachment_validate(info);
|
||||
invalid:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment: malformed wire metadata bytes=%zu", length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* attachment_content_type(enum attachment_kind kind) {
|
||||
switch (kind) {
|
||||
case ATTACHMENT_VOICE: return "audio/opus";
|
||||
case ATTACHMENT_VIDEO: return "video/mp4";
|
||||
case ATTACHMENT_IMAGE: return "image/jpeg";
|
||||
default: return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Общие метаданные готового вложения. Подготовка не знает адресата и транспорта.
|
||||
* PM шифрует компактный wire-формат; GUI получает разобранные поля.
|
||||
* encode/decode возвращают длину/0 или -1; неизвестные типы и лишние байты запрещены. */
|
||||
#ifndef ATTACHMENT_H
|
||||
#define ATTACHMENT_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum attachment_kind { ATTACHMENT_FILE = 1, ATTACHMENT_VOICE, ATTACHMENT_VIDEO, ATTACHMENT_IMAGE };
|
||||
#define ATTACHMENT_WAVE_SIZE 100
|
||||
#define ATTACHMENT_WIRE_MAX (11 + 255 + ATTACHMENT_WAVE_SIZE)
|
||||
struct attachment_info {
|
||||
enum attachment_kind kind;
|
||||
char name[256];
|
||||
uint32_t duration_ms;
|
||||
uint16_t width, height;
|
||||
char waveform[ATTACHMENT_WAVE_SIZE + 1]; /* 100 уровней из A-Za-z0-9+/; только voice. */
|
||||
};
|
||||
int attachment_validate(const struct attachment_info* info);
|
||||
int attachment_encode(const struct attachment_info* info, uint8_t* out, size_t capacity);
|
||||
int attachment_decode(const uint8_t* data, size_t length, struct attachment_info* info);
|
||||
const char* attachment_content_type(enum attachment_kind kind);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
Reference in new issue
Block a user