#!/usr/bin/perl -T

###########################################################
#
# notes II - WYSIWYG Editor
# 8/29/2011 3 p
#
# update: 8/14/2014
# update: 5/29/2015
# title bar adjustments, file name adjustments
# filename first, then folder
#
#
#
# maintained on fusionplant.com
#
# Stanley Lee
# contact:
# fusionplant.com/contact/
# maintained on www.fusionplant.com



# modules #################################
use CGI qw{param};
use strict;
use IO::All;
use Fcntl qw( :DEFAULT :flock );
require './lib/functions.pm';
require './lib/confReader.pm';
#require './functions.pm'; # for some configs
#require './confReader.pm'; # for some configs





# from confReader ######
my %readConf = readConf("config.txt");
#my $xxx = $readConf{"xxxx"};

# untaint ####################
$ENV{PATH}='/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};

# constants ###################################
#use constant FILES_EXT => "txt";
use constant TEMPLATE => "template_notes.html";
use constant TEMPLATE_viewFull => "template_viewFull.html";
#use constant UPLOAD_DIR => "files";
#use constant WEB_UPLOAD_PATH => "files";
use constant BUFFER_SIZE => 16_384;
use constant MAX_FILE_SIZE => 512 * 1048576; # Limit each upload to X MB
use constant MAX_DIR_SIZE => 1024 * 1048576; # Limit total uploads to X MB
use constant MAX_OPEN_TRIES => 100;



# variables #################################
my ($thisFileName, $thisFilePath, $thisFileWebPath, $thisFileWebServerName)=thisFileInfo();
my $helpFile = "Help_File.html";
my $returnToStartPage = $readConf{"returnToStartPage"};
my $start_files_path = $readConf{"start_files_path"};
my @filesUpdated = (
    "Help_File.html",
    "index.html",
    "notes.css",
    "notes2.cgi",
    "template_notes.html",
    "template_viewFull.html",
    "config.txt",
    "script.js",
    );


# parsed variables ##########################
my $option = param("option");
my $selectedFile = param("selectedFile");
my $renameFileTo = param("renameFileTo");
my $newFile = param("newFile");
my $newFolder = param("newFolder");
my $buffer = param("buffer");
my $files_path = param("files_path") ? param("files_path") : $start_files_path ; untaintIt ($files_path);
$files_path = preventRootFSAccess($files_path, $start_files_path);


# automatic variables #################################
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon++;
for ($mon, $mday, $hour, $min, $sec){
    $_ = sprintf ("%02d", $_);
}


# directory ################################
if ( $option eq "createNewNote" ){   # create ###############
    print "content-type: text/html\n\n";
    createNewNote( $files_path . "/" . $newFile ) if $newFile;
    genPage (
	qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path">},
	qq{Creating "$newFile"...},
	qq{<a href="$thisFileName?files_path=$files_path">please wait...</a>\n}
	);
} elsif ( $option eq "createFolder" ){   # create folder ###############
    print "content-type: text/html\n\n";
    $newFolder = cleanFilename($newFolder);
    untaintIt($files_path, $newFolder);
    mkdir "$files_path/$newFolder";
    genPage (
	qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path">},
	qq{Creating "$newFolder"...},
	qq{<a href="$thisFileName?files_path=$files_path">please wait...</a>\n}
	);
} elsif ( $option eq "edit" ) { # edit ######################
    print "content-type: text/html\n\n";
    my $title = filename2title($selectedFile);
    genPage (
	"",
	qq{ $title},
	edit ( $selectedFile )
	);
} elsif ( $option eq "editWrite" || $option eq "editWriteContinue") { # edit write
    print "content-type: text/html\n\n";
    my $writeMethod;
    if ( $option eq "editWrite" ){
	$writeMethod =	qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path">};
    } elsif ( $option eq "editWriteContinue") { 
	$writeMethod = qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path&selectedFile=$selectedFile&option=edit">};
    }
    genPage (
	$writeMethod,
	qq{ Editor Writing },
	editWrite ($selectedFile, $buffer)
	);
} elsif ( $option eq "deleteAsk" ) { # delete ask ##############
    print "content-type: text/html\n\n";
    genPage (
	"",
	qq{Delete File},
	deleteAsk($selectedFile),
	);
} elsif ( $option eq "deleteFile" ) { # delete file
    print "content-type: text/html\n\n";
    genPage (
	qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path">},
	qq{File Deleted},
	deleteFile($selectedFile),
	);
} elsif ( $option eq "renameAsk" ) { # rename ask ###################
    print "content-type: text/html\n\n";
    genPage (
	"",
	qq{Rename File},
	renameAsk($selectedFile),
	);
} elsif ( $option eq "renameFile" ) { # rename file
    print "content-type: text/html\n\n";
    genPage (
	qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path">},
	qq{File Renamed},
	renameFile($selectedFile, $renameFileTo),
	);
} elsif ( $option eq "displayPage") { # display page
    print "content-type: text/html\n\n";
    print displayPage($selectedFile);
} elsif ( $option eq "displayPageFull") { # display page full
    my $title = filename2title($selectedFile);
    print "content-type: text/html\n\n";
    genPageView(
	"",
	"$title", # title
	displayPage($selectedFile)
	)
} elsif ( $option eq "uploadFile") { # upload file
#    print "content-type: text/html\n\n";
#    genPage (
#	qq{<meta http-equiv="refresh" content="1;url=$thisFileName?files_path=$files_path">},
#	qq{File upload},

#	);
    CGI_Upload($files_path),
} else { # initial, no option ###############
    print "content-type: text/html\n\n";
    genPage (
	"",
	"All Files",

	listFiles($files_path . "/") .
	"<br />" .
	folderStats($files_path) .
	"<br />" . "<br />" .
	initialOptions(),

	);
}






