microbin/templates/pasta.html
Daniel Szabo 4983ac867d Fix text-only private pastas crashing server
- Fixed a bug that caused encrypted uploads with no file to crash the server, as we tried to decrypt a non-existent file
- Changed wording on pasta auth page
- Removed unused import
2023-07-04 21:56:15 +03:00

363 lines
No EOL
11 KiB
HTML

{% include "header.html" %}
<div style="float: left">
{% if pasta.content != "" %}
<button id="copy-text-button" class="small-button" style="margin-right:
0.5rem">
Copy Text
</button>
{% if args.public_path_as_str() != "" && pasta.pasta_type == "url" %}
<button id="copy-redirect-button" class="small-button" style="margin-right:
0.5rem">
Copy Redirect
</button>
{%- endif %}
<a style="margin-right: 1rem" href="{{ args.public_path_as_str() }}/raw/{{pasta.id_as_animals()}}">Raw Text
Content</a>
{%- endif %} {% if args.qr && args.public_path_as_str() != "" %}
<a style="margin-right: 1rem" href="{{ args.public_path_as_str() }}/qr/{{pasta.id_as_animals()}}">QR</a>
{%- endif %} {% if pasta.editable %}
<a style="margin-right: 1rem" href="{{ args.public_path_as_str() }}/edit/{{pasta.id_as_animals()}}">Edit</a>
{%- endif %}
<a style="margin-right: 1rem" href="{{ args.public_path_as_str() }}/remove/{{pasta.id_as_animals()}}">Remove</a>
</div>
<div style="float: right">
<a style="margin-right: 0.5rem"
href="{{ args.public_path_as_str() }}/pasta/{{pasta.id_as_animals()}}"><i>{{pasta.id_as_animals()}}</i></a>
{% if args.public_path_as_str() != "" %}
<button id="copy-url-button" class="small-button" style="margin-right: 0">
Copy URL
</button>
{%- endif %}
</div>
<br>
<br>
{% if pasta.encrypt_client && pasta.file.is_some() && !pasta.file_embeddable() %}
<span style="margin-left: auto; margin-right: auto; display: flex;
justify-content: center; align-items: center;">
<div id="decryption">
<label for="password-field" style="margin-bottom: 0.5em;">
Please enter your key to decrypt this upload. <sup> <a href="/guide#encryption"></a></sup>
</label>
<input class="small-button" placeholder="Key" style="margin-right: 0.5rem" type="password" id="password-field"
autocomplete="off" />
<button class="small-button" id="decrypt-button" style="margin-right:
0.5rem">
<b>
Decrypt text
</b>
</button>
<button class="small-button" id="download-button" style="margin-right:
0.5rem">
<b>
Download {{pasta.file.as_ref().unwrap().name()}}
[{{pasta.file.as_ref().unwrap().size}}]
</b>
</button>
</div>
</span>
{%- endif %}
<br>
{% if pasta.content != "" %}
<div class="code-container">
<div style="clear: both;">
{% if pasta.extension == "auto" || pasta.encrypt_client %}
<pre><code id="code">{{pasta.content_escaped()}}</code></pre>
{% else if args.highlightsyntax %}
<pre><code id="code">{{pasta.content_syntax_highlighted()}}</code></pre>
{% else %}
<pre><code id="code">{{pasta.content_not_highlighted()}}</code></pre>
{%- endif %}
</div>
</div>
{%- endif %}
{% if pasta.file.is_some() && pasta.file.as_ref().unwrap().is_image() &&
pasta.file_embeddable() && !pasta.encrypt_client %}
<img id="embed" src="{{ args.public_path_as_str()}}/file/{{pasta.id_as_animals()}}" height="300" />
<span style="margin-left: auto; margin-right: auto; display: flex;
justify-content: center; align-items: center;">
<p style="font-size: small;">{{pasta.file.as_ref().unwrap().name()}}
[{{pasta.file.as_ref().unwrap().size}}]</p>
<a href="{{ args.public_path_as_str() }}/file/{{pasta.id_as_animals()}}" id="download-link" download>
<button class="download-button" autofocus>
Download
</button>
</a>
</span>
{%- endif %}
{% if pasta.file.is_some() && pasta.file.as_ref().unwrap().is_video() &&
pasta.file_embeddable() && !pasta.encrypt_client %}
<video id="embed" controls src="{{ args.public_path_as_str()}}/file/{{pasta.id_as_animals()}}" height="300"></video>
<span style="margin-left: auto; margin-right: auto; display: flex;
justify-content: center; align-items: center;">
<p style="font-size: small;">{{pasta.file.as_ref().unwrap().name()}}
[{{pasta.file.as_ref().unwrap().size}}]</p>
<a href="{{ args.public_path_as_str() }}/file/{{pasta.id_as_animals()}}" download id="download-link">
<button class="download-button">
Download
</button>
</a>
</span>
{%- endif %}
<div>
{% if args.show_read_stats %} {% if pasta.read_count == 1 %}
<p style="font-size: small">Read {{pasta.read_count}} time, last
{{pasta.last_read_time_ago_as_string()}}</p>
{%- else %}
<p style="font-size: small">Read {{pasta.read_count}} times, last
{{pasta.last_read_time_ago_as_string()}}</p>
{%- endif %} {%- endif %}
</div>
<br>
<script type="text/javascript" src="{{ args.public_path_as_str() }}/static/highlight/highlight.min.js"></script>
<link rel="stylesheet" href="{{ args.public_path_as_str()}}/static/highlight/highlight.min.css">
<script>
const copyURLBtn = document.getElementById("copy-url-button")
const copyTextBtn = document.getElementById("copy-text-button")
const copyRedirectBtn = document.getElementById("copy-redirect-button")
var content = `{{ pasta.content_escaped() }}`
const contentElement = document.getElementById("code");
const url = (`{{ args.short_path_as_str()}}` === "") ? `{{ args.public_path_as_str() }}/pasta/{{pasta.id_as_animals()}}` : `{{ args.short_path_as_str()}}/p/{{pasta.id_as_animals()}}`
const redirect_url = (`{{ args.short_path_as_str()}}` === "") ? `{{ args.public_path_as_str() }}/url/{{pasta.id_as_animals()}}` : `{{ args.short_path_as_str()}}/u/{{pasta.id_as_animals()}}`
const te = new TextEncoder();
// {% if pasta.extension == "auto" && !pasta.encrypt_client %}
onload = (event) => {
contentElement.innerHTML = content;
hljs.highlightAll();
contentElement.innerHTML =
wrapStringInCodeLines(contentElement.innerHTML);
};
// {% endif %}
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function wrapStringInCodeLines(str) {
const lines = str.split(/\r?\n/); // split the string into an array of lines
const wrappedLines = lines.map((line) => `<code-line>${line}</code-line>`); // wrap each line in a "code-line" tag
return wrappedLines.join("\n"); // join the wrapped lines back into a single string with line breaks
}
const decodeEntity = (inputStr) => {
var textarea = document.createElement("textarea");
textarea.innerHTML = inputStr;
return textarea.value;
}
if (copyURLBtn) {
copyURLBtn.addEventListener("click", () => {
navigator.clipboard.writeText(url)
copyURLBtn.innerHTML = "Copied"
setTimeout(() => {
copyURLBtn.innerHTML = "Copy URL"
}, 1000)
})
}
// it will be undefined when the element does not exist on non-url pastas
if (copyRedirectBtn) {
copyRedirectBtn.addEventListener("click", () => {
navigator.clipboard.writeText(redirect_url)
copyRedirectBtn.innerHTML = "Copied"
setTimeout(() => {
copyRedirectBtn.innerHTML = "Copy Redirect"
}, 1000)
})
}
if (copyTextBtn) {
copyTextBtn.addEventListener("click", () => {
const decodeContent = decodeEntity(content)
navigator.clipboard.writeText(decodeContent)
copyTextBtn.innerHTML = "Copied"
setTimeout(() => {
copyTextBtn.innerHTML = "Copy Text"
}, 1000)
})
}
// {% if pasta.encrypt_client %}
const decryptDiv = document.getElementById('decryption');
const decryptButton = document.getElementById('decrypt-button');
const passwordField = document.getElementById("password-field");
const downloadButton = document.getElementById('download-button');
// Set up event listener for download link click
downloadButton.addEventListener('click', async (event) => {
event.preventDefault(); // prevent default click behavior
if (passwordField.value.trim() == "") {
passwordField.focus();
return false;
}
// Fetch encrypted file from server
const formData = new FormData();
// {% if pasta.encrypted_key.is_some() %}
let key = decryptWithPassword(passwordField.value.trim(), "{{ pasta.encrypted_key.as_ref().unwrap() }}");
// {%- endif %}
formData.append('password', key);
const response = await fetch('/secure_file/{{ pasta.id_as_animals() }}', {
method: 'POST',
body: formData,
})
// {% if pasta.file.is_some() %}
const encryptedFile = await response.text();
// Decrypt file contents
const decryptedContents = decryptWithPassword(passwordField.value.trim(), encryptedFile);
if (!decryptedContents) {
throw new Error('Failed to decrypt file');
}
// Create data URI for decrypted file
const dataUri = `data:text/plain;charset=utf-8,${encodeURIComponent(decryptedContents)}`;
// Create temporary anchor element
const tempAnchorEl = document.createElement('a');
tempAnchorEl.href = dataUri;
tempAnchorEl.download = '{{pasta.file.as_ref().unwrap().name()}}';
// Programmatically click anchor element to trigger download
tempAnchorEl.click();
// Remove temporary anchor element
document.re(tempAnchorEl);
// {%- endif %}
});
decryptButton.addEventListener("click", () => {
password = passwordField.value;
content = contentDecrypted = escapeHtml(decryptWithPassword(password, content));
if (contentDecrypted) {
contentElement.innerHTML = contentDecrypted;
// {% if pasta.extension == "auto" %}
hljs.highlightAll();
// {% endif %}
contentElement.innerHTML = wrapStringInCodeLines(contentElement.innerHTML);
// decryptDiv.style.display = 'none';
}
});
function decryptWithPassword(password, encryptedHex) {
const passwordBytes = aesjs.utils.utf8.toBytes(password.padStart(32, "#"));
const encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex);
const aesCtr = new aesjs.ModeOfOperation.ctr(passwordBytes);
const decryptedBytes = aesCtr.decrypt(encryptedBytes);
const res = aesjs.utils.utf8.fromBytes(decryptedBytes);
if (res.endsWith("!0K")) {
return res.substring(0, res.length - "!0K".length);
} else {
return null;
}
}
// {% endif %}
</script>
<style>
code-line {
counter-increment: line;
text-align: right;
float: left;
clear: left;
}
code-line::before {
content: counter(line);
display: inline-block;
padding-left: auto;
margin-left: auto;
text-align: left;
width: 1.8rem;
border-right: 1px solid lightgrey;
color: grey;
margin-right: 0.4rem;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#code {
min-height: 2rem;
}
#embed {
background-color: #f7f7f7;
border-radius: 6px;
margin-top: 1rem;
margin-bottom: 1rem;
width: fit-content;
margin-left: auto;
margin-right: auto;
max-height: 480px;
display: flex;
justify-content: center;
align-items: center;
}
.download-button {
margin-left: 1rem;
font-size: small;
padding: 4px;
padding-left: 0.8rem;
padding-right: 0.8rem;
cursor: pointer;
}
.code-container {
position: relative;
}
.hidden {
display: none;
}
.small-button {
font-size: small;
padding: 4px;
padding-left: 0.8rem;
padding-right: 0.8rem;
cursor: pointer;
}
</style>
{% if !args.pure_html %}
<style>
#decryption {
background-color: #f7f7f7;
border-radius: 6px;
padding: 10px;
width: fit-content;
}
</style>
{% endif %}
{% include "footer.html" %}