X-Git-Url: https://git.p6c8.net/jirafeau_project.git/blobdiff_plain/96707e02b8b24054e0827eaf169cc88504a1e78c..463c260d3cf16d2fa2fbcba04bac446165d1a618:/lib/functions.js.php?ds=inline diff --git a/lib/functions.js.php b/lib/functions.js.php index dab491c..64154d3 100644 --- a/lib/functions.js.php +++ b/lib/functions.js.php @@ -1,8 +1,9 @@ - * Copyright (C) 2015 Nicola Spanti (RyDroid) + * Copyright (C) 2008 Julien "axolotl" BERNARD + * Copyright (C) 2015 Jerome Jutteau + * Copyright (C) 2024 Jirafeau project (see AUTHORS.md) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,103 +19,249 @@ * along with this program. If not, see . */ -header('Content-Type: text/javascript'); + function template_js_preview_link() { + ?> + if (!!document.getElementById('preview_link')) + { + document.getElementById('upload_finished_preview').style.display = 'none'; + var preview_link_href = 'f.php?h=' + reference + '&p=1'; + if (crypt_key.length > 0) + { + preview_link_href += '&k=' + crypt_key; + } + // Test if content can be previewed + type = document.getElementById('file_select').files[0].type; + if ((type.startsWith('image/') + || type.startsWith('audio') + || type.startsWith('text/plain') + || type.startsWith('video/')) + && !type.includes('image/svg+xml')) + { + document.getElementById('preview_link').href = preview_link_href; + document.getElementById('preview_link_text').innerHTML = web_root + preview_link_href; + document.getElementById('upload_finished_preview').style.display = ''; + } + } + + // Download page + var download_link_href = 'f.php?h=' + reference; + if (crypt_key.length > 0) + { + download_link_href += '&k=' + crypt_key; + } + if (!!document.getElementById('upload_finished_download_page')) + { + document.getElementById('upload_link').href = download_link_href; + document.getElementById('upload_link_text').innerHTML = web_root + download_link_href; + } + + // Email link + var b = encodeURIComponent(" \"" + filename + "\":") + "%0D" + "%0A"; + b += encodeURIComponent(web_root + download_link_href) + "%0D" + "%0A"; + if (false == isEmpty(date)) + { + b += "%0D" + "%0A" + encodeURIComponent(": " + date.format('YYYY-MM-DD hh:mm (GMT O)')) + "%0D" + "%0A"; + document.getElementById('upload_link_email').href = "mailto:?body=" + b + "&subject=" + encodeURIComponent(filename); + } + + // Delete link + var delete_link_href = 'f.php?h=' + reference + '&d=' + delete_code; + document.getElementById('delete_link').href = delete_link_href; + document.getElementById('delete_link_text').innerHTML = web_root + delete_link_href; + + // Direct download link + var direct_download_link_href = 'f.php?h=' + reference + '&d=1'; + if (crypt_key.length > 0) + { + direct_download_link_href += '&k=' + crypt_key; + } + document.getElementById('direct_link').href = direct_download_link_href; + document.getElementById('direct_link_text').innerHTML = web_root + direct_download_link_href; + + // Validity date + if (isEmpty(date)) + { + document.getElementById('date').style.display = 'none'; + } + else { + document.getElementById('date').innerHTML = + '' + + date.format('YYYY-MM-DD hh:mm (GMT O)') + + ''; + document.getElementById('date').style.display = ''; + } + +// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later +var web_root = ""; + +var lang_array = ; +var lang_array_fallback = ; + +function translate (expr) { + if (lang_array.hasOwnProperty(expr)) { + var e = lang_array[expr]; + if (!isEmpty(e)) + return e; + } + if (lang_array_fallback.hasOwnProperty(expr)) { + var e = lang_array_fallback[expr]; + if (!isEmpty(e)) + return e; + } + return "FIXME: " + expr; +} + +function isEmpty(str) { + return (!str || 0 === str.length); +} + +// Extend date object with format method +Date.prototype.format = function(format) { + format = format || 'YYYY-MM-DD hh:mm'; + + var zeropad = function(number, length) { + number = number.toString(); + length = length || 2; + while(number.length < length) + number = '0' + number; + return number; + }, + formats = { + YYYY: this.getFullYear(), + MM: zeropad(this.getMonth() + 1), + DD: zeropad(this.getDate()), + hh: zeropad(this.getHours()), + mm: zeropad(this.getMinutes()), + O: (function() { + localDate = new Date; + sign = (localDate.getTimezoneOffset() > 0) ? '-' : '+'; + offset = Math.abs(localDate.getTimezoneOffset()); + hours = zeropad(Math.floor(offset / 60)); + minutes = zeropad(offset % 60); + return sign + hours + ":" + minutes; + })() + }, + pattern = '(' + Object.keys(formats).join(')|(') + ')'; + + return format.replace(new RegExp(pattern, 'g'), function(match) { + return formats[match]; + }); +}; + +function dateFromUtcString(datestring) { + // matches »YYYY-MM-DD hh:mm« + var m = datestring.match(/(\d+)-(\d+)-(\d+)\s+(\d+):(\d+)/); + return new Date(Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4], +m[5], 0)); +} + +function dateFromUtcTimestamp(datetimestamp) { + return new Date(parseInt(datetimestamp) * 1000) +} + +function dateToUtcString(datelocal) { + return new Date( + datelocal.getUTCFullYear(), + datelocal.getUTCMonth(), + datelocal.getUTCDate(), + datelocal.getUTCHours(), + datelocal.getUTCMinutes(), + datelocal.getUTCSeconds() + ).format(); +} + +function dateToUtcTimestamp(datelocal) { + return (Date.UTC( + datelocal.getUTCFullYear(), + datelocal.getUTCMonth(), + datelocal.getUTCDate(), + datelocal.getUTCHours(), + datelocal.getUTCMinutes(), + datelocal.getUTCSeconds() + ) / 1000); +} + +function convertAllDatetimeFields() { + datefields = document.getElementsByClassName('datetime') + for(var i=0; i; - if (lang_array.hasOwnProperty(expr)) - return lang_array[expr]; - return expr; + + + + var filename = file_name; + + + + var type = file_type; + } -function show_link (url, reference, delete_code, crypt_key, date) + + +function show_link (reference, delete_code, crypt_key, date) { // Upload finished document.getElementById('uploading').style.display = 'none'; document.getElementById('upload').style.display = 'none'; document.getElementById('upload_finished').style.display = ''; - document.title = 'Jirafeau - 100%'; - - // Download page - var download_link = url + 'f.php?h=' + reference; - var download_link_href = url + 'f.php?h=' + reference; - if (crypt_key.length > 0) - { - download_link += '&k=' + crypt_key; - download_link_href += '&k=' + crypt_key; - } - if (!!document.getElementById('upload_finished_download_page')) - { - document.getElementById('upload_link').innerHTML = download_link; - document.getElementById('upload_link').href = download_link_href; - } + document.title = "100% - "; + // Email link var filename = document.getElementById('file_select').files[0].name; - var b = encodeURIComponent("Download file \"" + filename + "\":") + "%0D"; - b += encodeURIComponent(download_link_href) + "%0D"; - if (date) - b += "%0D" + encodeURIComponent("This file will be available until " + date) + "%0D"; - document.getElementById('upload_link_email').href = "mailto:?body=" + b + "&subject=" + encodeURIComponent(filename); - // Delete link - var delete_link = url + 'f.php?h=' + reference + '&d=' + delete_code; - var delete_link_href = url + 'f.php?h=' + reference + '&d=' + delete_code; - document.getElementById('delete_link').innerHTML = delete_link; - document.getElementById('delete_link').href = delete_link_href; + + + + - // Validity date - if (date) - { - document.getElementById('date').innerHTML = date; - document.getElementById('validity').style.display = ''; - } - else - document.getElementById('validity').style.display = 'none'; // Preview link (if allowed) - if (!!document.getElementById('preview_link')) - { - document.getElementById('upload_finished_preview').style.display = 'none'; - var preview_link = url + 'f.php?h=' + reference + '&p=1'; - var preview_link_href = url + 'f.php?h=' + reference + '&p=1'; - if (crypt_key.length > 0) - { - preview_link += '&k=' + crypt_key; - preview_link_href += '&k=' + crypt_key; - } + + var type = document.getElementById('file_select').files[0].type; + - // Test if content can be previewed - type = document.getElementById('file_select').files[0].type; - if (type.indexOf("image") > -1 || - type.indexOf("audio") > -1 || - type.indexOf("text") > -1 || - type.indexOf("video") > -1) - { - document.getElementById('preview_link').innerHTML = preview_link; - document.getElementById('preview_link').href = preview_link_href; - document.getElementById('upload_finished_preview').style.display = ''; - } - } - - // Direct download link - var direct_download_link = url + 'f.php?h=' + reference + '&d=1'; - var direct_download_link_href = url + 'f.php?h=' + reference + '&d=1'; - if (crypt_key.length > 0) - { - direct_download_link += '&k=' + crypt_key; - direct_download_link_href += '&k=' + crypt_key; - } - document.getElementById('direct_link').innerHTML = direct_download_link; - document.getElementById('direct_link').href = direct_download_link_href; + // Hide preview and direct download link if password is set @@ -131,7 +278,7 @@ function show_upload_progression (percentage, speed, time_left) document.getElementById('uploaded_percentage').innerHTML = percentage; document.getElementById('uploaded_speed').innerHTML = speed; document.getElementById('uploaded_time').innerHTML = time_left; - document.title = 'Jirafeau - ' + percentage; + document.title = percentage + " - "; } function hide_upload_progression () @@ -139,7 +286,7 @@ function hide_upload_progression () document.getElementById('uploaded_percentage').style.display = 'none'; document.getElementById('uploaded_speed').style.display = 'none'; document.getElementById('uploaded_time').style.display = 'none'; - document.title = 'Jirafeau'; + document.title = ""; } function upload_progress (e) @@ -177,21 +324,28 @@ function control_selected_file_size(max_size, error_str) } else { - document.getElementById('options').style.display = ''; - document.getElementById('send').style.display = ''; + // add class to restyle upload form in next step + document.getElementById('upload').setAttribute('class', 'file-selected'); + // display options + document.getElementById('options').style.display = 'block'; + document.getElementById('send').style.display = 'block'; document.getElementById('error_pop').style.display = 'none'; - document.getElementById('file_select').style.left = 'inherit'; - document.getElementById('file_select').style.height = 'inherit'; - document.getElementById('file_select').style.opacity = '1'; + document.getElementById('send').focus(); } } +function XHRErrorHandler(e) +{ + var text = "${e.type}: ${e.loaded} bytes transferred" + console.log(text) +} + function pop_failure (e) { - var text = "An error occured"; + var text = "

