6 private function h(
$value) {
7 return htmlspecialchars((
string)
$value, ENT_QUOTES,
'UTF-8');
10 private function tpl(
string $name, array $data = array()) {
11 return dbx()->get_system_obj(
'dbxTPL')->get_tpl(
'dbxDownLoad|' . $name, $data);
14 private function config(
string $key, $default =
'') {
15 return dbx()->get_config(
'dbxDownLoad', $key, $default);
18 private function mail_from_param() {
19 $from = trim((
string)$this->config(
'mail_from'));
20 $fromName = trim((
string)$this->config(
'mail_from_name'));
21 return ($from !==
'') ? array(
'email' => $from,
'name' => $fromName) :
'';
24 private function mail_options(array $extra = array()) {
25 $profile = trim((
string)$this->config(
'mail_profile'));
32 private function module_url(array $params = array()):
string {
34 $query = array_merge(array(
'dbx_modul' =>
'dbxDownLoad'), $params);
35 return $base .
'?' . http_build_query($query,
'',
'&', PHP_QUERY_RFC3986);
38 private function secret():
string {
39 $secret = trim((
string)$this->config(
'token_secret'));
45 $secret = bin2hex(random_bytes(32));
46 $config[
'token_secret'] = $secret;
51 private function base64url(
string $bytes):
string {
52 return rtrim(strtr(base64_encode($bytes),
'+/',
'-_'),
'=');
55 private function base64url_decode(
string $value):
string {
59 $value .= str_repeat(
'=', 4 - $pad);
61 $decoded = base64_decode(
$value,
true);
62 return is_string($decoded) ? $decoded :
'';
65 private function create_token(
string $name,
string $email):
string {
66 $ttl = max(1, (
int)$this->config(
'download_ttl_hours', 48));
71 'exp' => time() + ($ttl * 3600),
72 'nonce' => bin2hex(random_bytes(8)),
74 $body = $this->base64url(json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
75 $sig = $this->base64url(hash_hmac(
'sha256',
$body, $this->secret(),
true));
76 return $body .
'.' . $sig;
79 private function read_token(
string $token): array {
80 $parts = explode(
'.', trim(
$token), 2);
81 if (count($parts) !== 2 || $parts[0] ===
'' || $parts[1] ===
'') {
85 $expected = $this->base64url(hash_hmac(
'sha256', $parts[0], $this->secret(),
true));
90 $payload = json_decode($this->base64url_decode($parts[0]),
true);
91 if (!is_array($payload) || (
int)($payload[
'exp'] ?? 0) < time()) {
98 private function download_file():
string {
99 $file = trim((
string)$this->config(
'download_zip',
'files/download/dbxapp-demo.zip'));
104 if (!preg_match(
'#^[A-Za-z]:/#',
$file) && strpos(
$file,
'/') !== 0) {
110 private function build_form() {
111 $form =
dbx()->get_system_obj(
'dbxForm');
112 $form->init(
'download-link',
'download-form');
113 $form->_fd =
'dbxDownLoad|download-link';
114 $form->load_fd_messages();
115 $form->_action =
'?dbx_modul=dbxDownLoad&dbx_run1=send_link';
116 $form->_try_reset = 6;
118 $form->_try_msg =
$form->get_fd_message(
'try_limit');
119 $form->set_msg_info(
'');
120 $form->set_msg_error(
$form->get_fd_message(
'validation_error'));
121 $form->set_msg_ok(
$form->get_fd_message(
'send_success'));
123 $form->_data = array_merge(
$form->_data, array(
124 'name' => trim((
string)(
$_POST[
'name'] ?? $_REQUEST[
'name'] ??
'')),
125 'email' => trim((
string)(
$_POST[
'email'] ?? $_REQUEST[
'email'] ??
'')),
127 $form->add_module_bar(
128 $form->get_fd_message(
'bar_title'),
130 $form->get_fd_message(
'bar_subtitle')
132 $form->prepare_form_shell(array(
'class' =>
'dbx-download-form'));
133 $form->add_fld(
'name');
134 $form->add_fld(
'email');
138 private function render_form() {
139 return $this->build_form()->run();
142 private function spam_reason(
string $name,
string $email,
string $subject =
''):
string {
143 $mail =
dbx()->get_system_obj(
'dbxMail');
144 if (!is_object(
$mail) || !method_exists(
$mail,
'spam_reason_for_text')) {
148 return (
string)
$mail->spam_reason_for_text(implode(
"\n", array($name, $email, $subject)));
151 private function save_demo_contact_request(
string $name,
string $email):
int {
153 $db =
dbx()->get_system_obj(
'dbxDB');
154 $message =
"Demo-Link wurde angefordert.\n\n"
155 .
"Name: " . $name .
"\n"
156 .
"E-Mail: " . $email .
"\n"
157 .
"Quelle: dbxDownLoad\n"
158 .
"Zeit: " . date(
'Y-m-d H:i:s');
163 'priority' =>
'normal',
167 'subject' =>
'Demo-Link angefordert',
168 'message' => $message,
169 'request_ip' => (
string)(
$_SERVER[
'REMOTE_ADDR'] ??
''),
170 'request_user_agent' => (
string)(
$_SERVER[
'HTTP_USER_AGENT'] ??
''),
172 'confirm_mail_sent' => 0,
176 $ok =
$db->insert(
'dbxContact|contactRequest', $values, 0, 1, 1, 1);
177 $ticketId =
$ok > 0 ? (int)
$db->get_insert_id() : 0;
181 dbx()->get_include_obj(
'dbxContactTicket',
'dbxContact');
182 if (class_exists(
'\\dbx\\dbxContact\\dbxContactTicket')) {
184 'author_uid' => (
int)
dbx()->
user(),
185 'author_type' =>
'requester',
186 'message_type' =>
'request',
187 'visibility' =>
'public',
189 'status_to' =>
'open',
193 }
catch (\Throwable $e) {
194 dbx()->sys_msg(
'warning',
'dbxDownLoad',
'contact_request',
'Kontakt-Nachricht konnte nicht ergaenzt werden', $e->getMessage());
199 }
catch (\Throwable $e) {
200 dbx()->sys_msg(
'error',
'dbxDownLoad',
'contact_request',
'Demo-Kontaktanfrage konnte nicht gespeichert werden', $e->getMessage());
205 private function send_link() {
206 $form = $this->build_form();
208 if (!
$form->submit()) {
212 if (
$form->errors()) {
216 $name = trim((
string)
$form->get_post_data(
'name',
'',
'varchar|min=2|max=160'));
217 $email = trim((
string)
$form->get_post_data(
'email',
'',
'email|min=6|max=180'));
218 $subject = trim((
string)$this->config(
'mail_subject',
'Ihr dbxapp Demo-Download'));
220 $spamReason = $this->spam_reason($name, $email, $subject);
221 if ($spamReason !==
'') {
222 $form->set_msg_error(
$form->get_fd_message(
'spam_error'));
223 $form->add_fld_error(
225 $form->get_fd_message(
'spam_field_error')
227 dbx()->sys_msg(
'security',
'dbxDownLoad',
'spam_guard',
'Demo-Link blockiert', $spamReason .
' email=' . $email);
231 $token = $this->create_token($name, $email);
232 $url = $this->module_url(array(
'dbx_run1' =>
'download',
'token' =>
$token));
233 $contactId = $this->save_demo_contact_request($name, $email);
234 $html = $this->tpl(
'mail-download-link', array(
235 'name' => $this->h($name),
236 'download_url' => $this->h(
$url),
237 'ttl_hours' => (
int)$this->config(
'download_ttl_hours', 48),
239 $text =
"Hallo " . $name .
"\n\n"
240 .
"hier ist Ihr dbxapp Demo-Download:\n" .
$url .
"\n\n"
241 .
"Der Link ist " . (int)$this->config(
'download_ttl_hours', 48) .
" Stunden gueltig.\n";
243 $ok =
dbx()->send_mail($this->mail_from_param(), $email, $subject,
$html,
'html', array(), $this->mail_options(array(
'text' => $text)));
245 dbx()->sys_msg(
'error',
'dbxDownLoad',
'send_link',
'Download-Mail konnte nicht gesendet werden', $email);
246 $form->set_msg_error(
$form->get_fd_message(
'mail_error'));
247 $form->add_fld_error(
249 $form->get_fd_message(
'email_field_error')
254 dbx()->sys_msg(
'info',
'dbxDownLoad',
'send_link',
'Download-Link versendet', $email .
' contact=' . $contactId);
257 array(
'email' => $this->h($email))
262 private function download_page() {
263 $token = trim((
string)($_REQUEST[
'token'] ??
''));
264 $payload = $this->read_token(
$token);
266 return $this->tpl(
'download-invalid', array());
269 $file = $this->download_file();
270 $ready = is_file(
$file) && is_readable(
$file);
271 return $this->tpl(
'download-ready', array(
272 'name' => $this->h($payload[
'name'] ??
''),
273 'email' => $this->h($payload[
'email'] ??
''),
274 'expires' => date(
'd.m.Y H:i', (
int)($payload[
'exp'] ?? time())),
275 'download_url' => $ready ? $this->h($this->module_url(array(
'dbx_run1' =>
'file',
'token' =>
$token))) :
'#',
276 'download_disabled' => $ready ?
'' :
'disabled aria-disabled="true" tabindex="-1"',
277 'download_btn_class' => $ready ?
'btn btn-primary' :
'btn btn-secondary disabled',
278 'download_note' => $ready
279 ?
'Die Demo-ZIP und die Hinweise zur Installation stehen bereit.'
280 :
'Die Demo-ZIP ist auf diesem System noch nicht hinterlegt. Erwarteter Pfad: ' . $this->h((
string)$this->config(
'download_zip')),
284 private function stream_file() {
285 $payload = $this->read_token(trim((
string)($_REQUEST[
'token'] ??
'')));
286 $file = $this->download_file();
287 if (!$payload || !is_file(
$file) || !is_readable(
$file)) {
288 http_response_code(404);
289 echo
'Download nicht verfuegbar.';
293 $name = trim((
string)$this->config(
'download_name', basename(
$file)));
295 $name = basename(
$file);
298 dbx()->sys_msg(
'info',
'dbxDownLoad',
'file',
'Demo-ZIP geladen', (
string)($payload[
'email'] ??
''));
299 while (ob_get_level()) {
302 header(
'Content-Type: application/zip');
303 header(
'Content-Disposition: attachment; filename="' . str_replace(
'"',
'', $name) .
'"');
304 header(
'Content-Length: ' . filesize(
$file));
305 header(
'Cache-Control: private, no-store, max-age=0');
311 $run = (string)
dbx()->get_modul_var(
'dbx_run1',
'form',
'parameter');
314 return $this->send_link();
316 return $this->download_page();
318 return $this->stream_file();
321 return $this->render_form();