v0.0.4
RustBox CI / rustbox (push) Canceled after 0s

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-30 00:23:11 +01:00
1 parent 93762fe9ef
commit 96a32ea8fb
17 files changed
+91 -34

No files matched your search

+1 -1
View File
@@ -126,7 +126,7 @@ async fn https_get_address(
format!("{host}:{port}")
};
let request = format!(
"GET {target} HTTP/1.1\r\nHost: {host_header}\r\nUser-Agent: RustBox/0.0.3\r\nAccept: application/octet-stream, application/pgp-keys, text/plain\r\nConnection: close\r\n\r\n"
"GET {target} HTTP/1.1\r\nHost: {host_header}\r\nUser-Agent: RustBox/0.0.4\r\nAccept: application/octet-stream, application/pgp-keys, text/plain\r\nConnection: close\r\n\r\n"
);
timeout(duration, stream.write_all(request.as_bytes()))
.await
+1 -1
View File
@@ -192,7 +192,7 @@ pub async fn export_ics(
.map_err(|e| AppError::InternalError(format!("Database error: {e}")))?;
let mut body = String::from(
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//RustBox//Calendar 0.0.3//EN\r\nCALSCALE:GREGORIAN\r\n",
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//RustBox//Calendar 0.0.4//EN\r\nCALSCALE:GREGORIAN\r\n",
);
for event in &events {
append_ics_event(&mut body, event);
+1 -1
View File
@@ -230,7 +230,7 @@ async fn post_one_click_to_address(
format!("{host}:{port}")
};
let request = format!(
"POST {path} HTTP/1.1\r\nHost: {host_header}\r\nUser-Agent: RustBox/0.0.3\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{ONE_CLICK_BODY}",
"POST {path} HTTP/1.1\r\nHost: {host_header}\r\nUser-Agent: RustBox/0.0.4\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{ONE_CLICK_BODY}",
ONE_CLICK_BODY.len()
);
+1 -1
View File
@@ -29,7 +29,7 @@ impl fmt::Display for SieveError {
write!(f, "ManageSieve server rejected the command: {msg}")
}
Self::Timeout => write!(f, "ManageSieve operation timed out"),
Self::ScriptTooLarge => write!(f, "Sieve script exceeds the RustBox 0.0.3 size limit"),
Self::ScriptTooLarge => write!(f, "Sieve script exceeds the RustBox 0.0.4 size limit"),
}
}
}
+5
View File
@@ -569,6 +569,11 @@
"Recovered draft": "Odzyskany szkic",
"Forward as attachment": "Przekaż jako załącznik",
"Open": "Otwórz",
"Preview": "Podgląd",
"Download": "Pobierz",
"Preview unavailable": "Podgląd niedostępny",
"This attachment is too large for preview. Download it instead?": "Ten załącznik jest za duży do podglądu. Pobrać go zamiast tego?",
"Preview is not available for this attachment type. Download it instead?": "Podgląd nie jest dostępny dla tego typu załącznika. Pobrać go zamiast tego?",
"Mark unread": "Oznacz jako nieprzeczytaną",
"Unflag": "Usuń oznaczenie",
"Move to…": "Przenieś do…",
+44 -15
View File
@@ -326,10 +326,9 @@ function renderMessageDetail(detail) {
event.preventDefault();
const filename = String(attachment.filename || "").toLowerCase();
const contentType = String(attachment.content_type || attachment.contentType || "").toLowerCase();
const actions = [
{ label: "Open", action: () => openAttachment(detail.folder, detail.uid, attachment) },
{ label: "Download", action: () => downloadAttachment(detail.folder, detail.uid, attachment) },
];
const actions = [];
if (attachmentCanPreview(attachment)) actions.push({ label: "Preview", action: () => openAttachment(detail.folder, detail.uid, attachment) });
actions.push({ label: "Download", action: () => downloadAttachment(detail.folder, detail.uid, attachment) });
if (contentType.includes("text/calendar") || filename.endsWith(".ics")) actions.push("separator", { label: "Add to calendar", action: () => importCalendarAttachment(detail.folder, detail.uid, attachment) });
if (contentType.includes("text/vcard") || contentType.includes("text/x-vcard") || filename.endsWith(".vcf")) actions.push("separator", { label: "Import contact", action: () => importVcardAttachment(detail.folder, detail.uid, attachment) });
if ((detail.attachments || []).length > 1) actions.push("separator", { label: "Download all as ZIP", action: () => downloadAllAttachmentsZip(detail) });
@@ -514,27 +513,59 @@ function attachmentMime(attachment, blob = null) {
return String(blob?.type || attachment?.content_type || attachment?.contentType || "application/octet-stream").toLowerCase();
}
function attachmentCanPreview(attachment) {
function attachmentPreviewLimitBytes(attachment) {
const mime = attachmentMime(attachment);
const filename = String(attachment?.filename || "").toLowerCase();
return mime.startsWith("image/")
|| mime === "application/pdf"
|| mime.startsWith("text/")
|| mime.startsWith("video/")
|| /\.(pdf|png|jpe?g|gif|webp|txt|log|md|csv|html?|xhtml|mp4|webm|ogv|mov)$/i.test(filename);
if (mime.startsWith("text/") || /\.(txt|log|md|csv|html?|xhtml)$/i.test(filename)) return 5 * 1024 * 1024;
if (mime.startsWith("image/") || mime === "application/pdf" || mime.startsWith("video/") || /\.(pdf|png|jpe?g|gif|webp|mp4|webm|ogv|mov)$/i.test(filename)) return 30 * 1024 * 1024;
return 0;
}
function attachmentCanPreview(attachment) {
return attachmentPreviewLimitBytes(attachment) > 0;
}
function attachmentExceedsPreviewLimit(attachment, actualSize = null) {
const limit = attachmentPreviewLimitBytes(attachment);
if (!limit) return false;
const size = Number(actualSize ?? attachment?.size ?? 0);
return Number.isFinite(size) && size > limit;
}
async function offerAttachmentDownload(folder, uid, attachment, reason) {
const confirmed = await appConfirm(trText(reason), {
title: trText("Preview unavailable"),
confirmLabel: trText("Download"),
});
if (confirmed) await downloadAttachment(folder, uid, attachment);
}
async function openAttachment(folder, uid, attachment) {
try {
if (!attachmentCanPreview(attachment)) {
await offerAttachmentDownload(folder, uid, attachment, "Preview is not available for this attachment type. Download it instead?");
return;
}
if (attachmentExceedsPreviewLimit(attachment)) {
await offerAttachmentDownload(folder, uid, attachment, "This attachment is too large for preview. Download it instead?");
return;
}
const blob = await fetchAttachmentBlob(folder, uid, attachment);
const mime = attachmentMime(attachment, blob);
const filename = String(attachment?.filename || "").toLowerCase();
const isHtml = mime.includes("html") || /\.(html?|xhtml)$/i.test(filename);
const isImage = mime.startsWith("image/") || /\.(png|jpe?g|gif|webp)$/i.test(filename);
const isVideo = mime.startsWith("video/") || /\.(mp4|webm|ogv|mov)$/i.test(filename);
const safe = attachmentCanPreview({ ...attachment, content_type: mime });
if (!safe) return toast("Preview is not available for this attachment type", "warning");
if (blob.size > 30 * 1024 * 1024) return toast("Attachment is too large for preview", "warning");
const previewAttachment = { ...attachment, content_type: mime };
const safe = attachmentCanPreview(previewAttachment);
if (!safe) {
await offerAttachmentDownload(folder, uid, attachment, "Preview is not available for this attachment type. Download it instead?");
return;
}
if (attachmentExceedsPreviewLimit(previewAttachment, blob.size)) {
await offerAttachmentDownload(folder, uid, attachment, "This attachment is too large for preview. Download it instead?");
return;
}
const dialog = el("dialog", "dialog attachment-preview-dialog");
const card = el("div", "dialog-card attachment-preview-card");
@@ -570,7 +601,6 @@ async function openAttachment(folder, uid, attachment) {
video.preload = "metadata";
body.append(video);
} else if (isHtml) {
if (blob.size > 5 * 1024 * 1024) return toast("HTML attachment is too large for inline preview", "warning");
const frame = document.createElement("iframe");
frame.setAttribute("sandbox", "");
frame.referrerPolicy = "no-referrer";
@@ -579,7 +609,6 @@ async function openAttachment(folder, uid, attachment) {
frame.srcdoc = `<!doctype html><html><head>${csp}<meta name="color-scheme" content="light"><style>body{margin:0;padding:20px;color:#202622;background:#fff;font:14px/1.55 system-ui,sans-serif}img{max-width:100%;height:auto}pre{white-space:pre-wrap}</style></head><body>${safeHtml}</body></html>`;
body.append(frame);
} else {
if (blob.size > 5 * 1024 * 1024) return toast("Text attachment is too large for inline preview", "warning");
const pre = document.createElement("pre");
pre.textContent = await blob.text();
body.append(pre);