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 8use lib ("$ENV{'THEME_ROOT'}/lib"); 9 10do("$ENV{'THEME_ROOT'}/authentic-funcs.pl"); 11 12use Cwd 'abs_path'; 13use Encode qw(decode encode); 14use File::Basename; 15use File::MimeInfo; 16use File::Find; 17use File::Copy; 18use File::Grep qw( fdo ); 19use POSIX; 20 21our (%access, 22 %gconfig, 23 %in, 24 %text, 25 %theme_text, 26 @remote_user_info, 27 $base_remote_user, 28 $config_directory, 29 $current_theme, 30 %userconfig, 31 @allowed_paths, 32 $base, 33 $cwd, 34 $path, 35 $remote_user); 36our $checked_path; 37our $module_path; 38 39our %request_uri = get_request_uri(); 40set_module($request_uri{'module'}); 41get_libs($request_uri{'module'}); 42 43sub set_module 44{ 45 my ($module) = @_; 46 47 $module_path = get_env('document_root') . '/' . $module; 48 49 set_env('foreign_module_name', $module); 50 set_env('foreign_root_directory', $module_path); 51} 52 53sub get_libs 54{ 55 my ($module) = @_; 56 57 do($module_path . '/' . $module . '-lib.pl'); 58 59 &ReadParse(); 60 61 get_paths(); 62 63 switch_to_user($in{'username'}); 64 65 $checked_path = $path; 66 if (join(" , ", @allowed_paths) ne '/') { 67 $checked_path =~ s/$in{'cwd'}\//\//ig; 68 } 69 70 %text = (load_language($current_theme), load_language($module), %text); 71 %theme_text = %text; 72} 73 74sub get_type 75{ 76 my ($dir) = @_; 77 if (-d $dir) { 78 return 1; 79 } else { 80 return 0; 81 } 82} 83 84sub get_errors 85{ 86 my %errors = %{ $_[0] }; 87 88 if (scalar %errors) { 89 return convert_to_json(\%errors); 90 } else { 91 return undef; 92 } 93 94} 95 96sub get_request_uri 97{ 98 (my $uri = get_env('request_uri')) =~ s/\?/&/; 99 my @r = split /&/, $uri; 100 my %c; 101 102 foreach (@r) { 103 my ($k, $v) = split /=/, $_; 104 $c{$k} = $v; 105 } 106 107 return %c; 108} 109 110sub get_user_config 111{ 112 my ($k) = @_; 113 114 my %t; 115 read_file("$config_directory/$current_theme/settings-$remote_user", \%t); 116 117 if ($k) { 118 my $v = $t{$k}; 119 $v =~ s/'|;//g; 120 return $v; 121 } else { 122 my %c = map {(my $v = $t{$_}) =~ s/'|;//g; $_ => $v} keys %t; 123 return %c; 124 } 125} 126 127sub kill_previous 128{ 129 my $pid = tokenize($_[0]); 130 if ($pid) { 131 kill(9, $pid); 132 } 133 tokenize($_[0], $_[1]); 134} 135 136sub tokenize 137{ 138 my ($key, $value) = @_; 139 my $salt = substr(encode_base64($main::session_id), 0, 16); 140 my %var; 141 my $user = $remote_user; 142 my $tmp_file; 143 144 $key =~ s/(?|([\w-]+$)|([\w-]+)\.)//; 145 $key = $1; 146 $key =~ tr/A-Za-z0-9//cd; 147 $user =~ tr/A-Za-z0-9//cd; 148 $salt =~ tr/A-Za-z0-9//cd; 149 150 $tmp_file = tempname('.theme_' . $salt . '_' . get_product_name() . '_' . $key . '_' . $user); 151 $var{$key} = $value; 152 153 if ($value) { 154 write_file($tmp_file, \%var); 155 } else { 156 my %theme_temp_data; 157 read_file($tmp_file, \%theme_temp_data); 158 unlink_file($tmp_file); 159 return $theme_temp_data{$key}; 160 } 161} 162 163sub get_pagination 164{ 165 166 my ($page, $pages, $query) = @_; 167 168 our ($path); 169 170 my $search_follow_symlinks = $in{'follow'}; 171 my $search_case_insensitive = $in{'caseins'}; 172 my $search_grep = $in{'grepstring'}; 173 my $fsid = $in{'fsid'}; 174 my $exclude = $in{'exclude'}; 175 my $regex = $in{'regex'}; 176 my $all_items = $in{'all_items'}; 177 178 my $left = $page - 2; 179 my $right = $page + 3; 180 my @range; 181 my $last; 182 my $pagination; 183 my $invisible = $pages == 1 ? ' invisible' : undef; 184 185 my $start = sub { 186 my $start; 187 my $disabled = ($page == 1 ? " disabled" : undef); 188 189 $start = "<div class=\"dataTables_paginate paging_simple_numbers spaginates$invisible\">"; 190 $start .= '<ul class="pagination">'; 191 $start .= "<li class='paginate_button previous$disabled'>"; 192 $start .= '<a><i class="fa fa-fw fa-angle-left"></i></a>'; 193 $start .= "</li>"; 194 return $start; 195 }; 196 197 my $current = sub { 198 my ($i) = @_; 199 my $end; 200 my $active = ($page eq $i ? " active" : undef); 201 $end = "<li class='paginate_button$active'>"; 202 $end .= 203"<a class='spaginated' href='list.cgi?page=$i&path=@{[urlize($path)]}&query=@{[urlize($query)]}&follow=$search_follow_symlinks&caseins=$search_case_insensitive&grepstring=$search_grep&fsid=$fsid&exclude=$exclude®ex=$regex&all_items=$all_items'>@{[nice_number($i, ',')]}</a>"; 204 $end .= "</li>"; 205 return $end; 206 }; 207 208 my $range = sub { 209 my $range; 210 $range = '<li class="paginate_button disabled">'; 211 $range .= '<a>...</a>'; 212 $range .= "</li>"; 213 214 }; 215 216 my $end = sub { 217 my $end; 218 my $disabled = ($page == $pages ? " disabled" : undef); 219 $end = "<li class='paginate_button next$disabled'>"; 220 $end .= '<a><i class="fa fa-fw fa-angle-right"></i></a>'; 221 $end .= "</li>"; 222 $end .= '</ul>'; 223 $end .= '</div>'; 224 return $end; 225 }; 226 227 for (my $i = 1; $i <= $pages; $i++) { 228 if ($i == 1 || $i == $pages || $i >= $left && $i < $right) { 229 push(@range, $i); 230 } 231 } 232 233 foreach my $i (@range) { 234 if ($last) { 235 if ($i - $last == 2) { 236 $pagination .= &$current($last + 1); 237 } elsif ($i - $last != 1) { 238 $pagination .= &$range(); 239 } 240 } 241 $pagination .= &$current($i); 242 $last = $i; 243 } 244 245 $pagination = &$start() . $pagination . &$end(); 246 return $pagination; 247} 248 249sub test_all_items_query 250{ 251 return $in{'all_items'} eq '3' ? 3 : 0; 252} 253 254sub get_entries_list 255{ 256 my @entries_list; 257 if (test_all_items_query()) { 258 if ($in{'query'}) { 259 @entries_list = exec_search('list'); 260 } else { 261 find( 262 { 263 wanted => sub { 264 my $found = $File::Find::name; 265 push(@entries_list, $_); 266 }, 267 }, 268 $cwd); 269 @entries_list = grep {$_ ne '.' && $_ ne '..'} @entries_list; 270 } 271 } else { 272 @entries_list = split(/\0/, $in{'name'}); 273 } 274 return @entries_list; 275} 276 277sub extra_query 278{ 279 my $page = $in{'page'}; 280 my $query = $in{'query'}; 281 my $paginate = $in{'paginate'}; 282 my $follow = $in{'follow'}; 283 my $caseins = $in{'caseins'}; 284 my $grepstring = $in{'grepstring'}; 285 my $fsid = $in{'fsid'}; 286 my $exclude = $in{'exclude'}; 287 my $regex = $in{'regex'}; 288 my $all_items = $in{'all_items'}; 289 return 290"&page=$page&query=$query&paginate=$paginate&follow=$follow&caseins=$caseins&grepstring=$grepstring&fsid=$fsid&exclude=$exclude®ex=$regex&all_items=$all_items"; 291} 292 293sub set_response 294{ 295 my ($c) = @_; 296 print "Set-Cookie: file-manager-response=" . $c . "; path=/\r\n"; 297} 298 299sub set_response_count 300{ 301 my ($c) = @_; 302 print "Set-Cookie: file-manager-response_count=" . $c . "; path=/\r\n"; 303} 304 305sub fatal_errors 306{ 307 my @errors = @_; 308 309 head(); 310 print $text{'errors_occured'}; 311 print "<ul>"; 312 foreach my $error (@errors) { 313 print("<li>$error</li>"); 314 } 315 print "</ul>"; 316} 317 318sub redirect_local 319{ 320 print "Location: $_[0]\n\n"; 321} 322 323sub print_error 324{ 325 my ($err_msg) = @_; 326 my %err; 327 $err{'error'} = $err_msg; 328 print_json([\%err]); 329 exit; 330} 331 332sub cache_search 333{ 334 my ($id, $searched_data) = @_; 335 $id || return (); 336 337 my $tmp_dir = tempname_dir(); 338 my $fname = ".$remote_user-file-manager-scache"; 339 my $fcached = $tmp_dir . "/$fname-$id"; 340 my $dcached = read_file_contents($fcached); 341 my $dcached_ready = $dcached ? unserialise_variable($dcached) : undef; 342 my @data; 343 344 # Clear previously cached data 345 opendir(my $dir, $tmp_dir); 346 my @tmps = grep {$_ =~ /$fname/} readdir($dir); 347 closedir $dir; 348 foreach (@tmps) { 349 my $file = "$tmp_dir/$_"; 350 my @stat = stat($file); 351 if (@stat && $stat[9] < time() - (24 * 60 * 60)) { 352 unlink_file($file); 353 } 354 } 355 356 # Check if cache with requested id is available 357 if (!$searched_data && -r $fcached && @$dcached_ready) { 358 359 # Use cache for now 360 @data = @$dcached_ready; 361 if (@data) { 362 return @data; 363 } else { 364 return (); 365 } 366 } elsif ($searched_data) { 367 368 # Write cache 369 my $fh = "cache"; 370 open_tempfile($fh, ">$fcached"); 371 print_tempfile($fh, serialise_variable($searched_data)); 372 close_tempfile($fh); 373 } else { 374 return (); 375 } 376} 377 378sub cache_search_delete 379{ 380 my ($id, $deleted_data) = @_; 381 382 my @results_cached = cache_search($id); 383 if (@results_cached) { 384 @results_cached = grep { 385 my $f = $_; 386 !grep $f =~ /^\Q$_\E/, @$deleted_data 387 } @results_cached; 388 cache_search($id, \@results_cached); 389 } 390 391} 392 393sub cache_search_rename 394{ 395 my ($id, $from, $to) = @_; 396 397 my @results_cached = cache_search($id); 398 if (@results_cached) { 399 my @updated_cache; 400 foreach my $file (@results_cached) { 401 if ($file eq "/$from") { 402 $file = "/$to"; 403 } 404 push(@updated_cache, $file); 405 } 406 cache_search($id, \@updated_cache); 407 } 408} 409 410sub exec_search 411{ 412 my ($list) = @_; 413 my $mask = $in{'query'}; 414 my $grep = $in{'grepstring'}; 415 my $fsid = $in{'fsid'}; 416 my $exclude = $in{'exclude'}; 417 my $replace = $in{'grepreplace'}; 418 my $caseins = $in{'caseins'}; 419 my $follow = ($in{'follow'} || $in{'limit_type'} == 3 ? 1 : 0); 420 my $regex = ($in{'regex'} ? 1 : 0); 421 my $ouser = $in{'limit_user'}; 422 my $ogroup = $in{'limit_group'}; 423 my $otype = $in{'limit_type'}; 424 my $osize = $in{'limit_size'}; 425 my @results; 426 my @excludes; 427 428 my @results_cached = cache_search($fsid); 429 if (@results_cached) { 430 return @results_cached; 431 } 432 433 find( 434 { 435 wanted => sub { 436 my $found = $File::Find::name; 437 if ($found ne $path) { 438 my $found_text = $_; 439 my $mask_text = $mask; 440 if ($caseins) { 441 $found_text = lc($found_text); 442 $mask_text = lc($mask_text); 443 $exclude = lc($exclude); 444 } 445 if ($exclude) { 446 @excludes = split(';', $exclude); 447 } 448 if (($mask_text eq "*" || !$regex && (index($found_text, $mask_text) != -1)) || 449 ($regex && $found_text =~ /$mask_text/)) 450 { 451 if (!$list) { 452 $found =~ s/^\Q$cwd\E//g; 453 } 454 if ($follow || (!$follow && !-l $_)) { 455 my $excluded; 456 my $found_ = $found; 457 $found_ = lc($found_) if ($caseins); 458 if (@excludes) { 459 foreach my $e (@excludes) { 460 if ((!$regex && index($found_, $e) != -1) || ($regex && $found_ =~ /$e/)) { 461 $excluded = 1; 462 } 463 } 464 } 465 my $extra_exclude; 466 if ($ouser || $ogroup || $osize) { 467 my $found_cwd = &simplify_path("$cwd/$found"); 468 my @found_stat = stat($found_cwd); 469 if ($ouser && $ouser ne getpwuid($found_stat[4])) { 470 $extra_exclude = 1; 471 } 472 if ($ogroup && $ogroup ne getgrgid($found_stat[5])) { 473 $extra_exclude = 1; 474 } 475 if ($osize) { 476 my ($osize_operator) = $osize =~ /\s*([\D]+)\d/; 477 my ($osize_size) = $osize =~ /\s*([\d]+)\s*/; 478 my ($osize_unit) = $osize =~ /\s*\d+\s*(\p{L}+)/; 479 my ($osize_operator_bi) = $osize =~ /\s*[\D]+\d.*?-\s*([\D]+)\d/; 480 my ($osize_size_bi) = $osize =~ /\s*[\d]+\s*.*?-.*?([\d]+)\s*/; 481 my ($osize_unit_bi) = $osize =~ /\s*\d+\s*\p{L}+\s*-.*?[\d]+\s*(\p{L}+)/; 482 my $file_size = $found_stat[7]; 483 my $osize_size_format = sub { 484 my ($unit, $size) = @_; 485 if ($unit eq lc($theme_text{'theme_xhred_nice_size_kB'}) || 486 $unit eq lc($theme_text{'theme_xhred_nice_size_kIB'}) || 487 string_starts_with($unit, "k") || 488 string_starts_with($unit, lc($theme_text{'theme_xhred_nice_size_kB'}))) 489 { 490 $size *= 1024; 491 } elsif ($unit eq lc($theme_text{'theme_xhred_nice_size_MB'}) || 492 $unit eq lc($theme_text{'theme_xhred_nice_size_MIB'}) || 493 string_starts_with($unit, "m") || 494 string_starts_with($unit, lc($theme_text{'theme_xhred_nice_size_MB'}))) 495 { 496 $size *= 1024 * 1024; 497 } elsif ($unit eq lc($theme_text{'theme_xhred_nice_size_GB'}) || 498 $unit eq lc($theme_text{'theme_xhred_nice_size_GIB'}) || 499 string_starts_with($unit, "g") || 500 string_starts_with($unit, lc($theme_text{'theme_xhred_nice_size_GB'}))) 501 { 502 $size *= 1024 * 1024 * 1024; 503 } elsif ($unit eq lc($theme_text{'theme_xhred_nice_size_TB'}) || 504 $unit eq lc($theme_text{'theme_xhred_nice_size_TIB'}) || 505 string_starts_with($unit, "t") || 506 string_starts_with($unit, lc($theme_text{'theme_xhred_nice_size_TB'}))) 507 { 508 $size *= 1024 * 1024 * 1024 * 1024; 509 } elsif ($unit eq lc($theme_text{'theme_xhred_nice_size_PB'}) || 510 $unit eq lc($theme_text{'theme_xhred_nice_size_PIB'}) || 511 string_starts_with($unit, "p") || 512 string_starts_with($unit, lc($theme_text{'theme_xhred_nice_size_PB'}))) 513 { 514 $size *= 1024 * 1024 * 1024 * 1024 * 1024; 515 } 516 return $size; 517 518 }; 519 my $extra_exclude_test = sub { 520 my ($operator, $osize, $fsize) = @_; 521 my $exclude; 522 if ($operator eq '<') { 523 if ($osize <= $fsize) { 524 $exclude = 1; 525 } 526 } elsif ($operator eq '<=') { 527 if ($osize < $fsize) { 528 $exclude = 1; 529 } 530 } elsif ($operator eq '>') { 531 if ($osize >= $fsize) { 532 $exclude = 1; 533 } 534 } elsif ($operator eq '>=') { 535 if ($osize > $fsize) { 536 $exclude = 1; 537 } 538 } elsif ($operator eq '!=') { 539 if ($osize == $fsize) { 540 $exclude = 1; 541 } 542 543 } elsif (!$operator || $operator eq '=' || $operator eq '==') { 544 if ($osize != $fsize) { 545 $exclude = 1; 546 } 547 } 548 return $exclude; 549 }; 550 if ($otype == 2 && -d $found_cwd) { 551 $file_size = recursive_disk_usage($found_cwd); 552 } 553 $osize_operator = trim($osize_operator); 554 $osize_size = int(trim($osize_size)); 555 $osize_unit = lc(trim($osize_unit)); 556 $osize_operator_bi = trim($osize_operator_bi); 557 $osize_size_bi = int(trim($osize_size_bi)); 558 $osize_unit_bi = lc(trim($osize_unit_bi)); 559 if ($osize_size) { 560 my $osize_size_ = $osize_size; 561 if ($osize_unit) { 562 $osize_size_ = &$osize_size_format($osize_unit, $osize_size_); 563 } 564 $extra_exclude = &$extra_exclude_test($osize_operator, $osize_size_, $file_size); 565 if (!$extra_exclude) { 566 if ($osize_size && $osize_size_bi) { 567 my $osize_size_bi_ = $osize_size_bi; 568 if ($osize_unit_bi) { 569 $osize_size_bi_ = &$osize_size_format($osize_unit, $osize_size_bi_); 570 } 571 $extra_exclude = 572 &$extra_exclude_test($osize_operator_bi, $osize_size_bi_, $file_size); 573 } 574 } 575 } 576 } 577 } 578 if ($otype) { 579 my $found_cwd = &simplify_path("$cwd/$found"); 580 if ($otype == 1 && !-f $found_cwd) { 581 $extra_exclude = 1; 582 } 583 if ($otype == 2 && !-d $found_cwd) { 584 $extra_exclude = 1; 585 } 586 if ($otype == 3 && !-l $found_cwd) { 587 $extra_exclude = 1; 588 } 589 } 590 if (!$extra_exclude && (!$exclude || (@excludes && !$excluded))) { 591 push(@results, $found); 592 } 593 } 594 } 595 } 596 }, 597 follow => $follow, 598 follow_skip => 2, 599 }, 600 $cwd); 601 602 my @replaces; 603 if (length($grep) || length($replace)) { 604 if (length($grep)) { 605 @results = map {&simplify_path("$cwd/$_")} @results; 606 my @matched; 607 fdo { 608 my ($file, $line, $text) = @_; 609 if ($caseins) { 610 $text = lc($text); 611 $grep = lc($grep); 612 } 613 if ((!$regex && index($text, $grep) != -1) || ($regex && $text =~ /$grep/)) { 614 if (!grep(/^\Q$results[$file]\E$/, @replaces)) { 615 push(@replaces, $results[$file]); 616 } 617 (my $sfile = $results[$file]) =~ s/^\Q$cwd\E//g; 618 if (!grep(/^\Q$sfile\E$/, @matched)) { 619 push(@matched, $sfile); 620 } 621 } 622 } 623 @results; 624 undef(@results); 625 @results = @matched; 626 } 627 if (length($replace)) { 628 foreach my $file (@replaces) { 629 if (-r $file) { 630 if ($caseins) { 631 (my $fc = read_file_contents($file)) =~ s/$grep/$replace/gi; 632 write_file_contents($file, $fc); 633 } else { 634 (my $fc = read_file_contents($file)) =~ s/$grep/$replace/g; 635 write_file_contents($file, $fc); 636 } 637 } 638 } 639 } 640 } 641 cache_search($fsid, \@results) if ($fsid && !length($replace)); 642 return @results; 643} 644 645sub server_pagination_enabled 646{ 647 my ($totals, $max_allowed, $query) = @_; 648 return ($totals > $max_allowed || ($query && $totals)); 649} 650 651sub print_content 652{ 653 my %list_data; 654 my $query = $in{'query'}; 655 my @list; 656 my $clear_path = sub { 657 my ($path) = @_; 658 $path =~ s/[\/]+/\//g; 659 return $path; 660 }; 661 662 # In case of search trim the list accordingly 663 if ($query) { 664 @list = exec_search(); 665 } else { 666 unless (opendir(DIR, $cwd)) { 667 print_error("$text{'theme_xhred_global_error'}: [tt]`$cwd`[/tt]- $!."); 668 exit; 669 } 670 @list = grep {$_ ne '.' && $_ ne '..'} readdir(DIR); 671 closedir(DIR); 672 } 673 674 # Filter out not allowed entries 675 if (test_allowed_paths()) { 676 677 # Leave only allowed 678 my @allowed_list; 679 for my $allowed_path (@allowed_paths) { 680 push( 681 @allowed_list, 682 grep { 683 my $list_path = &$clear_path("$cwd/$_"); 684 $list_path =~ /^\Q$allowed_path\E\// || 685 $allowed_path =~ /^\Q$list_path\E/ 686 } @list); 687 } 688 689 # Remove duplicates 690 my %hash = map {$_, 1} @allowed_list; 691 @list = keys %hash; 692 } 693 694 my $page = 1; 695 my $pagelimit = 4294967295; 696 my $pages = 0; 697 698 my $max_allowed = int($userconfig{'max_allowed'}); 699 if ($max_allowed !~ /^[0-9,.E]+$/ || $max_allowed < 100 || $max_allowed > 10000) { 700 $max_allowed = 1000; 701 } 702 703 my $totals = scalar(@list); 704 my $totals_spliced = $totals; 705 706 my $tuconfig_per_page = get_user_config('config_portable_module_filemanager_records_per_page'); 707 708 if (server_pagination_enabled($totals, $max_allowed, $query)) { 709 $page = int($in{'page'}) || 1; 710 $pagelimit = int($in{'paginate'}) || int($tuconfig_per_page) || 30; 711 $pages = ceil(($totals) / $pagelimit); 712 if ($page > $pages) { 713 $page = $pages; 714 $in{'page'} = $page; 715 } 716 my $splice_start = $pagelimit * ($page - 1); 717 my $splice_end = $pagelimit; 718 if ($totals > 100000) { 719 @list = sort {$a cmp $b} @list; 720 } else { 721 @list = 722 map $_->[0], sort {$a->[1] <=> $b->[1] || $a->[0] cmp $b->[0]} 723 map [$_, -f "$cwd/$_"], 724 @list; 725 } 726 @list = splice(@list, $splice_start, $splice_end); 727 $totals_spliced = scalar(@list); 728 } 729 730 @list = map {&simplify_path("$cwd/$_")} @list; 731 732 my %acls; 733 my %attributes; 734 my $setype = get_selinux_command_type(); 735 my %secontext; 736 737 # List ACLs 738 if ($userconfig{'columns'} =~ /acls/ && get_acls_status()) { 739 my $command = get_list_acls_command() . " " . join(' ', map {quotemeta("$_")} @list); 740 my $output = `$command`; 741 my @aclsArr; 742 foreach my $aclsStr (split(/\n\n/, $output)) { 743 $aclsStr =~ /#\s+file:\s*(.*)/; 744 my ($file) = ($aclsStr =~ /#\s+file:\s*(.*)/); 745 my @aclsA = ($aclsStr =~ /^(?!(#|user::|group::|other::))([\w\:\-\_]+)/gm); 746 push(@aclsArr, [$file, \@aclsA]); 747 } 748 %acls = map {$_->[0] => ('<span data-acls>' . join("<br>", (grep /\S/, @{ $_->[1] })) . '</span>')} @aclsArr; 749 } 750 751 # List attributes 752 if ($userconfig{'columns'} =~ /attributes/ && get_attr_status()) { 753 my $command = 754 get_attr_command() . join(' ', map {quotemeta("$_")} @list); 755 my $output = `$command`; 756 my @attributesArr = 757 map {[split(/\s+/, $_, 2)]} split(/\n/, $output); 758 %attributes = map {$_->[1] => ('<span data-attributes>' . $_->[0] . '</span>')} @attributesArr; 759 } 760 761 # List security context 762 if ($userconfig{'columns'} =~ /selinux/ && get_selinux_status()) { 763 my $command = 764 get_selinux_command() . join(' ', map {quotemeta("$_")} @list); 765 my $output = `$command`; 766 (!$setype && ($output =~ s/\n//g, $output =~ s/,\s/,/g)); 767 my $delimiter = ($setype ? '\n' : ','); 768 my @searray = 769 map {[split(/\s+/, $_, 2)]} split(/$delimiter/, $output); 770 %secontext = 771 map {$_->[1] => ($_->[0] eq "?" ? undef : ('<span data-secontext>' . $_->[0] . '</span>'))} @searray; 772 } 773 774 # Get info about directory entries 775 my @info = map {[$_, lstat($_), &mimetype($_), -d, -l $_, $secontext{$_}, $attributes{$_}, $acls{$_}]} @list; 776 my @folders = map {$_} grep {$_->[15] == 1} @info; 777 my @files = map {$_} grep {$_->[15] != 1} @info; 778 779 if (server_pagination_enabled($totals, $max_allowed, $query)) { 780 undef(@list); 781 push(@list, @info); 782 } else { 783 @folders = sort {"\L$a->[0]" cmp "\L$b->[0]"} @folders; 784 @files = sort {"\L$a->[0]" cmp "\L$b->[0]"} @files; 785 undef(@list); 786 push(@list, @folders, @files); 787 } 788 789 my $info_total; 790 my $info_files = scalar(@files); 791 my $info_folders = scalar(@folders); 792 793 my @allowed_for_edit = split(/\s+/, $access{'allowed_for_edit'}); 794 my %allowed_for_edit = map {$_ => 1} @allowed_for_edit; 795 796 # Set icons variables 797 my $edit_icon = "<i class='fa fa-edit' alt='$text{'edit'}'></i>"; 798 my $rename_icon = "<i class='fa fa-font' title='$text{'rename'}'></i>"; 799 my $extract_icon = "<i class='fa fa-external-link' alt='$text{'extract_archive'}'></i>"; 800 my $goto_icon = "<i class='fa fa-arrow-right' alt='$text{'goto_folder'}'></i>"; 801 802 my $server_pagination = undef; 803 $list_data{'pagination_limit'} = undef; 804 805 if (server_pagination_enabled($totals, $max_allowed, $query)) { 806 $page = int($in{'page'}) || 1; 807 $pagelimit = int($in{'paginate'}) || int($tuconfig_per_page) || 30; 808 $pages = ceil(($totals) / $pagelimit); 809 if ($page > $pages) { 810 $page = $pages; 811 $in{'page'} = $page; 812 } 813 $server_pagination = get_pagination($page, $pages, $query); 814 $list_data{'pagination_limit'} = $in{'paginate'} || undef; 815 816 my $pagination_text = $text{'theme_xhred_datatable_sinfo'}; 817 my $start = $page * $pagelimit - $pagelimit + 1; 818 my $end = $page * $pagelimit; 819 if ($end > $totals) { 820 $end = $totals; 821 } 822 $pagination_text =~ s/_START_/@{[nice_number($start, ",")]}/ig; 823 $pagination_text =~ s/_END_/@{[nice_number($end, ",")]}/ig; 824 $pagination_text =~ s/_TOTAL_/@{[nice_number($totals, ",")]}/ig; 825 $list_data{'pagination_text'} = $pagination_text; 826 } 827 828 $list_data{'pagination'} = $server_pagination; 829 my $total_with_pagination; 830 if ($server_pagination) { 831 $total_with_pagination = "_paginated"; 832 } 833 834 if ($info_files eq 1 && $info_folders eq 1) { 835 $info_total = ('filemanager_global_info' . $total_with_pagination . '_total1'); 836 } elsif ($info_files ne 1 && $info_folders eq 1) { 837 $info_total = ('filemanager_global_info' . $total_with_pagination . '_total2'); 838 } elsif ($info_files eq 1 && $info_folders ne 1) { 839 $info_total = ('filemanager_global_info' . $total_with_pagination . '_total3'); 840 } else { 841 $info_total = ('filemanager_global_info' . $total_with_pagination . '_total4'); 842 } 843 $list_data{'total'} = "<div class='total'>" 844 . 845 ( $query ? (trim($text{'filemanager_global_search_results'}) . ": ") : 846 ($server_pagination ? (trim($text{'filemanager_global_paginated_results'}) . ": ") : undef) 847 ) . 848 "" 849 . 850 ( 851 text(nice_number($info_total, ","), 852 nice_number($info_files, ","), 853 nice_number($info_folders, ","), 854 nice_number($totals, ","), 855 nice_number($pages, ",") 856 ) 857 ) . 858 "</div>"; 859 860 # Render current directory entries 861 $list_data{'form'} = &ui_form_start("", "post", undef, "id='list_form'"); 862 863 my @ui_columns = ('<input class="_select-unselect_" type="checkbox" onclick="selectUnselect(this)" />', ''); 864 push @ui_columns, ('<span data-head-name>' . $text{'name'} . '</span>'); 865 push @ui_columns, ('<span data-head-type>' . $text{'type'} . '</span>') 866 if ($userconfig{'columns'} =~ /type/); 867 push @ui_columns, ('<span data-head-actions>' . $text{'actions'} . '</span>'); 868 push @ui_columns, ('<span data-head-size>' . $text{'size'} . '</span>') 869 if ($userconfig{'columns'} =~ /size/); 870 push @ui_columns, ('<span data-head-owner_user>' . $text{'ownership'} . '</span>') 871 if ($userconfig{'columns'} =~ /owner_user/); 872 push @ui_columns, ('<span data-head-permissions>' . $text{'permissions'} . '</span>') 873 if ($userconfig{'columns'} =~ /permissions/); 874 push @ui_columns, ('<span data-head-acls>' . $text{'acls'} . '</span>') 875 if (get_acls_status() && $userconfig{'columns'} =~ /acls/); 876 push @ui_columns, ('<span data-head-attributes>' . $text{'attributes'} . '</span>') 877 if (get_attr_status() && $userconfig{'columns'} =~ /attributes/); 878 push @ui_columns, ('<span data-head-selinux>' . $text{'selinux'} . '</span>') 879 if (get_selinux_status() && $userconfig{'columns'} =~ /selinux/); 880 push @ui_columns, ('<span data-head-last_mod_time>' . $text{'last_mod_time'} . '</span>') 881 if ($userconfig{'columns'} =~ /last_mod_time/); 882 883 $list_data{'rows'} = ''; 884 for (my $count = 1; $count <= $totals_spliced; $count++) { 885 if ($count > $totals) {last;} 886 my $class = $count & 1 ? "odd" : "even"; 887 my $link = $list[$count - 1][0]; 888 $link =~ s/\Q$cwd\E\///; 889 $link =~ s/^\///g; 890 my $vlink = html_escape($link); 891 my $hlink = html_escape($vlink); 892 893 my $filename = $link; 894 $filename =~ /\/([^\/]+)$/; 895 if ($1 && $list[$count - 1][15] == 0) { 896 $filename = $1; 897 } 898 my $hlink_path = $hlink; 899 if ($query) { 900 if (!string_contains($hlink_path, '/') && $list[$count - 1][15] == 0) { 901 $hlink_path = undef; 902 } 903 $hlink_path =~ s/\/\Q$filename\E$//; 904 } 905 906 my $type = $list[$count - 1][14]; 907 $type =~ s/\//\-/g; 908 my $img = "images/icons/mime/$type.png"; 909 unless (-e $request_uri{'module'} . '/' . $img) { 910 $img = "images/icons/mime/unknown.png"; 911 } 912 913 my $actions = 914"<a class='action-link' href='javascript:void(0)' onclick='renameDialog(\"$hlink\")' title='$text{'rename'}' data-container='body'>$rename_icon</a>"; 915 my $href; 916 my $is_archive = 0; 917 my $is_file = 1; 918 my $is_gpg = 0; 919 my $is_img = 0; 920 if ($list[$count - 1][15] == 1) { 921 $is_file = 0; 922 $href = "index.cgi?path=" . &urlize("$path/$link"); 923 } else { 924 my ($fname, $fpath, $fsuffix) = 925 fileparse($list[$count - 1][0]); 926 if ($base ne '/') { 927 $fpath =~ s/^\Q$base\E//g; 928 } 929 $href = "download.cgi?file=" . &urlize($link) . "&path=" . &urlize($fpath); 930 if ($0 =~ /search.cgi/) { 931 $actions = 932 "$actions<a class='action-link' " . 933 "href='index.cgi?path=" . &urlize($fpath) . "' " . "title='$text{'goto_folder'}'>$goto_icon</a>"; 934 } 935 if ($type =~ /text-/ || 936 $type =~ /svg\+xml/ || 937 exists($allowed_for_edit{$type})) 938 { 939 $actions = 940 "$actions<a class='action-link' href='edit_file.cgi?file=" . &urlize($link) . 941 "&path=" . &urlize($path) . "' title='$text{'edit'}' data-container='body'>$edit_icon</a>"; 942 } 943 my $type_archive = $type; 944 if ($type =~ /^image/) { 945 $is_img = 1; 946 } 947 if ($type =~ /application-pgp-encrypted/) { 948 my $link_gpg = $link; 949 $link_gpg =~ s/\.(gpg|pgp)$//; 950 $type_archive = mimetype($link_gpg); 951 $is_gpg = 1; 952 } 953 if ($type_archive =~ /application-zip/ || 954 $type_archive =~ /application-x-7z-compressed/ || 955 $type_archive =~ /application-x-rar|application-vnd\.rar/ || 956 $type_archive =~ /application-x-rpm/ || 957 $type_archive =~ /application-x-deb|debian\.binary-package/ || 958 $type_archive =~ /x-compressed-tar/ || 959 $type_archive =~ /-x-tar/ || 960 $type_archive =~ /-x-bzip/ || 961 $type_archive =~ /-gzip/ || 962 $type_archive =~ /-x-xz/) 963 { 964 $is_archive = 1; 965 $actions = 966 "$actions <a class='action-link' href='extract.cgi?path=" . &urlize($path) . 967 "&file=" . &urlize($link) . "' title='$text{'extract_archive'}' data-container='body'>$extract_icon</a> "; 968 } 969 } 970 my @row_data = ("<a href='$href' data-filemin-link=\"$hlink\"" . 971 ($query ? " data-filemin-flink=\"$hlink_path\"" : undef) . "><img src=\"$img\"></a>", 972 "<a href=\"$href\" data-filemin-link=\"$hlink\"" . 973 ($query ? " data-filemin-flink=\"$hlink_path\"" : undef) . ">$vlink</a>"); 974 my @td_tags = (undef, 975 'class="col-icon"', 976 'class="col-name" data-xarchive="' . 977 $is_archive . '" data-xfile="' . $is_file . '" data-gpg="' . $is_gpg . 978 '" data-img="' . $is_img . '" data-order="' . ($is_file ? 1 : 0) . html_escape($filename) . '"'); 979 if ($userconfig{'columns'} =~ /type/) { 980 push(@row_data, $type); 981 push(@td_tags, 'class="col-type"'); 982 } 983 push @row_data, $actions; 984 push(@td_tags, 'class="col-actions"'); 985 986 if ($userconfig{'columns'} =~ /size/) { 987 my $size = &theme_nice_size_local($list[$count - 1][8]); 988 push @row_data, 989 ( 990"<span data-toggle=\"tooltip\" data-html=\"true\" data-title=\"$text{'theme_xhred_filemanager_global_size_in_bytes'}<br>@{[nice_number($list[$count - 1][8])]}\">" 991 . $size . "</span>"); 992 push(@td_tags, 'class="col-size"'); 993 } 994 if ($userconfig{'columns'} =~ /owner_user/) { 995 my $user; 996 my $group; 997 if (supports_users()) { 998 my $uid = getpwuid($list[$count - 1][5]); 999 my $gid = getgrgid($list[$count - 1][6]); 1000 $user = $uid ? $uid : $list[$count - 1][5]; 1001 $group = $gid ? $gid : $list[$count - 1][6]; 1002 } else { 1003 $user = $list[$count - 1][5]; 1004 $group = $list[$count - 1][6]; 1005 } 1006 push @row_data, 1007 ( 1008"<span data-toggle=\"tooltip\" data-html=\"true\" data-title=\"$text{'filemanager_global_user_group_id'}<br>$list[$count - 1][5]:$list[$count - 1][6]\">" 1009 . $user . ':' . $group . "</span>"); 1010 push(@td_tags, 'class="col-ownership"'); 1011 } 1012 1013 if ($userconfig{'columns'} =~ /permissions/) { 1014 my $permissions = sprintf("%04o", $list[$count - 1][3] & 07777); 1015 push @row_data, $permissions; 1016 push(@td_tags, 'class=col-permissions'); 1017 } 1018 1019 if (get_acls_status() && $userconfig{'columns'} =~ /acls/) { 1020 push @row_data, $list[$count - 1][19]; 1021 push(@td_tags, 'class="col-acls"'); 1022 } 1023 1024 if (get_attr_status() && $userconfig{'columns'} =~ /attributes/) { 1025 push @row_data, $list[$count - 1][18]; 1026 push(@td_tags, 'class="col-attrs"'); 1027 } 1028 if (get_selinux_status() && $userconfig{'columns'} =~ /selinux/) { 1029 push @row_data, $list[$count - 1][17]; 1030 push(@td_tags, 'class="col-selinux"'); 1031 } 1032 1033 if ($userconfig{'columns'} =~ /last_mod_time/) { 1034 my $access_time = POSIX::strftime('%Y/%m/%d - %T', localtime($list[$count - 1][9])); 1035 my $mod_time = POSIX::strftime('%Y/%m/%d - %T', localtime($list[$count - 1][10])); 1036 my $change_time = POSIX::strftime('%Y/%m/%d - %T', localtime($list[$count - 1][11])); 1037 push @row_data, 1038 ( 1039"<span data-toggle=\"tooltip\" data-html=\"true\" data-title=\"$text{'filemanager_global_access_change_time'}<br>$access_time<br>$change_time\">" 1040 . $mod_time . "</span>"); 1041 push(@td_tags, 'data-order="' . ($list[$count - 1][10]) . '" class="col-time"'); 1042 } 1043 1044 $list_data{'rows'} .= &ui_checked_columns_row(\@row_data, \@td_tags, "name", $vlink); 1045 } 1046 1047 $list_data{'form'} .= &ui_hidden("path", $path), "\n"; 1048 $list_data{'form'} .= '</form>'; 1049 $list_data{'success'} = (length $in{'success'} ? $in{'success'} : undef); 1050 $list_data{'error'} = (length $in{'error'} ? $in{'error'} : undef); 1051 $list_data{'error_fatal'} = (length $in{'error_fatal'} ? $in{'error_fatal'} : undef); 1052 $list_data{'output'} = (length $in{'output'} ? $in{'output'} : undef); 1053 $list_data{'page_requested'} = $page; 1054 $list_data{'pagination_requested'} = $in{'paginate'}; 1055 $list_data{'totals'} = $totals; 1056 $list_data{'searched'} = $query ? 1 : 0; 1057 $list_data{'flush'} = test_all_items_query() ? 1 : 0; 1058 $list_data{'flush_reset'} = $in{'flush_reset'} ? 1 : 0; 1059 $list_data{'udata'} = { user => $remote_user_info[0], 1060 home => $remote_user_info[7], 1061 uid => $remote_user_info[2], 1062 guid => $remote_user_info[3], 1063 allowed_paths => \@allowed_paths, 1064 base => $base, 1065 access => $access{'work_as_user'} }; 1066 1067 print_json([\%list_data]); 1068} 1069 1070sub get_tree 1071{ 1072 my ($p, $d, $e, $y) = @_; 1073 my %r; 1074 my @r; 1075 my $ic; 1076 my $rp; 1077 my $df = int($d); 1078 my @ap = @allowed_paths; 1079 my $fr = scalar(@ap) > 1; 1080 my @af = length($p) ? ($p) : @ap; 1081 my $fu = scalar(@af) == 1; 1082 1083 # Check the queried path is allowed in the first place 1084 if (length($p)) { 1085 return \@r if (grep {$_ =~ /^\Q$p\E/} @ap); 1086 } 1087 1088 my $wanted = sub { 1089 my $td = $File::Find::name; 1090 if (-d $td && !-l $td) { 1091 my $push_label = sub { 1092 my ($td, $afic) = @_; 1093 my ($pd, $cd) = $td =~ m|^ (.+) / ([^/]+) \z|x; 1094 my $pp = ($fu && $afic ne '/') ? $afic : undef; 1095 my $c = $r{$td} = 1096 { key => html_escape("$pp/$td"), title => (defined($cd) ? html_escape($cd) : html_escape($td)) }; 1097 defined $pd ? (push @{ $r{$pd}{'children'} }, $c) : (push @r, $c); 1098 }; 1099 1100 my $dc = $td =~ tr[/][]; 1101 my ($ix) = grep {$af[$_] eq $td} (0 .. @af - 1); 1102 $ic = $ix if (defined($ix)); 1103 my $afic = $af[$ic]; 1104 if (!grep {$afic =~ /^\Q$_\E/} @ap && 1105 !grep {$td =~ /^\Q$_\E/} @ap) 1106 { 1107 return; 1108 } 1109 1110 # Exclude non essentials on start 1111 if ($e && $afic eq '/' && $dc == 1) { 1112 if ($td =~ /^\/(cdrom|dev|lib|lost\+found|proc|run|snaps|sys|tmp|.trash)/i) { 1113 return; 1114 } 1115 } 1116 1117 # Home directory only 1118 if ($fu) { 1119 $td =~ s/^\Q$afic\E//; 1120 } 1121 1122 # Starting with sub-directory in multiple allowed paths 1123 elsif ($y && $fr && defined($ix) && $dc > 1) { 1124 my $tdx = $td; 1125 my @tdxs = split('/', $tdx); 1126 my @tdxss; 1127 for (my $i = 1; $i <= ($dc - 1); $i++) { 1128 push(@tdxss, $tdxs[$i]); 1129 my $tdxx = join("/", @tdxss); 1130 $tdxx =~ s|^\Q/\E/?||; 1131 &$push_label($tdxx, $afic); 1132 } 1133 } 1134 $td =~ s|^\Q/\E/?||; 1135 if ($r{$td} || !$td) { 1136 return; 1137 } 1138 &$push_label($td, $afic); 1139 } 1140 }; 1141 my $preprocess = sub { 1142 my $td = $File::Find::name; 1143 my $dc = $td =~ tr[/][]; 1144 my $xc = $af[$ic] =~ tr[/][]; 1145 my $dd = ($p || ($y && $af[$ic] ne '/')) ? $df + $xc : $df + 1; 1146 if ($dd) { 1147 if ($dc < $dd) { 1148 return sort {"\L$a" cmp "\L$b"} @_; 1149 } 1150 return; 1151 } 1152 return sort {"\L$a" cmp "\L$b"} @_; 1153 }; 1154 find( 1155 { wanted => $wanted, 1156 preprocess => $preprocess 1157 }, 1158 @af); 1159 return \@r; 1160} 1161 1162sub paster 1163{ 1164 my ($c, $f, $s, $d, $r, $m, $z) = @_; 1165 my $x; 1166 my $j = $c . ($f =~ m/^\// ? undef : '/') . $f; 1167 my $zz = sub { 1168 my ($q, $h) = @_; 1169 my ($u, $g); 1170 (undef, undef, $u) = getpwnam($h); 1171 $g = getgrnam($h); 1172 if (defined($u) && defined($g)) { 1173 system("chown -R $u:$g " . quotemeta("$q")); 1174 } 1175 }; 1176 1177 if (!$r && -f $j ne -d $j) { 1178 for (my $t = 1;; $t += 1) { 1179 my ($jn, $je) = $j =~ /(.*)\.(.*)/; 1180 if (!-e ($jn . '(' . $t . ')' . ".$je") && (!-e ($j . '(' . $t . ')'))) { 1181 $x = $t; 1182 last; 1183 } 1184 } 1185 } 1186 $s =~ s/\/\//\//g; 1187 if ($m && -d $j && $j =~ /^\Q$s\E/) { 1188 set_response('merr'); 1189 return; 1190 } 1191 if (-d $j) { 1192 $j = $j . (!$x ? '' : '(' . $x . ')'); 1193 } else { 1194 my ($jn, $je) = $j =~ /(.*)\.(.*)/; 1195 if ($je) { 1196 $j = $jn . (!$x ? '' : '(' . $x . ')') . ".$je"; 1197 } else { 1198 $j = $j . (!$x ? '' : '(' . $x . ')'); 1199 } 1200 } 1201 my ($o, $e); 1202 if ($m) { 1203 $o = move($s, $j); 1204 &$zz($j, $z) if ($o); 1205 if (!$o && $!) { 1206 $e = $!; 1207 } 1208 } else { 1209 ($o, $e) = copy_source_dest($s, $j); 1210 &$zz($j, $z) if ($o); 1211 } 1212 if ($x) { 1213 set_response('cc'); 1214 } 1215 1216 return $e; 1217 1218} 1219 1220sub get_element_index 1221{ 1222 my ($arr, $elem) = @_; 1223 my $idx; 1224 for my $i (0 .. $#$arr) { 1225 if ($arr->[$i] eq $elem) { 1226 $idx = $i; 1227 last; 1228 } 1229 } 1230 return $idx; 1231} 1232 1233sub get_tar_verbatim 1234{ 1235 my $test_param = 'verbatim-files-from'; 1236 my $out = &backquote_command("tar --help |grep $test_param"); 1237 if ($out && $out =~ /$test_param/m) { 1238 return " --$test_param"; 1239 } 1240 return ""; 1241} 1242 1243sub get_gpg_version 1244{ 1245 my ($gpg) = @_; 1246 $gpg = "gpg" if (!$gpg); 1247 $gpg = quotemeta($gpg); 1248 $gpg = `$gpg --version`; 1249 $gpg =~ /(\*|\d+(\.\d+){0,2})/; 1250 return $1; 1251} 1252 1253sub get_gpg_path 1254{ 1255 my $gnupg = 'gnupg'; 1256 my $gnupg_target = foreign_available($gnupg) ? $gnupg : get_product_name(); 1257 my %gpgconfig = foreign_config($gnupg_target); 1258 my $gpgpath = quotemeta($gpgconfig{'gpg'} || "gpg"); 1259 return $gpgpath; 1260 1261} 1262 1263sub switch_to_user 1264{ 1265 if (!supports_users()) { 1266 return undef; 1267 } 1268 my ($username) = @_; 1269 my @uinfo = getpwnam($username); 1270 if (@uinfo) { 1271 switch_to_unix_user(\@uinfo); 1272 } 1273} 1274 1275sub is_linux 1276{ 1277 return $gconfig{'os_type'} =~ /-linux$/; 1278} 1279 1280sub is_root 1281{ 1282 return ($base_remote_user eq 'root' ? 1 : 0); 1283} 1284 1285sub get_env 1286{ 1287 my ($key) = @_; 1288 return $ENV{ uc($key) }; 1289} 1290 1291sub set_env 1292{ 1293 my ($k, $v) = @_; 1294 $ENV{ uc($k) } = $v; 1295} 1296 12971; 1298