An error occured"; if (typeof e !== 'undefined') - text = e; - text = "

" + text + "

"; + text += ": " + e; + text += "

"; document.getElementById('error_pop').innerHTML = e; document.getElementById('uploading').style.display = 'none'; @@ -227,56 +381,73 @@ function add_time_string_to_date(d, time) d.setSeconds (d.getSeconds() + 604800); return true; } + if (time == 'fortnight') + { + d.setSeconds (d.getSeconds() + 1209600); + return true; + } if (time == 'month') { - d.setSeconds (d.getSeconds() + 2419200); + d.setSeconds (d.getSeconds() + 2592000); return true; } if (time == 'quarter') { - d.setSeconds (d.getSeconds() + 7257600); + d.setSeconds (d.getSeconds() + 7776000); return true; } if (time == 'year') { - d.setSeconds (d.getSeconds() + 29030400); + d.setSeconds (d.getSeconds() + 31536000); return true; } return false; } -function classic_upload (url, file, time, password, one_time, upload_password) +function classic_upload (file, time, password, one_time) { // Delay time estimation init as we can't have file size upload_time_estimation_init(0); var req = new XMLHttpRequest (); req.upload.addEventListener ("progress", upload_progress, false); - req.addEventListener ("error", pop_failure, false); - req.addEventListener ("abort", pop_failure, false); + req.addEventListener ("error", XHRErrorHandler, false); + req.addEventListener ("abort", XHRErrorHandler, false); req.onreadystatechange = function () { if (req.readyState == 4 && req.status == 200) { var res = req.responseText; - if (res == "Error") + + // if response starts with "Error" then show a failure + if (/^Error/.test(res)) { - pop_failure (); + pop_failure (res); return; } + res = res.split ("\n"); + var expiryDate = ''; if (time != 'none') { - var d = new Date(); - if(!add_time_string_to_date(d, time)) + // convert time (local time + selected expiry date) + var localDatetime = new Date(); + if(!add_time_string_to_date(localDatetime, time)) + { + pop_failure ('Error: Date can not be parsed'); return; - show_link (url, res[0], res[1], res[2], d.toString()); + } + expiryDate = localDatetime; } - else - show_link (url, res[0], res[1], res[2]); + + show_link (res[0], res[1], res[2], expiryDate); + } + else + { + pop_failure (""); } } - req.open ("POST", url + 'script.php' , true); + req.open ("POST", 'script.php' , true); var form = new FormData(); form.append ("file", file); @@ -286,9 +457,6 @@ function classic_upload (url, file, time, password, one_time, upload_password) form.append ("key", password); if (one_time) form.append ("one_time_download", '1'); - if (upload_password.length > 0) - form.append ("upload_password", upload_password); - req.send (form); } @@ -298,41 +466,42 @@ function check_html5_file_api () } var async_global_transfered = 0; -var async_global_url = ''; var async_global_file; var async_global_ref = ''; var async_global_max_size = 0; var async_global_time; var async_global_transfering = 0; +var async_global_last_code; -function async_upload_start (url, max_size, file, time, password, one_time, upload_password) +function async_upload_start (max_size, file, time, password, one_time) { async_global_transfered = 0; - async_global_url = url; async_global_file = file; async_global_max_size = max_size; async_global_time = time; var req = new XMLHttpRequest (); - req.addEventListener ("error", pop_failure, false); - req.addEventListener ("abort", pop_failure, false); + req.addEventListener ("error", XHRErrorHandler, false); + req.addEventListener ("abort", XHRErrorHandler, false); req.onreadystatechange = function () { if (req.readyState == 4 && req.status == 200) { var res = req.responseText; - if (res == "Error") + + if (/^Error/.test(res)) { - pop_failure (); + pop_failure (res); return; } + res = res.split ("\n"); async_global_ref = res[0]; var code = res[1]; async_upload_push (code); } } - req.open ("POST", async_global_url + 'script.php?init_async' , true); + req.open ("POST", 'script.php?init_async' , true); var form = new FormData(); form.append ("filename", async_global_file.name); @@ -343,8 +512,6 @@ function async_upload_start (url, max_size, file, time, password, one_time, uplo form.append ("key", password); if (one_time) form.append ("one_time_download", '1'); - if (upload_password.length > 0) - form.append ("upload_password", upload_password); // Start time estimation upload_time_estimation_init(async_global_file.size); @@ -375,6 +542,7 @@ function async_upload_progress (e) function async_upload_push (code) { + async_global_last_code = code; if (async_global_transfered == async_global_file.size) { hide_upload_progression (); @@ -383,29 +551,53 @@ function async_upload_push (code) } var req = new XMLHttpRequest (); req.upload.addEventListener ("progress", async_upload_progress, false); - req.addEventListener ("error", pop_failure, false); - req.addEventListener ("abort", pop_failure, false); + req.addEventListener ("error", XHRErrorHandler, false); + req.addEventListener ("abort", XHRErrorHandler, false); req.onreadystatechange = function () { - if (req.readyState == 4 && req.status == 200) + if (req.readyState == 4) { - var res = req.responseText; - if (res == "Error") + if (req.status == 200) { - pop_failure (); + var res = req.responseText; + + // This error may be triggered when Jirafeau does not receive any file in POST. + // This may be due to bad php configuration where post_max_size is too low + // comparing to upload_max_filesize. Let's retry with lower file size. + if (res === "Error 23") + { + async_global_max_size = Math.max(1, async_global_max_size - 500); + async_upload_push (async_global_last_code); + return; + } + else if (/^Error/.test(res)) + { + pop_failure (res); + return; + } + + res = res.split ("\n"); + var code = res[0] + async_global_transfered = async_global_transfering; + async_upload_push (code); + return; + } + else + { + // lower async_global_max_size and retry + // This can occurs in several cases: + // - Request Entity Too Large (413) due to server bad configuration relative to PHP configuration + // - Server Error (500) which can happen when PHP's `max_execution_time` is too low comparared to sent size + async_global_max_size = Math.max(1, parseInt (async_global_max_size * 0.5)); + async_upload_push (async_global_last_code); return; } - res = res.split ("\n"); - var code = res[0] - async_global_transfered = async_global_transfering; - async_upload_push (code); } } - req.open ("POST", async_global_url + 'script.php?push_async' , true); + req.open ("POST", 'script.php?push_async' , true); - var chunk_size = parseInt (async_global_max_size * 0.50); var start = async_global_transfered; - var end = start + chunk_size; + var end = start + async_global_max_size; if (end >= async_global_file.size) end = async_global_file.size; var blob = async_global_file.slice (start, end); @@ -421,31 +613,37 @@ function async_upload_push (code) function async_upload_end (code) { var req = new XMLHttpRequest (); - req.addEventListener ("error", pop_failure, false); - req.addEventListener ("abort", pop_failure, false); + req.addEventListener ("error", XHRErrorHandler, false); + req.addEventListener ("abort", XHRErrorHandler, false); req.onreadystatechange = function () { if (req.readyState == 4 && req.status == 200) { var res = req.responseText; - if (res == "Error") + + if (/^Error/.test(res)) { - pop_failure (); + pop_failure (res); return; } + res = res.split ("\n"); + var expiryDate = ''; if (async_global_time != 'none') { - var d = new Date(); - if(!add_time_string_to_date(d, async_global_time)) - return; - show_link (async_global_url, res[0], res[1], res[2], d.toString()); + // convert time (local time + selected expiry date) + var localDatetime = new Date(); + if(!add_time_string_to_date(localDatetime, async_global_time)) { + pop_failure ('Error: Date can not be parsed'); + return; + } + expiryDate = localDatetime; } - else - show_link (async_global_url, res[0], res[1], res[2]); + + show_link (res[0], res[1], res[2], expiryDate); } } - req.open ("POST", async_global_url + 'script.php?end_async' , true); + req.open ("POST", 'script.php?end_async' , true); var form = new FormData(); form.append ("ref", async_global_ref); @@ -453,28 +651,27 @@ function async_upload_end (code) req.send (form); } -function upload (url, max_size) +function upload (max_chunk_size) { - if (check_html5_file_api () - && document.getElementById('file_select').files[0].size >= max_size) + var one_time_checkbox = document.getElementById('one_time_download'); + var one_time = one_time_checkbox !== null ? one_time_checkbox.checked : false; + if (check_html5_file_api ()) { - async_upload_start (url, - max_size, + async_upload_start ( + max_chunk_size, document.getElementById('file_select').files[0], document.getElementById('select_time').value, document.getElementById('input_key').value, - document.getElementById('one_time_download').checked, - document.getElementById('upload_password').value + one_time ); } else { - classic_upload (url, + classic_upload ( document.getElementById('file_select').files[0], document.getElementById('select_time').value, document.getElementById('input_key').value, - document.getElementById('one_time_download').checked, - document.getElementById('upload_password').value + one_time ); } } @@ -498,9 +695,11 @@ function upload_time_estimation_add(total_transfered_size) // Let's compute the current speed var d = new Date(); var speed = upload_time_estimation_moving_average_speed; - if (d.getTime() - upload_time_estimation_transfered_date != 0) + if (d.getTime() - upload_time_estimation_transfered_date != 0) { speed = (total_transfered_size - upload_time_estimation_transfered_size) / (d.getTime() - upload_time_estimation_transfered_date); + speed = Math.max(0, speed); + } // Let's compute moving average speed on 30 values var m = (upload_time_estimation_moving_average_speed * 29 + speed) / 30; // Update global values @@ -518,17 +717,17 @@ function upload_time_estimation_speed_string() if (s <= 1000) { res = s.toString(); - scale = "o/s"; + scale = "B/s"; } else if (s < 1000000) { res = Math.floor(s/100) / 10; - scale = "Ko/s"; + scale = "KB/s"; } else { res = Math.floor(s/100000) / 10; - scale = "Mo/s"; + scale = "MB/s"; } if (res == 0) return ''; @@ -538,37 +737,37 @@ function upload_time_estimation_speed_string() function milliseconds_to_time_string (milliseconds) { function numberEnding (number) { - return (number > 1) ? 's' : ''; + return (number > 1) ? translate ('PLURAL_ENDING') : ''; } var temp = Math.floor(milliseconds / 1000); var years = Math.floor(temp / 31536000); if (years) { - return years + ' ' + translate ('year') + numberEnding(years); + return years + ' ' + translate ('YEAR') + numberEnding(years); } var days = Math.floor((temp %= 31536000) / 86400); if (days) { - return days + ' ' + translate ('day') + numberEnding(days); + return days + ' ' + translate ('DAY') + numberEnding(days); } var hours = Math.floor((temp %= 86400) / 3600); if (hours) { - return hours + ' ' + translate ('hour') + numberEnding(hours); + return hours + ' ' + translate ('HOUR') + numberEnding(hours); } var minutes = Math.floor((temp %= 3600) / 60); if (minutes) { - return minutes + ' ' + translate ('minute') + numberEnding(minutes); + return minutes + ' ' + translate ('MINUTE') + numberEnding(minutes); } var seconds = temp % 60; if (seconds) { - return seconds + ' ' + translate ('second') + numberEnding(seconds); + return seconds + ' ' + translate ('SECOND') + numberEnding(seconds); } - return translate ('less than a second'); + return translate ('LESS_1_SEC'); } function upload_time_estimation_time() { // Estimate remaining time - if (upload_time_estimation_moving_average_speed == 0) + if (upload_time_estimation_moving_average_speed <= 0) return 0; return (upload_time_estimation_total_size - upload_time_estimation_transfered_size) / upload_time_estimation_moving_average_speed; @@ -615,3 +814,79 @@ function upload_speed_refresh_limiter(speed_str) } return upload_speed_refresh_limiter_last_value; } + +// document.ready() +document.addEventListener('DOMContentLoaded', function(event) { + // Search for all datetime fields and convert the time to local timezone + convertAllDatetimeFields(); +}); + +// Add copy event listeners +function copyLinkToClipboard(link_id) { + var focus = document.activeElement; + var e = document.getElementById(link_id); + + var tmp = document.createElement("textarea"); + document.body.appendChild(tmp); + tmp.textContent = e.href; + tmp.focus(); + tmp.setSelectionRange(0, tmp.value.length); + document.execCommand("copy"); + document.body.removeChild(tmp); + + focus.focus(); +} + +function addCopyListener(button_id, link_id) { + if(document.getElementById(button_id)){ + document.getElementById(button_id) + .addEventListener("click", function() { + copyLinkToClipboard(link_id);}); + } +} + +function copyTextToClipboard(text_id){ + var copyText = document.getElementById(text_id); + copyText.select(); + copyText.setSelectionRange(0, 99999); + navigator.clipboard.writeText(copyText.value); +} + +function addTextCopyListener(button_id, text_id) { + if(document.getElementById(button_id)){ + document.getElementById(button_id) + .addEventListener("click", function() { + copyTextToClipboard(text_id);}); + } +} + +function set_dark_mode() { + let steel_sheet = ""; + let shortcut_icon = ""; + document.getElementById('stylesheet').href = steel_sheet; + document.getElementById('shortcut_icon').href = shortcut_icon; +} + +function set_light_mode() { + let steel_sheet = ""; + let shortcut_icon = ""; + document.getElementById('stylesheet').href = steel_sheet; + document.getElementById('shortcut_icon').href = shortcut_icon; +} + +function color_scheme_preferences() { + + let dark_mode_steel_sheet = "" + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + set_dark_mode(); + } else { + set_light_mode(); + } + + // When user change its preference + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', lightMode => { + lightMode.matches ? set_dark_mode() : set_light_mode(); + }); +} + +// @license-end