package ONO::IO;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################
use CGI;
use strict;
use ONO::Lib::Basic;
use ONO::Core::Kernel;
use ONO::Core::HostOS;
use ONO::Lib::DateTime::ToolBox;
use ONO::Lib::Web::Client;
###############################################################################
# ONO
###############################################################################
#: ONO::IO is responsible for all file and directory accesses, as long as they
#: are located within the ONO document root directory. All I/O should either
#: be handled by ONO::IO, or by ONO::Core::HostIO, you should not use low level
#: Perl I/O stuff in your ONO projects.
#:
#: Important I/O calls include:
#:
#; load(), list(), store(), append(), dump(), ls() / dir(), mkdir(), mkpath(), exists(), mv(), cp(), cpr(), rm(), rmdir()
#:
#: ONO::IO also allows to manage cache and configuration files for example:
#; cacheread(), cachewrite(), confread(), confwrite()
#:
#: Most ONO::IO calls respect the presence of thumbnail and preview files,
#: those can also be detected if necessary:
#; getthumbs(), getthumbs_ifexists()
#:
#: Furthermore, the module offers access to system information and allows for
#: system interaction:
#; os(), cpu(), mem(), disks(), domain(), sys(), exec(), http(), https(), http_domain_base(), sysload()
#:
#: ONO::IO also helps with software development, by detecting development
#: stations and offering appropriate tools, for example:
#; devstation(), devprint(), debug()
#:
#: For file and directory I/O outside of the ONO document root, see
#: ONO::Core::HostIO.
my @SYSLOADMSG = (
"This content cannot be displayed right now. Please try again in a few minutes – Thank you!",
"Dieser Inhalt kann momentan nicht angezeigt werden. Bitte versuche es in ein paar Minuten nochmals - Vielen Dank!",
"Ce contenu ne peut pas être affiché actuellement. Veuillez réessayer dans quelques minutes - Merci beaucoup!",
"Dëse Contenu ka momentan net ugewise ginn. Probéier w.e.g. an e puer Minutte nach eng Kéier - Villmols Merci!",
);
my $PIXEL = "Content-Type: image/gif\n\n";
open (FILE, &path."/ono/osr/images/spacer/trans.gif");
flock FILE, 1;
$PIXEL .= <FILE>;
close (FILE);
my ($devstation_file,$devstation_option);
if (&exists("","cgi-bin/devstation.txt")) {
$devstation_file = 1;
}
# make sure that devstation will run cron bots too:
if (-e "/mnt/ono/cgi-bin/devstation.txt" && !$ENV{'SERVER_NAME'}) {
$devstation_option = 1;
}
my $is_local;
if ($ENV{'SERVER_NAME'} eq "127.0.0.1" || $ENV{'SERVER_NAME'} =~ /^192\.168\./ || $ENV{'SERVER_NAME'} =~ /^172\.16\./ || $ENV{'SERVER_NAME'} =~ /\.local$/) {
$is_local = 1;
}
###############################################################################
# SUBS
###############################################################################
sub path {
#: Returns the UNIX path to the ONO document root directory. This is the
#: same as ONO::Core::Kernel->vpath, so you may simply get this info via
#: ONO::IO.
return ONO::Core::Kernel->vpath;
}
sub binary_dirs {
#: List UNIX directories that may contain useful UNIX binaries that can be
#: used by certain ONO::Lib modules for example.
return ("/usr/bin","/usr/local/bin","/opt/local/bin");
}
sub base {
#: Return the software base (if cgi-bin is not on the document root level,
#: as in www.example.com/path/lang/cgi-bin/... for example). Note that the
#: base always needs to be composed of 2 subdirectories.
my $base;
if (!$ENV{'SCRIPT_NAME'}) {
if (&exists("","etc/software_base.conf")) {
$base = &load("","etc/software_base.conf");
$base =~ s~(\n|\r|\t)~~g;
}
} else {
if ($ENV{'SCRIPT_NAME'} =~ m~/(.*?)/(.*?)/cgi-bin/~) {
$base = "/$1/$2";
}
}
return $base;
}
sub project {
#: Return the name of an ONO project, as in
#: www.example.com/cgi-bin/local/perl/project_name/... for example.
my $community;
if ($ENV{'SCRIPT_NAME'} =~ m~/cgi-bin/local/perl/(.*?)/~) {
$community = $1;
}
return $community;
}
sub community {
#: Same as project().
return &project();
}
sub path2dir {
#: Demove everything behind the last slash (including the last slash itself).
#: This will remove any file info from the directory and return the pure
#: directory path.
#:
#: Also see getdir(), getdirfile(), and getdirfileext(), which will be more
#: useful in most cases.
my $path = $_[1];
$path =~ s~/[^/]*$~~;
return $path;
}
sub cachefile {
#: Turn a file (directory/path plus filename) into a cache ID filename.
#: This is usually not required, but it's used internally by other
#: cache related functions.
my ($self,$file) = @_;
$file = ONO::Lib::Basic->remove_doubles($file,"-");
for (my $i = 0; $i < 4; $i++) {
$file =~ s~-~/~;
}
return $file;
}
sub cacheexists {
#: Check if a cache entry exists (returns 1 if yes, or 0 if not).
my ($self,$file) = @_;
$file = &cachefile("",$file);
if (&exists("","var/tmp/cache/$file.txt")) {
return 1;
} else {
return 0;
}
}
sub cacheclean {
#: Removes a cache file.
#:
#: Note that empty dirs in /var/tmp/cache are not being removed, which
#: means that some sort of garbage collection will be required some day...
my ($self,$file) = @_;
$file = &cachefile("",$file);
&rm("","var/tmp/cache/$file.txt");
}
sub cachetimestamp {
#: Get the timestamp of a cache file, which may be useful to decide
#: if it's time to remove or replace it.
my ($self,$file) = @_;
$file = &cachefile("",$file);
return ×tamp("","var/tmp/cache/$file.txt");
}
sub cachereadsimple {
#: Simplified form of cacheread(), simple and fast, supplies no useful info,
#: just returns the content of the cache, doesn't automatically manage or
#: delete anything by itself.
my ($self,$file,$verbose) = @_;
$file = &cachefile("",$file);
if ($verbose) {
return "<!-- ONOIOCacheStart -->".&load("","var/tmp/cache/$file.txt")."<!-- ONOIOCacheEnd -->";
} else {
return &load("","var/tmp/cache/$file.txt");
}
}
sub cachelistsimple {
#: Simplified form of cachelist(), simple and fast, supplies no useful info,
#: just returns the content of the cache, doesn't automatically manage or
#: delete anything by itself.
my ($self,$file) = @_;
$file = &cachefile("",$file);
return &list("","var/tmp/cache/$file.txt");
}
sub cacheread {
#: Read a cache file, and do some cache management at the same time.
#:
#: If $time is provided, then we'll only read the cache if it's not older
#: than T minutes. if it's older, then the cache file will simply be removed.
#:
#: Switches:
#:
#: -d debugging mode
#: -L list mode, allows to read arrays
#: -S silent mode
#: -v verbose (can be useful when debuggin HTML)
#: -V verbose only if not empty
#:
#: If no options and no cache management is required, then cachereadsimple()
#: will be a better and faster option here.
my ($self,$file,$time,$switches) = @_;
$file = &cachefile("",$file);
my $TIME;
if ($time) {
$TIME = ×tamp("","var/tmp/cache/$file.txt");
my $AGE = (int((time() - $TIME)/6)/10);
if ($AGE < $time) {
$time = 0;
if ($switches !~ /S/) {
$TIME = "\n<!-- ONOCacheAge:${AGE}m -->";
}
}
}
if (!$time) {
if ($switches =~ /L/) {
# return an array
my (@array,$count);
foreach my $line (&list("","var/tmp/cache/$file.txt")) {
$count++;
$line =~ s~\n$~~;
@array = (@array,$line);
}
if ($count) {
return @array;
} else {
return ();
}
} else {
# return a string
my $CACHE = &load("","var/tmp/cache/$file.txt");
if ($switches =~ /v/) {
if ($switches !~ /V/ || $CACHE) {
return "\n<!-- ONOCacheStart:$file -->$TIME\n$CACHE\n<!-- ONOCacheEnd:$file -->\n";
} else {
return $CACHE;
}
} else {
return $CACHE;
}
}
} else {
&rm("","var/tmp/cache/$file.txt");
if ($switches =~ /L/) {
return ();
} else {
return "";
}
}
}
sub cachewritesimple {
#: Simplified form of cachewrite(), simple and fast, doesn't do anything special.
#: Just writes the data, doesn't automatically manage or
#: delete anything by itself.
my ($self,$file,$data) = @_;
$file = &cachefile("",$file);
my ($path) = &getdirfile("","var/tmp/cache/$file.txt");
&mkpath("",$path);
&store_rm("","var/tmp/cache/$file.txt",$data);
}
sub cachewrite {
#: Write a cache file, and do some cache management at the same time.
#:
#: Switches:
#:
#: -D delete if empty
#: -E don't write if empty
#: -L list mode, allows to store arrays, requires $data to be a hash reference
#: -v verbose
#: -V Verbose only if not empty
#:
#: If no options and no cache management is required, then cachewritesimple()
#: will be a better and faster option here.
my ($self,$file,$data,$switches) = @_;
$file = &cachefile("",$file);
if ($switches !~ /L/ && $switches =~ /v/ && ($switches !~ /V/ || $data)) {
my $time = ONO::Lib::DateTime::ToolBox->convert("classic",time());
$data = "<!-- ONOCacheTimestamp:$time -->\n$data";
}
if ($switches =~ /L/) {
my @lines = @$data;
$data = "";
foreach my $line (@lines) {
$data .= "$line\n";
}
}
if ($switches !~ /E/ || $data) {
my ($path) = &getdirfile("","var/tmp/cache/$file.txt");
&mkpath("",$path,777);
&store("","var/tmp/cache/$file.txt",$data);
}
if ($switches =~ /D/ && !$data) {
&rm("","var/tmp/cache/$file.txt");
}
}
sub cleanup {
#: The cleanup() function will remove all special characters form a
#: filename, directory name, or a path. The override option may be
#: used for any data stored within the /servers tree.
my ($self,$file,$override) = @_;
$file =~ s~^/~~;
if ($override && $file =~ m~^servers/~) {
# server mode allows override
} else {
# cleanup will not be executed on files located on a fileserver
if ($file !~ m~^servers/~) {
$file =~ s~[^A-Za-z0-9\_\-\.\/\@\(\)]~~g;
}
}
return $file;
}
sub colread {
#: This allows to quickly read files in name:value list format,
#: data will be returned in hash format.
my %conf;
foreach my $line (&list("",$_[1])) {
$line =~ s~(\n|\r|\t)~~g;
if ($line =~ /^(.*?):(.*?)$/) {
$conf{$1} = $2;
}
}
return %conf;
}
sub confread {
my ($self,$file,$switches) = @_;
#: Read an ONO configuration file.
#:
#: Switches:
#:
#: -L make value lower case
my %conf;
foreach my $line (&list("",$file)) {
if (substr($line,0,1) ne "#" && $line =~ /[A-Za-z0-9]/) {
my @lp = split(/ /,$line);
$line =~ s~^$lp[0] ~~;
if ($switches !~ /L/) {
$lp[0] = lc $lp[0];
}
if ($line =~ /("|')/) {
my $separator = $1;
$line =~ /$separator(.*)$separator/;
$lp[1] = $1;
}
$lp[1] =~ s/\n//;
if ($lp[0]) {
$conf{"$lp[0]"} = $lp[1];
}
}
}
return %conf;
}
sub confwrite {
#: Write an ONO configuration file.
#: This is basically the same as the store() function, which means that
#: YOU will need to take care of formatting the data before sending it.
&store("",$_[1],$_[2]);
}
sub confupdate {
#: Update an ONO configuration file, which replaces the $value of the
#: $name entry. The remaining data will not be touched.
my ($self,$file,$name,$value) = @_;
my ($DATA,$exists);
foreach my $line (&list("",$file)) {
$line =~ s~(\n|\r|\t)~~g;
if ($exists == 0 && ($line =~ /^$name / || $line =~ /^$name$/ || $line =~ /^(#|# )$name / || $line =~ /^(#|# )$name$/)) {
# disable the line
if (($line =~ /^$name / || $line =~ /^$name$/) && $value eq "#") {
$line = "#$line";
$exists = 1;
}
# save an empty value, but leave activated
if (!$value) {
$line = qq~$name~;
$exists = 1;
}
# update the line
if ($value && $value ne "#") {
if ($value =~ / /) {
$value = qq~"$value"~;
}
$line = qq~$name $value~;
$exists = 1;
}
}
$DATA .= "$line\n";
}
if (!$exists) {
$DATA .= "\n$name $value\n";
}
&store("",$file,$DATA);
}
sub curl {
#: Retrieve a file from the web, return the data as a stream.
#: Be careful not to pass the input unchecked, and don't allow
#: user input to be passed to this function!
my ($self,$url) = @_;
if ($url && $url !~ /^http/) {
$url = "http://$url";
}
$url !~ s~(\`|\||\')~~g;
return `curl -k '$url'`;
}
sub curlstore {
#: Same as curl(), but dump / store the data into a file.
my ($self,$url,$file) = @_;
my $CURL = &curl("",$url);
if ($CURL) {
$file =~ s~[^A-Za-z0-9\_\-\.\/\@\(\)]~~g;
&dump("",$file,$CURL);
}
}
sub debug {
#: Append to /__debug.txt. This should not be used on production systems,
#: but only on development stations. It may be used temporarely on
#: production systems to analyze problems that need a quick fix, but
#: make sure to remove /debug.txt later on then.
my ($self,$data) = @_;
my $vpath = &path;
open (FILE,">>$vpath/__debug.txt");
print FILE $data;
close FILE;
}
sub device {
#: Return the type of device in use.
#:
#: 3 ways to use this:
#:
#: device() will return either computer, tablet or phone
#:
#: device(type) will return either 0 or 1
#:
#: device("useragent") will return the user agent
#:
#: iPad Warning:
#:
#: Note that current iPads identify themselves as Intel Macs.
#: A dirty trick is needed to identify them correcty:
#: 1. First test if it's a Mac, using ONO;;IO->device("mac")
#: 2. The use if (navigator.maxTouchPoints > 1) {...} to execute JS
my $input = $_[1];
my $device = "computer";
if ($ENV{'HTTP_USER_AGENT'} =~ /(mobile|tablet|ipad|android)/i) {
$device = "tablet";
}
if ($ENV{'HTTP_USER_AGENT'} =~ /phone/i) {
$device = "phone";
}
if ($input) {
$device = 0;
if ($input eq "computer" && $ENV{'HTTP_USER_AGENT'} !~ /(mobile|tablet|ipad|android|phone)/i) {
$device = 1;
}
if ($input eq "mac" && $ENV{'HTTP_USER_AGENT'} =~ /macintosh/i) {
$device = 1;
}
if ($input eq "tablet" && $ENV{'HTTP_USER_AGENT'} =~ /(mobile|tablet|ipad|android)/i) {
$device = 1;
}
if ($input eq "phone" && $ENV{'HTTP_USER_AGENT'} =~ /phone/i && $ENV{'HTTP_USER_AGENT'} !~ /ipad/i) {
$device = 1;
}
if ($input eq "mobile" && $ENV{'HTTP_USER_AGENT'} =~ /(mobile|tablet|ipad|android|iphone)/i) {
$device = 1;
}
if ($input eq "useragent") {
$device = $ENV{'HTTP_USER_AGENT'};
}
}
return $device;
}
sub devstation {
#: This will return 1 if we're on a devstation, or 0 if we're on a production system.
#: This will only work if...
#:
#: ... we're in a local network and a file named /cgi-bin/devstation.txt is available,
#: or ...
#:
#: ... the file /mnt/ono/cgi-bin/devstation.txt is present on the UNIX root level.
#:
#: Note that the devstation.txt file should contain the devstation client's IP
#: address as content.
if (($devstation_file && &local()) || $devstation_option) {
return &load("","cgi-bin/devstation.txt");
} else {
return 0;
}
}
sub devstation_variation {
#: This will return the variation code (if present) which will allow to test
#: different domain-based variations of a site/platform on your devstation.
#:
#: This is used by the variation call in ONO_Lib_Web_Doamin for example, which
#: should be the default method to check for variations of a platform.
#:
#: The variation code should be in /cgi-bin/devstation_variation.txt, and this
#: will only work on a devstation, not on a production system.
if (($devstation_file && &local()) || $devstation_option) {
return &load("","cgi-bin/devstation_variation.txt");
} else {
return "";
}
}
sub devstation_refresh {
#: Only refresh if devstation - probably deprecated ?
# if (&devstation) {
# my $DEVSTATION_IP = &devstation();
# if ($ENV{'SERVER_NAME'} eq "127.0.0.1" && $DEVSTATION_IP ne "127.0.0.1") {
# my $REQUEST_URI = $ENV{'REQUEST_URI'};
# return qq~<meta http-equiv="refresh" content="0; url=http://$DEVSTATION_IP$REQUEST_URI">\n~;
# } else {
# return "";
# }
# } else {
# return "";
# }
return "";
}
sub devprint {
#: Return content only if we're in devstation mode. This allows to print
#: debugging info on a web page on a development station, this info will
#: not be displayed on production systems for example.
#: Args(int): disable, replace return/linefeed by HTML br, bold
if (&devstation && !$_[2]) {
if ($ENV{'HTTP_USER_AGENT'}) {
my $DEV = $_[1];
if ($_[3]) {
$DEV =~ s~(\n|\r)~<br>~g;
}
if ($_[4]) {
return qq~<div class="bold">$DEV</div>~;
} else {
return qq~<div>$DEV</div>~;
}
} else {
return $_[1];
}
} else {
return "";
}
}
sub devstore {
#: Like store, but will only execute on a devstation, which may be useful
#: when debugging code.
if (&devstation) {
&store("",$_[1],$_[2]);
}
}
sub devdump {
#: Write to /__devdump.txt if we're on a devstation, so this can be used for
#: debugging purposes. The file will be overwritten each time, so this should
#: be used at the end of the script. Date and time will be automatically added
#: to the top of the content.
if (&devstation) {
my (
$sec,$min,$hour,
$mday,$mon,$year,
$wday,$yday,$isdst
) = ONO::Lib::DateTime::ToolBox->get;
if ($_[2]) {
&append("","__devdump.txt","$mday/$mon/$year \@ $hour:$min:$sec:\n\n$_[1]");
} else {
&store("","__devdump.txt","$mday/$mon/$year \@ $hour:$min:$sec:\n\n$_[1]");
}
return $_[1];
}
}
sub devguessuri {
#: If we're on a devstation, then guess the absolute URI, based on the name
#: of the input script. This can be use useful to debug links.
my $URI = $ENV{'REQUEST_URI'};
if (&devstation) {
$URI =~ s~^(.*)/local/perl/(.*?)/~~;
$URI =~ s~^index_~~;
$URI =~ s~\.pl(.*)$~~;
$URI = "/$URI/";
}
return $URI;
}
sub local {
#: Are we in a local network? Returns 1 = yes, 0 = no. This will only work
#: if the SERVER_NAME is an IP address, not a domain, unless the domain
#: will be of type example.local.
if ($is_local) {
return 1;
} else {
return 0;
}
}
sub dump {
#: Dump data to a file, without any processing.
#: Use this only if store() stoesn't work for some reason.
#: This may be used to store uploaded files containing special data for
#: example.
my ($self,$file,$data) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,">$vpath/$file");
print FILE $data;
close FILE;
}
sub dump_append {
#: Dump data to a file, without any processing.
#: Use this only if append() stoesn't work for some reason.
my ($self,$file,$data) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,">>$vpath/$file");
print FILE $data;
close FILE;
}
sub dump_load {
#: Load an entire file, return as string, don't process anything.
#: Use this only if load() stoesn't work for some reason.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE, "$vpath/$file");
my @DATAS = <FILE>;
close FILE;
my $DATA;
foreach my $data (@DATAS) {
$DATA .= $data;
}
return $DATA;
}
sub exists {
#: Check if file or path exists. Returns 1 if exists, or nothing if the
#: object doesn't exist.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
my $exists;
if (-e "$vpath/$file") {
$exists = 1;
}
return $exists;
}
sub exists_wait {
#: Wait until a file has been created, which is useful if complex file
#: generation tools are being used. Waiting for up to 3 secs is default,
#: higher values can be set manually.
my ($self,$file,$secs) = @_;
if (!$secs) {
$secs = 3;
}
my $whilecounter;
while ($whilecounter < $secs && !(&exists("",$file))) {
$whilecounter++;
sleep 1;
}
}
sub open {
#: This will simply open a file handle, as required by
#: ONO_Lib_Image_Check for example. Also see close().
my $file = &cleanup("",$_[1]);
my $vpath = &path;
open my $FH,"$vpath/$file";
return $FH;
}
sub close {
#: This will simply close a file handle. Also see open().
close $_[1];
}
sub load {
#: Load a file, return its content.
#:
#: Switches:
#:
#: -n remove new lines
#: -h HTML: replace \n by <br>
my (
$self,
$file,
$switches,
) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
my $dump;
foreach my $dat (@data) {
$dat =~ s~\r~~g;
if ($switches =~ /n/) {
$dat =~ s~\n~~g;
}
if ($switches =~ /h/) {
$dat =~ s~\n~<br>~g;
}
$dump .= $dat;
}
return $dump;
}
sub list {
#: List a file. Similar to load(), but returns lines as an array.
#:
#: Switches:
#:
#: -E don't return lines that don't contain numbers or letters
my (
$self,$file,$switches,
) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
my @new_data;
foreach my $dat (@data) {
if ($switches !~ /E/ || $dat =~ /[A-Za-z0-9]/) {
$dat =~ s~\r~~g;
@new_data = (@new_data,$dat);
}
}
return @new_data;
}
sub list_raw {
#: List_raw() is identical to list(), but allows to load files without
#: checking the file name, which means that it also loads files with
#: special characters.
my ($self,$file) = @_;
my $vpath = &path;
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
my @new_data;
foreach my $dat (@data) {
$dat =~ s~\r~~g;
@new_data = (@new_data,$dat);
}
return @new_data;
}
sub list_raw_fast {
#: List_raw_fast() is identical to list_raw(), but it's faster as it
#: doesn't clean up or check anything
my ($self,$file) = @_;
my $vpath = &path;
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
return @data;
}
sub list_line {
#: Like list(), but returns only one specific line from the file, which
#: must be indicated as a number.
my ($self,$file,$line) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
my ($return,$counter);
foreach my $dat (@data) {
$counter++;
if ($counter == $line) {
$dat =~ s~\r~~g;
$return = $dat;
}
}
return $return;
}
sub chomplist {
#: Similar to list(), but removes all carriage returns, lewlines and
#: tabs.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
my @new_data;
foreach my $dat (@data) {
$dat =~ s~(\r|\n|\t)~~g;
@new_data = (@new_data,$dat);
}
return @new_data;
}
sub quicklist {
#: Return file as content as an array, without doing anything else.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,"$vpath/$file");
my @data = <FILE>;
close FILE;
return @data;
}
sub save {
# Same as store(), and you should use store() instead.
my ($self,$file,@data) = @_;
&store($self,$file,@data);
}
sub create {
#: An intelligent version of "store" that will not overwrite
#: existing files, but add a number to the filename, if the
#: file already exists.
my ($self,$dir,$file,$ext,@data) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
$file = &cleanup("",$file);
$ext = &cleanup("",$ext);
if (!$file) {
$file = "untitled";
}
if (!$ext) {
$ext = "txt";
}
my $filename = $file;
my $whilecounter;
while ($whilecounter < 8192 && &exists("","$dir/$filename.$ext")) {
$whilecounter++;
$filename = "$file\_$whilecounter";
}
open (FILE,">$vpath/$dir/$filename.$ext");
foreach my $dat (@data) {
print FILE $dat;
}
close FILE;
return $filename;
}
sub store {
#: Store a file. If the file exists, it will be overwritten.
#: Data can be a string, or an array.
my ($self,$file,@data) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,">$vpath/$file");
foreach my $dat (@data) {
print FILE $dat;
}
close FILE;
}
sub store_rm {
#: Store a file if data is provided (just like store()), remove
#: the file if data is empty. Unlinke store(), data cannot be
#: an array, online strings are supported.
my ($self,$file,$data) = @_;
if ($data) {
&store("",$file,$data);
} else {
&rm("",$file);
}
}
sub append {
#: Append data to a file, data can be a string or an array.
my ($self,$file,@data) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
open (FILE,">>$vpath/$file");
foreach my $dat (@data) {
print FILE $dat;
}
close FILE;
}
sub copy {
#: Create a copy of of filename.ext, at the same location. The new
#: file will be named filename_copy.ext. If filename_copy.ext exists,
#: the name will become filename_copy_X.ext, where X is a number
#: starting at 1.
#:
#: If present, thumbnails will also be copied.
my ($self,$path) = @_;
my $vpath = &path;
$path = &cleanup("",$path);
my ($dir,$file,$ext) = &getdirfileext("",$path);
my $new = "${file}_copy.$ext";
my $whilecounter;
while (&exists("","$dir/$new") && $whilecounter < 1000) {
$whilecounter++;
$new = "${file}_copy_$whilecounter.$ext";
}
`cp "$vpath/$path" "$vpath/$dir/$new"`;
my ($pr1,$tn1) = &getthumbs("",$path);
if ($pr1 && $tn1 && (-e "$vpath/$pr1" || -e "$vpath/$tn1")) {
my ($pr2,$tn2) = &getthumbs("","$dir/$new");
`cp "$vpath/$pr1" "$vpath/$dir/$pr2"`;
`cp "$vpath/$tn1" "$vpath/$dir/$tn2"`;
}
return $new;
}
sub rm {
#: Remove (aka delete) a file, including its (optional)
#: thumbnails and setup files.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
if ($file =~ /[A-Za-z0-9]/) {
if (-e "$vpath/$file") {
`rm "$vpath/$file"`;
}
my ($pr1,$tn1) = &getthumbs("",$file);
if ($pr1 && $tn1 && (-e "$vpath/$pr1" || -e "$vpath/$tn1")) {
`rm "$vpath/$pr1"`;
`rm "$vpath/$tn1"`;
}
my $setup = &getsetup("",$file);
if ($setup && -e "$vpath/$setup") {
`rm "$vpath/$setup"`;
}
}
}
sub rmimage {
#: Like rm(), but a special version that will additionally remove the
#: file with multiple possible image extensions.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
foreach my $ext (&imageexts) {
&rm("","$file.$ext");
}
}
sub renumber {
#: Renumber files and/or directories in a directory, shtml/setup
#: files as well as pt/tn files will be ignored. Note that all thumbs
#: and previews will be removed during this process.
#:
#: -d renumber directories
#: -f renumber files
#: -u keep string after underscore
#: -U collect numbering if underscore (001_x and 001_y will be kept as is)
my (
$self,
$dir,
$start,
$step,
$switches,
) = @_;
# first we rename the files that shall be renumbered
foreach my $obj (sort &ls("",$dir)) {
if ($obj !~ /^\./ && $obj !~ /\.(setup|shtml)$/) {
if ($switches =~ /f/ && $obj =~ /\./) {
&mv("","$dir/$obj","$dir/onoio_renumber_$obj");
my ($pr,$tn) = &getthumbs("",$obj);
&rm("","$dir/$pr");
&rm("","$dir/$tn");
}
if ($switches =~ /d/ && $obj !~ /\./) {
&mv("","$dir/$obj","$dir/onoio_renumber_$obj");
}
}
}
# list files, and number them back...
my $number = $start;
my ($counter,$string_mode,$last_pre);
foreach my $obj (sort &ls("",$dir)) {
if ($obj =~ /^onoio_renumber_/ && $obj !~ /\.(setup|shtml)$/) {
my ($pre,$string);
if ($switches =~ /f/ && $switches =~ /u/ && $obj =~ /^onoio_renumber_(.*?)_(.*)\./) {
$pre = $1;
$string = "_$2";
}
if ($switches =~ /d/ && $switches =~ /u/ && $obj =~ /^onoio_renumber_(.*?)_(.*)$/) {
$pre = $1;
$string = "_$2";
}
my $override;
if ($switches =~ /U/ && $string && $pre eq $last_pre) {
$override = 1;
if (!$string_mode) {
$override = 0;
}
}
if ($counter && !$override) {
$number = $number + $step;
if (length $start > length $number) {
$number = ONO::Lib::Basic->add_leading_zeroes($number,length $start);
}
}
if ($string) {
$string_mode = 1;
} else {
$string_mode = 0;
}
$counter++;
$last_pre = $pre;
if ($obj =~ /\./) {
# file
my ($file,$ext) = &getfileext("",$obj);
&mv("","$dir/$obj","$dir/$number$string.$ext");
} else {
# dir
&mv("","$dir/$obj","$dir/$number$string");
}
}
}
}
sub dirfile {
#: Is this a directory (returns 'dir') or a file (returns 'file') ?
my $type = "dir";
if ($_[1] =~ /\./) {
$type = "file";
}
if ($_[2] eq "mediaserver") {
my $vpath = &path;
if (-d "$vpath/$_[1]") {
$type = "dir";
} else {
$type = "file";
}
if (&dirisfile("",$_[1])) {
$type = "file";
}
}
return $type;
}
sub dirisfile {
#: Extensions that will always be treated as files, even if they're dirs.
if ($_[1] =~ /\.(app|jar|pkg|mpkg|logicx|fcpbundle)$/) {
return 1;
} else {
return 0;
}
}
sub fileisdir {
#: Extensions used by files that are actually not files, but dirs
if ($_[1] =~ /\.(app|logicx|fcpbundle)$/) {
return 1;
} else {
return 0;
}
}
sub ls {
#: Same as dir().
my ($self,$dir,$filter,$ext,$sort,$opts,$excluded,$prefix) = @_;
&dir("",$dir,$filter,$ext,$sort,$opts,$excluded,$prefix);
}
sub lst {
#: List directory items by timestamp. This will simply dump the UNIX
#: ls output.
my ($self,$dir) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
my @objects = `ls -lt $vpath/$dir`;
return @objects;
}
sub dir {
#: List dirs and files.
#:
#: Switches:
#:
#: -B omit Base directory from list
#: -C cleanup (remove slashes at beginning)
#: -D only directories
#: -H no hidden files (starting by a dot)
#: -L list (option list for HTML forms)
#: -O option list -> shorten names if very long
#: -R recursive (does NOT work with sort mode! -- the -H switch is automatically enabled -- use -X to improve speed and to reduce CPU load)
#: -s sort (works with both list and string)
#: -S return list as a string (useful when using the -L option for example)
#: -W RAW mode allows special characters
#: -X use UNIX ls command to get recursive directories (only affects the -R switch)
#:
#: Note that the RX modes will ONLY list directories, NOT files!
my (
$self,$dir,$filter,$ext,$sort,$opts,$excluded,$prefix
) = @_;
my $vpath = &path;
if ($opts =~ /W/) {
$dir = &cleanup("",$dir);
$filter = &cleanup("",$filter);
}
my @objects;
if ($sort eq "size" || $sort eq "time") {
if ($sort eq "size") {
foreach my $item (`ls -S $vpath/$dir | tr '\n' '\n'`) {
$item =~ s~(\n|\r|\t)~~g;
@objects = (@objects,$item);
}
}
if ($sort eq "time") {
# this ONLY works for files, not for dirs (will be omitted)
# used by apps exercise temporary storage, make sure not to
# break this when making modifications !!!
foreach my $item (`ls -lt $vpath/$dir`) {
if ($item !~ /^\./ && $item =~ /\./) {
$item =~ s~(\n|\r|\t)~~g;
$item =~ s~^(.*) ~~;
@objects = (@objects,$item);
}
}
}
} else {
opendir(DIR, "$vpath/$dir");
if (!$sort) {
if ($opts =~ /R/) {
if ($opts =~ /X/) {
@objects = &_dir_recursive_ux("",$dir,$dir);
} else {
@objects = &_dir_recursive("",$dir,$dir);
}
} else {
@objects = readdir(DIR);
}
}
if ($sort eq "date") {
@objects = sort {-M $a <=> -M $b} map {"$vpath/$dir/$_"} readdir(DIR);
}
closedir DIR;
}
if (!$sort) {
@objects = sort @objects;
}
if ($sort eq "date") {
my $counter;
foreach my $object (@objects) {
$objects[$counter] =~ s~$vpath/$dir/~~;
$counter++;
}
}
my ($string,$excluded_recursive);
if ($excluded =~ s/\*$//) {
$excluded_recursive++;
}
if ($filter || $ext || $opts || $excluded) {
my @new;
foreach my $object (@objects) {
my $fext = $object;
$fext =~ s~^(.*)\.~~;
my $exclude;
if ($opts =~ /B/ && $object eq $dir) {
$exclude++;
}
if ($opts =~ /C/) {
$object =~ s~^/~~;
}
if (($opts =~ /H/ && $object =~ /^\./) || ($opts =~ /D/ && $object =~ /\./) || $object eq $excluded || "$object/" eq $excluded) {
$exclude++;
}
if ($excluded_recursive && "$object/" =~ /^$excluded/) {
$exclude++;
}
if ((!$filter || $object =~ /$filter/) && (!$ext || $object =~ /\.$ext$/ || ":$ext:" =~ /:$fext:/) && !$exclude) {
if ($opts =~ /L/) {
my $object2 = $object;
if ($opts =~ /O/ && length $object2 > 44) {
$object2 = substr($object,0,16)." (...) ".substr($object,(length $object)-24,24);
}
$object = qq~<option value="$prefix$object">$prefix$object2</option>~;
}
@new = (@new,$object);
$string .= $object;
}
}
@objects = @new;
}
if ($opts =~ /s/) {
@objects = sort @objects;
if ($opts =~ /S/) {
$string = "";
foreach my $object (@objects) {
$string .= $object;
}
}
}
if ($opts =~ /S/) {
return $string;
} else {
return @objects;
}
}
sub _dir_recursive {
#: Do NOT use this routine if possible, we recommend using the simple 'dir' with option -R instead
#: note that this routine ignores hidden files and folders, and it does not support directories with
#: dots in their names (which would be ONO-incompatible anyway).
my (
$self,$basedir,$dir
) = @_;
my $vpath = &path;
$basedir = &cleanup("",$basedir);
$dir = &cleanup("",$dir);
opendir(DIR, "$vpath/$dir");
my @objects = readdir(DIR);
closedir DIR;
my @new;
foreach my $object (@objects) {
# add the basic object
@new = (@new,$object);
# go into the object if it's a directory
if ($object !~ /\./ && -d "$vpath/$dir/$object") {
my $display_dir = $dir;
$display_dir =~ s~$basedir~~;
foreach my $sub (&_dir_recursive("",$basedir,"$dir/$object")) {
my $use_dir = "$display_dir/$object/$sub";
$use_dir =~ s~//~/~g;
@new = (@new,$use_dir);
}
}
}
return @new;
}
sub _dir_recursive_ux {
#: Do NOT use this routine if possible, we recommend using the simple 'dir' with option -RX instead
my (
$self,$basedir,$dir
) = @_;
my $vpath = &path;
$basedir = &cleanup("",$basedir);
$dir = &cleanup("",$dir);
my @new;
foreach my $sub (`ls -R $vpath/$dir`) {
if ($sub =~ m~/(.*?):~) {
$sub = "/$1";
$sub =~ s~$vpath/~~;
$sub =~ s~$basedir/~~;
@new = (@new,"$sub");
}
}
return @new;
}
sub dirprotect {
#: Protect a directory by putting an index.shtml into it.
#: This will still allow access to files within the directory, but listing it will be prohibited.
#: Also see htaccess() for additional protection.
if (!&exists("","$_[1]/index.shtml")) {
&store("","$_[1]/index.shtml",qq~<html><head><meta name="robots" content="noindex,nofollow"></head><body><h1>403 - Forbidden</h1></body></html>~);
&chmod("","$_[1]/index.shtml","777");
}
}
sub hashread {
#: Return file data as a hash.
#: This works for lists separated by semicolon.
my %hash;
foreach my $line (&list("",&cleanup("",$_[1]))) {
$line =~ s~(\n|\r|\t)~~g;
$line =~ m~^(.*?);(.*)$~;
my $key = $1;
my $value = $2;
$key =~ s/^\s+//;
$key =~ s/\s+$//;
$value =~ s/^\s+//;
$value =~ s/\s+$//;
$hash{$key} = $value;
}
return %hash;
}
sub hashwrite {
#: Write a hash to a file, using semicolon separation.
#: Input needs to be a hash reference in this case.
my $hash_ref = $_[1];
my %hash = %$hash_ref;
my $HASH;
# this has never been tested!!!
foreach my $key (keys %hash) {
$HASH .= qq~$key;$hash{$key}\n~;
}
&store("",&cleanup("",$_[1]),$HASH);
}
sub objtype {
#: Return the type of a object:
#:
#: 'none' if object doesn't exist
#: 'dir' if object is a directory
#: 'file' if object is a file
#:
#: Also see dirfile(), which does more or less the same thing
#: (only the 'none' option is missing), only better.
my $vpath = &path;
my $obj = &cleanup("",$_[1]);
my $ret = "none";
if (-e "$vpath/$obj" && -d "$vpath/$obj") {
$ret = "dir";
}
if (-e "$vpath/$obj" && -f "$vpath/$obj") {
$ret = "file";
}
return $ret;
}
sub dirsize {
#: Return the size of a directory (including all files and
#: directories contained in it).
#:
#: Switches:
#:
#: -M machine mode in megabytes (disable human readable)
my ($self,$dir,$switches) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
if ($switches =~ /M/) {
my $size = `du -sm $vpath/$dir`;
$size =~ s~ (.*)$~~g;
$size =~ s~(\n|\r|\t)(.*)$~~g;
return $size;
} else {
my $size = `du -sh $vpath/$dir`;
$size = ONO::Lib::Basic->remove_bad_spaces($size);
$size =~ s~( |\t)(.*)$~~;
$size =~ s~K~ kb~;
$size =~ s~M~ mb~;
$size =~ s~(\n|\r|\t)~~gi;
if ($size < 1) {
$size = "";
}
return $size;
}
}
sub disks {
#: List hard drives, display disk usage (percentage).
my @disks;
foreach my $data (`df -k`) {
$data =~ s/\s+/\^/g;
if ($data =~ m~^/~ && $data !~ /\@/) {
@disks = (@disks,"$data\n");
}
}
return sort @disks;
}
sub ip {
#: Return the IP address of the host.
my $ip;
if (&os eq "linux") {
foreach my $data (`ifconfig`) {
if (!$ip && $data =~ /inet addr:(.*?) / && $data !~ /^127\.0\.0\.1/ && $data !~ /^192\.168\./) {
$ip = $1;
}
}
}
if (&os eq "macos") {
foreach my $data (`ifconfig`) {
if (!$ip && $data =~ /inet (.*?) netmask/ && $data !~ /^127\.0\.0\.1/ && $data !~ /^192\.168\./) {
$ip = $1;
}
}
}
return $ip;
}
sub os {
#: Return the OS of the host ('linux' or 'macos'). This is similar
#: to ONO::Core::HostOS->host_os(), although the output is different.
#:
#: -v first letter is uppercase
#: -V get more detailed information
my $switches = $_[1];
my $os = "linux";
if ($switches =~ /v/) {
$os = ucfirst $os;
}
if ($switches =~ /V/) {
my $MORE = `grep 'PRETTY_NAME' /etc/os-release | cut -d= -f2 | tr -d '"'`;
if ($MORE =~ /[A-Za-z0-9]/) {
$os .= " - $MORE";
}
}
if (&uname =~ /Darwin/i) {
$os = "macos";
if ($switches =~ /v/) {
$os .= " ".`sw_vers -productVersion`;
}
}
return $os;
}
sub uname {
#: Return the host's uname string, which offers some host system information.
return `uname -a`;
}
sub cpu {
#: Return CPU info. This should display the type of processor, as well as the
#: number of available cores (multithreading is being considered).
#:
#: -b make bold
my $switches = $_[1];
my $cpu = "unknown cpu";
if (&os eq "linux") {
foreach my $dat (`/bin/cat /proc/cpuinfo`) {
if ($dat =~ /model name(.*)/) {
$cpu = $1;
}
}
}
if (&os eq "macos") {
$cpu = `sysctl -n machdep.cpu.brand_string`;
}
if ($switches =~ /b/) {
$cpu = qq~<span class="bold">$cpu</span>~;
}
my $cores = ONO::Core::HostOS->cpu_cores;
if ($cores) {
$cpu .= " ($cores cores/threads)";
}
$cpu =~ s~\((r|tm)\)~~gi;
$cpu =~ s~:~~g;
$cpu =~ s~^ ~~g;
return $cpu;
}
sub mem {
#: Return the total amount of RAM available, in GB / GigaBytes.
my $mem = "memory unknown";
if (&os eq "linux") {
foreach my $dat (`/bin/cat /proc/meminfo`) {
if ($dat =~ /MemTotal(.*)/) {
$mem = $1;
$mem =~ s~[^0-9]~~g;
# this needs to be 1000 and 1024, don't know why...
$mem = 1+int($mem/1024/1024)." GB";
}
}
}
if (&os eq "macos") {
$mem = `sysctl hw.memsize`;
$mem =~ s~[^0-9]~~g;
$mem = 1+int($mem/1024/1024/1024)." GB";
}
return $mem;
}
sub sys {
#: Direct access to the UNIX system, via ONO::Core::Kernel's sys() function.
my ($self,$mode,$opts) = @_;
return ONO::Core::Kernel->sys($mode,$opts);
}
sub sysload {
#: Returns the current system load (percent of total available),
#: CPU cores and multithreading are taken into consideration.
#:
#: This can be used to run or disable code depending on the
#: system load of the host, sysload(50) is true if current load
#: is LOWER than 50 for examaple.
#:
#: This uses cached system load info, which will be updated
#: about every 5 seconds.
#:
#: Note that the threshold feature is NOT working on development
#: stations, which makes developing under high load conditions
#: a lot easier (devstations always return 1).
# Lots of parantheses to make sure the compiler won't throw error messages...:
my $percent = int(100/((ONO::Core::HostOS->cpu_cores())*100)*(ONO::Core::HostOS->cpu_load_cached()));
if ($_[1]) {
# devstation will always return 1, which makes testing easier
if ($percent < $_[1] || &devstation) {
return 1;
} else {
return 0;
}
} else {
return $percent;
}
}
sub sysloadmsg {
#: Returns a system overload message. Note that this function only
#: dumps some text, it does not do or detect anything. It may be a
#: good idea to use this in combination with sysload().
my $MSG;
if ($_[1] eq "de") {
$MSG = $SYSLOADMSG[1];
}
if ($_[1] eq "fr") {
$MSG = $SYSLOADMSG[2];
}
if ($_[1] eq "lu") {
$MSG = $SYSLOADMSG[3];
}
if (!$MSG) {
$MSG = $SYSLOADMSG[0];
}
return qq~<div class="lh125">$MSG</div>~;
}
sub is_local_ip {
#: Is the provided IP a local IP ? Returns 1 of local, or 0 if not
#: local. Also see local().
#:
#: Not to be confused with local(), which tells you if the host's
#: IP is local.
my $res = 0;
if ($_[1] =~ /^(0|10|127)\.0/) {
$res++;
}
if ($_[1] =~ /^172\.16/) {
$res++;
}
if ($_[1] =~ /^192\.168\./) {
$res++;
}
if ($_[1] =~ /^255\.255\.255\.255$/) {
$res++;
}
return $res;
}
sub count {
#: Count objects (directories and files) inside a directory.
#: Note that this does NOT work recursively, only the first
#: level of the directory is being taken into consideration.
#:
#: Switches:
#:
#: -d only directories
#: -f only files
#: -i only images
#: -S omit .setup
#: -t only txt files
#: -T omit .thumbs and previews
my (
$self,
$dir,
$init,
$switches,
) = @_;
foreach my $file (&dir("",$dir)) {
if ($file =~ /[A-Za-z0-9]/ && $file !~ /^\./) {
$init++;
if (
($switches =~ /f/ && $file !~ /\./) ||
($switches =~ /i/ && $file !~ /\.(jpg|jpeg|gif|png|heic|webp|tiff|psd|ico|crimg)$/i) ||
($switches =~ /d/ && $file =~ /\./) ||
($switches =~ /S/ && $file =~ /\.setup$/) ||
($switches =~ /t/ && $file !~ /\.txt$/i) ||
($switches =~ /T/ && $file =~ /^\.(tn|pr)_/)
) {
$init--;
}
}
}
if (!$init) {
$init = 0;
}
return $init;
}
sub compare {
#: Compare files.
#:
#: Returns NULL if files are identical
#: Returns difference details if NOT identical
#:
#: $max: if file bigger than $max then only check file size (no diff)
#:
#: $fast: avoid using diff if possible, does not always return differences though
my ($self,$file1,$file2,$max,$fast) = @_;
my $vpath = &path;
$file1 = &cleanup("",$file1);
$file2 = &cleanup("",$file2);
if ($max && &size("",$file1) > $max) {
if (&size("",$file1) == &size("",$file2)) {
return 0;
} else {
return 1;
}
} else {
if ($fast) {
if (&size("",$file1) == &size("",$file2)) {
return `diff '$vpath/$file1' '$vpath/$file2'`;
} else {
return 1;
}
} else {
return `diff '$vpath/$file1' '$vpath/$file2'`;
}
}
}
sub mkpath {
#: Like mkdir(), but generates the entire path, including all of its subdirs.
#: Permissions may be set at the same time.
my ($self,$dir,$mode) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
$mode =~ s~[^0-7]~~g;
if ($dir) {
unless (-e "$vpath/$dir") {
my $path;
my @parts = split(/\//,$dir);
foreach my $part (@parts) {
$path .= "/$part";
unless (-e "$vpath$path") {
`mkdir $vpath$path`;
}
if (length $mode == 3) {
`chmod $mode $vpath$path`;
}
}
}
}
}
sub mkdir {
#: Create a new directory.
#: Permissions may be set at the same time.
#: Also see mkpath() for a more powerful alternative.
my ($self,$dir,$mode) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
if ($dir) {
unless (-e "$vpath/$dir") {
`mkdir $vpath/$dir`;
}
}
$mode =~ s~[^0-7]~~g;
if (length $mode == 3) {
`chmod $mode $vpath/$dir`;
}
}
sub mkdir_new {
#: This will create a new subdir, add a number if such a subdir already exists,
#: and return the created name. The new subdir will always have permissions
#: set to 777.
my ($self,$dir,$new) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
$new = &cleanup("",$new);
my $name = $new;
my $whilecounter;
while ($whilecounter < 4096 && &exists("","$dir/$name")) {
$whilecounter++;
$name = "${new}_$whilecounter";
}
&mkdir("","$dir/$name","777");
return $name;
}
sub rmdir {
#: Remove directory, including all of its contents.
#: Recursive and with force delete enabled,
#: use rmdirsoft() for a less dangerous operation.
my ($self,$dir) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
if ($vpath =~ /[A-Za-z0-9]/ && $dir =~ /[A-Za-z0-9]/ && $dir !~ /\.\./ && $dir !~ /(\*|\(|\)|\[|\])/ && &exists("",$dir)) {
`rm -rf $vpath/$dir`;
return "rm -rf $vpath/$dir";
} else {
return "ABORT";
}
}
sub rmdirsoft {
#: Like rmdir(), but without force.
my ($self,$dir) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
if ($vpath =~ /[A-Za-z0-9]/ && $dir =~ /[A-Za-z0-9]/ && $dir !~ /\.\./ && $dir !~ /(\*|\(|\)|\[|\])/ && &exists("",$dir)) {
`rmdir $vpath/$dir`;
return "rmdir $vpath/$dir";
} else {
return "ABORT";
}
}
sub htaccess {
#: Password management and protection only.
#: See dirprotect to prevent directory listing.
#:
#: You need to specify $key, if this is empty then the protection
#: will be removed. To protect without any login option, simply
#: use ':' as $key.
my ($self,$dir,$key) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
if ($key =~ /:/) {
&store("","$dir/.htaccess",qq~AuthName "Password Required"
AuthType Basic
AuthUserFile $vpath/$dir
require valid-user
~);
&store("","$dir/.htpasswd","$key\n");
&chmod("","$dir/.htaccess","777");
&chmod("","$dir/.htpasswd","777");
} else {
`rm -rf $vpath/$dir/.htaccess`;
`rm -rf $vpath/$dir/.htpasswd`;
}
}
sub htaccess_ip {
#: Restrict access to a directory only for one IP address.
my ($self,$dir,$ip) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
if ($ip) {
&store("","$dir/.htaccess","order deny,allow\ndeny from all\nallow from $ip\n");
&chmod("","$dir/.htaccess","777");
} else {
`rm -rf $vpath/$dir/.htaccess`;
}
}
sub htaccess_rewrite {
#: Add a rewrite rule via .htaccess, simply indicate the target,
#: which must be a path to a script.
my ($self,$dir,$com) = @_;
my $vpath = &path;
$dir = &cleanup("",$dir);
&store("","$dir/.htaccess","RewriteEngine on\nRewriteRule \^\(\.\*\)\$ $com \[L\]\n");
&chmod("","$dir/.htaccess","777");
}
sub htmlencode {
#: Encode a string by replacing HTML lt, gt and quot.
#: This will be useful in input fields for example.
my $input = $_[1];
$input =~ s~\<~<~g;
$input =~ s~\>~>~g;
$input =~ s~\"~"~g;
return $input;
}
sub size {
#: Return the size of a file, by default this will simply return the
#: number of bytes.
#:
#: Switches:
#:
#: -d recursive directory mode
#: -h human readable (add "bytes", "kb", or "mb")
#: -k kb
#: -l count lines
#: -w count words
my ($self,$file,$switches) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
my $size;
if ($file) {
if (!$switches || $switches =~ /(h|k)/) {
if ($switches =~ /d/ || &fileisdir("",$file)) {
# recursive dir size
$size = `du -sk "$vpath/$file"`;
$size =~ s~( |\n|\r|\t)(.*)$~~;
$size = $size * 1024;
} else {
# file size
$size = -s "$vpath/$file";
}
if ($switches =~ /h/) {
my $ext = "bytes";
if ($size > 4000000000) {
$size = 1+int($size/1024/1024/1024);
$ext = "GB";
} else {
if ($size > 4000000) {
$size = 1+int($size/1024/1024);
$ext = "MB";
} else {
if ($size > 8000) {
$size = 1+int($size/1024);
$ext = "KB";
}
}
}
$size .= " $ext";
}
if ($switches =~ /k/) {
$size = 1+int($size/1024);
}
}
if ($switches =~ /l/) {
$size = `wc -l '$vpath/$file'`;
$size =~ s~^\s+~~;
$size =~ s~ (.*)$~~;
$size =~ s~( |\n|\r|\t)~~g;
if (-s "$vpath/$file") {
$size++;
}
}
if ($switches =~ /w/) {
$size = `wc '$vpath/$file'`;
$size =~ s~(\n|\r|\t)~ ~g;
$size =~ s~\s+~ ~g;
$size =~ s~^\s+~~;
$size =~ m~^(.*?) (.*?) (.*?)~;
$size = $2;
}
}
return $size;
}
sub timestamp {
#: Return the modification (or creation) timestamp of a file or directory.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
my (
$dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
) = stat("$vpath/$file");
return $mtime;
}
sub touch {
#: Update the modification timestamp of a file or directory.
my ($self,$file,$time) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
$time =~ s~[^0-9]~~g;
if ($time =~ /[0-9]/) {
`touch -mt $time $vpath/$file`;
} else {
`touch $vpath/$file`;
}
}
sub type {
#: This is a file or a directory? Returns 'dir' or 'file'.
#: Please use dirfile() instead, or objtype(), which do
#: the same thing.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
my $type = "dir";
if (-f "$vpath/$file") {
$type = "file";
}
return $type;
}
sub stat {
#: Returns file or directory stats as an array.
my ($self,$file) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
my (
$dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks
) = stat("$vpath/$file");
return ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
}
sub symlink {
#: Create a symlink.
my ($self,$file,$symlink) = @_;
my $vpath = &path;
$file = &cleanup("",$file);
$symlink = &cleanup("",$symlink);
if ($file && $symlink) {
`ln -s $vpath/$file $vpath/$symlink`;
}
}
sub unlink {
#: Remove a symlink.
my ($self,$symlink) = @_;
my $vpath = &path;
$symlink = &cleanup("",$symlink);
if ($symlink) {
`unlink $vpath/$symlink`;
}
}
sub cp {
#: Copy a file to a new target.
#: Related thumbnails and setup files will also be
#: taken into consideration.
my ($self,$source,$target) = @_;
my $vpath = &path;
$source = &cleanup("",$source);
$target = &cleanup("",$target);
if ($source && $target) {
`cp $vpath/$source $vpath/$target`;
my ($pr1,$tn1) = &getthumbs("",$source);
if ($pr1 && $tn1) {
my ($pr2,$tn2) = &getthumbs("",$target);
`cp $vpath/$pr1 $vpath/$pr2`;
`cp $vpath/$tn1 $vpath/$tn2`;
}
my $setup1 = &getsetup("",$source);
if ($setup1) {
my $setup2 = &getsetup("",$target,"x");
`cp $vpath/$setup1 $vpath/$setup2`;
}
}
}
sub cpr {
#: Recursively copy a directory.
#:
#: Switches:
#:
#: -p preserve file properties
#: -n do not overwrite an existing file
my ($self,$source,$target,$switches) = @_;
my $vpath = &path;
$source = &cleanup("",$source);
$target = &cleanup("",$target);
my $flags;
if ($switches =~ /p/) {
$flags .= "p";
}
if ($switches =~ /n/) {
$flags .= "n";
}
if ($source && $target) {
`cp -r$flags $vpath/$source $vpath/$target`;
}
}
sub mv {
#: Move or rename a file or a directory.
#: Related thumbnails and setup files will also be
#: taken into consideration.
my ($self,$source,$target) = @_;
my $vpath = &path;
$source = &cleanup("",$source,1);
$target = &cleanup("",$target);
if ($source && $target) {
`mv '$vpath/$source' '$vpath/$target'`;
my ($pr1,$tn1) = &getthumbs("",$source);
if ($pr1 && $tn1) {
my ($pr2,$tn2) = &getthumbs("",$target);
`mv '$vpath/$pr1' '$vpath/$pr2'`;
`mv '$vpath/$tn1' '$vpath/$tn2'`;
}
my $setup1 = &getsetup("",$source);
if ($setup1) {
my $setup2 = &getsetup("",$target,"x");
`mv '$vpath/$setup1' '$vpath/$setup2'`;
}
}
}
sub mvdirty {
#: Like mv(), but for "dirty" sources containing
#: bad characters.
#: Related thumbnails and setup files will also be
#: taken into consideration.
my ($self,$source,$target) = @_;
my $vpath = &path;
$source =~ s~\&~\\\&~g;
$source =~ s~\(~\\\)~g;
$source =~ s~\)~\\\)~g;
$source =~ s~\'~\\\'~g;
$source =~ s~\"~\\\"~g;
if ($source && $target) {
`mv '$vpath/$source' '$vpath/$target'`;
my ($pr1,$tn1) = &getthumbs("",$source);
if ($pr1 && $tn1) {
my ($pr2,$tn2) = &getthumbs("",$target);
`mv $vpath/$pr1 $vpath/$pr2`;
`mv $vpath/$tn1 $vpath/$tn2`;
}
my $setup1 = &getsetup("",$source);
if ($setup1) {
my $setup2 = &getsetup("",$target,"x");
`mv $vpath/$setup1 $vpath/$setup2`;
}
}
return "mv '$vpath/$source' '$vpath/$target'";
}
sub split {
#: Split a file into pieces.
my ($self,$source,$target,$size) = @_;
my $vpath = &path;
$source = &cleanup("",$source);
$target = &cleanup("",$target);
if ($source && $target) {
if (!$size) {
$size = 65536;
}
`split -b $size $vpath/$source $vpath/$target`;
}
}
sub chmod {
#: Change file or directory permissions.
my ($self,$object,$mode) = @_;
my $vpath = &path;
$object = &cleanup("",$object);
$mode =~ s~[^0-7\-\r\R\ ]~~gi;
if ($object && length $mode > 2) {
`chmod $mode $vpath/$object`;
}
return "chmod $mode $vpath/$object";
}
sub exec {
#: Execure a UNIX command, via ONO::Core::Kernel's exec() function.
my ($self,$exec) = @_;
return ONO::Core::Kernel->exec($exec);
}
sub exec2 {
#: Execure a UNIX command.
#: This is a less restrictive version of exec() (allows (\',\",\@,\:,\[,\])).
my ($self,$exec) = @_;
$exec =~ s~[^A-Za-z0-9\ \-\_\.\/\'\"\@\:\[\]]~~g;
return `$exec`;
}
sub exec3 {
#: Execure a UNIX command.
#: This is a zero restrictions version of exec (allows everything)
#: THIS MAY BE DANGEROUS TO USE - especially if you're using it in a script that's running with root privileges.
my ($self,$exec) = @_;
return `$exec`;
}
sub domain {
#: Return the domain, remove stuff like 'www' and other subdomains.
my $domain = &domain_full();
$domain =~ s~^(www|en|us|uk|de|at|ch|fr|be|lu|lb|nl|es|pt|it|dk|da|fi|no|sv|se)\.~~;
return $domain;
}
sub domain_full {
#: Return the domain, including 'www' or subdomains for example.
#: The domain can also be set via a configuration file (etc/domain.conf),
#: which may be required in some proxy environments.
my $domain = $ENV{'HTTP_HOST'};
if (!$domain) {
$domain = $ENV{'SERVER_NAME'};
}
if (!$domain && &exists("","etc/domain.conf")) {
$domain = &load("","etc/domain.conf");
$domain =~ s~(\n|\r|\t)~~g;
}
return $domain;
}
sub http {
#: Returns 'http' or 'https', depending on the current SSL status of the domain.
my $HTTP = "http";
if ($ENV{'HTTPS'} || &exists("","etc/https.conf")) {
$HTTP = "https";
}
return $HTTP;
}
sub https {
#: Returns 1 if SSL is available, or 0 is SSL is not available.
#: Returning 1 can be forced, by adding a dedicated setup file
#: (etc/https.conf), which may be required in certain proxy
#: environments.
if ($ENV{'HTTPS'} || &exists("","etc/https.conf")) {
return 1;
} else {
return 0;
}
}
sub http_domain_base {
#: Returns http(s)://www.example.com/ono/path/. This basically
#: glues the http(), domain() and base() functions.
return &http()."://".&domain().&base();
}
sub refresh {
#: HTML refresh, this will simply auto-redirect to a different page.
#: In most cases, you will simply push refresh($url) to the client.
#:
#: You may specify seconds to wait, and you may also force using
#: SSL/https for the destination.
#:
#: For testing purposes, the 'auto' feature is disabled on
#: development stations, which will display a clickable link
#: displaying the entire URL instead. Using $devstation_overrule
#: will override this behaviour and enable the 'auto' features
#: on development stations too.
my (
$self,
$URL,
$SECS,
$HTTP_FORCE,
$devstation_overrule,
) = @_;
$URL =~ s~^http(|s)\://~~;
if ($SECS < 1) {
$SECS = 0;
}
my $HTTP = &http;
if ($HTTP_FORCE) {
$HTTP = $HTTP_FORCE;
}
$URL = ONO::Lib::Basic->remove_doubles($URL,"/");
if ($URL !~ /^\//) {
$URL = "${HTTP}://$URL";
}
if (&devstation && !$devstation_overrule) {
my $SHOW_URL = ONO::Lib::Basic->pathbreaks($URL);
my $chars = length $URL;
return qq~Devstation: refresh after $SECS secs, $chars chars: <a href="$URL">$SHOW_URL</a>~;
} else {
return qq~<meta http-equiv="refresh" content="$SECS; url=$URL">~;
}
}
sub upload_url {
#: Returns the rawdata.pl upload script URL.
my $RND = 9000000000 + int(rand(999999999));
my $URL = "/cgi-bin/local/perl/$_[1]/ajax/rawdata.pl?";
# $URL .= "?ono_ref_id=$RND&";
return $URL;
}
sub upload {
#: CGI upload, which allows single file upload directly via ONO::IO.
#: In most cases, you will avoid this and use ONO::ToolBox::Upload
#: and ONO::Lib::Web::Upload instead.
#:
#: $max = file size limit (absolute limit = 4 MB)
#:
#: -g only accept GIF
#: -i only accept images (JPG, PNG, GIF, WEBP)
#: -j only accept JPG
#: -p only accept PNG
#: -P only accept PDF
#: -w only accept WebP
my ($self,$handle,$dir,$file,$max,$switches) = @_;
my $vpath = &path;
$handle = &cleanup("",$handle);
$dir = &cleanup("",$dir);
$file = &cleanup("",$file);
my $cgi = new CGI;
if (!$file) {
$file = lc $cgi->param($handle);
}
my ($data,$data1);
if (my $fh = $cgi->upload($handle)) {
$data1 .= $data while read $fh, $data, 1024;
}
my $valid_file = 1;
my $ext;
my $magic = lc unpack('H8',substr($data1,0,1)).unpack('H8',substr($data1,1,1)).unpack('H8',substr($data1,2,1));
if ($switches =~ /(g|i|j|p)/) {
$valid_file = 0;
if ($switches =~ /(i|j)/ && ($data1 =~ m/JFIF/ || $magic eq "ffd8ff")) {
$valid_file = 1;
$ext = "jpg";
}
if ($switches =~ /(i|g)/ && ($data1 =~ m/GIF87a/ || $data1 =~ m/GIF89a/ || $magic eq "474946")) {
$valid_file = 1;
$ext = "gif";
}
if ($switches =~ /(i|p)/ && ($data1 =~ m/PNG/ || $magic eq "89504e")) {
$valid_file = 1;
$ext = "png";
}
if ($switches =~ /(i|w)/ && ($data1 =~ m/^RIFF/)) {
$valid_file = 1;
$ext = "webp";
}
}
if ($switches =~ /P/) {
$valid_file = 0;
if ($data1 =~ m/PDF/ || $magic eq "255044") {
$valid_file = 1;
$ext = "pdf";
}
}
if ($ext) {
# don't do this for files starting by a dot (required for /.icon.jpg)
if ($file !~ /^\./) {
$file =~ s~\.(.*)$~~;
}
$file .= ".$ext";
}
if ($data1 && $valid_file && (!$max || $max*1024 > length $data1) && length $data1 < 4096*1024) {
&mkpath("",$dir);
&dump("","$dir/$file",$data1);
return "$dir/$file";
} else {
return "";
}
}
sub which {
#: Find a UNIX binary. Accepts the binary name as input,
#: returns the path to the binary.
my ($self,$source) = @_;
$source = &cleanup("",$source);
my $res = `which $source`;
$res =~ s~[^A-Za-z0-9\/\-]~~gi;
return $res;
}
sub unzip {
#: Unzip a file or directory.
my ($self,$source,$target) = @_;
my $vpath = &path;
$source = &cleanup("",$source);
$target = &cleanup("",$target);
`unzip $vpath/$source -d $vpath/$target`;
return "unzip unzip $vpath/$source -d $vpath/$target";
}
sub tar {
# Archive a file or directory.
my ($self,$source,$target) = @_;
my $vpath = &path;
$source = &cleanup("",$source);
$target = &cleanup("",$target);
`tar -zcf $vpath/$target $vpath/$source`;
return "tar -zcf $vpath/$target $vpath/$source";
}
sub deepdir {
#: Turns 'ABCXYZ' into 'A/AB/ABC'.
my $sep = $_[2];
if (!$sep) {
$sep = "/";
}
return substr($_[1],0,1).$sep.substr($_[1],0,2).$sep.substr($_[1],0,3);
}
sub deepdirs {
#: Turns 'ABCXYZ' into ('A','AB','ABC').
return (substr($_[1],0,1),substr($_[1],0,2),substr($_[1],0,3));
}
sub deepnum {
# Like deepdir(), but this one is working with numbers, the input
# number will automatically be expanded to 10 digits.
my $id = ONO::Lib::Basic->add_leading_zeroes($_[1],9);
my $sep = $_[2];
if (!$sep) {
$sep = "/";
}
return substr($id,0,3).$sep.substr($id,3,3).$sep.substr($id,6,3);
}
sub zip {
#: Compress a file.
#: Warning: gzip only works with single files, use 'tar' to zip entire directories
my ($self,$file) = @_;
my $vpath = &path;
`gzip $vpath/$file`;
}
sub readsetup {
#: Read a setup file.
#: -H no HTML parsing (preserve <br>)
my (
$self,
$object,
$flags,
) = @_;
my $vpath = &path;
$object = &cleanup("",$object);
my $setup = &getsetup("",$object);
my %hash;
$hash{'debug'} = "FILE: $object -> $setup<br>";
if ($setup) {
foreach my $data (&list("",$setup)) {
my @dp = split(/\^/,$data);
if ($flags !~ /H/) {
$dp[1] =~ s~\<br\>~\n~g;
}
$hash{$dp[0]} = $dp[1];
}
}
return \%hash;
}
sub savesetup {
#: Save / store a setup file.
#: -m multi-line mode for meta data, replaces newline/return by HTML <br>
my (
$self,
$object,
$vars_ref,
$switches,
) = @_;
my $vpath = &path;
$object = &cleanup("",$object);
my %vars = %$vars_ref;
my $setup = &getsetup("",$object,"x");
my $DEBUG = "FILE: $object -> $setup<br>";
my $DATA;
# this does not yet check security (_locks), therefore this can't be universally used
foreach my $meta ('title','subtitle','date','sort','description','instructions','branch','rename_lock','delete_lock','system_lock') {
if ($vars{"setup_$meta"}) {
$vars{"setup_$meta"} =~ s~(\n|\r|\t)~\<br\>~g;
$DATA .= qq~$meta\^$vars{"setup_$meta"}\^\n~;
}
}
# custom data, may be used by any application:
foreach my $key (keys %vars) {
if ($key =~ /^setup_custom_(.*?)$/) {
my $meta = $1;
if ($vars{$key} =~ /[A-Za-z0-9\-]/) {
if ($switches =~ /m/) {
$vars{$key} =~ s~(\n|\r)~<br>~g;
$vars{$key} =~ s~(\t)~~g;
} else {
$vars{$key} =~ s~(\n|\r|\t)~~g;
}
$DATA .= qq~custom_$meta\^$vars{$key}\^\n~;
}
}
}
if ($DATA) {
&store("",$setup,$DATA);
} else {
&rm("",$setup);
}
return $DEBUG;
}
sub getdir {
#: Transform /my/path/to/file.ext into /my/path/to/.
#:
#: Only works correctly if the input is using a correct
#: /my/path/to/file.ext format.
my ($self,$path) = @_;
my $file = $path;
$file =~ s~^(.*)/~~;
return $1;
}
sub getdirfile {
#: Transform /my/path/to/file.ext into ('/my/path/to/','file.ext').
#:
#: Only works correctly if the input is using a correct
#: /my/path/to/file.ext format.
my ($self,$path) = @_;
my $file = $path;
$file =~ s~^(.*)/~~;
my $dir = $1;
return ($dir,$file);
}
sub getdirfileext {
# Transform /my/path/to/file.txt into ('/my/path/to/','file','ext').
#
# Only works correctly if the input is using a correct
# /my/path/to/file.ext format.
my ($self,$path) = @_;
$path =~ s~^(.*)/~~;
my $dir = $1;
$path =~ m~^(.*)\.(.*?)$~;
return ($dir,$1,$2);
}
sub getfileext {
#: Return the file and its extension, eg file.ext becomes ('file','ext').
my $file = $_[1];
$file =~ m~^(.*)\.(.*?)$~;
return ($1,$2);
}
sub getfiletype {
#: Return file type: image, audio, video or pdf.
#: Will be empty if it's none of those.
my $filetype;
if ($_[1] =~ m~\.(jpg|jpeg|png|webp|gif|tiff|psd|ico|crimg)$~i) {
$filetype = "image";
}
if ($_[1] =~ m~\.(mp3|wav|flac|ogg)$~i) {
$filetype = "audio";
}
if ($_[1] =~ m~\.(mp4|m4v|mpg|mpeg|m2ts|mov|mkv|f4v|avi|vob|wmv|flv|f4v|m4v|divx|webm|mp4c|ts)$~i) {
$filetype = "video";
}
if ($_[1] =~ m~\.(pdf)$~i) {
$filetype = "pdf";
}
return $filetype;
}
sub getthumbs {
#: Returns paths to preview and thumbnail files.
#: Returns nothing if the file type does not allow for thumbnail files.
my ($self,$path) = @_;
if ($path =~ m~\.(jpg|png|gif|webp|heic|tiff|mp4|m4v|mpg|mpeg|mov|mkv|avi|vob|wmv|flv|f4v|divx|webm|mp4c|ts|pdf)$~i) {
my $file = $path;
$file =~ s~^(.*)/~~;
my $dir = $path;
$dir =~ s~/$file~~;
$file =~ s~\.(png|gif|webp|heic|tiff|mp4|m4v|mpg|mpeg|mov|mkv|avi|vob|wmv|flv|f4v|divx|webm|mp4c|ts|pdf)$~\.jpg~i;
# $file =~ s~\.JPG$~\.jpg~;
return ("$dir/.pr_$file","$dir/.tn_$file");
} else {
return ("","");
}
}
sub getthumbs_ifexists {
#: Returns paths to preview and thumbnail files, but onyl if they exist, and if the file type supports them.
my ($self,$path) = @_;
my ($pr,$tn) = &getthumbs("",$path);
if (&exists("",$pr) && &exists("",$tn)) {
return ($pr,$tn);
} else {
return ("","");
}
}
sub rmthumbs {
#: Remove thumbs and previews from a file, leave the file intact.
my ($self,$path) = @_;
my $vpath = &path;
my $del;
my ($pr1,$tn1) = &getthumbs("",$path);
if ($pr1 && -e "$vpath/$pr1") {
`rm "$vpath/$pr1"`;
$del .= ":$pr1";
}
if ($tn1 && -e "$vpath/$tn1") {
`rm "$vpath/$tn1"`;
$del .= ":$tn1";
}
if ($del) {
return "$path$del;";
} else {
return "";
}
}
sub getsetup {
#: Returns the path to an optional setup file.
#:
#: Switches:
#:
#: -x override exists check (required for creating a new file)
my ($self,$path,$flags) = @_;
if ($path =~ /\./) {
$path =~ s~\.(.*?)$~\.setup~;
} else {
$path .= ".setup";
}
if (&exists("",$path) || $flags =~ /x/) {
return $path;
} else {
return "";
}
}
sub getimage {
#: Replace an image URL into a preview or thumbnail URL if the
#: required files exist, and if the image dimensions are adequate.
my ($self,$img,$w,$h) = @_;
my $vpath = &path;
if ($w < 241 && $h < 181) {
my ($pr,$tn) = &getthumbs("",$img);
if ($pr && -e "$vpath/$pr") {
$img = $pr;
}
if ($tn && -e "$vpath/$tn" && $w < 121 && $h < 121) {
$img = $tn;
}
}
return $img;
}
sub image {
#: Returns 1 if file extension is image, or 0 if not.
#: This works for both filenames, and directory/filename
#: combinations.
if ($_[1] =~ /\.(jpg|jpeg|gif|png|heic|webp|tiff|psd|ico|crimg)$/i) {
return 1;
} else {
return 0;
}
}
sub imagefile {
#: Same as image(), but does not work for preview and thumbnail files.
if (&image("",$_[1]) && $_[1] !~ /^\./) {
return 1;
} else {
return 0;
}
}
sub imageexts {
#: Return a list of common (not all) image extensions.
return ('jpg','jpeg','png','gif','webp');
}
sub pixel {
#: Returns a pixel image as a data stream.
return $PIXEL;
}
sub video {
#: Returns 1 if file extension is video, or 0 if not.
#: This works for both filenames, and directory/filename
#: combinations.
if ($_[1] =~ /\.(mp4|m4v|mpg|mpeg|m2ts|mov|mkv|f4v|avi|vob|wmv|flv|f4v|m4v|divx|webm|mp4c|ts)$/i) {
return 1;
} else {
return 0;
}
}
sub inputfilter {
#: Filter and return input, by removing a number of special characters
my $input = $_[1];
$input =~ s~(\"|\<|\>|\(|\)|\*|\!|\\)~~gi;
return $input;
}
sub inputfilters {
#: Like inputfilter(), but for arrays.
my (
$self,
@inputs,
) = @_;
my @new;
foreach my $input (@inputs) {
@new = (@new,&inputfilter("",$input));
}
return @new;
}
sub xmlget {
#: Get / extract XML data. This only works for very simple XML files,
#: and it will only return the first instance of the data.
my $XML = &load("",$_[1]);
$XML =~ s~(\n|\r|\t)~~g;
$XML =~ s~\<$_[2]\>(.*?)\</$_[2]\>~~i;
return $1;
}
###############################################################################
# end of script
###############################################################################
1;
__END__