350 lines
No EOL
12 KiB
PHP
Executable file
350 lines
No EOL
12 KiB
PHP
Executable file
<?php // This file is part of LibreQR, which is distributed under the GNU AGPLv3+ license
|
|
|
|
use Endroid\QrCode\Builder\Builder;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelQuartile;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
|
|
use Endroid\QrCode\Color\Color;
|
|
|
|
require "config.inc.php";
|
|
require "vendor/autoload.php";
|
|
|
|
define("LIBREQR_VERSION", "2.0.1");
|
|
|
|
// Defines the locale to be used
|
|
$locale = DEFAULT_LOCALE;
|
|
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
|
$clientLocales = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
|
$clientLocales = preg_replace("#[A-Z0-9]|q=|;|-|\.#", "", $clientLocales);
|
|
$clientLocales = explode(',', $clientLocales);
|
|
foreach (array_diff(scandir("locales"), array('..', '.')) as $key => $localeFile)
|
|
$availableLocales[$key] = basename($localeFile, ".php");
|
|
foreach ($clientLocales as $clientLocale) {
|
|
if (in_array($clientLocale, $availableLocales)) {
|
|
$locale = $clientLocale;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
require "locales/" . $locale . ".php";
|
|
$chosen_loc = $loc;
|
|
|
|
if ($locale != DEFAULT_LOCALE) {
|
|
require "locales/" . DEFAULT_LOCALE . ".php";
|
|
$default_loc = $loc;
|
|
} else
|
|
$default_loc = $chosen_loc;
|
|
|
|
require "locales/template.php";
|
|
$template_loc = $loc;
|
|
|
|
// Function to get a specific string from the locale file, fall back on default then template if missing
|
|
function getIntlString(
|
|
$string_label,
|
|
$raw = false
|
|
) {
|
|
global $chosen_loc, $default_loc, $template_loc, $locale;
|
|
|
|
if (array_key_exists($string_label, $chosen_loc))
|
|
return $chosen_loc[$string_label];
|
|
|
|
if ($locale != DEFAULT_LOCALE AND array_key_exists($string_label, $default_loc)) {
|
|
if ($raw)
|
|
return $default_loc[$string_label];
|
|
return "<span lang=\"" . DEFAULT_LOCALE . "\">" . $default_loc[$string_label] . "</span>";
|
|
}
|
|
|
|
if ($raw)
|
|
return $template_loc[$string_label];
|
|
return "<span lang=\"en\">" . $template_loc[$string_label] . "</span>";
|
|
}
|
|
|
|
require "themes/" . THEME . "/theme.php";
|
|
$colorScheme['theme'] = THEME;
|
|
|
|
$css_filename = Less_Cache::Get(
|
|
less_files: ['style.less' => ''],
|
|
parser_options: ['cache_dir' => 'css/', 'compress' => true],
|
|
modify_vars: $colorScheme,
|
|
);
|
|
|
|
preg_match('#.*/(?<page>.*)$#', $_SERVER['REQUEST_URI'], $matches);
|
|
define('PAGE', match ($matches['page']) {
|
|
'wifi' => 'wifi',
|
|
'' => 'home',
|
|
default => 'unknown',
|
|
});
|
|
|
|
if (PAGE === 'unknown') {
|
|
$allowed_filenames['css/' . $css_filename] = 'text/css';
|
|
foreach ($themeDimensionsIcons as $icon_dimension)
|
|
$allowed_filenames['themes/' . THEME . '/icons/' . $icon_dimension . '.png'] = 'image/png';
|
|
foreach ($allowed_filenames as $filename => $type) {
|
|
if (str_ends_with($_SERVER['REQUEST_URI'], $filename)) {
|
|
header('Content-Type: ' . $type);
|
|
echo file_get_contents($filename);
|
|
exit();
|
|
}
|
|
}
|
|
http_response_code(404);
|
|
}
|
|
|
|
$_POST = [
|
|
"form" => $_POST['form'] ?? NULL,
|
|
"wifi" => [
|
|
"ssid" => $_POST['wifi']['ssid'] ?? "",
|
|
"password" => $_POST['wifi']['password'] ?? "",
|
|
],
|
|
"main" => [
|
|
"txt" => $_POST['main']['txt'] ?? "",
|
|
"redundancy" => $_POST['main']['redundancy'] ?? DEFAULT_REDUNDANCY,
|
|
"margin" => $_POST['main']['margin'] ?? DEFAULT_MARGIN,
|
|
"size" => $_POST['main']['size'] ?? DEFAULT_SIZE,
|
|
"bgColor" => $_POST['main']['bgColor'] ?? "#" . DEFAULT_BGCOLOR,
|
|
"fgColor" => $_POST['main']['fgColor'] ?? "#" . DEFAULT_FGCOLOR,
|
|
],
|
|
];
|
|
|
|
if ($_POST['wifi']['ssid'] !== "") {
|
|
if (!(strlen($_POST['wifi']['ssid']) >= 1 AND strlen($_POST['wifi']['ssid']) <= 4096)) {
|
|
http_response_code(400);
|
|
exit("Wrong value for ssid");
|
|
}
|
|
$escaped_ssid = preg_replace("/([\\\;\,\"\:])/", "\\\\$1", $_POST['wifi']['ssid']);
|
|
|
|
if ($_POST['wifi']['password'] === "")
|
|
$_POST['main']['txt'] = "WIFI:T:nopass;S:{$escaped_ssid};;";
|
|
else {
|
|
if (strlen($_POST['wifi']['password']) > 4096) {
|
|
http_response_code(400);
|
|
exit("Wrong value for password");
|
|
}
|
|
|
|
$escaped_password = preg_replace("/([\\\;\,\"\:])/", "\\\\$1", $_POST['wifi']['password']);
|
|
|
|
$_POST['main']['txt'] = "WIFI:T:WPA;S:{$escaped_ssid};P:{$escaped_password};;";
|
|
}
|
|
}
|
|
|
|
$qrCodeAvailable = NULL;
|
|
|
|
if ($_POST['main']['txt'] !== "") {
|
|
$qrCodeAvailable = true;
|
|
|
|
if (!(strlen($_POST['main']['txt']) >= 1 AND strlen($_POST['main']['txt']) <= 4096)) {
|
|
http_response_code(400);
|
|
exit("Wrong value for txt");
|
|
}
|
|
|
|
if (!in_array($_POST['main']['redundancy'], ["low", "medium", "quartile", "high"], strict: true)) {
|
|
http_response_code(400);
|
|
exit("Wrong value for redundancy");
|
|
}
|
|
|
|
if (!(is_numeric($_POST['main']['margin']) AND $_POST['main']['margin'] >= 0 AND $_POST['main']['margin'] <= 1024)) {
|
|
http_response_code(400);
|
|
exit("Wrong value for margin");
|
|
}
|
|
|
|
if (!(is_numeric($_POST['main']['size']) AND $_POST['main']['size'] >= 21 AND $_POST['main']['size'] <= 4096)) {
|
|
http_response_code(400);
|
|
exit("Wrong value for size");
|
|
}
|
|
|
|
if (preg_match("/^#[abcdefABCDEF0-9]{6}$/", $_POST['main']['bgColor']) === false) {
|
|
http_response_code(400);
|
|
exit("Wrong value for bgColor");
|
|
}
|
|
|
|
if (preg_match("/^#[abcdefABCDEF0-9]{6}$/", $_POST['main']['fgColor']) === false) {
|
|
http_response_code(400);
|
|
exit("Wrong value for fgColor");
|
|
}
|
|
|
|
$rgbBgColor = [
|
|
'r' => hexdec(substr($_POST['main']['bgColor'],1,2)),
|
|
'g' => hexdec(substr($_POST['main']['bgColor'],3,2)),
|
|
'b' => hexdec(substr($_POST['main']['bgColor'],5,2)),
|
|
];
|
|
|
|
$qrCode = Builder::create()
|
|
->data($_POST['main']['txt'])
|
|
->margin($_POST['main']['margin'])
|
|
->size($_POST['main']['size'])
|
|
->errorCorrectionLevel(match ($_POST['main']['redundancy']) {
|
|
"low" => new ErrorCorrectionLevelLow(),
|
|
"medium" => new ErrorCorrectionLevelMedium(),
|
|
"quartile" => new ErrorCorrectionLevelQuartile(),
|
|
"high" => new ErrorCorrectionLevelHigh(),
|
|
})
|
|
->backgroundColor(new Color(
|
|
$rgbBgColor['r'],
|
|
$rgbBgColor['g'],
|
|
$rgbBgColor['b'],
|
|
))
|
|
->foregroundColor(new Color(
|
|
hexdec(substr($_POST['main']['fgColor'],1,2)),
|
|
hexdec(substr($_POST['main']['fgColor'],3,2)),
|
|
hexdec(substr($_POST['main']['fgColor'],5,2)),
|
|
));
|
|
|
|
try {
|
|
$result = $qrCode->build();
|
|
} catch (Exception $ex) {
|
|
http_response_code(500);
|
|
$qrCodeAvailable = false;
|
|
error_log("LibreQR encountered an error while generating a QR code: " . $ex);
|
|
}
|
|
}
|
|
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="<?= $locale ?>">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title><?=
|
|
match (PAGE) {
|
|
'home' => 'LibreQR · ' . getIntlString('subtitle'),
|
|
'wifi' => 'LibreQR · ' .getIntlString('tab_wifi_title'),
|
|
'unknown' => 'LibreQR · ' .getIntlString('error_404'),
|
|
}
|
|
?></title>
|
|
<meta name="description" content="<?= getIntlString('description', raw: true) ?>">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="color-scheme" content="dark light">
|
|
<meta name="application-name" content="LibreQR">
|
|
<meta name="referrer" content="no-referrer">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' data:; style-src 'self'; form-action 'self';">
|
|
|
|
<link rel="stylesheet" media="screen" href="css/<?= $css_filename ?>">
|
|
<?php
|
|
foreach($themeDimensionsIcons as $dimFav) // Set all icons dimensions
|
|
echo ' <link rel="icon" type="image/png" href="themes/' . THEME . '/icons/' . $dimFav . '.png" sizes="' . $dimFav . 'x' . $dimFav . '">' . "\n";
|
|
?>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<header>
|
|
<a id="linkTitles" href="./">
|
|
<hgroup id="titles">
|
|
<h1>LibreQR</h1>
|
|
<p><?= getIntlString('subtitle') ?></p>
|
|
</hgroup>
|
|
</a>
|
|
</header>
|
|
|
|
<nav>
|
|
<h2 class="sr-only">Type de code QR</h2>
|
|
<ul>
|
|
<li<?php if (PAGE == 'home') echo ' class="tab-selected"' ?>><a href="./"><div><?= getIntlString('tab_text') ?></div></a></li>
|
|
<li<?php if (PAGE == 'wifi') echo ' class="tab-selected"' ?>><a href="./wifi"><div><?= getIntlString('tab_wifi') ?></div></a></li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<?php if (PAGE === 'wifi') { ?>
|
|
<form method="post" action="./wifi#output">
|
|
<div class="param textboxParam">
|
|
<label for="ssid"><?= getIntlString('label_wifi_ssid') ?></label>
|
|
<input type="text" id="ssid" placeholder="<?= getIntlString('placeholder_wifi_ssid', raw: true) ?>" name="wifi[ssid]" required maxlength="4096" value="<?= htmlspecialchars($_POST['wifi']['ssid']) ?>">
|
|
</div>
|
|
<div class="param textboxParam">
|
|
<details>
|
|
<summary><label for="password"><?= getIntlString('label_wifi_password') ?></label></summary>
|
|
<div class="helpText">
|
|
<?= getIntlString('help_wifi_password') ?>
|
|
</div>
|
|
</details>
|
|
<input type="text" id="password" placeholder="<?= getIntlString('placeholder_wifi_password', raw: true) ?>" name="wifi[password]" maxlength="4096" value="<?= htmlspecialchars($_POST['wifi']['password']) ?>">
|
|
</div>
|
|
<?php require 'common.php' ?>
|
|
</form>
|
|
<?php } else if (PAGE === 'home') { ?>
|
|
<form method="post" action="./#output">
|
|
<div class="param textboxParam" id="txtParam">
|
|
<details>
|
|
<summary><label for="txt"><?= getIntlString('label_content') ?></label></summary>
|
|
<div class="helpText">
|
|
<?= getIntlString('help_content') ?>
|
|
</div>
|
|
</details>
|
|
<textarea rows="3" id="txt" placeholder="<?= getIntlString('placeholder', raw: true) ?>" name="main[txt]"><?= htmlspecialchars($_POST['main']['txt']) ?></textarea>
|
|
</div>
|
|
<?php require 'common.php' ?>
|
|
</form>
|
|
<?php } else { ?>
|
|
<p>
|
|
<?= getIntlString('error_404') ?>
|
|
</p>
|
|
<?php } ?>
|
|
|
|
<?php
|
|
|
|
if ($qrCodeAvailable) {
|
|
$dataUri = $result->getDataUri();
|
|
|
|
$qrSize = $_POST['main']['size'] + 2 * $_POST['main']['margin'];
|
|
|
|
?>
|
|
|
|
<section id="output">
|
|
<div class="centered" id="downloadQR">
|
|
<a href="<?= $dataUri ?>" class="button" download="<?= htmlspecialchars($_POST['main']['txt']); ?>.png"><?= getIntlString('button_download') ?></a>
|
|
</div>
|
|
|
|
<div class="centered" id="showOnlyQR">
|
|
<a title="<?= getIntlString('title_showOnlyQR', raw: true) ?>" href="<?= $dataUri ?>"><img width="<?= $qrSize ?>" height="<?= $qrSize ?>" alt='<?= getIntlString('alt_QR_before', raw: true) ?><?= htmlspecialchars($_POST['main']['txt']); ?><?= getIntlString('alt_QR_after', raw: true) ?>' id="qrCode"<?php
|
|
|
|
// Compute the difference between the QR code and theme background colors
|
|
$diffLight = abs($rgbBgColor['r']-hexdec(substr($colorScheme['bg-light'],-6,2))) + abs($rgbBgColor['g']-hexdec(substr($colorScheme['bg-light'],-4,2))) + abs($rgbBgColor['b']-hexdec(substr($colorScheme['bg-light'],-2,2)));
|
|
$diffDark = abs($rgbBgColor['r']-hexdec(substr($colorScheme['bg-dark'],-6,2))) + abs($rgbBgColor['g']-hexdec(substr($colorScheme['bg-dark'],-4,2))) + abs($rgbBgColor['b']-hexdec(substr($colorScheme['bg-dark'],-2,2)));
|
|
|
|
// Determine whether a CSS corner is needed to let the user see the margin of the QR code
|
|
$contrastThreshold = 64;
|
|
if ($diffLight < $contrastThreshold)
|
|
echo " class='needLightContrast'";
|
|
if ($diffDark < $contrastThreshold)
|
|
echo " class='needDarkContrast'";
|
|
?> src="<?= $dataUri ?>"></a>
|
|
</div>
|
|
<?php if (PAGE === "wifi") { ?>
|
|
<p>
|
|
<?= getIntlString('wifi_raw_content_before') ?><code><?= htmlspecialchars($_POST['main']['txt']) ?></code><?= getIntlString('wifi_raw_content_after') ?>
|
|
</p>
|
|
<form method="POST" action="./">
|
|
<?php foreach ($_POST['main'] as $name => $value) { ?>
|
|
<input type="hidden" name="main[<?= htmlspecialchars($name) ?>]" value="<?= htmlspecialchars($value) ?>" />
|
|
<?php } ?>
|
|
<input type="submit" value="<?= getIntlString('button_edit') ?>" />
|
|
</form>
|
|
<?php } ?>
|
|
</section>
|
|
|
|
<?php
|
|
} else if ($qrCodeAvailable === false) {
|
|
echo " <p><strong>" . getIntlString('error_generation') . "</strong></p></body></html>";
|
|
}
|
|
?>
|
|
|
|
<footer>
|
|
|
|
<section id="info" class="metaText">
|
|
<?= getIntlString('metaText_qr') ?>
|
|
</section>
|
|
|
|
<?php if (CUSTOM_TEXT_ENABLED) { ?>
|
|
<section class="metaText">
|
|
<?= CUSTOM_TEXT ?>
|
|
</section>
|
|
<?php } ?>
|
|
|
|
<section class="metaText">
|
|
<small><?= getIntlString('metaText_legal') ?></small>
|
|
</section>
|
|
|
|
</footer>
|
|
|
|
</body>
|
|
</html>
|