You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

123 lines
4.6 KiB

#include "attachment_sender.h"
#include "instance_lite.h"
#include "../../../lib/debug_config.h"
#include "../../../lib/mem.h"
#include "../../../lib/u_async.h"
#include "../../../src/chat/chat_core.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#define MEDIA_BLOCK_MIN (10 * 1024 * 1024)
#define MEDIA_BLOCK_MAX (25 * 1024 * 1024)
#define MEDIA_BLOCK_TARGET 30
static uint32_t media_calc_block_size(uint64_t file_size) {
if (file_size == 0) return 0;
uint64_t target = (file_size + MEDIA_BLOCK_TARGET - 1) / MEDIA_BLOCK_TARGET;
if (target < MEDIA_BLOCK_MIN) target = MEDIA_BLOCK_MIN;
if (target > MEDIA_BLOCK_MAX) target = MEDIA_BLOCK_MAX;
return (uint32_t)target;
}
int attachment_send(const char* channel_id, const char* src_file_path, const char* db_path) {
if (!channel_id || !src_file_path || !db_path) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "attachment_send: invalid args");
return -1;
}
struct UASYNC* ua = instance_lite_get_uasync();
if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "attachment_send: no uasync"); return -1; }
FILE* src = fopen(src_file_path, "rb");
if (!src) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "attachment_send: cannot open %s", src_file_path); return -1; }
fseek(src, 0, SEEK_END);
uint64_t file_size = (uint64_t)ftell(src);
fseek(src, 0, SEEK_SET);
if (file_size == 0) { DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "attachment_send: empty file %s", src_file_path); fclose(src); return -1; }
/* extract basename and ext from path */
char path_copy[1024];
snprintf(path_copy, sizeof(path_copy), "%s", src_file_path);
char* fname = basename(path_copy);
char* dot = strrchr(fname, '.');
char ext[32] = "";
if (dot) {
snprintf(ext, sizeof(ext), "%s", dot + 1);
*dot = '\0';
}
/* generate dt/suffix */
time_t t = time(NULL);
struct tm tm_buf; localtime_r(&t, &tm_buf);
char dt[32]; snprintf(dt, sizeof(dt), "%04d%02d%02d-%02d%02d%02d",
tm_buf.tm_year + 1900, tm_buf.tm_mon + 1, tm_buf.tm_mday,
tm_buf.tm_hour, tm_buf.tm_min, tm_buf.tm_sec);
int rnd = rand() & 0xFFFF;
char suffix[8]; snprintf(suffix, sizeof(suffix), "%04x", rnd);
uint32_t block_size = media_calc_block_size(file_size);
int num_blocks = (int)((file_size + block_size - 1) / block_size);
/* ensure media dir */
char media_dir[1024];
snprintf(media_dir, sizeof(media_dir), "%s/media/%s", db_path, channel_id);
{
char tmp[1024]; size_t off = 0;
for (size_t i = 0; media_dir[i] && off < sizeof(tmp) - 1; i++) {
tmp[off++] = media_dir[i];
if (media_dir[i] == '/' && off > 1) { tmp[off] = '\0'; mkdir(tmp, 0755); }
}
mkdir(tmp, 0755);
}
/* write blocks */
for (int n = 0; n < num_blocks; n++) {
char block_path[1280];
snprintf(block_path, sizeof(block_path), "%s/%s_%d_%s_%s.%s",
media_dir, dt, n, fname, suffix, ext[0] ? ext : "bin");
FILE* dst = fopen(block_path, "wb");
if (!dst) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "attachment_send: cannot create block %s", block_path);
continue;
}
uint8_t buf[65536];
uint64_t remaining = block_size;
while (remaining > 0) {
size_t to_read = remaining < sizeof(buf) ? (size_t)remaining : sizeof(buf);
size_t rd = fread(buf, 1, to_read, src);
if (rd == 0) break;
fwrite(buf, 1, rd, dst);
remaining -= rd;
}
fclose(dst);
}
fclose(src);
/* post chat_msg_submit */
struct chat_msg_submit* req = u_calloc(1, sizeof(struct chat_msg_submit) + 1);
if (!req) return -1;
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel_id);
snprintf(req->content_type, sizeof(req->content_type), "application/octet-stream");
snprintf(req->media_dt, sizeof(req->media_dt), "%s", dt);
snprintf(req->media_basename, sizeof(req->media_basename), "%s", fname);
snprintf(req->media_suffix, sizeof(req->media_suffix), "%s", suffix);
snprintf(req->media_ext, sizeof(req->media_ext), "%s", ext[0] ? ext : "bin");
req->media_num_blocks = (uint32_t)num_blocks;
req->data = (uint8_t*)(req + 1);
req->data_len = 0;
req->timestamp = 0;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "attachment_send: ch=%s file=%s blocks=%d size=%llu",
channel_id, src_file_path, num_blocks, (unsigned long long)file_size);
uasync_post(ua, chat_core_submit_trampoline, req);
return 0;
}