1#
2# Authentic Theme (https://github.com/authentic-theme/authentic-theme)
3# Copyright Ilia Rostovtsev <programming@rostovtsev.io>
4# Licensed under MIT (https://github.com/authentic-theme/authentic-theme/blob/master/LICENSE)
5#
6use strict;
7
8our (@theme_bundle_css,
9     @theme_bundle_js,
10     %module_text_full,
11     %theme_config,
12     %theme_text,
13     %theme_temp_data,
14     $get_user_level,
15     $theme_webprefix,
16     $theme_server_webprefix,
17     $has_cloudmin,
18     $has_usermin_conf_dir,
19     $has_usermin_root_dir,
20     $has_usermin_version,
21     $has_usermin,
22     $has_virtualmin,
23     $http_x_url,
24     $server_x_goto,
25     $xnav,
26     %config,
27     %gaccess,
28     %gconfig,
29     %in,
30     %tconfig,
31     %text,
32     $config_directory,
33     $current_lang,
34     $current_theme,
35     $remote_user,
36     $root_directory,
37     $theme_root_directory,
38     $title);
39
40do("$ENV{'THEME_ROOT'}/authentic-funcs.pl");
41
42init_vars();
43
44sub settings_filter
45{
46    my (%in_data) = @_;
47
48    delete @in_data{ grep(!/^config_portable_|^settings_/, keys %in_data) };
49    delete @in_data{ grep(!m/^\w*$/,                       keys %in_data) };
50    for (values %in_data) {s/(.*)/'$1';/}
51    for (values %in_data) {s/\$|`*//g}
52    for (values %in_data) {s/<<//g}
53    for (values %in_data) {s/"/'/g}
54    for (values %in_data) {s/\/\//&#47;&#47;/g}
55    for (values %in_data) {s/'true'/true/g}
56    for (values %in_data) {s/'false'/false/g}
57    for (values %in_data) {s/'1'/1/g}
58    for (values %in_data) {s/'0'/0/g}
59
60    for (values %in_data) {
61        s/
62         \G
63         (
64            (?: ^ [^']* ' | (?!^) )
65            (?: [^'\\]+ | \\. )*
66         )
67         '
68         (?! [^']* \z )
69      /
70         $1 . "\\'"
71      /xseg;
72    }
73    return %in_data;
74}
75
76sub get_theme_color
77{
78    my %theme_colors = ('blue'   => '#0d5ab7',
79                        'brown'  => '#5d4839',
80                        'gold'   => '#917851',
81                        'green'  => '#277b4c',
82                        'grey'   => '#4c5250',
83                        'orange' => '#946a44',
84                        'purple' => '#543c53',
85                        'red'    => '#a02638',
86                        'white'  => '#ffffff');
87
88    my $color = $theme_config{'settings_navigation_color'};
89    return $theme_colors{$color};
90}
91
92sub embed_favicon
93{
94    my ($is_login_page) = @_;
95
96    return if ($theme_config{"settings_embed_favicon_privileged"} eq 'false');
97
98    my $product      = get_product_name() eq 'usermin' ? 'usermin' : 'webmin';
99    my $product_name = $product;
100    if ($get_user_level eq '1' || $get_user_level eq '2') {
101        $product_name = 'virtualmin';
102    }
103    if ($get_user_level eq '4') {
104        $product_name = 'cloudmin';
105    }
106
107    my $theme_config_dir = "$config_directory/$current_theme";
108    my $theme_user_color = get_theme_color() || "#0d5ab7";
109
110    my $favicon_path = $theme_webprefix . '/images/favicons/' . $product_name;
111    my $ref_link     = 'data-link-ref';
112
113    my $favicon_dpath = "$root_directory/$current_theme/images/favicons/$product_name";
114    my $favicon_cpath = "$theme_config_dir/favicons/$product_name";
115    my $favicon_spath = -r $favicon_cpath ? $favicon_cpath : $favicon_dpath;
116
117    # Embed standard favicons using a direct link
118    if (!-r $favicon_cpath) {
119        print ' <link ' .
120          $ref_link . ' rel="apple-touch-icon" sizes="180x180" href="' . $favicon_path . '/apple-touch-icon.png">' . "\n";
121        print ' <link ' .
122          $ref_link . ' rel="icon" type="image/png" sizes="32x32" href="' . $favicon_path . '/favicon-32x32.png">' . "\n";
123        print ' <link ' . $ref_link .
124          ' rel="icon" type="image/png" sizes="192x192" href="' . $favicon_path . '/android-chrome-192x192.png">' . "\n";
125        print ' <link ' .
126          $ref_link . ' rel="icon" type="image/png" sizes="16x16" href="' . $favicon_path . '/favicon-16x16.png">' . "\n";
127        print ' <link ' . $ref_link .
128          ' rel="mask-icon" href="' . $favicon_path . '/safari-pinned-tab.svg" color="' . $theme_user_color . '">' . "\n";
129        print ' <meta ' .
130          $ref_link . ' name="msapplication-TileImage" content="' . $favicon_path . '/mstile-150x150.png">' . "\n";
131    }
132
133    # Embed custom favicons using base64 encoding
134    else {
135        print ' <link ' . $ref_link . ' rel="apple-touch-icon" sizes="180x180" href="data:text/css;base64,' .
136          trim_lines(trim(encode_base64(read_file_contents($favicon_spath . '/apple-touch-icon.png')))) . '">' . "\n";
137        print ' <link ' . $ref_link . ' rel="icon" type="image/png" sizes="32x32" href="data:text/css;base64,' .
138          trim_lines(trim(encode_base64(read_file_contents($favicon_spath . '/favicon-32x32.png')))) . '">' . "\n";
139        print ' <link ' . $ref_link . ' rel="icon" type="image/png" sizes="192x192" href="data:text/css;base64,' .
140          trim_lines(trim(encode_base64(read_file_contents($favicon_spath . '/android-chrome-192x192.png')))) . '">' . "\n";
141        print ' <link ' . $ref_link . ' rel="icon" type="image/png" sizes="16x16" href="data:text/css;base64,' .
142          trim_lines(trim(encode_base64(read_file_contents($favicon_spath . '/favicon-16x16.png')))) . '">' . "\n";
143    }
144    print ' <meta name="msapplication-TileColor" content="' . $theme_user_color . '">' . "\n";
145    print ' <meta name="theme-color" content="' . $theme_user_color . '">' . "\n";
146    if (!$is_login_page) {
147
148        # Generate manifest file from template
149        my $display_hostname         = get_display_hostname();
150        my $manifest_product_name_uc = ucfirst($product_name);
151        my $manifest_product_name_uc_with_hostname =
152          $manifest_product_name_uc . ($display_hostname ? " on " . $display_hostname : "");
153        my %manifest_prod_descs = ('webmin'     => 'Powerful and flexible web-based server management control panel',
154                                   'usermin'    => 'Powerful and flexible web-based user management interface',
155                                   'virtualmin' => 'Powerful and flexible web hosting control panel',
156                                   'cloudmin'   => 'Powerful and flexible cloud computing platform');
157        my $manifest_desc     = $manifest_prod_descs{$product_name};
158        my $manifest_file     = "$root_directory/$current_theme/manifest.template";
159        my $manifest_contents = read_file_contents($manifest_file);
160        $manifest_contents =~ s/\%name\%/$manifest_product_name_uc_with_hostname/;
161        $manifest_contents =~ s/\%name_short\%/$manifest_product_name_uc/;
162        $manifest_contents =~ s/\%desc\%/$manifest_desc/;
163        $manifest_contents =~ s/\%prod\%/$product_name/g;
164        $manifest_contents =~ s/\%color\%/$theme_user_color/;
165        write_file_contents("$theme_config_dir/manifest-$product_name.json", $manifest_contents) if (-w $theme_config_dir);
166
167        print ' <script src="' . $theme_webprefix . '/service-worker.js" defer></script>' . "\n";
168        print ' <link ' . $ref_link . ' crossorigin="use-credentials" rel="manifest" href="' .
169          $theme_webprefix . '/manifest-' . $product_name . '.json">' . "\n";
170    }
171}
172
173sub embed_header
174{
175    my (@args) = @_;
176    my $charset = defined($main::force_charset) ? $main::force_charset : get_charset();
177
178    print "<!DOCTYPE html>\n";
179    print '<html ' . header_html_data(undef, undef, @args) . '>', "\n";
180    print '<head>', "\n";
181    embed_noscript();
182    print ' <meta charset="utf-8">', "\n";
183    embed_favicon() if (!http_x_request());
184    print ' <title>',
185      ( $args[4] ?
186          (get_product_name() eq 'usermin' ? $theme_text{'theme_xhred_titles_um'} : $theme_text{'theme_xhred_titles_wm'}) :
187          $args[0]
188      ),
189      '</title>', "\n";
190
191    print ' <meta name="viewport" content="width=device-width, initial-scale=1.0">' . "\n" if (!http_x_request());
192
193    ($args[1] && (print($args[1] . "\n")));
194
195    if (http_x_request()) {
196        print "</head>\n";
197        return;
198    }
199
200    # Print default options
201    print " <script src=\"$theme_webprefix/unauthenticated/js/defaults.js?" . theme_version(1) . "\"></script>\n";
202    print ' <script>';
203    print 'config_portable_theme_locale_languages="' . get_current_user_language(1) . '";';
204    print "</script>\n";
205
206    #
207    # Server statuses to JavaScript. Start.
208    # This code is only called once upon main page load
209    # and requires full page reload to have it re-applied
210    my $_sstj = sub {
211        my ($var, $sub, $mod, $jsFunc, $perlSubArgs) = @_;
212        my $quote_opening    = '"';
213        my $quote_closing    = $quote_opening;
214        my $quotes_resetting = sub {
215            $quote_opening = $quote_closing = '';
216        };
217        if ($jsFunc) {
218            &$quotes_resetting();
219        }
220        my $rs;
221        local $main::error_must_die = 1;
222        eval {
223            if ($mod && &foreign_available($mod)) {
224                &foreign_require($mod);
225                $rs = &foreign_call($mod, $sub);
226            } elsif (!$mod) {
227                $rs = &foreign_call('main', $sub);
228            }
229            if ($jsFunc) {
230                $rs = "$jsFunc($rs)";
231            } else {
232                if ($rs =~ /^[-+]?([\d]+|[\d]+.[\d]+)$/) {
233                    &$quotes_resetting();
234                }
235                if ($rs =~ /^(true|false|null|undefined)$/) {
236                    &$quotes_resetting();
237                }
238            }
239        };
240        if (!defined($rs)) {
241            $rs = "null";
242            &$quotes_resetting();
243        }
244        if (!length(trim($rs)) && !$quote_opening) {
245            $rs = "null";
246        }
247        return "$var=$quote_opening$rs$quote_closing;";
248    };
249    print ' <script>';
250    print &$_sstj('theme_server_data_available_acls', 'get_acls_status', 'filemin');
251    print &$_sstj('theme_server_data_available_selinux', 'is_selinux_enabled');
252    print "</script>\n";
253
254    # Server statuses to JavaScript. End.
255    #
256
257    embed_settings();
258    embed_tconfig();
259
260    # Print object with language strings
261    print ' <script>';
262    print 'var v___theme_language = ' . get_theme_language();
263    print "</script>\n";
264
265    if ($args[2]) {
266        do("$ENV{'THEME_ROOT'}/dependencies.pl");
267    }
268
269    if ($args[3] eq '1') {
270
271        if ($args[2]) {
272            foreach my $css (@theme_bundle_css) {
273                print ' <link href="' . $theme_webprefix .
274                  '/unauthenticated/css/' . $css . '.src.css?' . theme_version(1) . '" rel="stylesheet">' . "\n";
275            }
276            embed_css_fonts();
277        } else {
278            embed_css_bundle();
279        }
280
281        embed_css_night_rider();
282
283        embed_background();
284        embed_styles();
285
286        if ($args[2]) {
287            foreach my $js (@theme_bundle_js) {
288                if (sysstats_available() &&
289                    $js eq 'timeplot')
290                {
291                    next;
292                }
293
294                print ' <script src="' .
295                  $theme_webprefix . '/unauthenticated/js/' . $js . '.src.js?' . theme_version(1) . '"></script>' . "\n";
296            }
297        } else {
298            embed_js_bundle();
299        }
300    } else {
301        if ($args[2]) {
302            foreach my $css (@theme_bundle_css) {
303                print ' <link href="' . $theme_webprefix .
304                  '/unauthenticated/css/' . $css . '.src.css?' . theme_version(1) . '" rel="stylesheet">' . "\n";
305            }
306            embed_css_fonts();
307        } else {
308            embed_css_bundle();
309        }
310
311        embed_css_night_rider();
312
313        if ((length $theme_config{'settings_navigation_color'} && $theme_config{'settings_navigation_color'} ne 'blue') ||
314            theme_night_mode())
315        {
316            print ' <link href="' . $theme_webprefix . '/unauthenticated/css/palettes/' .
317              (theme_night_mode() ? 'gunmetal' : lc($theme_config{'settings_navigation_color'})) . '.' .
318              ($args[2]           ? 'src' : 'min') . '.css?' . theme_version(1) . '" rel="stylesheet" data-palette>' . "\n";
319
320        }
321
322        embed_background();
323        embed_styles();
324
325        if ($args[2]) {
326            foreach my $js (@theme_bundle_js) {
327
328                if (sysstats_available() &&
329                    $js eq 'timeplot')
330                {
331                    next;
332                }
333
334                print ' <script src="' .
335                  $theme_webprefix . '/unauthenticated/js/' . $js . '.src.js?' . theme_version(1) . '"></script>' . "\n";
336            }
337        } else {
338            embed_js_bundle();
339        }
340
341    }
342    embed_js_scripts();
343
344    # Head theme overlay
345    embed_overlay_head();
346
347    print '</head>', "\n";
348}
349
350sub embed_overlay_head
351{
352    print "$tconfig{'headhtml'}\n" if ($tconfig{'headhtml'});
353    if ($tconfig{'headinclude'}) {
354        my ($theme, $overlay) = split(' ', $gconfig{'theme'});
355        my $file_contents = read_file_contents("$root_directory/$overlay/$tconfig{'headinclude'}");
356        $file_contents = replace_meta($file_contents);
357        print $file_contents;
358    }
359}
360
361sub embed_overlay_prebody
362{
363    if (defined(&theme_prebody)) {
364        &theme_prebody(@_);
365    }
366    my $prebody = $tconfig{'prebody'};
367    if ($prebody) {
368        $prebody = replace_meta($prebody);
369        print "$prebody\n";
370    }
371    if ($tconfig{'prebodyinclude'}) {
372        my ($theme, $overlay) = split(' ', $gconfig{'theme'});
373        my $file_contents = read_file_contents("$root_directory/$overlay/$tconfig{'prebodyinclude'}");
374        $file_contents = replace_meta($file_contents);
375        print $file_contents;
376    }
377}
378
379sub embed_overlay_postbody
380{
381    my $postbody = $tconfig{'postbody'};
382    if ($postbody) {
383        $postbody = replace_meta($postbody);
384        print "$postbody\n";
385    }
386    if ($tconfig{'postbodyinclude'}) {
387        my ($theme, $overlay) = split(' ', $gconfig{'theme'});
388        my $file_contents = read_file_contents("$root_directory/$overlay/$tconfig{'postbodyinclude'}");
389        $file_contents = replace_meta($file_contents);
390        print $file_contents;
391    }
392    if (defined(&theme_postbody)) {
393        &theme_postbody(@_);
394    }
395}
396
397sub embed_settings
398{
399
400    my $admin_def_config_file = get_taconfig_file();
401    my $global_config_file    = get_tgconfig_file();
402    my $user_config_file      = get_tuconfig_file();
403
404    # Embed admin defaults
405    if (-r $admin_def_config_file) {
406        $admin_def_config_file = read_file_contents($admin_def_config_file);
407        $admin_def_config_file =~ tr/\r\n/;/d;
408        $admin_def_config_file =~ s/\s*(.*?=)'([\d\.]+)'(;)\s*/$1$2$3/g;
409        print ' <script>' . $admin_def_config_file . '</script>' . "\n";
410    }
411
412    # Embed global configuration
413    if (-r $global_config_file) {
414        $global_config_file = read_file_contents($global_config_file);
415        $global_config_file =~ tr/\r\n/;/d;
416        $global_config_file =~ s/\s*(.*?=)'([\d\.]+)'(;)\s*/$1$2$3/g;
417        print ' <script>' . $global_config_file . '</script>' . "\n";
418    }
419
420    # Embed user configuration
421    if (-r $user_config_file) {
422        $user_config_file = read_file_contents($user_config_file);
423        $user_config_file =~ tr/\r\n/;/d;
424        $user_config_file =~ s/\s*(.*?=)'([\d\.]+)'(;)\s*/$1$2$3/g;
425        print ' <script>' . $user_config_file . '</script>' . "\n";
426    }
427}
428
429sub embed_tconfig
430{
431    print ' <script>tconfig_beta_updates=' . ($tconfig{'beta_updates'} ne '1' ? 0 : 1) . '</script>' . "\n";
432}
433
434sub embed_styles
435{
436    if ($theme_config{'settings_contrast_mode'} eq 'true') {
437        print ' <link href="' .
438          $theme_webprefix . '/unauthenticated/css/high-contrast.' . (theme_debug_mode() ? 'src' : 'min') . '.css?' .
439          time() . '" rel="stylesheet" data-high-contrast>' . "\n";
440    }
441
442    my $css = $config_directory . "/$current_theme/styles.css";
443    if (-r $css && -s $css) {
444        print ' <link data-custom-style href="data:text/css;base64,' .
445          trim(encode_base64(read_file_contents($css))) . '" rel="stylesheet">' . "\n";
446    }
447
448}
449
450sub embed_background
451{
452    if ($ENV{'HTTP_X_REQUESTED_WITH'} eq "XMLHttpRequest") {
453        return;
454    }
455
456    my $background_type;
457
458    ((get_env('script_name') eq '/session_login.cgi' || get_env('script_name') eq '/pam_login.cgi') ?
459       ($background_type = 'content') :
460       ($background_type = 'aside'));
461
462    my $lnk = $config_directory . "/$current_theme/background_" . $background_type . ".png";
463    if (-r $lnk) {
464        my $background_base64 = encode_base64(read_file_contents($lnk));
465        my $background_css;
466        if ($background_type eq 'content') {
467            $background_css = <<EOF;
468              <style>
469              body.session_login {
470                background: url(data:image/png;base64,$background_base64) no-repeat center center fixed;
471                background-size: cover;
472              }
473
474              html.session_login .container:not(.form-signin-banner) {
475                background-color: transparent !important;
476              }
477              </style>
478EOF
479            $background_css =~ tr/\r\n//d;
480            $background_css =~ s/\s+/ /g;
481            print $background_css, "\n";
482        }
483    }
484}
485
486sub embed_pm_scripts
487{
488    my $scripts = "$config_directory/$current_theme/scripts.pl";
489    if (-r $scripts && -s $scripts) {
490        do($scripts);
491    }
492}
493
494sub embed_css_fonts
495{
496    print ' <link href="' . $theme_webprefix . '/unauthenticated/css/fonts-roboto.' .
497      (theme_debug_mode() ? 'src' : 'min') . '.css?' . theme_version(1) . '" rel="stylesheet">' . "\n";
498}
499
500sub embed_css_bundle
501{
502    print ' <link href="' .
503      $theme_webprefix . '/unauthenticated/css/bundle.min.css?' . theme_version(1) . '" rel="stylesheet">' . "\n";
504    embed_css_fonts();
505}
506
507sub embed_css_night_rider
508{
509    if (theme_night_mode_login() || theme_night_mode()) {
510        print ' <link href="' . $theme_webprefix . '/unauthenticated/css/palettes/nightrider.' .
511          (theme_debug_mode() ? 'src' : 'min') . '.css?' . theme_version(1) . '" rel="stylesheet" data-palette>' . "\n";
512    }
513}
514
515sub embed_js_timeplot
516{
517    print ' <script src="' . $theme_webprefix . '/unauthenticated/js/timeplot.' .
518      (theme_debug_mode() ? 'src' : 'min') . '.js?' . theme_version(1) . '"></script>' . "\n";
519}
520
521sub embed_js_bundle
522{
523    print ' <script src="' .
524      $theme_webprefix . '/unauthenticated/js/bundle.min.js?' . theme_version(1) . '"></script>' . "\n";
525}
526
527sub embed_js_scripts
528{
529
530    return if (http_x_request());
531
532    my $js = $config_directory . "/$current_theme/scripts.js";
533    if (-r $js && -s $js) {
534        $js = read_file_contents($js);
535        print ' <script data-custom-script>' . $js . '</script>' . "\n";
536    }
537}
538
539sub embed_noscript
540{
541    return if (http_x_request());
542    my $noscript = <<EOF;
543      <noscript>
544      <style>
545        html[data-bgs="gainsboro"]
546        {
547          background-color: #d6d6d6;
548        }
549        html[data-bgs="nightRider"]
550        {
551          background-color: #1a1c20;
552        }
553        html[data-bgs="nightRider"] div[data-noscript]
554        {
555          color: #979ba080;
556        }
557        html[data-slider-fixed='1']
558        {
559          margin-right: 0 !important;
560        }
561        body > div[data-noscript] ~ *
562        {
563            display: none !important;
564        }
565        div[data-noscript]
566        {
567            visibility: hidden;
568
569            animation: 2s noscript-fadein;
570            animation-delay: 1s;
571            text-align: center;
572
573            animation-fill-mode: forwards;
574        }
575        \@keyframes noscript-fadein
576        {
577            0%
578            {
579                opacity: 0;
580            }
581            100%
582            {
583                visibility: visible;
584
585                opacity: 1;
586            }
587        }
588      </style>
589        <div data-noscript>
590          <div class="fa fa-3x fa-exclamation-triangle margined-top-20 text-danger"></div>
591          <h2>$theme_text{'body_no_javascript_title'}</h2>
592          <p>$theme_text{'body_no_javascript_message'}</p>
593        </div>
594      </noscript>
595EOF
596
597    $noscript =~ tr/\r\n//d;
598    $noscript =~ s/\s+/ /g;
599    print $noscript, "\n";
600}
601
602sub embed_port_shell
603{
604    if (!@_ &&
605        get_env('script_name') ne '/session_login.cgi' &&
606        get_env('script_name') ne '/pam_login.cgi'     &&
607        get_env('script_name') ne '/401.cgi'           &&
608        get_env('script_name') ne '/403.cgi'           &&
609        get_env('script_name') ne '/404.cgi')
610    {
611        my $prefix;
612        my $hostname = ($prefix) = split(/\./, get_display_hostname());
613        my $host     = ($prefix ? $prefix : get_display_hostname());
614        print '<div data-autocomplete="' . (has_command('bash') ? 1 : 0) . '" class="-shell-port-">
615  <div class="-shell-port-container">
616    <div data-shell-config><i aria-label="' .
617          $theme_text{'theme_xhred_global_configuration'} . '" class="fa fa-lg fa-cogs"></i></div>
618    <div aria-label="' . $theme_text{'theme_xhred_global_close'} . '" class="-shell-port-close"></div>
619    <div data-output="true"><pre data-xconsole></pre></div>
620    <div class="-shell-port-cmd">
621      <span class="-shell-port-prompt"><span class="-shell-port-type">['
622          . $remote_user .
623          '@<span data-shell-host="' . $host . '">' . $host . '</span> <span class="-shell-port-pwd" data-home="' .
624          get_user_home() . '" data-pwd="' . get_user_home() . '">~</span>]' . ($get_user_level eq '0' ? '#' : '$') .
625'</span></span><input type="text" data-command="true" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false"><span class="-shell-port-cursor">&nbsp;</span>
626    </div>
627  </div>
628</div>', "\n";
629    }
630}
631
632sub embed_footer
633{
634    my (@args) = @_;
635    if (get_env('script_name') !~ /session_login.cgi/ &&
636        get_env('script_name') !~ /pam_login.cgi/     &&
637        get_env('script_name') !~ /password_form.cgi/ &&
638        get_env('script_name') !~ /password_change.cgi/)
639    {
640
641        # Load `MySQL/PostgreSQL` specific scripts
642        if (get_module_name() =~ /mysql/ ||
643            get_module_name() =~ /postgresql/)
644        {
645            print ' <script src="' . $theme_webprefix . '/extensions/sql.' .
646              ($args[0] ? 'src' : 'min') . '.js?' . theme_version(1) . '"></script>' . "\n";
647        }
648
649        # Load `File Manager` specific scripts
650        if (get_module_name() =~ /file-manager/ ||
651            get_module_name() =~ /filemin/)
652        {
653            print ' <script src="' . $theme_webprefix . '/extensions/file-manager/file-manager.' .
654              ($args[0] ? 'src' : 'min') . '.js?' . theme_version(1) . '"></script>' . "\n";
655        }
656
657    }
658}
659
660sub sysstats_available
661{
662    return ($http_x_url !~ /\/virtual-server\/pro\/history.cgi/ &&
663            $http_x_url !~ /\/server-manager\/bwgraph.cgi/ &&
664            $http_x_url !~ /\/server-manager\/history.cgi/ &&
665            $http_x_url !~ /\/server-manager\/one_history.cgi/) ?
666      1 :
667      0;
668}
669
670sub theme_text
671{
672
673    my $rv = $theme_text{ $_[0] };
674    $rv =~ s/\$(\d+)/$1 < @_ ? $_[$1] : '$'.$1/ge;
675    return $rv;
676}
677
678sub init_vars
679{
680    if (theme_debug_mode()) {
681        do("$root_directory/$current_theme/.debug.pl");
682    }
683
684    our %theme_config = (settings(get_tdconfig_file()),
685                         settings(get_taconfig_file()),
686                         settings(get_tgconfig_file(), "settings_"),
687                         settings(get_tuconfig_file(), "settings_"));
688    our $http_x_url =
689      (get_env('http_x_pjax_url') || get_env('http_x_progressive_url'));
690
691    # Load theme language
692    our %theme_text = (load_language($current_theme), %text);
693
694    # Load other modules language strings conditionally
695    if (!http_x_request() ||
696        ($http_x_url =~ /sysinfo\.cgi/ || grep {/xhr-info/} keys %in))
697    {
698        my @text_mods = ("virtual-server", "server-manager");
699        foreach my $mod (@text_mods) {
700            if (foreign_available($mod)) {
701                %theme_text = (load_language($mod), %theme_text);
702            }
703        }
704    }
705
706    our ($get_user_level, $has_virtualmin, $has_cloudmin) = get_user_level();
707    our ($has_usermin, $has_usermin_version, $has_usermin_root_dir, $has_usermin_conf_dir) = get_usermin_vars();
708
709    # Set webprefix that should be used by the theme
710    $theme_webprefix = $gconfig{'webprefix'};
711    my ($server_webprefix) = parse_remote_server_webprefix();
712    if ($server_webprefix) {
713        $theme_webprefix        = $server_webprefix;
714        $theme_server_webprefix = 1;
715    }
716
717    our $xnav = "xnavigation=1";
718
719    our %gaccess = &get_module_acl();
720    our $title   = &get_html_framed_title();
721    our %cookies = get_cookies();
722
723    $server_x_goto = get_theme_temp_data('goto');
724
725}
726
727sub check_pro_package
728{
729    my ($id) = @_;
730    if (&foreign_available("virtual-server") && $id eq "vm") {
731        my %virtualmin = &get_module_info("virtual-server");
732        if ($virtualmin{'version'} =~ /pro/is) {
733            return 1;
734        } elsif ($virtualmin{'version'} =~ /gpl/is) {
735            return 0;
736        } else {
737            return 1;
738        }
739    } elsif (&foreign_available("server-manager") && $id eq "cm") {
740        my %cloudmin = &get_module_info("server-manager");
741        if ($cloudmin{'version'} =~ /pro/is || $cloudmin{'version'} =~ /real/is) {
742            return 1;
743        } elsif ($cloudmin{'version'} =~ /\..*?\./is) {
744            return 0;
745        } else {
746            return 1;
747        }
748    } else {
749        return 0;
750    }
751}
752
753sub get_usermin_vars
754{
755    my ($has_usermin, $has_usermin_version, $has_usermin_root_dir, $has_usermin_conf_dir);
756    eval {
757        if (&foreign_exists("usermin")) {
758            &foreign_require("usermin");
759            my %uminiserv;
760            &usermin::get_usermin_miniserv_config(\%uminiserv);
761
762            # Usermin config dir
763            $has_usermin_conf_dir = $uminiserv{'env_WEBMIN_CONFIG'};
764
765            # Usermin root dir
766            $has_usermin_root_dir = $uminiserv{'root'};
767
768            # Usermin version
769            $has_usermin_version = $uminiserv{'server'};
770            $has_usermin_version =~ /\/([\d\.]+)/;
771            $has_usermin_version = "$1";
772            if (length($has_usermin_version) > 6) {
773                $has_usermin_version =
774                  substr($has_usermin_version, 0, 5) . "." .
775                  substr($has_usermin_version, 5, 5 - 1) . "." .
776                  substr($has_usermin_version, 5 * 2 - 1);
777            }
778
779            # Usermin installed
780            $has_usermin = -r $has_usermin_root_dir;
781
782            # Usermin Authentic Theme config dir exists
783            my $theme_conf_usermin = "$has_usermin_conf_dir/$current_theme";
784            if ($has_usermin &&
785                $has_usermin_conf_dir &&
786                !-d $theme_conf_usermin)
787            {
788                mkdir($theme_conf_usermin, 0755);
789            }
790        }
791    };
792    return ($has_usermin, $has_usermin_version, $has_usermin_root_dir, $has_usermin_conf_dir);
793}
794
795sub get_current_user_language
796{
797    my ($full) = @_;
798    my $language;
799    my @languages;
800    my $language_browser = $gconfig{'acceptlang'};
801
802    if ($language_browser) {
803        $language = $ENV{'HTTP_ACCEPT_LANGUAGE'};
804        $language =~ s/;.*//;
805        @languages = split /,/, $language;
806        $language  = $languages[0];
807    }
808
809    if (($language_browser && !$language) || !$language_browser) {
810        $language = $gconfig{ 'lang' . '_' . $remote_user };
811        $language = ($language ? $language : $gconfig{'lang'});
812    }
813
814    $language = substr($language, 0, ($full ? 5 : 2));
815    $language =~ s/\..*//;
816    $language =~ s/_/-/;
817    return lc($language);
818}
819
820sub get_filters
821{
822    return 'filter: grayscale(' . $theme_config{'settings_grayscale_level_navigation'} .
823      ') ' . 'sepia(' . $theme_config{'settings_sepia_level_navigation'} .
824      ')' . ' saturate(' . $theme_config{'settings_saturation_level_navigation'} .
825      ') hue-rotate(' . $theme_config{'settings_hue_level_navigation'} .
826      'deg)' . ' invert(' . $theme_config{'settings_invert_level_navigation'} .
827      ') brightness(' . $theme_config{'settings_brightness_level_navigation'} .
828      ') contrast(' . $theme_config{'settings_contrast_level_navigation'} . ')' . ';';
829}
830
831sub get_user_level
832{
833    my ($level, $has_virtualmin, $has_cloudmin);
834    $has_cloudmin   = &foreign_available("server-manager");
835    $has_virtualmin = &foreign_available("virtual-server");
836    if ($has_cloudmin) {
837        &foreign_require("server-manager", "server-manager-lib.pl");
838    }
839    if ($has_virtualmin) {
840        &foreign_require("virtual-server", "virtual-server-lib.pl");
841    }
842    if ($has_cloudmin) {
843        no warnings 'once';
844        $level = $server_manager::access{'owner'} ? 4 : 0;
845    } elsif ($has_virtualmin) {
846        $level =
847          &virtual_server::master_admin()   ? 0 :
848          &virtual_server::reseller_admin() ? 1 :
849          2;
850    } elsif (&get_product_name() eq "usermin") {
851        $level = 3;
852    } else {
853        $level = 0;
854    }
855    return ($level, $has_virtualmin, $has_cloudmin);
856}
857
858sub get_user_icon
859{
860    my $user_icon = 'fa2-user-cog';
861    if ($get_user_level eq '1') {
862        $user_icon = 'fa2-user-plus';
863    } elsif ($get_user_level eq '2') {
864        $user_icon = 'fa2-user-check';
865    } elsif ($get_user_level eq '3') {
866        $user_icon = 'fa2-user';
867    } elsif ($get_user_level eq '4') {
868        $user_icon = 'fa2-user-friends';
869    }
870    return $user_icon;
871}
872
873sub set_user_level
874{
875    if ($get_user_level ne '0' && $get_user_level ne '1') {
876        switch_to_remote_user();
877    }
878}
879
880sub get_initial_wizard
881{
882    # Prevent running Virtualmin post installation wizard
883    my $mod_vm = 'virtual-server';
884    if ($get_user_level eq '0' && foreign_exists($mod_vm)) {
885        my %virtualmin_config = foreign_config($mod_vm);
886        return $virtualmin_config{'wizard_run'};
887    }
888    return 1;
889}
890
891sub get_button_style
892{
893
894    my $label = quote_escape(@_);
895
896    my %module_text = module_text_full();
897    my (@keys) = grep {$module_text{$_} eq $label} keys %module_text;
898
899    my $keys = "@keys";
900    my $icon;
901    my $class = "default";
902
903    if (string_contains($keys, 'mail_fchange')) {
904        $class = "default";
905    } elsif (string_contains($keys, 'sform_ok')) {
906        $icon  = "search";
907        $class = "info";
908    } elsif (string_contains($keys, 'edit_createnow') || string_contains($keys, 'edit_savenow')) {
909        $icon = "backup fa-1_25x";
910    } elsif (string_contains($keys, "pass_ok")) {
911        $icon  = " fa2 fa2-key";
912        $class = "warning ";
913    } elsif (string_contains($keys, "newips")) {
914        $icon = "pencil-square-o";
915    } elsif (string_contains($keys, "docker_reg")) {
916        $class = "success ";
917        $icon  = "server-add";
918    } elsif (string_contains($keys, "save") ||
919             string_contains($keys, "backup_ok2")    ||
920             string_contains($keys, "sharedips_ok")  ||
921             string_contains($keys, "categories_ok") ||
922             string_contains($keys, "frame_ok")      ||
923             string_contains($keys, "newquotas_ok")  ||
924             string_contains($keys, "newdynip_ok"))
925    {
926        $class = "success ";
927        $icon  = "check-circle";
928    } elsif (string_contains($keys, "form_ok")) {
929        $class = "success ";
930        $icon  = "check-circle";
931    } elsif (string_contains($keys, "apply")) {
932        $class = "info ";
933        $icon  = "check-circle-o";
934    } elsif (string_contains($keys, "migrate_show") || string_contains($keys, "import_show")) {
935        $class = "success ";
936        $icon  = " fa2 fa2-import";
937    } elsif (string_contains($keys, "update") ||
938             string_contains($keys, "index_sync"))
939    {
940        $class = "info ";
941        $icon  = "refresh";
942    } elsif ((string_contains($keys, "delete") && !string_contains($keys, "users_delete")) ||
943             string_contains($keys, "wipe")          ||
944             string_contains($keys, "ddrop_ok")      ||
945             string_contains($keys, "dbs_dok")       ||
946             string_contains($keys, "tprivs_dok")    ||
947             string_contains($keys, "hosts_dok")     ||
948             string_contains($keys, "cprivs_dok")    ||
949             string_contains($keys, "dbase_drop")    ||
950             string_contains($keys, "ddrop_title")   ||
951             string_contains($keys, "dbase_delete2") ||
952             string_contains($keys, "table_drop")    ||
953             string_contains($keys, "tdrop_title")   ||
954             string_contains($keys, "tdrop_ok")      ||
955             string_contains($keys, "index_drops")   ||
956             string_contains($keys, "delq_confirm")  ||
957             string_contains($keys, "umass_del2")    ||
958             string_contains($keys, "index_gmass")   ||
959             string_contains($keys, "master_del")    ||
960             string_contains($keys, "newstyles_del") ||
961             string_contains($keys, "html_dtitle"))
962    {
963        $class = "danger ";
964
965        $icon = "times-circle";
966    } elsif (string_contains($keys, "disable_ok")) {
967        $class = "warning ";
968        $icon  = "lock";
969    } elsif (string_contains($keys, "enable_ok")) {
970        $class = "success ";
971        $icon  = "unlock";
972    } elsif (string_contains($keys, "twofactor_enable")) {
973        $class = "info ";
974        $icon  = "lock";
975    } elsif (string_contains($keys, "twofactor_disable")) {
976        $class = "warning ";
977        $icon  = "unlock";
978    } elsif (string_contains($keys, "zonekey")) {
979        if ($keys eq "zonekey_disable") {
980            $class = "danger ";
981            $icon  = " fa2 fa2-key-minus fa-1_15x";
982        } else {
983            $icon = " fa2 fa2-key";
984        }
985    } elsif (
986             (string_contains($keys, "install")     ||
987              string_contains($keys, "recsok")      ||
988              string_contains($keys, "scripts_iok") ||
989              string_contains($keys, "missing_now") ||
990              string_contains($keys, "right_upok")
991             ) &&
992             !string_contains($keys, "uninstall"))
993    {
994        $class = "success ";
995        $icon  = "package-install fa-1_25x";
996    } elsif (string_contains($keys, "uninstall") ||
997             string_contains($keys, "edit_uninst") ||
998             string_contains($keys, "scripts_uok") ||
999             string_contains($keys, "drecs_ok"))
1000    {
1001        $class = "danger ";
1002        $icon  = "times-circle-o";
1003    } elsif (string_contains($keys, "cert_remove")) {
1004        $class = "warning ";
1005        $icon  = " fa2 fa2-certificate-delete";
1006    } elsif (string_contains($keys, "cert_copyall2")) {
1007        $class = "info ";
1008        $icon  = " fa2 fa2-certificate-global";
1009    } elsif (string_contains($keys, "cert_copyall")) {
1010        $class = "info ";
1011        $icon  = " fa2 fa2-certificate-add";
1012    } elsif (string_contains($keys, "upgrade") ||
1013             string_contains($keys, "massg_ok") ||
1014             string_contains($keys, "massscript_ok"))
1015    {
1016        $class = "info ";
1017        $icon  = "update";
1018    } elsif (string_contains($keys, "index_srefresh")) {
1019        $icon = "user-md";
1020    } elsif (string_contains($keys, "quota")) {
1021        $icon = "pie-chart";
1022    } elsif (string_contains($keys, "addboot") ||
1023             string_contains($keys, "enable") ||
1024             string_contains($keys, "massdomains_enaok"))
1025    {
1026        $icon = "toggle-switch  fa-1_25x";
1027    } elsif (string_contains($keys, "shutdown")) {
1028        $icon = "power-off";
1029    } elsif (string_contains($keys, "index_shut")) {
1030        $icon  = "power-off";
1031        $class = "danger ";
1032    } elsif (string_contains($keys, "index_reboot reboot_title")) {
1033        $icon  = "refresh-mdi fa-1_25x";
1034        $class = "warning ";
1035    } elsif (string_contains($keys, "docker_reg")) {
1036        $icon = "check-circle-o";
1037    } elsif (string_contains($keys, "tmpl_nprev") || string_contains($keys, "wizard_prev")) {
1038        $icon = "arrow-circle-o-left";
1039    } elsif (string_contains($keys, "tmpl_nnext") ||
1040             string_contains($keys, "wizard_next") ||
1041             string_contains($keys, "tmpl_cnext")  ||
1042             string_contains($keys, "tmpl_snext")  ||
1043             string_contains($keys, "continue")    ||
1044             string_contains($keys, "download_cont"))
1045    {
1046        $icon = "arrow-circle-o-right";
1047        if (string_contains($keys, "continue")) {
1048            $class = "success ";
1049        }
1050    } elsif (string_contains($keys, "cancel")) {
1051        $icon = "times-circle-o";
1052    } elsif (string_contains($keys, "ticket_submit")) {
1053        $icon = "question-circle";
1054    } elsif (string_contains($keys, "trace_change")) {
1055        $icon  = " fa2 fa2-toggle-off  fa-0_90x";
1056        $class = "warning ";
1057    } elsif (string_contains($keys, "passwd_change")) {
1058        $icon  = "key-li";
1059        $class = "warning ";
1060    } elsif (string_contains($keys, "nf_seen")) {
1061        $icon = "clear-all fa-1_25x";
1062    } elsif (string_contains($keys, "history_ok")) {
1063        $icon = "area-chart";
1064    } elsif (string_contains($keys, "edit_open") || string_contains($keys, "edit_list")) {
1065        $icon = "files-o";
1066    } elsif (string_contains($keys, "reboot") ||
1067             string_contains($keys, "view_refresh") ||
1068             string_contains($keys, "refreshmods")  ||
1069             string_contains($keys, "index_buttinit"))
1070    {
1071        if (string_contains($keys, "refreshmods")) {
1072            $class = "primary ";
1073        } elsif (!string_contains($keys, "reboot_ok") && !string_contains($keys, "index_reboot") ||
1074                 string_contains($keys, "index_buttinit"))
1075        {
1076            $class = "warning ";
1077        }
1078
1079        if (string_contains($keys, "view_refresh")) {
1080            $icon = "refresh-fi fa-1_25x";
1081        } else {
1082            $icon = "refresh-mdi fa-1_25x";
1083        }
1084    } elsif (string_contains($keys, "search") ||
1085             string_contains($keys, "index_broad")    ||
1086             string_contains($keys, "scripts_findok") ||
1087             string_contains($keys, "kill_title"))
1088    {
1089        $class = "info ";
1090        $icon  = "search";
1091    } elsif (string_contains($keys, "dmass_move") || string_contains($keys, "domains_move")) {
1092        $class = "warning ";
1093        $icon  = " fa2 fa2-transfer";
1094    } elsif (string_contains($keys, "restart") || string_contains($keys, "edit_kill")) {
1095        $class = "warning ";
1096        $icon  = "refresh";
1097    } elsif (string_contains($keys, "ddrop_empty")) {
1098        $class = "warning ";
1099        $icon  = "times-circle-o";
1100    } elsif (string_contains($keys, "start") || string_contains($keys, "index_run")) {
1101        $class = "success ";
1102        $icon  = "play";
1103    } elsif (string_contains($keys, "index_stop") ||
1104             string_contains($keys, "edit_stopnow"))
1105    {
1106        $class = "danger ";
1107        $icon  = "stop";
1108    } elsif (string_contains($keys, "ok_ok")) {
1109        $icon  = "check-square-o";
1110        $class = "success ";
1111    } elsif (string_contains($keys, "index_delboot")) {
1112        $class = "grey ";
1113        $icon  = "toggle-switch-off fa-1_25x";
1114    } elsif (string_contains($keys, "index_refsel") ||
1115             string_contains($keys, "index_reset") ||
1116             string_contains($keys, "index_regen") ||
1117             string_contains($keys, "index_reload"))
1118    {
1119        $class = "warning ";
1120        $icon  = "refresh";
1121    } elsif (string_contains($keys, "scripts_ustop")) {
1122        $icon = "stop";
1123    } elsif (string_contains($keys, "index_script")) {
1124        $icon = "update";
1125    } elsif (string_contains($keys, "cron_ok")) {
1126        $icon  = "check-circle-o";
1127        $class = "success ";
1128    } elsif (string_contains($keys, "status")) {
1129        $icon = "info-circle";
1130    } elsif (string_contains($keys, "warnok")) {
1131        $icon  = "check-circle-o";
1132        $class = "warning ";
1133    } elsif (string_contains($keys, "index_clear") || string_contains($keys, "shell_clear")) {
1134        $icon = "history";
1135    } elsif (string_contains($keys, "index_clearcmds") || string_contains($keys, "shell_clearcmds")) {
1136        $icon = "broom fa-1_25x";
1137    } elsif (string_contains($keys, "index_boot") ||
1138             string_contains($keys, "index_bootup")      ||
1139             string_contains($keys, "index_atboot")      ||
1140             string_contains($keys, "massdomains_disok") ||
1141             string_contains($keys, "disable"))
1142    {
1143        $icon = "toggle-switch-off fa-1_25x";
1144    } elsif (string_contains($keys, "index_global") ||
1145             string_contains($keys, "umass_ok")    ||
1146             string_contains($keys, "vars_edit")   ||
1147             string_contains($keys, "lusers_mass") ||
1148             string_contains($keys, "root_ok")     ||
1149             string_contains($keys, "index_edit"))
1150    {
1151        $class = "primary ";
1152        $icon  = "pencil-square-o";
1153    } elsif (string_contains($keys, "clone")) {
1154        $icon = "clone";
1155    } elsif (string_contains($keys, "index_tmpls")) {
1156        $icon = "table-edit fa-1_25x";
1157    } elsif (string_contains($keys, "index_sched") ||
1158             string_contains($keys, "sched_title"))
1159    {
1160        if (string_contains($keys, "sched_title")) {
1161            $class = "primary ";
1162        }
1163        $icon = "clock";
1164    } elsif (string_contains($keys, "uedit_mail") || string_contains($keys, "newnotify_ok")) {
1165        $icon = "envelope-o";
1166    } elsif (string_contains($keys, "reply_send")) {
1167        $icon  = "send";
1168        $class = "success ";
1169    } elsif (string_contains($keys, "sendmail")) {
1170        $icon  = "envelope-o";
1171        $class = "info ";
1172    } elsif (string_contains($keys, "uedit_swit") || string_contains($keys, "user_switch")) {
1173        $icon = "webmin";
1174    } elsif (string_contains($keys, "uedit_logins") ||
1175             string_contains($keys, "index_logins") ||
1176             string_contains($keys, "login_enable"))
1177    {
1178        $icon = "key";
1179    } elsif (string_contains($keys, "index_who")) {
1180        $icon = "sign-in";
1181    } elsif (string_contains($keys, "dbase_add") || string_contains($keys, "databases_import")) {
1182        $class = "success ";
1183        $icon  = "database-plus fa-1_25x";
1184    } elsif (
1185        (string_contains($keys, "add") && !string_contains($keys, "dbase_addview") && !string_contains($keys, "edit_addinc"))
1186        ||
1187        (string_contains($keys, "create") &&
1188            !string_contains($keys, "user_priv_create_view")) ||
1189        string_contains($keys, "index_crnow") ||
1190        string_contains($keys, "view_new")    ||
1191        string_contains($keys, "mass_ok")     ||
1192        string_contains($keys, "rmass_ok"))
1193    {
1194        $class = "success ";
1195        $icon  = "plus-circle";
1196    } elsif (string_contains($keys, "force_title") ||
1197             string_contains($keys, "index_force"))
1198    {
1199        $class = "warning ";
1200        $icon  = "rotate-3d fa-1_25x margined-left--3 margined-right--3";
1201    } elsif (string_contains($keys, "csv")) {
1202        $icon = "export";
1203    } elsif (string_contains($keys, "restore")) {
1204        $icon = "restore fa-1_25x";
1205    } elsif (string_contains($keys, "backup_title") ||
1206             string_contains($keys, "dbase_backup") ||
1207             string_contains($keys, "index_dump")   ||
1208             string_contains($keys, "backup_ok")    ||
1209             string_contains($keys, "export")       ||
1210             string_contains($keys, "backup_now"))
1211    {
1212        $icon = "backup fa-1_25x";
1213    } elsif (string_contains($keys, "dbase_exec") ||
1214             string_contains($keys, "exec_exec")         ||
1215             string_contains($keys, "user_priv_execute") ||
1216             string_contains($keys, "exec_title")        ||
1217             string_contains($keys, "exec_tabexec"))
1218    {
1219        $icon = "database";
1220    } elsif (string_contains($keys, "create_view") ||
1221             string_contains($keys, "addview") ||
1222             string_contains($keys, "view_title1"))
1223    {
1224        $icon = "list";
1225    } elsif (string_contains($keys, "table_data")) {
1226        $icon = "database-outline";
1227    } elsif (string_contains($keys, "index_title1") || string_contains($keys, "table_index")) {
1228        $icon = "key-plus fa-1_25x";
1229    } elsif (string_contains($keys, "transfer_transferok")) {
1230        $icon = "transform fa-1_25x";
1231    } elsif (string_contains($keys, "transfer_uploadok") ||
1232             string_contains($keys, "transfer_tabupload") ||
1233             string_contains($keys, "html_uploadok"))
1234    {
1235        $class = "primary ";
1236        $icon  = "upload";
1237    } elsif (string_contains($keys, "index_down") ||
1238             string_contains($keys, "transfer_downloadok") ||
1239             string_contains($keys, "index_up")            ||
1240             string_contains($keys, "download_need"))
1241    {
1242        $class = "primary ";
1243        $icon  = "download";
1244    } elsif (string_contains($keys, "images_get")) {
1245        $class = "success ";
1246        $icon  = "download";
1247    } elsif (string_contains($keys, "umass_del1") ||
1248             string_contains($keys, "gdel_del")    ||
1249             string_contains($keys, "gdel_title")  ||
1250             string_contains($keys, "drecs_title") ||
1251             string_contains($keys, "rdmass_ok"))
1252    {
1253        $icon = "times-circle-o";
1254    } elsif (string_contains($keys, "users_dok") ||
1255             string_contains($keys, "users_delete") ||
1256             string_contains($keys, "users_dconfirm"))
1257    {
1258        $class = "danger ";
1259        $icon  = "user-times";
1260    } elsif (string_contains($keys, "index_mass2")) {
1261        $class = "warning ";
1262        $icon  = "toggle-switch  fa-1_25x";
1263    } elsif (string_contains($keys, "index_mass3")) {
1264        $class = "success ";
1265        $icon  = "toggle-switch-off  fa-1_25x";
1266    } elsif (string_contains($keys, "lang")) {
1267        $icon  = "globe";
1268        $class = "warning ";
1269    } elsif (string_contains($keys, "_ok")) {
1270        $icon  = "check-circle-o";
1271        $class = "success ";
1272    } elsif (string_contains($keys, "_change") &&
1273             !string_contains($keys, "edit_change") &&
1274             !string_contains($keys, "trace_change"))
1275    {
1276        $class = "warning ";
1277        $icon  = "pencil-square-o";
1278    } elsif (string_contains($keys, "lkeys_sok2") ||
1279             string_contains($keys, "mail_login"))
1280    {
1281        $class = "success ";
1282        $icon  = "key";
1283    } elsif (string_contains($keys, "letsencrypt_title") ||
1284             string_contains($keys, "ssl_copycert"))
1285    {
1286        $class = "success ";
1287        $icon  = " fa2 fa2-certificate-request";
1288    } elsif (string_contains($keys, "cert_letsonly")) {
1289        $icon  = " fa2 fa2-certificate-update-time";
1290        $class = "info ";
1291    } elsif (string_contains($keys, "index_tree")) {
1292        $icon = "tree";
1293    }
1294
1295    if ($icon) {
1296        $icon = "<i class=\"fa fa-fw fa-$icon\"></i>";
1297    }
1298
1299    return ($keys, $class, $icon);
1300}
1301
1302sub theme_night_mode
1303{
1304    if ($theme_config{'settings_force_night_mode'} eq '1') {
1305        return 1;
1306    } else {
1307        return 0;
1308    }
1309}
1310
1311sub theme_night_mode_login
1312{
1313    if ($theme_config{'settings_global_palette_unauthenticated'} eq 'dark') {
1314        return 1;
1315    } else {
1316        return 0;
1317    }
1318}
1319
1320sub theme_version
1321{
1322    my ($string, $minor) = @_;
1323    my %tinfo        = get_theme_info($current_theme);
1324    my $version      = $tinfo{'version'};
1325    my $mversion     = $tinfo{'mversion'};
1326    my $is_alpha     = string_contains($version, 'alpha');
1327    my $is_beta      = string_contains($version, 'beta');
1328    my $is_rc        = string_contains($version, 'RC');
1329    my $is_devel_ver = $is_alpha || $is_beta || $is_rc;
1330
1331    # Return minor version only
1332    if ($minor) {
1333
1334        # Format minor version suffix
1335        if ($string) {
1336            $mversion = int($mversion);
1337            if ($mversion > 1) {
1338                $mversion = "-$mversion";
1339            } else {
1340                $mversion = undef;
1341            }
1342        }
1343        return $mversion;
1344    }
1345
1346    # Return theme version as timestamp
1347    if ($string) {
1348        $version =~ s/(alpha|beta|RC)\d*|\.|-//ig;
1349        if (theme_debug_mode() || $is_devel_ver) {
1350            $version .= ("9" . time() . "$mversion");
1351        } else {
1352            $version .= ('99999999999' . $mversion);
1353        }
1354    }
1355    return $version;
1356}
1357
1358sub theme_debug_mode
1359{
1360    if (-r "$ENV{'THEME_ROOT'}/dependencies.pl") {
1361        return 1;
1362    } else {
1363        return 0;
1364    }
1365}
1366
1367sub theme_post_update
1368{
1369    my $update = $root_directory . "/$current_theme/update";
1370
1371    if (-f $update && $get_user_level eq '0') {
1372        unlink $update;
1373        return '1';
1374    } else {
1375        return '0';
1376    }
1377}
1378
1379sub header_html_data
1380{
1381    my ($module, $skip, @args) = @_;
1382    return 'data-redirect="' . get_theme_temp_data('redirected') .
1383      '" data-host="' . get_env('http_host') . '" data-hostname="' . get_display_hostname() . '" data-title-initial="' .
1384      format_document_title($args[0]) . '" data-debug="' . theme_debug_mode() . '" data-session="' .
1385      ($remote_user ? '1' : '0') . '" data-script-name="' . ($module ? "/$module/" : get_env('script_name')) .
1386      '"' . ($skip ? '' : ' data-bgs="' . (theme_night_mode() ? 'nightRider' : 'gainsboro') . '"') .
1387      '' .  ($skip ? '' : ' data-night-mode="' . theme_night_mode() . '"') .
1388      ' data-high-contrast="' .         ($theme_config{'settings_contrast_mode'} eq 'true'              ? '1' : '0') .
1389      '" data-navigation-collapsed="' . ($theme_config{'settings_navigation_always_collapse'} eq 'true' ? '1' : '0') .
1390      '" data-slider-fixed="' .         ($theme_config{'settings_side_slider_fixed'} eq "true" &&
1391                                 $get_user_level eq '0' &&
1392                                 $theme_config{'settings_side_slider_enabled'} ne "false" ? '1' : '0') .
1393      '" data-shell="' .
1394      foreign_available("shell") . '" data-webmin="' . foreign_available("webmin") . '" data-usermin="' . $has_usermin .
1395      '" data-navigation="' . ($args[3] eq '1' ? '0' : '1') . '" data-status="' . foreign_available("system-status") .
1396      '" data-package-updates="' . foreign_available("package-updates") . '" data-csf="' . foreign_available("csf") . '"' .
1397      ($skip ? '' : ' data-theme="' . (theme_night_mode() ? 'gunmetal' : $theme_config{'settings_navigation_color'}) . '"')
1398      . '' . ($skip ? '' : ' data-default-theme="' . $theme_config{'settings_navigation_color'} . '"') .
1399      ' data-editor-palette="' . $theme_config{'settings_cm_editor_palette'} .
1400      '" data-theme-version="' . theme_version(0) . '" data-theme-mversion="' . theme_version(0, 1) .
1401      '"  data-level="' . $get_user_level . '" data-user-home="' . get_user_home() . '" data-user-id="' . get_user_id() .
1402      '" data-user="' . $remote_user . '" data-ltr="' . get_text_ltr() . '" data-language="' . get_current_user_language() .
1403      '" data-language-full="' . get_current_user_language(1) . '" data-charset="' . get_charset() . '" data-notice="' .
1404      theme_post_update() . '" data-initial-wizard="' . get_initial_wizard() . '" data-webprefix="' . $theme_webprefix .
1405      '" data-current-product="' . get_product_name() . '" data-module="' . ($module ? "$module" : get_module_name()) .
1406      '" data-uri="' .      ($module ? "/$module/" : html_escape(un_urlize(get_env('request_uri'), 1))) .
1407      '" data-progress="' . ($theme_config{'settings_hide_top_loader'} ne 'true' ? '1' : '0') . '" data-product="' .
1408      get_product_name() . '" data-access-level="' . $get_user_level . '" data-time-offset="' . get_time_offset() . '"';
1409}
1410
1411sub header_body_data
1412{
1413    my ($module) = @_;
1414    return 'data-uri="' . ($module ? "/$module/" : html_escape(get_env('request_uri'))) . '"'
1415      .
1416      ( (get_module_name() || $module) ?
1417          ' class="' .
1418          ($module ? $module : get_module_name()) . '" data-module="' . ($module ? $module : get_module_name()) . '"' :
1419          undef
1420      ) .
1421      '' .
1422      (get_env('request_uri') =~ /\/config.cgi\?/ ? ' id="configCGI"' : '') . '';
1423}
1424
1425sub get_version
1426{
1427    my ($version) = @_;
1428    return $version =~ /([0-9]+[.][0-9]+)/;
1429}
1430
1431sub get_version_range
1432{
1433    my ($start, $end, $step) = @_;
1434    $step ||= 1;
1435    return map {sprintf("%.2f", $_ * $step)} (sprintf("%.2f", $start / $step) .. sprintf("%.2f", $end / $step));
1436
1437}
1438
1439sub get_version_link
1440{
1441    my ($version, $type) = @_;
1442
1443    if ($version =~ /\.\.\./) {
1444        ($version) = $version =~ /([0-9]+[.][0-9]+\.\.\.[0-9]+[.][0-9]+)/;
1445        my $links_start = '<div class="pull-right versionSeparatorContainer">';
1446        my $links_end   = '</div>';
1447        my @versions    = split(/\.\.\./, $version);
1448
1449        if ($type eq '1' || $type eq '2') {
1450            return "$links_start <span class=\"versionSeparator\">$version</span> $links_end";
1451        }
1452
1453        @versions = get_version_range($versions[0], $versions[1], 0.01);
1454        foreach my $version (@versions) {
1455            $links_start .= get_version_link($version) . " ";
1456        }
1457        $links_start .= $links_end;
1458        return $links_start;
1459    }
1460
1461    if ($type eq '2') {
1462        return "<span class=\"versionSeparator\">" . get_version_full($version, 1) . "</span>";
1463    } else {
1464        return '<a href="https://github.com/authentic-theme/authentic-theme/releases/tag/' .
1465          get_version_full($version) . '" class="versionSeparator">' . get_version_full($version, 1) . '</a>';
1466    }
1467
1468}
1469
1470sub get_version_full
1471{
1472    my ($version, $beta) = @_;
1473    ($version) = $version =~ /([0-9]+[.][0-9]+(?:.\d+|-alpha[\d]+|-beta[\d]+|-RC[\d]+|))/;
1474
1475    if ($version =~ /alpha|beta|RC/ && $beta) {
1476        return undef;
1477    }
1478
1479    return $version;
1480}
1481
1482sub get_env
1483{
1484    my ($key) = @_;
1485    return $ENV{ uc($key) };
1486}
1487
1488sub set_theme_temp_data
1489{
1490    my ($key, $value) = @_;
1491    my $salt = substr(encode_base64($main::session_id), 0, 16);
1492    my %var;
1493
1494    $salt =~ tr/A-Za-z0-9//cd;
1495    $key  =~ tr/A-Za-z0-9//cd;
1496
1497    $value =~ s/[?|&]$xnav//g;
1498    $value =~ s/[?|&]randomized=[\d]+//g;
1499    $value =~ s/.cgi&/.cgi?/g;
1500    $value =~ s/[^\p{L}\p{N},;:.%&#=_@\+\?\-\/]//g;
1501
1502    $var{$key} = $value;
1503
1504    write_file(tempname('.theme_' . $salt . '_' . get_product_name() . '_' . $key . '_' . $remote_user), \%var);
1505}
1506
1507sub get_theme_temp_data
1508{
1509    my ($key, $keep) = @_;
1510    my $salt = substr(encode_base64($main::session_id), 0, 16);
1511    my $data;
1512    my %theme_temp_data;
1513
1514    $salt =~ tr/A-Za-z0-9//cd;
1515
1516    my $tmp_file = tempname('.theme_' . $salt . '_' . get_product_name() . '_' . $key . '_' . $remote_user);
1517
1518    # Process multiple goto requests
1519    if ($key eq 'goto') {
1520        my (%theme_goto_temp);
1521        my $tmp_dir = tempname_dir();
1522        my @gotos;
1523        opendir(my $dir, $tmp_dir);
1524        @gotos = grep {/^\.theme/ && $_ =~ /$salt/ && $_ =~ /goto/ && -f "$tmp_dir/$_"} readdir($dir);
1525        closedir $dir;
1526        foreach (@gotos) {
1527            $tmp_file = "$tmp_dir/$_";
1528            if (-r $tmp_file) {
1529                my $tmp_file_mod = (stat($tmp_file))[9];
1530                my $tmp_file_age = time() - $tmp_file_mod;
1531                if ($tmp_file_age >= 15) {
1532                    unlink_file($tmp_file);
1533                    next;
1534                }
1535                read_file($tmp_file, \%theme_temp_data);
1536                last;
1537            }
1538        }
1539    } else {
1540        read_file($tmp_file, \%theme_temp_data);
1541    }
1542
1543    if (!$keep && -r $tmp_file) {
1544        unlink_file($tmp_file);
1545    }
1546
1547    $data = $theme_temp_data{$key};
1548    $data =~ s/[?|&]$xnav//g;
1549    $data =~ s/[?|&]randomized=[\d]+//g;
1550    $data =~ s/.cgi&/.cgi?/g;
1551
1552    return $data;
1553}
1554
1555sub get_user_home
1556{
1557    if (!supports_users()) {
1558        return undef;
1559    }
1560    my @my_user_info = $remote_user ? getpwnam($remote_user) : getpwuid($<);
1561    return $my_user_info[7];
1562}
1563
1564sub get_user_id
1565{
1566    if (!supports_users()) {
1567        return undef;
1568    }
1569    my @my_user_info = $remote_user ? getpwnam($remote_user) : getpwuid($<);
1570    return $my_user_info[2];
1571}
1572
1573sub get_fm_jailed_user
1574{
1575    if (!supports_users()) {
1576        return undef;
1577    }
1578    my $jailed_user = 0;
1579    my %fmaccess    = get_module_acl(undef, $_[0]);
1580    if ($get_user_level eq '0' && %fmaccess && $fmaccess{'work_as_user'} && $fmaccess{'work_as_user'} ne $remote_user) {
1581        my @user_info = getpwnam($fmaccess{'work_as_user'});
1582        $jailed_user = $_[1] ? $user_info[0] : $user_info[7];
1583    }
1584    return $jailed_user;
1585}
1586
1587sub get_tdconfig_file
1588{
1589    return "$root_directory/$current_theme/unauthenticated/js/defaults.js";
1590}
1591
1592sub get_taconfig_file
1593{
1594    return "$config_directory/$current_theme/defaults.js";
1595}
1596
1597sub get_tgconfig_file
1598{
1599    return "$config_directory/$current_theme/settings.js";
1600}
1601
1602sub get_tuconfig_file
1603{
1604    my $oconfig = "$config_directory/$current_theme/settings-$remote_user";
1605    return -r $oconfig ? $oconfig : "$oconfig.js";
1606}
1607
1608sub http_x_request
1609{
1610    if (get_env('http_x_requested_with') eq "XMLHttpRequest") {
1611        return 1;
1612    } else {
1613        return 0;
1614    }
1615}
1616
1617sub fetch_content
1618{
1619    if (get_env('request_uri') =~ /fetch-content=1/ ||
1620        get_module_name() eq "file")
1621    {
1622        return 1;
1623    } else {
1624        return 0;
1625    }
1626}
1627
1628sub get_link
1629{
1630    my ($string, $type) = @_;
1631    my $url;
1632
1633    if ($type eq 'ugly') {
1634        $string =~ /<a.*href=([\s\S]+?)>/;
1635        $url = $1;
1636    } elsif ($type eq 'bad') {
1637        $string =~ /<a.*href='([\s\S]+?)'.*>/;
1638        $url = $1;
1639    } else {
1640        $string =~ /<a.*href="([\s\S]+?)".*>/;
1641        $url = $1;
1642    }
1643    $string =~ /<a.*href.*>([\s\S]+?)<\/a>/;
1644
1645    return [$url, $1];
1646}
1647
1648sub get_button_tooltip
1649{
1650    my ($label, $key, $placement, $html, $force, $container, $br_label_on) = @_;
1651
1652    my $mod_key = $theme_config{'settings_hotkey_toggle_modifier'};
1653    my $hot_key = ($key ? ucfirst($theme_config{$key}) : undef);
1654    if (!$container) {
1655        $container = '#content';
1656    }
1657    my $tooltip_text = ($theme_text{$label} ? $theme_text{$label} : ($text{$label} ? $text{$label} : $label));
1658    if ($br_label_on) {
1659        my @tooltip_text = split(/\Q$br_label_on\E/, $tooltip_text, 2);
1660        $tooltip_text = join('<br>' . $br_label_on, @tooltip_text);
1661    }
1662
1663    return (
1664           ' aria-label="' . strip_html($tooltip_text) . '" data-container="' . $container . '" data-placement="' .
1665             $placement . '" data-toggle="tooltip" data-html="' . ($html ? 'true' : 'false') . '" data-title="'
1666             .
1667             ($tooltip_text
1668                .
1669                (length $theme_config{'settings_hotkeys_active'} &&
1670                   $theme_config{'settings_hotkeys_active'} ne 'false' &&
1671                   $hot_key ?
1672                   " (" . ($mod_key eq "altKey" ? "Alt" : $mod_key eq "ctrlKey" ? "Ctrl" : "Meta") . '+' . $hot_key . ")" :
1673                   '')
1674             ) .
1675             '"');
1676}
1677
1678sub error_40x_handler
1679{
1680    my %miniserv;
1681    get_miniserv_config(\%miniserv);
1682    if ($_[0]) {
1683        if ($miniserv{'error_handler_403'}) {
1684            $miniserv{'error_handler_401'} = undef;
1685            $miniserv{'error_handler_403'} = undef;
1686            $miniserv{'error_handler_404'} = undef;
1687            put_miniserv_config(\%miniserv);
1688            reload_miniserv();
1689        }
1690    } else {
1691        if (!$miniserv{'error_handler_403'}) {
1692            $miniserv{'error_handler_401'} = "401.cgi";
1693            $miniserv{'error_handler_403'} = "403.cgi";
1694            $miniserv{'error_handler_404'} = "404.cgi";
1695            put_miniserv_config(\%miniserv);
1696            reload_miniserv();
1697        }
1698    }
1699}
1700
1701sub lib_csf_control
1702{
1703    my ($action) = @_;
1704    if (foreign_check("csf") && foreign_available("csf") && has_command("csf") && $current_theme =~ /authentic-theme/) {
1705        do("$root_directory/$current_theme/extensions/csf/csf-lib.pl");
1706        if ($action eq 'load') {
1707            csf_mod();
1708        } elsif ($action eq 'unload') {
1709            csf_clear();
1710        } elsif ($action eq 'strings') {
1711            return csf_strings();
1712        }
1713    }
1714}
1715
1716sub embed_product_branding
1717{
1718    return if ($theme_config{"settings_embed_product_branding_privileged"} eq 'false');
1719    return &custom_embed_product_branding(@_)
1720      if (defined(&custom_embed_product_branding));
1721
1722    my ($brand,
1723        $brand_name,
1724        $brand_dir_default,
1725        $brand_dir_custom,
1726        $brand_dir,
1727        $loader,
1728        $request_uri,
1729        $vm_mod_name,
1730        $vm_available,
1731        $cm_mod_name,
1732        $cm_available);
1733
1734    # Set brand directory
1735    $brand_name;
1736    $brand_dir_default = "$root_directory/$current_theme/images/brand";
1737    $brand_dir_custom  = "$config_directory/$current_theme/brand";
1738    $brand_dir         = -r $brand_dir_custom ? $brand_dir_custom : $brand_dir_default;
1739    $loader            = read_file_contents("$brand_dir/loader.html");
1740    $request_uri       = get_theme_temp_data('goto', 1);
1741
1742    # Virtualmin available
1743    $vm_mod_name  = "virtual-server";
1744    $vm_available = foreign_available($vm_mod_name);
1745
1746    # Cloudmin available and requested
1747    $cm_mod_name  = "server-manager";
1748    $cm_available = foreign_available($cm_mod_name);
1749
1750    # Define brand image for Virtualmin
1751    if ($vm_available && !$cm_available) {
1752        $brand      = read_file_contents("$brand_dir/virtualmin.html");
1753        $brand_name = "brand-virtualmin";
1754    }
1755
1756    # Define brand image for Cloudmin
1757    elsif ($cm_available) {
1758        $brand      = read_file_contents("$brand_dir/cloudmin.html");
1759        $brand_name = "brand-cloudmin";
1760    }
1761
1762    # Webmin/Usermin brand image
1763    else {
1764        my $prod = get_product_name();
1765        $brand      = read_file_contents("$brand_dir/$prod.html");
1766        $brand_name = "brand-$prod";
1767    }
1768    $brand =
1769"<div tabindex=\"1\" class=\"branding-backdrop $brand_name\"><div class=\"centered\">$brand<br><div class=\"branding-loader\">$loader</div></div></div>";
1770    $brand .= "<script>page.branding.process()</script>";
1771    print $brand;
1772}
1773
17741;
1775