diff --git a/src/chat/chat_msg.c b/src/chat/chat_msg.c index 5840d054..fb5ab0cc 100644 --- a/src/chat/chat_msg.c +++ b/src/chat/chat_msg.c @@ -434,6 +434,22 @@ static void md_download_done_cb(void* arg, int err) { u_free(ctx); } +static void sanitize_filename(char* buf, size_t size) { + char orig[256]; snprintf(orig, sizeof(orig), "%s", buf); + size_t w = 0; + for (size_t r = 0; buf[r]; r++) { + char c = buf[r]; + if ((unsigned char)c < 0x20) continue; + if (c == '/' || c == '\\' || c == ':' || c == '*' || c == '?' + || c == '"' || c == '<' || c == '>' || c == '|') continue; + if (w < size - 1) buf[w++] = c; + } + buf[w] = '\0'; + if (w == 0) snprintf(buf, size, "file"); + if (strcmp(orig, buf) != 0) + DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "%s: filename sanitized [%s] -> [%s]", CC_ID, orig, buf); +} + static int md_start_download(struct UTUN_INSTANCE* inst, const char* data_str, size_t data_len, const char* ch_id, const char* base_filename, @@ -503,8 +519,8 @@ static int md_start_download(struct UTUN_INSTANCE* inst, snprintf(ctx->dest_relpath, sizeof(ctx->dest_relpath), "%s", fp_name); if (author_sig) memcpy(ctx->author_sig, author_sig, 64); - DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: download start ch=%s media=%02x%02x... blocks=%d size=%lld dest=%s", - CC_ID, ch_id, result.media_id[0], result.media_id[1], nb, (long long)fsize, dest); + DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: download start ch=%s file=%s blocks=%d size=%lld dest=%s", + CC_ID, ch_id, base_filename, nb, (long long)fsize, dest); media_download_start(inst, 0, &result, dest, media_base, author_node_id, md_download_done_cb, ctx, md_download_progress_cb, ctx); media_index_result_free(&result); return 0; @@ -561,9 +577,10 @@ void on_msg_inserted(struct DB_SYNC_INSTANCE* si, uint64_t record_ts, const char uint8_t dec[256]; int dlen = (int)b64_decode(base_filename + 1, bfi - 1, dec, sizeof(dec)); if (dlen > 0 && dlen < (int)sizeof(base_filename)) { memcpy(base_filename, dec, (size_t)dlen); base_filename[dlen] = '\0'; } } - if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file"); + if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file"); + sanitize_filename(base_filename, sizeof(base_filename)); - const uint8_t* sig_field = (const uint8_t*)strstr(buf, "\"sig\":\""); + const uint8_t* sig_field = (const uint8_t*)strstr(buf, "\"sig\":\""); uint8_t author_sig[64] = {0}; if (sig_field) { /* read author_signature from DB — find by ts in on_msg_inserted we don't have sig @@ -647,6 +664,7 @@ void chat_core_attachment_download(const char* channel_id, int64_t msg_id) { if (dlen > 0 && dlen < (int)sizeof(base_filename)) { memcpy(base_filename, dec, (size_t)dlen); base_filename[dlen] = '\0'; } } if (bfi == 0) snprintf(base_filename, sizeof(base_filename), "file"); + sanitize_filename(base_filename, sizeof(base_filename)); uint8_t author_sig[64]; memcpy(author_sig, sig_blob, 64); sqlite3_finalize(st); diff --git a/src/media_delivery/media_download.c b/src/media_delivery/media_download.c index 26050329..cbf3e266 100644 --- a/src/media_delivery/media_download.c +++ b/src/media_delivery/media_download.c @@ -45,8 +45,11 @@ static void md_dl_free(struct media_download* dl) { static void md_mkdir_parent(const char* filepath) { char path[1024]; snprintf(path, sizeof(path), "%s", filepath); - char* p = path[0] == '/' ? path + 1 : path; - while ((p = strchr(p, '/'))) { + char* p = path; + if (((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')) && p[1] == ':') + p += 2; + while (*p == '/' || *p == '\\') p++; + while ((p = strpbrk(p, "/\\"))) { *p = '\0'; if (utun_mkdir(path, 0755) != 0 && errno != EEXIST) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: mkdir(%s) failed: %s", MDL_ID, path, strerror(errno)); @@ -381,7 +384,7 @@ void media_download_handle_chunk(struct UTUN_INSTANCE* inst, char tmp[2048]; snprintf(tmp, sizeof(tmp), "%s.chunk_%d", dl->dest_path, bi); FILE* f = fopen(tmp, "ab"); - if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for chunk write", MDL_ID, tmp); return; } + if (!f) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for chunk write: %s", MDL_ID, tmp, strerror(errno)); return; } size_t wlen = ch->data_len; if (len >= MEDIA_BLOCK_CHUNK_HDR_SIZE + wlen) { fwrite(d + MEDIA_BLOCK_CHUNK_HDR_SIZE, 1, wlen, f); @@ -540,7 +543,7 @@ void media_download_handle_done(struct UTUN_INSTANCE* inst, if (dl->blocks_validated == dl->num_blocks) { /* assemble file */ FILE* out = fopen(dl->dest_path, "wb"); - if (!out) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for assembly", MDL_ID, dl->dest_path); } + if (!out) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: fopen(%s) failed for assembly: %s", MDL_ID, dl->dest_path, strerror(errno)); } else { for (int n = 0; n < dl->num_blocks; n++) { char tmp2[2048]; diff --git a/tools/chatgui/src/accountlist.cpp b/tools/chatgui/src/accountlist.cpp index 2dc1989d..5a655f64 100644 --- a/tools/chatgui/src/accountlist.cpp +++ b/tools/chatgui/src/accountlist.cpp @@ -84,6 +84,8 @@ AccountList::AccountList(DbManager* db, QWidget *parent) memberSplitter->setSizes({360, 240}); layout->addWidget(memberSplitter, 1); + connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, + m_listView->viewport(), qOverload<>(&QWidget::update)); connect(m_listView, &QListView::clicked, this, &AccountList::onMemberClicked); connect(m_listView, &QListView::doubleClicked, this, &AccountList::onMemberDoubleClicked);