# show files and options ############
# edit/view/delete/rename
sub listFiles {
    my $path = shift;
    ( my $pathMod = $path) =~ s/$start_files_path// ; # remove initial path - for use as title "breadcrumb"
    my $io = io($path);
    my @files = $io->all;
    my $returnIt = ();
    my @breadcrumb = pathToBreadcrumb($pathMod, $start_files_path);

    unless ( $pathMod eq "/"){
	$returnIt .= join '/', @breadcrumb;
	$returnIt .= "/<br />\n";
    }

    for my $file (@files){
	#variables
	my $fileType = fileType($file);

	$returnIt .= qq[<span class="hoverHighlight">]; # hover open

	# edit
	untaintIt ($file);
	$returnIt .= qq{<a href="$thisFileName?option=edit&selectedFile=$file&files_path=$files_path" class="buttonsFaint">edit</a>} unless -d $file;
	$returnIt .= qq{<a href="" class="separator"> </a>};

	# delete
	$returnIt .= qq{<a href="$thisFileName?option=deleteAsk&selectedFile=$file&files_path=$files_path" class="buttonsFaint">del</a>};
	$returnIt .= qq{<a href="" class="separator"> </a>};

	# rename
	$returnIt .= qq{<a href="$thisFileName?option=renameAsk&selectedFile=$file&files_path=$files_path" class="buttonsFaint">rename/move</a>};
#	$returnIt .= qq{<a href="" class="separator"> </a>};

	# view
	my $filename2title = filename2title($file);
	$filename2title = findFilename($filename2title);

	if ( -d $file ) {
	    $returnIt .= qq{ <a href="$thisFileName?files_path=$file" title="$file" style="cursor: pointer;">$filename2title (Open Folder)</a>};
	} else {
	    # set values for view <form>
	    my $URL_x;
	    if ($fileType =~ m/HTML|plain text/ ){
		$URL_x = "$thisFileName?option=displayPage&selectedFile=$file&files_path=$files_path";
	    } else {
		$URL_x = "$file";
	    }
	    $returnIt .= qq{ <a href="$URL_x" title="$file" target="displayPage" 
onclick='document.getElementById("displayPage").className="showDiv"; 
document.form22.button22.value="file: $filename2title";
document.form22.selectedFile.value="$file";
document.form22.files_path.value="$files_path";
 ' style="cursor: pointer;">$filename2title</a>};
	}



	my $URL_x;
	if ($fileType =~ m/HTML|plain text/ ){
	    $URL_x = "$thisFileName?option=displayPageFull&selectedFile=$file&files_path=$files_path";
	} else {
	    $URL_x = "$file";
	}

	$returnIt .= qq{ <a href="$URL_x" title="$file" > - view full</a>} unless -d $file;

	# stats 7 size, 9 mtime
	my ($size, $mtime) = (stat("$file"))[7,9];
	my $size2 = itemStatsBytes($size) . "B";
	$returnIt .= " <span title='$size2'>" . humanReadableSize($size) . "</span>";
	$returnIt .= " [" . itemStatsTime( $mtime ) . "]";
	$returnIt .= " $fileType";

	$returnIt .= qq[</span>]; # hover close
	$returnIt .= "<br />\n";

    }


    return $returnIt;
}













# initial options #############
# create new file
sub initialOptions {
    my $returnIt;
    $returnIt .= qq{<a href="$thisFileName?files_path=$files_path" class="buttons">Refresh</a>};
    $returnIt .= qq{<a href="" class="separator"> </a>};
    $returnIt .= "<br />\n" x 2;

    # create new note button
    $returnIt .= qq{<form name="form1" action="$thisFileName" method="get" style="margin: -10px 0px 0px 0px; padding: 0px; " >\n};
    $returnIt .= qq{<input type="hidden" name="files_path" value="$files_path" />\n};
    $returnIt .= qq{<input type="hidden" name="option" value="createNewNote" />\n};
    $returnIt .= qq{<input type="text" name="newFile" />};
    $returnIt .= qq{<a class="buttons" onclick="form1.submit()">Create New Note</a>};
    $returnIt .= qq{</form>\n};

    $returnIt .= "<br />\n";

    # create new folder button
    $returnIt .= qq{<form name="form2" action="$thisFileName" method="get" style="margin: -15px 0px 0px 0px ; padding: 0px; " >\n};
    $returnIt .= qq{<input type="hidden" name="files_path" value="$files_path" />\n};
    $returnIt .= qq{<input type="hidden" name="option" value="createFolder" />\n};
    $returnIt .= qq{<input type="text" name="newFolder" />};
    $returnIt .= qq{<a class="buttons" onclick="form2.submit()">Create Folder</a>};
    $returnIt .= qq{</form>\n};


    # upload form
    $returnIt .= qq[<form name="form478" action="$thisFileName" method="post" enctype="multipart/form-data">\n];
    $returnIt .= qq[<input type="hidden" name="option" value="uploadFile">\n];
    $returnIt .= qq[<input type="hidden" name="files_path" value="$files_path">\n];
    $returnIt .= qq[<input type="file" name="file" >];
    $returnIt .= qq[<input type="submit" value="Upload File">\n];
    $returnIt .= "</form>\n";



    $returnIt .= qq[<hr width="100%" noshade size="1" />\n];

    $returnIt .= qq[<a onclick='document.getElementById("help_file").className = "showDiv";' style="cursor: pointer;">Help</a> | ];
    $returnIt .= qq[<a onclick='document.getElementById("updated").className = "showDiv";' style="cursor: pointer;">Updated</a> | ];
    $returnIt .= qq[ <a href="$returnToStartPage">return to start page</a>];


    return $returnIt;
}




sub createNewNote {
    my $file = shift;
    my $content = $file;


    untaintIt( $content );

    $content = filename2title($content);
    $content .= "\n";
    $content .= "-" x ( length($content) - 1);
    $content .= "\nNew File\n";


    $file = cleanFilename($file);
#    $file = checkFilenameExt($file);

    untaintIt( $file );

    $content > io($file);

}


sub edit {
    my $file = shift;
    untaintIt ($file);
    my $buffer < io $file;
    my $class;
#    (my $title = $file) =~ s|.*/(.*)$|$1|;
    my $title = findFilename($file);
    my $returnIt = ();
    my $textareaID;

    if ( $file =~ m/\.html{0,1}$/ ) {
	$class = qq[class="ckeditor"];
	$textareaID = "editor897";
    } else {
	$class = qq[class="textarea1"];
	$textareaID = "";
    }

    my $documentChangedFnct = qq[onchange='document.getElementById("label1").innerHTML = "** Document Changed, Unsaved **"; return true;'];

    $returnIt .= "($title)";

    $returnIt .= qq[<form name="form1" method="post">\n];
    $returnIt .= qq[<input type="hidden" name="option" value="" />\n];
    $returnIt .= qq[<input type="hidden" name="selectedFile" value="$file" />\n];
    $returnIt .= qq[<input type="hidden" name="files_path" value="$files_path" />\n];
    $returnIt .= qq[<textarea name="buffer" $class $documentChangedFnct id="$textareaID" >$buffer</textarea><br />\n];
    # ckeditor textarea dimensions
    $returnIt .= qq[
<script type="text/javascript">
hh = window.innerHeight - 330;
CKEDITOR.replace( 'editor897',
{
height : hh,
});
</script>
];
    $returnIt .= qq{<a class="buttons" onclick="form1.option.value='editWrite'; form1.submit();">Write and Close</a> };
    $returnIt .= qq{<a class="buttons" onclick="form1.option.value='editWriteContinue'; form1.submit();">Write and Continue Edit</a>};
    $returnIt .= qq{ - - <label id="label1">Document Unchanged</label>};






    $returnIt .= qq[</form>\n];


    $returnIt .= qq{<a href="$thisFileName?files_path=$files_path" class="buttons">Cancel/Done</a>};
    $returnIt .= "<br />" x 2;



    # delete
    $returnIt .= qq{<a href="$thisFileName?option=deleteAsk&selectedFile=$file&files_path=$files_path" class="buttonsFaint">del</a>};

    $returnIt .= qq{<a href="" class="separator"> </a>};

    # rename
    $returnIt .= qq{<a href="$thisFileName?option=renameAsk&selectedFile=$file&files_path=$files_path" class="buttonsFaint">rename/move</a>};



    return $returnIt;
}


sub editWrite {
    my ($file, $buffer) = @_;
    $buffer =~ s/\015\012/\012/g;
    untaintIt ($file);

    $buffer > io($file);

    return qq{<a href="$thisFileName?files_path=$files_path">editor writing file please wait...</a>};
}



sub renameAsk {
    my $fileOld = shift;
    my $file = $fileOld;
#    my $newFileName;
    my $returnIt = ();

    $file = findFilename($file);
    $returnIt .= qq{<form name="form152">\n};
    $returnIt .= qq{<input type="hidden" name="option" value="renameFile" />\n};
    $returnIt .= qq{<input type="hidden" name="selectedFile" value="$fileOld" />\n};
    $returnIt .= qq{<input type="hidden" name="files_path" value="$files_path" />\n};
    $returnIt .= "Rename File: '" . filename2title($fileOld) . "'<br />";
    $returnIt .= qq{<input type="text" name="renameFileTo" value="$file" size="50" />\n};
    $returnIt .= qq{<a class="buttons" onclick="form152.submit()">Rename/Move File</a>};
    $returnIt .= "</form>\n";

    # this <form> only sets form above, this form does not submit itself
    $returnIt .= qq[<form name="form875" >\n<select name="pickone" onchange="form152.renameFileTo.value=form875.pickone.value+'$file';" >\n];
	$returnIt .= qq[<option value="">Move File</option>];
    for ( listFolders($files_path) ){
	$returnIt .= qq[<option value="$_/">move to "$_"</option>];
    }
    $returnIt .= "</select>\n</form>\n";

#    $returnIt .= qq[<a onclick="form152.renameFileTo.value=form875.pickone.value+'/$file'" class="buttons">Move File</a><br /><br />];


    $returnIt .= qq{<a href="$thisFileName?files_path=$files_path" class="buttons">Cancel</a>};

    return $returnIt;
}


sub renameFile {
    my ($fileOld, $fileNew) = @_;
    $fileNew = $files_path . "/$fileNew";

    $fileNew = cleanFilename($fileNew);
#    $fileNew = checkFilenameExt($fileNew);

    untaintIt($fileOld, $fileNew);

    rename $fileOld, $fileNew;
    

    return qq{<a href="$thisFileName?files_path=$files_path">renaming file please wait...</a>};
}



sub deleteAsk {
    my $file = shift;
    my $filename2title = $file;
    my $filename = $file;
    my $returnIt = ();

    $filename2title = filename2title($filename2title);
    $filename = findFilename ($filename);



    $returnIt .= qq[Delete "$filename2title"?<br />\n];
    $returnIt .= qq[<a href="$thisFileName?option=deleteFile&selectedFile=$file&files_path=$files_path" class="buttons">Delete "$filename"</a>];
    $returnIt .= qq[<a href class="separator"> </a>];
    $returnIt .= qq[<a href="$thisFileName?files_path=$files_path" class="buttons">Cancel</a>];

    return $returnIt;
}


sub deleteFile {
    my $file = shift;
    untaintIt ($file);

    if ( -d $file ) {
	rmdir $file;
    } else {
	unlink $file;
    }

    return qq{<a href="$thisFileName?files_path=$files_path">deleting file please wait...</a>};
}





sub displayPage {
    my $file = shift; untaintIt($file);
    my $textOrHtml = textOrHtml($file);
    my $src < io $file;
    my $returnIt = $textOrHtml eq "html" ? $src : "<pre>$src</pre>";
    return $returnIt;
}










# CGI upload handler ##############################
# taken from fusionplant.com...website [upload] management
sub CGI_Upload {
    my $files_path = shift;

    $CGI::DISABLE_UPLOADS = 0;
    $CGI::POST_MAX = MAX_FILE_SIZE;

    my $q = new CGI;
    $q->cgi_error and error( $q, "Error transferring file: " . $q->cgi_error );

    my $file      = $q->param( "file" ) || error( $q, "No file received." );
    #my $filename  = $q->param( "filename" ) || error( $q, "No filename entered." );
    my $filename = $file;
    my $fh        = $q->upload( "file" );
    my $buffer    = "";

    if ($filename =~ m[\\]){
        # internet explorer on windows sends full paths
        $filename = substr ($filename, rindex($filename, "\\") + 1);
    }

    if ( dir_size( $files_path ) + $ENV{CONTENT_LENGTH} > MAX_DIR_SIZE ) {
        error( $q, "Upload directory is full." );
    }

    # Allow letters, digits, periods, underscores, dashes
    # Convert anything else to an underscore
    $filename =~ s/[^\w.-]/_/g;
    if ( $filename =~ /^(\w[\w-]*)(.[\w.-]+)?/ ) {
        $filename = $1 . ($2 || '.no_extension');
    } else {
        error( $q, "Invalid file name; files must start with a letter or number." );
    }

    # Open output file, making sure the name is unique
    until ( sysopen OUTPUT, $files_path . "/$filename", O_CREAT | O_RDWR | O_EXCL ) {
        $filename =~ s/(\d*)(\.\w+)$/($1||0) + 1 . $2 . "_duplicate_" . "$year\_$mon\_$mday\-$hour\_$min\_$sec"/e;
        $1 >= MAX_OPEN_TRIES and error( $q, "Unable to save your file." );
    }

    # This is necessary for non-Unix systems; does nothing on Unix
    binmode $fh;
    binmode OUTPUT;

    # Write contents to output file
    while ( read( $fh, $buffer, BUFFER_SIZE ) ) {
        print OUTPUT $buffer;
    }

    close OUTPUT;

    print "content-type: text/html\n\n";
    print "<html>\n<head>\n<title>File received, your upload was processed</title>\n";
    print qq{<META HTTP-EQUIV="Refresh" CONTENT=".5;URL=$thisFileName?files_path=$files_path">\n};
    print "</head>\n<body>\n";
    print qq{File received, your upload was processed<br /><br />[ <a href=\"$thisFileName\">OK</a> ]};
    print "</body>\n</html>\n";



}





########################################################
##           FUNCTIONS                                ##
########################################################

# generate page from template
sub genPage {
    my $inHead = shift;
    my $title = shift;
    my $main = shift;

    my $src < io TEMPLATE;
    my $helpSrc < io $helpFile;

    my $docName = title2docName( $title );

    $src =~ s/<!--inHead-->/$inHead/g;
    $src =~ s/<!--title-->/$docName$title/g;
    $src =~ s/<!--main-->/$main/g;
    $src =~ s/<!--helpFile-->/$helpSrc/g;
    my $updated = updated();
    $src =~ s/<!--updated-->/$updated/g;
    $src =~ s/<!--host-->/$ENV{HTTP_HOST}/g;
    $src =~ s/<!--displayPage(.*)displayPage-->/$1/gs unless ($option eq "displayPage");


    print $src;
}



# generate full page view
# 9/18/2011
# taken from genPage ()
sub genPageView {
    my $inHead = shift;
    my $title = shift;
    my $main = shift;

    my $src < io TEMPLATE_viewFull;

    $src =~ s/<!--inHead-->/$inHead/g;
    $src =~ s/<!--title-->/$title/g;
    $src =~ s/<!--main-->/$main/g;
    $src =~ s/<!--host-->/$ENV{HTTP_HOST}/g;

    print $src;
}


# convert path+file > title #################
sub filename2title {
    my $name = shift;
    $name =~ s/_/ /g; # spaces
    $name =~ s|\.\w+$||; # extension
    $name =~ s|^\w+/||; # path
    return $name;
}





# untaint ##########################
sub untaintIt {
    for (@_){
        $_ = $1 if ( $_ =~ /^([-\@\w.\/_]+)$/ );
    }
}






# clean filename ########################
sub cleanFilename {
    my $file = shift;

    $file =~ s/\s/_/g;

    return $file;
}


# check Filename Extension ########################
#sub checkFilenameExt {
#    my $file = shift;


#    if ( rindex( $file, '.') < 1 ){
#	$file .= "." . FILES_EXT;
#    }


#    return $file;
#}



# human readable size ##################
sub humanReadableSize {
    my $size = shift;
    my $len = length($size);
    my $unit = ();

    if ( $len > 9 ){
	$unit = "GB";
	$size = $size / (2**30);
    } elsif ( $len > 6 ) {
	$unit = "MB";
	$size = $size / (2**20);
    } elsif ( $len > 3 ) {
	$unit = "KB";
	$size = $size / (2**10);
    } else {
	$unit = "B";
    }

    $size = sprintf("%.2f", $size);
    $size =~ s/\.00|(\..)0/$1/;

    return "$size$unit";
}




# format bytes ###################
# 10/20/2010 taken from forms3/all2.cgi
# commas
sub itemStatsBytes{
    local $_  = shift;
    1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
    return $_;
}


# format time #####################
# 10/20/2010 taken from forms3/all2.cgi
# modified 9/2/2011 to include time
sub itemStatsTime{
    my $time = shift;
    my ($min,$hour, $mday, $mon, $year,) = ( localtime( $time ) )[1..5];

    # date
    $year += 1900;
    $mon++;
    $_ = sprintf ("%02d", $_) for ($mon, $mday);

    # time
    $min = sprintf("%02d", $min);

    return "$mon/$mday/$year $hour:$min";
}






# file type #########################
# if file types change, also change in: textOrHtml
sub fileType {
    my $file = shift;
    my %type = (
	"htm" => "HTML",
	"html" => "HTML",
	"shtml" => "SHTML",
	"txt" => "plain text",
	"pdf" => "PDF",
	"gif" => "GIF",
	"jpg" => "JPEG",
	"jpeg" => "JPEG",
	"png" => "PNG",
	"psd" => "Photoshop PSD",
	"conf" => "Config File",
	"pl" => "Perl Script",
	"py" => "Python Script",
	"sh" => "Shell Script",
	"ico" => "Icon",
	"js" => "Javascript",
	"css" => "Cascading Style Sheet",
	"php" => "PHP",
	"asp" => "ASP",
	"db" => "Flatfile Database",
	"avi" => "AVI",
	"doc" => "MS DOC",
	"docx" => "MS DOC XML",
	"log" => "Log File",
	"rtf" => "Rich Text File",
	"wpd" => "Wordperfect",
	"epub" => "EPUB",
	"tar" => "Tape ARchive",
	"gz" => "gzip",
	"zip" => "ZIP",
	"xml" => "XML",
	"xhtml" => "XHTML",
	"mp3" => "mp3 audio",
	"mid" => "MIDI Audio",
	"wav" => "Wave Audio",
	"wma" => "Windows Media",
	);
    $file = findFilename($file); # remove path
    $file = findFileExt($file); # remove filename
    $file = lc ($file); # case insensitive
    return $type{$file};
}


# find file name from full path #####################
sub findFilename {
    my $path_file = shift;
    $path_file =~ s|.*/(.*)$|$1|;
    return $path_file;
}


# solves problem where having many tabs leaves little room for full titles. filename part of title is shown first
# 5/29/2015
sub title2docName {
    my $title = shift;
    my $returnIt = ();

    # if find slash then...
    if ( $title =~ m|.*/(.*+)| ){
	$returnIt = "$1 |"; # placeholder
    }

    return $returnIt;
}


# find file extension ##########################
sub findFileExt {
    my $file = shift;
    $file =~ s/.*\.(\w+)$/$1/;
    return $file;
}



# updated - show files modify date #################
sub updated {
    my $returnIt;

    $returnIt .= qq[<h4>Files Updated On:</h4>];


    for my $file (@filesUpdated){
	$returnIt .= "<b>$file:</b> ";

	# stats 7 size, 9 mtime
	my ($size, $mtime) = (stat("$file"))[7,9];
	my $size2 = itemStatsBytes($size) . "B";
	$returnIt .= " <span title='$size2'>" . humanReadableSize($size) . "</span>";
	$returnIt .= " [" . itemStatsTime( $mtime ) . "]";

	$returnIt .= "<br />\n";
    };


    $returnIt .= qq[<br /><br /><a onclick='document.getElementById("updated").className = "hideDiv";' ><span style="background-color:#ffd700; cursor: pointer;">Exit</span></a><br /><br />\n];


    $returnIt .= qq[open source project at <a href="http://fusionplant.com/">fusionplant.com</a>\n];

    return $returnIt;
}






# <pre> text or html file ########
sub textOrHtml {
    my $file = shift;
    my $type = fileType($file);
    my $textOrHtml;

    if ( $type eq "HTML" ) {
	$textOrHtml = "html";
    } elsif ( $type eq "plain text" ) {
	$textOrHtml = "text";
    }

    return $textOrHtml;
}


# convert path to breadcrumb (href) #############
sub pathToBreadcrumb {
    my $path = shift;
    my $start_files_path = shift;
    my @path = split /\//, $path;
    shift @path; #remove first blank
    my $last = pop @path;

    my @crumbs = (qq[<a href="$thisFileName?files_path=$start_files_path">Home</a>]);

    for my $L1 ( 0 .. $#path ){
	my $x = "$start_files_path";
	my $last;
	for my $L2 ( 0 .. $L1 ){
	    $x .= "/$path[$L2]";
	    $last = $path[$L2];
	}
	my $href = qq[<a href="$thisFileName?files_path=$x">$last</a>];
	push @crumbs, $href;
    }

    push @crumbs, $last;

    return (@crumbs);
}



# prevent user passing root params thru CGI, eg "/", ".."
sub preventRootFSAccess {
    my $path = shift;
    my $startPath = shift; untaintIt($startPath);

    $path = $startPath if $path eq "/"; # root  /
    $path = $startPath if $path =~ m/\.\./ ; # up DIR ..

    return $path;
}




# folder: number of files, size of dir perhaps
sub folderStats {
    my $path = shift;
    my $error;
    my $folderCount="0";
    my $fileCount="0";
    my $fileSize;

    opendir DIR, $path;
    for my $file (readdir DIR){
	if ( $file eq '.' || $file eq '..' ){
	    next;
	} elsif ( -f "$path/$file" ){
	    $fileCount++;
	    my $size = (stat "$path/$file")[7];
	    $fileSize += $size;
	} elsif ( -d "$path/$file" ) {
	    $folderCount++;
	} else {
	    $error .= "ELSE $path/$file, ERROR!!";
	}


    }
    closedir DIR;


    return "Folders=$folderCount, Files=$fileCount, Total Files Size=" .
	"<span title='" . itemStatsBytes($fileSize) . "Bytes'>" .
	humanReadableSize($fileSize) . "</span>" .
	$error;
}




# list folders in current path
sub listFolders {
    my $path = shift;
    my @folders;

    opendir DIR, $path;
    for my $file (readdir DIR){
	if ( $file eq '.' ){
	    next;
	} elsif ( $file eq '..' ){
	    push @folders, $file;
	} elsif ( -f "$path/$file" ){
	    next;
	} elsif ( -d "$path/$file" ) {
	    push @folders, $file;
	}


    }
    closedir DIR;

    return sort @folders;
}




# dir size #########################################################
sub dir_size {
    my $dir = shift;
    my $dir_size = 0;

    # Loop through files and sum the sizes; doesn't descend down subdirs
    opendir DIR, $dir or die "Unable to open $dir: $!";
    while ( readdir DIR ) {
        $dir_size += -s "$dir/$_";
    }
    return $dir_size;
}


# error ##########################################################
sub error {
    my( $q, $reason ) = @_;

    print $q->header( "text/html" ),
    $q->start_html( "Error" ),
    $q->h1( "Error" ),
    $q->p( "Your upload was not procesed because the following error", "occured: " ), $q->p( $q->i( $reason ) ), $q->end_html;
    exit;
}




