#!/usr/bin/perl # # filechucker.cgi # ###################################################################### # # DO NOT EDIT THIS FILE unless absolutely necessary; in most cases you # should be editing filechucker_prefs.cgi instead. If you do edit # this file, be sure to make a backup copy first. # ###################################################################### # # This program is the copyrighted work of Encodable Industries. # Redistribution is prohibited, and copies are permitted only for # backup purposes. You are free to modify the program for your # own use, but you may not distribute any modified copies of it. # # Use of this program requires a one-time license fee. You can # obtain a license here: # # http://encodable.com/filechucker/#download # # This software comes with no warranty. It is our hope that you # find it useful, but it comes with no guarantees. Under no # circumstances shall Encodable Industries be held liable in any # situation arising from your use of this program. # # For more information about this program, as well as for help # and support, please visit the following pages: # # Homepage: http://encodable.com/filechucker/ # Contact: http://encodable.com/contact/ my $version = "4.83"; use CGI::Carp 'fatalsToBrowser'; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; ($ENV{DOCUMENT_ROOT}) = ($ENV{DOCUMENT_ROOT} =~ /(.*)/); # untaint. #$ENV{DOCUMENT_ROOT} = 'c:\some\dir'; # If autodetection fails, uncomment & set this. #$ENV{SCRIPT_NAME} = '/cgi-bin/filechucker.cgi'; # If autodetection fails, uncomment & set this. use lib './perlmodules'; use lib '../cgi-bin/perlmodules'; use lib '../../cgi-bin/perlmodules'; use lib "$ENV{DOCUMENT_ROOT}/cgi-bin/perlmodules"; use lib "$ENV{DOCUMENT_ROOT}/../cgi-bin/perlmodules"; my (%PREF,%TEXT,%COOKIE) = (); #use Time::HiRes 'gettimeofday'; $PREF{script_start_time_highres} = gettimeofday(); my $debuglog = undef; $| = 1; use strict; #use warnings; use Fcntl; use POSIX; use File::Copy; use Digest::MD5 'md5_hex'; sub printd; sub die_nice; sub enc_warn; sub encdebug; sub encdebuglog; sub exit_with_error; # Set globals. TODO: some of these need to be re-scoped. my ($qs, $starttime, $total_upload_size, %temp, $num_files_in_progress_or_done, $total_file_count, $shortdatetime, $shortdatetime_forfilename, $datestring8) = (); sub print_new_upload_form() { exit_with_access_denied('upload') unless user_is_allowed_to('upload'); $PREF{userdir} = $PREF{enable_userdirs} = '' if $PREF{enforce_userdir_restrictions_on_upload_page} =~ /no/i; my $juststatus = $qs =~ /(?:^|&)juststatus(?:&|$)/ ? 1 : 0; my $hiddenstyle = qq`style="position: absolute; left: -10000px; overflow: hidden; height: 0;"`; $PREF{on_page} = $juststatus ? 'popupstatus' : 'uploader'; print_http_headers(); print_new_upload_form___firsthalf( $juststatus,$hiddenstyle) unless $qs =~ /output=secondhalf/; print_new_upload_form___secondhalf($juststatus,$hiddenstyle) unless $qs =~ /output=firsthalf/; } sub print_new_upload_form___firsthalf { my ($juststatus,$hiddenstyle) = @_; my @dirs = get_all_writable_directories(); exit_with_access_denied() unless @dirs; start_html_output($PREF{subtitle___uploader}); my $numitems = $total_file_count; $numitems = 1 if $numitems > $PREF{max_files_allowed}; $numitems = $PREF{num_custom_file_elements} if $PREF{using_custom_file_elements} =~ /yes/i; if($PREF{in_reprocessing_mode}) { $numitems = 0; my @qsitems = split(/&/, $qs); foreach my $item (@qsitems) { if($item =~ /^ffs\d+=file-(.+)/) { my $filename = $1; enc_urldecode($filename); $numitems++; $PREF{reprocessing_file_names}{$numitems} = $filename; } } } # The onsubmit() in this
is only fired when someone presses Enter when a textbox is focused. The upload button has its own call to onsubmit(). # print qq`\n\n`; print get_special_upload_note(); print qq`\n`; print qq`\n`; print process_custom_form_fields_code($PREF{custom_form_fields_top___code}); my $top_textboxes = get_textboxes('top'); print qq`$top_textboxes\n\n\n\n` if $top_textboxes; print $PREF{extra_HTML_output_just_before_file_fields}; my $subdir_dropdown_visible = 0; my $new_subdir_field_visible = 0; if($PREF{using_custom_file_elements} !~ /yes/i && $PREF{num_default_file_elements} != 0) { print qq`
\n` if $PREF{print_filefields_wrapper_div} =~ /yes/i; print qq`
$PREF{choosefiles_title}
\n` if $PREF{choosefiles_title}; my $subdir_from_url = ''; if($qs =~ /(?:^|&)path=(.+?)(?:&|$)/) { $subdir_from_url = $1; enc_urldecode($subdir_from_url); $subdir_from_url = enc_untaint($subdir_from_url, 'keep_path'); slashify($subdir_from_url); } for(my $i=1; $i<=$numitems; $i++) { my $row = ($i % 2) ? 'odd' : 'even'; my $custom_perfile_code = $PREF{custom_form_fields_perfile___code}; $custom_perfile_code =~ s/_%i/_$i/g; my $perfile_textboxes = get_textboxes('perfile', $i); if($PREF{in_reprocessing_mode}) { print qq`
`; if($i == 1 && $PREF{reprocessing_mode_file_list_message}) { my ($folder_name) = ($subdir_from_url =~ m!([^/]+)/*$!); $folder_name = '/' unless $folder_name; $PREF{reprocessing_mode_file_list_message} =~ s/%%folder_name%%/$folder_name/g; $PREF{reprocessing_mode_file_list_message} =~ s/%%num_files%%/$numitems/g; print $PREF{reprocessing_mode_file_list_message}; } print qq`\n`; my $subdir = (); if($subdir_from_url) { $subdir = $subdir_from_url; } else { $subdir = $PREF{userdir} ? qq`/$PREF{userdir_folder_name}/$PREF{userdir}/` : '/'; } print qq`\n`; print process_custom_form_fields_code($custom_perfile_code) if $custom_perfile_code; print $perfile_textboxes if $perfile_textboxes; print qq`
\n`; } else { my $filefield_template = $PREF{filefield_template}; my ($subdir_template) = ($filefield_template =~ m!%%%template:subdirlist%%%(.+?)%%%end-template:subdirlist%%%!gs); my $subdir_template_output = ''; $filefield_template =~ s!%%file_upload_field_label%%!$PREF{"file_upload_field_label_$i"} || $PREF{file_upload_field_label}!eg; $filefield_template =~ s!%%i%%!$i!g; $filefield_template =~ s!%%numitems%%!$numitems!g; $filefield_template =~ s!%%row_classname%%!$row!g; $filefield_template =~ s!%%first_classname%%!$i == 1 ? 'first' : ''!eg; $filefield_template =~ s!%%last_classname%%!$i == $numitems ? 'last' : ''!eg; $filefield_template =~ s!%%required_classname%%!$i == 1 && $PREF{allow_form_submissions_without_files} eq 'no' ? 'required' : ''!eg; $filefield_template =~ s!%%auto_add_more_files%%!$PREF{add_new_file_upload_fields_automatically} eq 'yes' && $PREF{max_files_allowed} > 1 ? qq`add_file_element_if_necessary();` : ''!eg; my $display_subdir_dropdown = $PREF{display_dropdown_box_for_subdir_selection} =~ /yes/i && user_is_allowed_to('choose_subdir_during_upload') && !($subdir_from_url && $PREF{hide_subdir_dropdown_when_passed_on_url} =~ /yes/i) && !($i > 1 && $PREF{only_allow_one_subdir_dropdown_per_upload} =~ /yes/i) ; my $display_newsubdir_field = $PREF{show_the_create_new_subdir_field_on_upload_form} =~ /yes/i && user_is_allowed_to('create_folders_during_upload') && !($PREF{only_allow_one_new_subdir_per_upload} =~ /yes/i && $i > 1) ; my $static_subdir = ''; my $newsubdir_instructions = ''; if($PREF{enable_subdirs} =~ /yes/i) { my (@dirs_pruned,@displaynames_pruned) = (); my $uploaded_files_url_path = get_uploaded_files_url_path('without_trailing_slash'); foreach my $dir (@dirs) { slashify($dir); # Each $dir here can contain one or more levels (i.e. can contain slashes), # does NOT include the leading "/upload/files", and for userdirs, does # include the leading "/users/Bob". next if $PREF{force_all_uploads_into_subdirectories} =~ /yes/i && $dir eq '/'; next if $PREF{force_all_uploads_into_subdirectories} =~ /yes/i && $PREF{userdir} && $dir eq qq`$uploaded_files_url_path/$PREF{userdir_folder_name}/$PREF{userdir}/`; next if item_is_hidden($dir); next if $PREF{hide_upload_form_folders_matching_this_regex} && $dir =~ m!$PREF{hide_upload_form_folders_matching_this_regex}!i; my $dirlist = get_qs_var('dirlist'); next if $dirlist && $dir !~ m!$dirlist!i; push @dirs_pruned, $dir; my $displayname = $uploaded_files_url_path . $dir; push @displaynames_pruned, $displayname; } if($display_subdir_dropdown) { $subdir_dropdown_visible = 1; remove_common_leading_substring(@displaynames_pruned) if $PREF{hide_common_leading_path_on_upload_form_subdirs} =~ /yes/i; my $dir_i = 0; for(@dirs_pruned) { my $displayname = $displaynames_pruned[$dir_i]; my $subdir_template_local = $subdir_template; $subdir_template_local =~ s!%%subdir_displayname%%!$displayname!g; $subdir_template_local =~ s!%%subdir_value%%!$_!g; # # 20110421: why were we ever matching subdir_from_url against the *display*name? # #$subdir_template_local =~ s!%%subdir_selected%%!$displayname eq $subdir_from_url ? qq`selected="selected"` : ''!eg; $subdir_template_local =~ s!%%subdir_selected%%!$_ eq $subdir_from_url ? qq`selected="selected"` : ''!eg; $subdir_template_output .= $subdir_template_local; $dir_i++; } } else { if($subdir_from_url) { $static_subdir = $subdir_from_url; } else { $static_subdir = $PREF{userdir} ? qq`/$PREF{userdir_folder_name}/$PREF{userdir}/` : $dirs_pruned[0]; } } if($display_newsubdir_field) { $new_subdir_field_visible = 1; #$newsubdir_instructions = $display_subdir_dropdown ? qq`$TEXT{will_be_created_inside___}` : ''; $newsubdir_instructions = $display_subdir_dropdown ? $TEXT{will_be_created_inside___} : ''; } } $filefield_template =~ s!%%%if-subdirs%%%(.*?)%%%endif-subdirs%%%!$PREF{enable_subdirs} eq 'yes' ? $1 : ''!egs; $filefield_template =~ s!%%%ifelse-choosesubdir%%%(.*?)%%%else%%%(.*?)%%%endelse-choosesubdir%%%!$display_subdir_dropdown ? $1 : $2!egs; $filefield_template =~ s!%%%if-newdir%%%(.*?)%%%endif-newdir%%%!$display_newsubdir_field ? $1 : ''!egs; $filefield_template =~ s!%%%template:subdirlist%%%(.+?)%%%end-template:subdirlist%%%!$subdir_template_output!gs; $filefield_template =~ s!%%static_subdir%%!$static_subdir!g; $filefield_template =~ s!%%newsubdir_instructions%%!$newsubdir_instructions!g; interpolate_vars_from_prefs($filefield_template); $filefield_template =~ s!%%custom_perfile_code%%!process_custom_form_fields_code($custom_perfile_code)!eg; $filefield_template =~ s!%%perfile_textboxes%%!$perfile_textboxes!g; print $filefield_template; } } print qq`
\n\n\n\n` if $PREF{print_filefields_wrapper_div} =~ /yes/i; if($PREF{max_files_allowed} > 1 && $PREF{using_custom_file_elements} !~ /yes/i && !$PREF{in_reprocessing_mode} && $PREF{show_add_another_file_link} =~ /yes/i) { print qq`\n
$TEXT{Add_another_file_}
`; } } print $PREF{extra_HTML_output_just_after_file_fields}; my $bottom_textboxes = get_textboxes('bottom'); print $bottom_textboxes if $bottom_textboxes; print process_custom_form_fields_code($PREF{custom_form_fields_bottom___code}); print qq`
\n` if $subdir_dropdown_visible; print qq`
\n` if $new_subdir_field_visible; } sub print_new_upload_form___secondhalf { my ($juststatus,$hiddenstyle) = @_; print get_human_test_form() if ($PREF{enable_human_test} =~ /yes/i && image_humantest_possible()); print qq`
\n` if $PREF{show_upload_status_in_popup_window} =~ /yes/i; print qq` $PREF{upload_button}
`; print $juststatus ? qq`
$PREF{progress_bar_placeholder_message__popup}
` : qq`
$PREF{progress_bar_placeholder_message}
` ; my $chosen_filenames_element = qq`
` if $PREF{show_chosen_filenames_during_upload} =~ /yes/i; print qq`
$chosen_filenames_element
? $PREF{KB}/s
$TEXT{Connecting_please_wait_}
? %
` . ($PREF{show_progress_table_during_uploads} =~ /yes/i ? qq`
$TEXT{Files}$TEXT{Size}$TEXT{Time}
$TEXT{Total} $total_file_count ? ??:??:??
$TEXT{Completed} 0 0 00:00:00
$TEXT{Remaining} $total_file_count ? ??:??:??
` : undef) . qq` $PREF{cancelbutton}
`; print qq`\n` if $PREF{use_iframe_for_upload} =~ /yes/i; print qq`
\n`; print qq`\n`; print qq`\n` if $PREF{humantest_hash}; print qq`\n` if $PREF{humantest_code} && $PREF{human_test_is_invisible} =~ /yes/i; print qq`
\n`; print qq`
\n` if $PREF{debug}; print qq`
\n` if $PREF{clear_page_during_upload} =~ /yes/i; print qq`
\n` if $PREF{show_progress_table_during_uploads} =~ /yes/i; print qq`
\n` if $juststatus; print qq`
\n` if $PREF{upload_progress_bar_disabled} =~ /yes/i; print qq`\n` if $PREF{show_upload_status_in_popup_window} =~ /yes/i; print qq`
\n` if $PREF{human_test_is_invisible} =~ /yes/i; print qq`
\n` if $PREF{only_allow_one_subdir_dropdown_per_upload} =~ /yes/i; print qq`
\n` if $PREF{only_allow_one_new_subdir_per_upload} =~ /yes/i; print qq`
\n` if $PREF{use_iframe_for_upload} =~ /yes/i; delete_old_files(); finish_html_output(); } sub hook { my ($current_filename, $buffer, $bytes_read, $logfh) = @_; my $current_file_has_been_logged = 0; my ($progress,$currentfile,$totalfiles,$totalsize,$start_time) = (); my $serial = $PREF{serial}; my @logcontents = (); # We're still the original process that's accepting the upload, so # we don't need to ask the backend for this now, we can store it # in a hash for easier retrieval: # $progress = $PREF{uploaddata}{$serial}{progress}; $currentfile = $PREF{uploaddata}{$serial}{currentfile}; $totalfiles = $PREF{uploaddata}{$serial}{totalfiles}; $totalsize = $PREF{uploaddata}{$serial}{totalsize}; $start_time = $PREF{uploaddata}{$serial}{start_time}; # There are three possibilities here: # # 1. $current_filename has already been logged (i.e. it's in @allfiles) # and its size has either gone up, or stayed the same; # # 2. $current_filename is in @allfiles but its size appears to have gone # down, meaning the user has uploaded two files that have the same # filename, so we'll handle this with if(!$current_file_has_been_logged); # # 3. $current_filename is NOT in @allfiles, which we'll also handle with # if(!$current_filename_has_been_logged). my $new_progress = (); my (@allfiles) = split(m!///!, $progress); for(@allfiles) { if(/(.+)=(\d+)$/) { my ($file,$old_progress) = ($1,$2); if($file eq $current_filename && $bytes_read >= $old_progress) { $new_progress .= "${current_filename}=${bytes_read}"; $current_file_has_been_logged = 1; } else { $new_progress .= "${file}=${old_progress}"; } $new_progress .= "///"; } } if(!$current_file_has_been_logged) { unless(!$current_filename || $bytes_read !~ /^\d+$/) { $new_progress .= "${current_filename}=${bytes_read}"; $num_files_in_progress_or_done++; } } # Update our hash for the next time hook() is called. We'll still update # the backend below, so the client can get the info too. # $PREF{uploaddata}{$serial}{progress} = $new_progress; $PREF{uploaddata}{$serial}{currentfile} = $num_files_in_progress_or_done; $PREF{uploaddata}{$serial}{totalfiles} = $total_file_count; $PREF{uploaddata}{$serial}{totalsize} = $total_upload_size; $PREF{uploaddata}{$serial}{start_time} = $starttime; if($PREF{use_database_for_temp_data} =~ /yes/i) { sql_untaint($new_progress, $num_files_in_progress_or_done, $total_file_count, $total_upload_size, $starttime, $PREF{serial}); my $sth = $PREF{dbh}->prepare("UPDATE $PREF{table_name_for_temp_data} SET progress='$new_progress', currentfile='$num_files_in_progress_or_done', totalfiles='$total_file_count', totalsize='$total_upload_size', start_time='$starttime' WHERE serial='$PREF{serial}';"); $sth->execute or die "$0: $DBI::errstr\n"; } else { seek $logfh, 0, 0; # seek to the beginning again, before we start writing. my $logline = "${new_progress}:|:${num_files_in_progress_or_done}:|:${total_file_count}:|:${total_upload_size}:|:${starttime}:|:ppd_false"; print $logfh "$logline\n"; # print the static info truncate $logfh, tell $logfh; # truncate the file (on the off chance that the new size is less than the old) flock $logfh, 8; # release the lock if(0) { # this has promise, but still doesn't seem to work around write-caching nonsense. do_alt_progbar_logging_stage1($serial, $logline) if !$PREF{num_hook_calls} || time() =~ /[02468]$/; } } $PREF{num_hook_calls}++; } sub do_alt_progbar_logging_stage1($$) { my ($serial, $logline) = @_; enc_urlencode($logline); my $url = "$PREF{protoprefix}$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?action=altprogbarlog&serial=$serial&logline=$logline"; print STDERR "$$: about to call lynx for url: $url\n"; `lynx -dump '$url'`; # TODO: if/when we get this working (currently it's still defeated by those # idiotic write-caching servers), we might want to do a module-based request # rather than an external call to lynx, etc. But until then, keep this # commented so as not to pull in that extra code needlessly. #use LWP::Simple qw(!head); #my $content = get $url; # that's LWP::Simple's get() function. } sub do_alt_progbar_logging_stage2() { my ($serial) = get_qs_var('serial'); my ($logline) = get_qs_var('logline'); return if length($logline) > $PREF{max_logline_length_for_alt_progbar_logging}; # security precaution. print STDERR "$$: about to do altprogbarlogging stage2 for logline: $logline\n"; my $altlogfile = get_progbar_log_alt_filename($serial); open(my $outfh, ">$altlogfile") or die_nice qq`Couldn't open altlog '$altlogfile' for writing: $!`; flock $outfh, 2; seek $outfh, 0, 0; print $outfh "$logline\n"; truncate $outfh, tell $outfh; # unlikely but just in case. close $outfh or die_nice qq`Couldn't close altlog '$altlogfile' after writing: $!`; print_http_headers(); exit; } sub get_progbar_log_filename($) { my $serial = shift; return "$PREF{datadir}/$serial.fctemp.log"; } sub get_progbar_log_alt_filename($) { my $serial = shift; return "$PREF{datadir}/$serial.fctemp.altlog"; } sub ajax_get_progress { print_xml_headers(); my $output = ''; my ($serial) = ($qs =~ /(?:^|&)serial=([0-9a-zA-Z]+)(?:&|$)/); if(!$serial) { $output = "ERROR: the URL is missing its serial number (serial=NNNNNN...)."; } else { my $fcvar = get_progress_and_size($serial); if($$fcvar{progress} eq 'ENOLOG') { $output = qq`ERROR: the log file hasn't been created yet.  Your server is probably doing some write-caching so the log doesn't get created when we create it; instead, it actually gets created AFTER the upload is complete, making progress reporting impossible.  Your upload is most likely proceeding normally, though, just without the progress bar. See this FAQ item for details.`; } elsif($$fcvar{progress} eq 'ENORAWPOST') { # TODO: on servers with ancient versions of Perl (i.e. where ENORAWPOST can happen), when using the popup status window, how do we know when to close the window? $output = qq`ERROR: the rawpost file hasn't been created yet.  Your server is probably doing some write-caching so the file doesn't get created when we create it; instead, it actually gets created AFTER the upload is complete, making progress reporting impossible.  Your upload is most likely proceeding normally, though, just without the progress bar. See this FAQ item for details.`; } else { if($$fcvar{total_size} > $CGI::POST_MAX) { $$fcvar{size_error} = 'toobig'; $$fcvar{size_limit} = $CGI::POST_MAX; } if($$fcvar{total_file_count} > $PREF{max_files_allowed}){ $$fcvar{count_error} = 'toomany'; $$fcvar{count_limit} = $PREF{max_files_allowed}; } if(data_exceeds_global_quota($$fcvar{total_size})) { $$fcvar{size_error} = 'globalquotaexceeded'; $$fcvar{size_limit} = $PREF{quota_for_entire_upload_directory}; } if(data_exceeds_user_quota($$fcvar{total_size})) { $$fcvar{size_error} = 'userquotaexceeded'; $$fcvar{size_limit} = $PREF{quota_for_member_userdirs}; } foreach my $var (sort keys %$fcvar) { $output .= "$var=$$fcvar{$var}|:|:|"; } } } #print qq`\n\n`; print qq`\n\n`; print $output; print qq`\n\n`; } sub get_progress_and_size { printd(qq`starting get_progress_and_size()\n`); unless(user_is_allowed_to('upload')) { exit_with_access_denied('upload'); } my $serial = shift; $serial = enc_untaint($serial); my ($progress,$currentfile,$totalfiles,$totalprogress,$totalsize,$start_time,$elapsedtime,$ppd_status) = ('','','','','','','',''); if($PREF{using_upload_hook} =~ /yes/i) { if($PREF{use_database_for_temp_data} =~ /yes/i) { sql_untaint($serial); my $sth = $PREF{dbh}->prepare("SELECT progress,currentfile,totalfiles,totalsize,start_time FROM $PREF{table_name_for_temp_data} WHERE serial='$serial';"); $sth->execute; ($progress,$currentfile,$totalfiles,$totalsize,$start_time) = $sth->fetchrow; } else { my $reglog = get_progbar_log_filename($serial); my $altlog = get_progbar_log_alt_filename($serial); my $logfile = -e $reglog ? $reglog : -e $altlog ? $altlog : undef; #my $logfile = -e $altlog ? $altlog : undef; if($logfile) { open(READLOGFILE,"<$logfile") or die "$0: couldn't open $logfile for reading: $!\n"; flock READLOGFILE, 1; seek READLOGFILE, 0, 0; my $line = ; chomp $line; close READLOGFILE or die "$0: couldn't close $logfile after reading: $!\n"; ($progress,$currentfile,$totalfiles,$totalsize,$start_time,$ppd_status) = split(/:\|:/, $line); } else { $totalprogress = 'ENOLOG'; } } unless($totalprogress eq 'ENOLOG') { my (@allfiles) = split(m!///!, $progress); for(@allfiles) { my ($file,$progress) = (/(.+)=(\d+)$/); $progress = 0 unless $progress; $totalprogress += $progress; } $elapsedtime = offsettime() - $start_time; } } else { # If we're not using the upload hook from CGI.pm, then we can't detect # the file boundaries within the raw post data, which means we can't # display the info for files total/completed/remaining. So we just # need the totalsize, already-uploaded-size, and starttime/elapsedtime # here. if($PREF{use_database_for_temp_data} =~ /yes/i) { sql_untaint($serial); my $sth = $PREF{dbh}->prepare("SELECT progress,currentfile,totalfiles,totalsize,start_time FROM $PREF{table_name_for_temp_data} WHERE serial='$serial';"); $sth->execute; ($progress,$currentfile,$totalfiles,$totalsize,$start_time) = $sth->fetchrow; ($totalprogress) = ($progress =~ /.+=(\d+)/); } else { opendir(GETPROGRESSDIRFH, $PREF{datadir}) or die "$0: couldn't read directory $PREF{datadir}: $!\n"; my $dirh = \*GETPROGRESSDIRFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". my (@rawposts) = grep { /^$serial\.CL_\d+\.ST_\d+\.rawpost$/ } readdir($dirh); closedir $dirh or enc_warn "$0: couldn't close directory $PREF{datadir}: $!\n"; #FIXME: why doesn't this close properly? my $rawpost = $rawposts[0]; if(-e "$PREF{datadir}/$rawpost") { ($totalsize,$start_time) = ($rawpost =~ /^$serial\.CL_(\d+)\.ST_(\d+)\.rawpost$/); $totalprogress = -s "$PREF{datadir}/$rawpost"; } else { $totalprogress = 'ENORAWPOST'; } } $elapsedtime = offsettime() - $start_time; ($currentfile,$totalfiles) = (1,1); } my %fcvar = ( progress => $totalprogress, total_size => $totalsize, elapsed_time => $elapsedtime, finished_file_count => $currentfile ? $currentfile - 1 : 0, total_file_count => $totalfiles, ppd_status => $ppd_status eq 'ppd_true' ? 1 : 0, ); return \%fcvar; } sub tainted { return ! eval { eval("#" . substr(join("", @_), 0, 0)); 1 }; } sub data_exceeds_global_quota { my $datasize = shift; if($PREF{quota_for_entire_upload_directory} =~ /^\d+$/ && $PREF{quota_for_entire_upload_directory} > 0) { if( ($datasize + get_dir_size($PREF{uploaded_files_realpath})) > $PREF{quota_for_entire_upload_directory} ) { return 1; } } return 0; } sub data_exceeds_user_quota { my $datasize = shift; if($PREF{quota_for_member_userdirs} =~ /^\d+$/ && $PREF{quota_for_member_userdirs} > 0 && $PREF{userdir} && !$PREF{admin_is_logged_in}) { if( ($datasize + get_dir_size("$PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}/$PREF{userdir}")) > $PREF{quota_for_member_userdirs} ) { return 1; } } return 0; } sub get_special_upload_note { my $note = ''; if($PREF{in_replace_mode}) { $note .= "Note: in Replace Mode.  Any file that you upload must have the exact same name as one of these files on the server:
"; my @qsitems = split(/&/, $qs); foreach my $item (@qsitems) { if($item =~ /^rfn\d+=file-(.+)/) { my $filename = $1; enc_urldecode($filename); $PREF{replacement_file_names}{$filename} = 1; $note .= "
$1"; } } } if($PREF{in_reprocessing_mode}) { $note .= "Note: in Reprocessing Mode.  Using your selected files from the server instead of uploading new files.
"; } if($PREF{in_addfile_mode}) { $note .= "Note: in AddFile Mode.  Upload your new file(s) to existing sets.
"; } if($note) { $note = qq`
$note
`; } return $note; } sub process_upload() { ## Debug: show the raw POST data and then exit: #my $input = ''; $input .= $_ for (); #print "Content-type: text/plain\n\n"; #print $input; #exit; printd( qq`010: starting process_upload()` ); unless(user_is_allowed_to('upload')) { exit_with_access_denied('upload'); } $PREF{userdir} = $PREF{enable_userdirs} = '' if $PREF{enforce_userdir_restrictions_on_upload_page} =~ /no/i; if($PREF{urls_allowed_to_post_to_us_01}) { my $url_allowed = 0; foreach my $pref (sort keys %PREF) { if($pref =~ /urls_allowed_to_post_to_us_\d+$/) { $url_allowed = 1 if $ENV{HTTP_REFERER} =~ m!^$PREF{$pref}!i; } } die_nice("Error: posting from a non-allowed URL.") unless $url_allowed; } die_nice(qq`Error: you didn't pass the upload serial number (serial=NNNNNN...) on the URL.\n`) unless $PREF{serial}; $PREF{serial} = enc_untaint($PREF{serial}); my $serial = $PREF{serial}; $total_upload_size = $ENV{CONTENT_LENGTH}; my ($logfile,$logfh) = (); # We'll use this hash in the main/parent/original-getting-POSTed-to process, # so we never need to read from the backend, only write to it. # $PREF{uploaddata}{$serial}{progress} = 0; $PREF{uploaddata}{$serial}{currentfile} = $num_files_in_progress_or_done; $PREF{uploaddata}{$serial}{totalfiles} = $total_file_count; $PREF{uploaddata}{$serial}{totalsize} = $total_upload_size; $PREF{uploaddata}{$serial}{start_time} = $starttime; if($PREF{use_database_for_temp_data} =~ /yes/i) { sql_untaint($PREF{serial}, $num_files_in_progress_or_done, $total_file_count, $total_upload_size, $starttime); my $sth = $PREF{dbh}->prepare("INSERT INTO $PREF{table_name_for_temp_data} (serial,progress,currentfile,totalfiles,totalsize,start_time) VALUES('$PREF{serial}', '0', '$num_files_in_progress_or_done', '$total_file_count', '$total_upload_size', '$starttime');"); $sth->execute or die "$0: $DBI::errstr\n"; } else { $logfile = "$PREF{datadir}/$PREF{serial}.fctemp.log"; printd( qq`011: about to sysopen() logfile $logfile` ); unlink($logfile) if -e $logfile; # in case the user has hit their "Back" button after a failed upload, leaving the log behind. sysopen(LOGFHFORTEMPDATA, $logfile, O_RDWR | O_EXCL | O_CREAT) or die "$0: couldn't create logfile $logfile for R/W: $!\n"; $logfh = \*LOGFHFORTEMPDATA; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". # RDWR=R/W, EXCL=die if already exists, CREAT=create if DNE. select((select($logfh), $| = 1)[0]); flock $logfh, 2; close $logfh or die "$0: couldn't write new (empty) file $logfile to disk: $!\n"; sysopen(LOGFHFORTEMPDATA, $logfile, O_RDWR) or die "$0: couldn't open $logfile for R/W: $!\n"; $logfh = \*LOGFHFORTEMPDATA; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". select((select($logfh), $| = 1)[0]); flock $logfh, 2; seek $logfh, 0, 0; my $firstline = "0:|:${num_files_in_progress_or_done}:|:${total_file_count}:|:${total_upload_size}:|:${starttime}:|:ppd_false"; print $logfh $firstline; truncate $logfh, tell $logfh; # unlikely but just in case. flock $logfh, 8; printd( qq`015: wrote first line to logfile` ); printd( qq`016: firstline: $firstline` ); printd( qq`017: unlocked logfile` ); } if($ENV{CONTENT_LENGTH} > $CGI::POST_MAX) { print "Content-type: text/plain\n\n"; print "Error: your upload was too big.\nYou tried to upload " . format_filesize_nicely($ENV{CONTENT_LENGTH}) . ",\nbut the current limit is " . format_filesize_nicely($CGI::POST_MAX) . ".\nPlease go back and choose a smaller file.\n"; exit; # TODO: why does printing the error as above work properly, while # calling exit_with_error() causes the script to hang? # #exit_with_error("Error: your upload was too big.\nYou tried to upload " . format_filesize_nicely($ENV{CONTENT_LENGTH}) . ",\nbut the current limit is " . format_filesize_nicely($CGI::POST_MAX) . ".\nPlease go back and choose a smaller file.\n"); } if(data_exceeds_global_quota($ENV{CONTENT_LENGTH})) { enc_redirect("$PREF{here_error}?error=globalquotaexceeded&size=$ENV{CONTENT_LENGTH}&limit=$PREF{quota_for_entire_upload_directory}$PREF{default_url_vars}"); } if(data_exceeds_user_quota($ENV{CONTENT_LENGTH})) { enc_redirect("$PREF{here_error}?error=userquotaexceeded&size=$ENV{CONTENT_LENGTH}&limit=$PREF{quota_for_member_userdirs}}$PREF{default_url_vars}"); } my ($query,$rawpost) = (); if($PREF{using_upload_hook} =~ /yes/i) { $query = CGI->new(\&hook,$logfh); } else { # Receive the upload data manually and save it to a temporary file, # rather than using "my $query = CGI->new(\&hook,$logfh);" , so # that we can function on servers whose CGI.pm is too old to support # the upload hook functionality. We'll still use CGI.pm to parse # the post-data afterwards. # $rawpost = "$PREF{datadir}/$PREF{serial}.CL_${total_upload_size}.ST_" . (offsettime()) . ".rawpost"; $rawpost = enc_untaint($rawpost,'keep_path'); unlink($rawpost) if -e $rawpost; # in case the user has hit their "Back" button after a failed upload, leaving the rawpost behind. sysopen(UPLOADRAWDATAFH, $rawpost, O_RDWR | O_EXCL | O_CREAT) or die "$0: couldn't create $rawpost for R/W: $!\n"; my $upfh = \*UPLOADRAWDATAFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". flock $upfh, 2; close $upfh or die "$0: couldn't write new (empty) file $rawpost to disk: $!\n"; sysopen(UPLOADRAWDATAFH, $rawpost, O_RDWR) or die "$0: couldn't open $rawpost for R/W: $!\n"; $upfh = \*UPLOADRAWDATAFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". flock $upfh, 2; seek $upfh, 0, 0; select((select($upfh), $| = 1)[0]); my ($bytes_uploaded_so_far, $chunk) = (0, ''); while( ($bytes_uploaded_so_far < $total_upload_size) && ($bytes_uploaded_so_far += read(STDIN, $chunk, 8192)) ) { select(undef, undef, undef, $PREF{sleep_time_during_nonhook_uploads}); # sleep for a few ms (see "perldoc -f select") print $upfh $chunk; # We don't use the logfile at all in nonhook mode, so this call is unnecessary. # TODO: maybe we should, then we wouldn't need to check for using_upload_hook # in the get_progress_and_size() sub? And then the ENORAWPOST+popup-status # issue would be resolved? #hook('dummy_filename_for_nonhook_version.foo', undef, $bytes_uploaded_so_far, $logfh); } truncate $upfh, tell $upfh; close $upfh or die "$0: couldn't write post-data to file $rawpost: $!\n"; # Re-open it on STDIN so that CGI.pm can process it. open(STDIN,"<$rawpost") or die "$0: couldn't open post-data file $rawpost on STDIN: $!\n"; flock STDIN, 1; seek STDIN, 0, 0; $query = new CGI(); } if($logfh) { flock $logfh, 2; seek $logfh, 0, 0; print $logfh "$PREF{uploaddata}{$serial}{progress}:|:$PREF{uploaddata}{$serial}{currentfile}:|:$PREF{uploaddata}{$serial}{totalfiles}:|:$PREF{uploaddata}{$serial}{totalsize}:|:$PREF{uploaddata}{$serial}{start_time}:|:ppd_true"; truncate $logfh, tell $logfh; # unlikely but just in case. flock $logfh, 8; } $PREF{uploaddata}{$serial}{end_time} = offsettime(); # For hosts like GoDaddy that drop MySQL connections every 10 seconds: get_db_connection('force') if (database_required() && $PREF{reconnect_to_db_after_upload} =~ /yes/i); if($PREF{enable_human_test} =~ /yes/i && image_humantest_possible()) { my $passed_test = do_human_test(param("fcht1"), param("fcht2")); die_nice($TEXT{Error__failed_human_test__please_try_again_}) unless $passed_test; } if($PREF{enable_upload_counter_number} =~ /yes/i) { my $cfile = $PREF{datadir} . '/' . '_fc_counter_value.txt'; create_file_if_DNE($cfile,$PREF{writable_file_perms}); open(CFILE,"+<$cfile") or die_nice("$PREF{internal_appname}: process_upload(): could not open file '$cfile' for R/W: $!\n"); flock CFILE, 2; seek CFILE, 0, 0; $PREF{upload_counter_value} = ; chomp $PREF{upload_counter_value}; unless($PREF{upload_counter_value} =~ /^\d+$/) { enc_warn "$PREF{internal_appname}: process_upload(): invalid counter value '$PREF{upload_counter_value}'; using 1 instead.\n"; $PREF{upload_counter_value} = 1; } seek CFILE, 0, 0; print CFILE ($PREF{upload_counter_value} + 1) . "\n"; truncate CFILE, tell CFILE; close CFILE or die_nice("$PREF{internal_appname}: process_upload(): could not close file '$cfile' after R/W: $!\n"); if($PREF{pad_with_zeros_to_this_length} =~ /^\d+$/ && $PREF{pad_with_zeros_to_this_length} > 0) { while($PREF{upload_counter_value} !~ /^\d{$PREF{pad_with_zeros_to_this_length}}$/) { $PREF{upload_counter_value} = '0' . $PREF{upload_counter_value}; } } } my (%output, %textboxes, %files_left_blank_by_user, %cookies_to_set, $at_least_one_file_successfully_uploaded, %upload_info, $some_files_were_blocked, $textbox_values_for_qs) = (); my $numitems = $query->param('numitems'); my $f = $ENV{chr(72).chr(84).chr(84).chr(80)."_".chr(72).chr(79).chr(83).chr(84)}; $f =~ s/^w{3}\.//i; $f =~ s/:\d+$//i; $f =~ s/^(?:[^\.]+\.)+([^\.]+\.[^\.]+)$/$1/; if($f =~ /^([a-zA-Z0-9]).*([a-zA-Z0-9])\.([a-zA-Z]).*([a-zA-Z])$/) { unless((ord($1)==103&&ord($2)==100&&ord($3)==99&&ord($4)==109)) { print "Content-type: text/html\n\n"; print chr(93)."\n"; exit; } } printd( qq`030: numitems=$numitems` ); my $i = 1; foreach my $formfield (get_textbox_pref_keys('top', 'bottom')) { next if $PREF{"${formfield}_is_group_master"} =~ /yes/i; my $shortname = $PREF{"${formfield}_shortname"}; my $formfield_value = $query->param($shortname); next if $PREF{"${formfield}_skip_if_null"} =~ /yes/i && !$formfield_value; # Checkbox processing: # if($PREF{"${formfield}_group"}) # the groups feature here is intended for formfields that are checkboxes. { # since we're aggregating all members of this group into a single string, # we want the value to be null (not "off" etc) if the box is unchecked. # #$formfield_value = $query->param($PREF{$formfield}) =~ /on/i ? $PREF{$formfield} : ''; $formfield_value = $formfield_value =~ /on/i ? $PREF{$formfield} : ''; my $groupname = $PREF{"${formfield}_group"}; $formfield = "formfield_${groupname}"; } elsif($PREF{"${formfield}_checkbox"} =~ /yes/i && $PREF{"${formfield}_binary"} =~ /yes/i) { $formfield_value = $formfield_value =~ /on/i ? 1 : 0; } elsif($PREF{"${formfield}_checkbox"} =~ /yes/i && $PREF{"${formfield}_raw"} !~ /yes/i) { $formfield_value = $formfield_value =~ /on/i ? $TEXT{checkbox_yes} : $TEXT{checkbox_no}; } $formfield_value = $PREF{"${formfield}_transform_${formfield_value}"} if exists $PREF{"${formfield}_transform_${formfield_value}"}; $formfield_value = $PREF{upload_counter_value} if $PREF{enable_upload_counter_number} =~ /yes/i && $PREF{"${formfield}_iscounter"} =~ /yes/i; $textboxes{$formfield}{multiline} = $PREF{"${formfield}_multiline"} =~ /yes/i ? 1 : 0; $textboxes{$formfield}{name} = $PREF{$formfield} ? $PREF{$formfield} : $shortname; if($textboxes{$formfield}{value}) { $textboxes{$formfield}{value} .= $formfield_value ? $PREF{"${formfield}_group_separator"} . $formfield_value : ''; } else { $textboxes{$formfield}{value} = $formfield_value; } clean_up_text($textboxes{$formfield}{value}) if $PREF{"${formfield}_clean"} =~ /yes/i; $textboxes{$formfield}{value} =~ s/\r\n/\n/g; if( $PREF{"${formfield}_email"} =~ /yes/i ) { my $j = 1; for( split(/[,\s]+/, $formfield_value) ) { if($PREF{email_notifications_to_userEntered_addresses} =~ /yes/i && $PREF{"${formfield}_dont_send_email"} !~ /yes/i) { $PREF{"${formfield}_emailtemplate"} =~ /(\w+)/ ? $PREF{"email_notification_recipient_fromtextbox_${1}template_${i}_${j}"} = $_ : $PREF{"email_notification_recipient_fromtextbox_${i}_${j}"} = $_; } if($j == 1) { $PREF{first_user_entered_email_address} = $_ unless $PREF{first_user_entered_email_address}; } $j++; } } if( $PREF{"${formfield}_save"} =~ /yes/i ) { $cookies_to_set{$shortname} = $formfield_value; } $textboxes{_by_shortname}{$shortname} = $textboxes{$formfield}{value}; $i++; } #print "Content-type: text/plain\n\n"; my %uploadedfiles = (); my $num_files_really_sent = 0; my $num_file_elements = param('numfileelements'); for(my $h=1; $h<=$num_file_elements; $h++) { my $filename = $query->param("$PREF{filefield_namebase}$h"); if(!$filename) { $files_left_blank_by_user{$h} = 1; next; } foreach my $tmpfile ($query->upload("$PREF{filefield_namebase}$h")) # what upload() returns is apparently both a handle and a filename? { $num_files_really_sent++; #print "filename from query->upload($PREF{filefield_namebase}$h): $tmpfile\n"; $uploadedfiles{$num_files_really_sent}{param} = $query->param("$PREF{filefield_namebase}$h"); $uploadedfiles{$num_files_really_sent}{handle} = $tmpfile; $uploadedfiles{$num_files_really_sent}{filename} = $tmpfile; $uploadedfiles{$num_files_really_sent}{file_subgroup_num} = $h; } } $numitems = $PREF{uploaddata}{$serial}{totalfiles} = $num_files_really_sent; if($num_files_really_sent > $PREF{max_files_allowed}) { # For small quick uploads, the AJAX never has a chance to catch & report # this error, so we need to check for it here on the backend, too. $TEXT{upload_too_many_files_error} =~ s!%%upload_count%%!$num_files_really_sent!g; $TEXT{upload_too_many_files_error} =~ s!%%limit%%!$PREF{max_files_allowed}!g; exit_with_error($TEXT{upload_too_many_files_error}); } exit_with_error(qq`No files included in upload.`) if $PREF{allow_form_submissions_without_files} eq 'no' && $num_files_really_sent == 0; # for spambots who POST directly to the script. my $recipient_i = 0; # $num_file_elements is the total number of s, # whereas numitems is the number of file elements that the user # actually filled in. # #my $num_file_elements = param('numfileelements'); $i = 0; # no "my" because we used $i above. #for(my $h=1; $h<=$num_file_elements; $h++) foreach my $g (sort { $a <=> $b } keys %uploadedfiles) { my $h = $uploadedfiles{$g}{file_subgroup_num}; #my $filename = $query->param("$PREF{filefield_namebase}$h"); my $filename = $uploadedfiles{$g}{filename}; $i++; my $rawname = $filename; $rawname =~ s!.*[/\\](.+)!$1!; # remove any path info that's sometimes present (on IE/Windows uploads only?) my $extension = ($filename =~ /.*\.(.+)/)[0] || ''; # the $extension variable doesn't contain a dot. printd( qq`040: file $i of $numitems: $filename` ); #print STDERR "020: filename=$filename\n"; $filename = enc_untaint($filename); #print STDERR "025: filename=$filename\n"; unless($PREF{in_reprocessing_mode}) # if we're using files from the server, then we can't block them; they're already there, and must be OK. { if(filename_is_illegal($filename)) { $output{"filesize$i"} = $upload_info{$i}{size} = 'EILLEGALEXT'; $output{"linktofile$i"} = $upload_info{$i}{name} = $filename; $output{"linktofile_for_email$i"} = qq`"$filename": skipped because the filetype is not allowed.`; $some_files_were_blocked = 1; next; } } if($PREF{in_replace_mode}) { $PREF{overwrite_existing_files} = 'yes'; my @qsitems = split(/&/, $qs); foreach my $item (@qsitems) { if($item =~ /^rfn\d+=file-(.+)/) { my $fname = $1; enc_urldecode($fname); $PREF{replacement_file_names}{$fname} = 1; } } unless($PREF{replacement_file_names}{$filename}) { $output{"filesize$i"} = $upload_info{$i}{size} = 'ENOREPLACE'; $output{"linktofile$i"} = $upload_info{$i}{name} = $filename; $output{"linktofile_for_email$i"} = qq`"$filename": skipped because we are in Replace Mode and that file does not exist on the server.`; $some_files_were_blocked = 1; next; } } foreach my $textbox (get_textbox_pref_keys('perfile')) { my $shortname = $PREF{"${textbox}_shortname"}; next if $PREF{"${textbox}_skip_if_null"} =~ /yes/i && !$query->param("${shortname}_$h"); $textboxes{"${textbox}_$i"}{multiline} = $PREF{"${textbox}_multiline"} =~ /yes/i ? 1 : 0; $textboxes{"${textbox}_$i"}{name} = $PREF{$textbox} ? $PREF{$textbox} : $shortname; $textboxes{"${textbox}_$i"}{value} = $query->param("${shortname}_$h"); clean_up_text($textboxes{"${textbox}_$i"}{value}) if $PREF{"${textbox}_clean"} =~ /yes/i; $textboxes{"${textbox}_$i"}{value} =~ s/\r\n/\n/g; if( $PREF{"${textbox}_email"} =~ /yes/i ) { my $j = 1; for( split(/[,\s]+/, $query->param("${shortname}_$h")) ) { if($PREF{email_notifications_to_userEntered_addresses} =~ /yes/i && $PREF{"${textbox}_dont_send_email"} !~ /yes/i) { $PREF{"${textbox}_emailtemplate"} =~ /(\w+)/ ? $PREF{"email_notification_recipient_fromperfiletextbox_${1}template_${i}_${j}"} = $_ : $PREF{"email_notification_recipient_fromperfiletextbox_${i}_${j}"} = $_; } if($j == 1) { $PREF{first_user_entered_email_address} = $_ unless $PREF{first_user_entered_email_address}; } $j++; } } if( $PREF{"${textbox}_save"} =~ /yes/i ) { $cookies_to_set{"${shortname}_$h"} = $query->param("${shortname}_$h"); } } unless($PREF{in_reprocessing_mode}) # files are already on the server. { if($PREF{reformat_filenames_for_all_uploads}) { my $reformatted_filename = $PREF{reformat_filenames_for_all_uploads}; my ($original_filename, $original_ext) = ($filename =~ /(.+)\.(.+)/); $original_filename = $filename unless $original_filename; # in case the file had no extension. my $userdir = $PREF{userdir}; interpolate_vars_from_URL_and_cookies('include_undefined', $reformatted_filename); while($reformatted_filename =~ /(%FIELD\{(\w+)\})/g) { my ($to_replace, $shortname) = ($1, $2); my $formfield_key = get_formfield_key_from_shortname($shortname); my $replacement = exists $textboxes{$formfield_key} ? $textboxes{$formfield_key}{value} : $textboxes{"${formfield_key}_$h"}{value}; # the latter is for perfile formfields. $reformatted_filename =~ s/$to_replace/$replacement/; } $reformatted_filename =~ s/#C/$PREF{upload_counter_value}/g; $reformatted_filename =~ s/#O/$original_filename/g; $reformatted_filename =~ s/#E/$original_ext/g; $reformatted_filename =~ s/#U/$userdir/g; $reformatted_filename =~ s/#N/$i/g; interpolate_vars_from_prefs($reformatted_filename); interpolate_vars_from_date("etime=$PREF{uploaddata}{$serial}{end_time}", $reformatted_filename); $filename = $reformatted_filename; #printd "reformatted filename: $reformatted_filename\n"; } $filename = lc($filename) if $PREF{convert_upload_filenames_to_case} eq 'lowercase'; $filename = uc($filename) if $PREF{convert_upload_filenames_to_case} eq 'uppercase'; } unless($PREF{in_reprocessing_mode}) # if we're using files from the server, then we can't affect their filenames at this point. { clean_up_filename($filename) if $PREF{clean_up_filenames} =~ /yes/i; } my $the_entered_new_subdir = $PREF{only_allow_one_new_subdir_per_upload} =~ /yes/i ? $query->param("newsubdir1") : $query->param("newsubdir$h"); my ($subdir, $num_subdir_levels, $newsubdir) = ('', 0, ''); if($PREF{ignore_chosen_subdir_during_upload_if_new_subdir_entered} =~ /yes/i && $the_entered_new_subdir) { $subdir = '/'; } elsif($PREF{serial_is_userdir} =~ /yes/i) { $subdir = $PREF{userdir_folder_name} . '/' . $PREF{userdir}; slashify($subdir); } elsif($PREF{enable_subdirs} =~ /yes/i) { $subdir = $PREF{only_allow_one_subdir_dropdown_per_upload} =~ /yes/i ? $query->param("subdir1") : $query->param("subdir$h"); $subdir = enc_untaint($subdir, 'keep_path') if $subdir; if(!$subdir) # which is the case when using custom file elements. { if($PREF{userdir}) { $subdir = $PREF{userdir_folder_name} . '/' . $PREF{userdir}; slashify($subdir); } } else { $num_subdir_levels = 0; while($subdir =~ m!(/|\\)[^/\\]+!g) { $num_subdir_levels++; } slashify($subdir); } } elsif($PREF{userdir}) { $subdir = $PREF{userdir_folder_name} . '/' . $PREF{userdir}; slashify($subdir); } else { $subdir = '/'; } my $finalpath_url = $PREF{uploaded_files_urlpath} . $subdir; my $finalpath_real = $PREF{uploaded_files_realpath} . $subdir; my $finalpath_local= $subdir; condense_slashes('leave_leading_UNC', $finalpath_real); condense_slashes($finalpath_url); die_nice "Error: \$finalpath_real ($finalpath_real) does not exist...\n" unless -d $finalpath_real; die_nice "Error: \$finalpath_real ($finalpath_real) is not writable...\n" unless -w $finalpath_real; # 20110224: shouldn't we always just check get_effective_folder_permissions(), rather than going through all the dirs (via user_has_write_access_to_path()) ? # #exit_with_error("Insufficient permissions on target folder ('$finalpath_local').") unless user_has_write_access_to_path($finalpath_local); exit_with_error("Insufficient permissions on target folder ('$finalpath_local').") unless folder_perms_allow_writing($finalpath_local); if(($PREF{integrate_with_userbase} =~ /yes/i || $PREF{integrate_with_userbase_method_b} =~ /yes/i) && $PREF{enable_userdirs} =~ /yes/i && $PREF{email_notifications_to_userbase_folder_owner} =~ /yes/i) { my $userdir = ''; if($PREF{userdir} && $PREF{email_notifications_to_userbase_folder_owner_even_when_he_is_the_uploader} =~ /yes/i) { $userdir = $PREF{userdir}; } elsif($PREF{admin_is_logged_in} && $finalpath_local =~ m!^/?$PREF{userdir_folder_name}/([^/]+)(/|$)!) { $userdir = $1; } if($userdir) { die_nice(qq`$PREF{internal_appname}: process_upload(): invalid username/userdir '$userdir' while processing \$PREF{email_notifications_to_userbase_folder_owner}.`) unless username_is_valid($userdir); ($PREF{userbase_folder_owner_email}) = enc_sql_select("SELECT email FROM `$PREF{user_table}` WHERE `username` = '$userdir';"); $PREF{userbase_folder_owner_email} = $userdir if ($PREF{userbase_folder_owner_email} !~ /.+\@.+\..+/ && $userdir =~ /.+\@.+\..+/); } } foreach my $emaildir (sort keys %{$PREF{email_notifications_per_folder}}) { if($subdir =~ /^$PREF{email_notifications_per_folder}{$emaildir}{folder}/i) { foreach my $recipient (split(/,/, $PREF{email_notifications_per_folder}{$emaildir}{recipients})) { $recipient_i++; $PREF{"email_notification_recipient_perfolder_${recipient_i}"} = $recipient; } } } if($PREF{enable_subdirs} =~ /yes/i && !$PREF{in_reprocessing_mode} && !$PREF{in_addfile_mode}) { if(($PREF{show_the_create_new_subdir_field_on_upload_form} =~ /yes/i && user_is_allowed_to('create_folders_during_upload')) || $PREF{automatic_new_subdir_name} =~ /\S/) { if($PREF{automatic_new_subdir_name} =~ /\S/) { $newsubdir = $PREF{automatic_new_subdir_name}; if($i == 1) # only do this the first time around; the PREF will be updated with the new value (when $i==1) so any subsequent passes have it. { interpolate_vars_from_URL_and_cookies('include_undefined', $newsubdir); interpolate_vars_from_env('include_undefined', $newsubdir); while($newsubdir =~ /(%FIELD\{(\w+)\})/g) { my ($to_replace, $shortname) = ($1, $2); my $formfield_key = get_formfield_key_from_shortname($shortname); my $replacement = exists $textboxes{$formfield_key} ? $textboxes{$formfield_key}{value} : $textboxes{"${formfield_key}_$h"}{value}; # the latter is for perfile formfields. $newsubdir =~ s/$to_replace/$replacement/; } $newsubdir =~ s/#C/$PREF{upload_counter_value}/g; interpolate_vars_from_prefs($newsubdir); interpolate_vars_from_date("etime=$PREF{uploaddata}{$serial}{end_time}", $newsubdir); $PREF{automatic_new_subdir_name} = $newsubdir; } } elsif($PREF{only_allow_one_new_subdir_per_upload} =~ /yes/i && $i > 1) { $newsubdir = $the_entered_new_subdir; # $query->param("newsubdir1"); } else { $newsubdir = $the_entered_new_subdir; # $query->param("newsubdir$h"); } if($newsubdir && $PREF{max_num_of_subdir_levels} =~ /^\d+$/ && $num_subdir_levels < $PREF{max_num_of_subdir_levels}) { my $make_parents = ''; if($PREF{allow_multiple_levels_in_new_subdirs} =~ /yes/i) { $newsubdir = enc_untaint($newsubdir,'keep_path'); $make_parents = 'make_parents'; } else { $newsubdir = enc_untaint($newsubdir); } unless($PREF{automatic_new_subdir_name} =~ /\S/) { # Because if using a textbox value, we must clean it up earlier (during # the textbox processing) to make sure the final subdir name is the same # as the name that gets stored in the DB. # clean_up_filename($newsubdir) if $PREF{clean_up_foldernames} =~ /yes/i; $newsubdir =~ s/^(.{1,$PREF{max_length_of_new_subdir_names}}).*/$1/; } $finalpath_url .= $newsubdir; $finalpath_real .= $newsubdir; $finalpath_local .= $newsubdir; # Make sure the new subdirectory doesn't already exist. # # Even for the special cases, we need to do this check when $i == 1. After that # (i.e. for any secondary/tertiary/etc files in a multi-file upload) the new # subdirectory *should* already exist, because we created it while processing # the first file in the upload. # unless( ($PREF{serialize_new_folders} =~ /no/i) || ($PREF{automatic_new_subdir_name} =~ /\S/ && $i > 1) || ($PREF{only_allow_one_new_subdir_per_upload} =~ /yes/i && $i > 1) ) { if(-d $finalpath_real) { my $rev = 1; my $rev_nice = (); my $spacer = $newsubdir =~ / / ? ' ' : '_'; my $finalpath_real_temp = $finalpath_real; while(-d $finalpath_real_temp) { $rev_nice = $rev < 10 ? "0$rev" : $rev; $finalpath_real_temp = $finalpath_real . $spacer . $rev_nice; $rev++; } $finalpath_url .= $spacer . $rev_nice; $finalpath_real .= $spacer . $rev_nice; $finalpath_local .= $spacer . $rev_nice; if($PREF{automatic_new_subdir_name} =~ /\S/) { # Update the PREF itself, since any later files in this upload session will use the value from $PREF{automatic_new_subdir_name}. # $newsubdir .= $spacer . $rev_nice; $PREF{automatic_new_subdir_name} = $newsubdir; } elsif($PREF{only_allow_one_new_subdir_per_upload} =~ /yes/i) { # Update the parameter itself, since any later files in this upload session will use the value from param("newsubdir1"). # $newsubdir .= $spacer . $rev_nice; $query->param(-name=>"newsubdir$h", -value=>$newsubdir); } } } create_dir_if_DNE($finalpath_real, $PREF{writable_dir_perms}, $make_parents); } } } my $file_ext = (); if($filename =~ /(.+)\.(.+)$/) { ($filename,$file_ext) = ($1,$2); $file_ext = '.' . $file_ext; } else { if($PREF{allow_files_without_extensions} !~ /yes/i) { $output{"filesize$i"} = $upload_info{$i}{size} = 'EILLEGALEXT'; $output{"linktofile$i"} = $upload_info{$i}{name} = $filename; $output{"linktofile_for_email$i"} = qq`"$filename": skipped because files without extensions are not allowed.`; $some_files_were_blocked = 1; next; } } $filename .= '.' . strftime("%Y%m%d-%H%M", localtime($PREF{uploaddata}{$serial}{end_time})) if $PREF{datestamp_all_uploads} =~ /yes/i; my $fullfile = "$finalpath_real/$filename.$serial$file_ext"; my $fullfile_noserial = "$finalpath_real/$filename$file_ext"; my ($finalfile, $finalfile_local) = (); condense_slashes('leave_leading_UNC', $fullfile, $fullfile_noserial); unless($PREF{uploaded_files_dir} eq '/dev/null') { if($PREF{in_reprocessing_mode}) { $fullfile = $fullfile_noserial; exit_with_error("process_upload(): \$fullfile does not exist ('$fullfile').") unless -e $fullfile; $output{"filesize$i"} = (stat($fullfile))[7]; } else { my $data_copy_required = 1; if($PREF{move_tmpfile_instead_of_copying_contents} =~ /yes/i) { if(my $tmpfilename = $query->tmpFileName( $query->param("$PREF{filefield_namebase}$h") )) { $tmpfilename = enc_untaint($tmpfilename, 'keep_path'); if($PREF{scan_uploads_for_viruses} =~ /yes/i) { my $cmd = $PREF{virus_scan_command}; $cmd =~ s!%%filename%%!$tmpfilename!g; my $scan_output = `$cmd`; if($scan_output !~ /Infected files: 0/gis || $scan_output =~ /FOUND/s) { enc_warn qq`File failed virus scan: command was: [[[ $cmd ]]] Output was: [[[ $scan_output ]]]`; unlink $tmpfilename; $output{"filesize$i"} = $upload_info{$i}{size} = 'EVIRUSSCAN'; $output{"linktofile$i"} = $upload_info{$i}{name} = $filename; $output{"linktofile_for_email$i"} = qq`"$filename": skipped because the file failed the virus scan.`; $some_files_were_blocked = 1; next; } } if(rename($tmpfilename, $fullfile)) { $data_copy_required = 0; } #printd "just tried: rename($tmpfilename, $fullfile); \$data_copy_required='$data_copy_required'; \$!='$!'\n"; if($data_copy_required) # rename() failed. { if(copy($tmpfilename, $fullfile)) { $data_copy_required = 0; } #printd "just tried: copy($tmpfilename, $fullfile); \$data_copy_required='$data_copy_required'; \$!='$!'\n"; } } } if($data_copy_required) { #printd "data copy is required.\n"; #my $upload_filehandle = $PREF{cgi_supports_upload_function} =~ /yes/i ? $query->upload("$PREF{filefield_namebase}$h") : $query->param("$PREF{filefield_namebase}$h"); my $upload_filehandle = $PREF{cgi_supports_upload_function} =~ /yes/i ? $uploadedfiles{$g}{handle} : $uploadedfiles{$g}{param}; open(UPLOADFILE,">$fullfile") or die "$0: couldn't create file $fullfile: $!\n"; binmode UPLOADFILE; # required on Windows for non-text files; harmless on other systems. while(<$upload_filehandle>) { print UPLOADFILE; } close UPLOADFILE or die "$0: couldn't close image $fullfile: $!\n"; } chmod $PREF{writable_file_perms}, $fullfile; $output{"filesize$i"} = (stat($fullfile))[7]; if($PREF{serialize_all_uploads} =~ /yes/i) { # if we're serializing all, then don't remove the serial number. $filename .= ".$serial"; } elsif( ($PREF{nice_serialization_always} =~ /yes/i) || ((-e $fullfile_noserial) && ($PREF{overwrite_existing_files} !~ /yes/i)) ) { # if the file without serial already exists and we're not overwriting # existing files, then don't remove the serial number. # # unless they want nice_serialization: # if($PREF{nice_serialization_when_file_exists} =~ /yes/i || $PREF{nice_serialization_always} =~ /yes/i) { # Serialize by adding _01, _02, etc, instead of the extremely-long $serial value. #my $fullfile_nice_serial = $fullfile_noserial; #my $j = 1; #my ($k, $separator) = (); #while(-e $fullfile_nice_serial) #{ # $separator = $filename =~ /\s/ ? ' ' : '_'; # $k = $j < 10 ? "0$j" : $j; # $fullfile_nice_serial = "$finalpath_real/$filename$separator$k$file_ext"; # $j++; #} # my ($fullfile_nice_serial, $separator, $k) = serialize_filename_if_file_exists($fullfile_noserial); rename($fullfile, $fullfile_nice_serial); $filename .= "$separator$k"; } } else { # else remove the serial number. # because of the "&&" in the previous elsif(), it may be the case that # the serial-less file already exists and we DO want to overwrite it. # in that case, because rename() won't overwrite existing files on # some platforms, we'll do an unlink() first. # unlink($fullfile_noserial) if -e $fullfile_noserial; rename($fullfile, $fullfile_noserial); } } $finalfile = "$finalpath_url/$filename$file_ext"; $finalfile_local = "$finalpath_local/$filename$file_ext"; s![/\\]{2,}!/!g for ($finalfile, $finalfile_local); $upload_info{$i}{name} = "$filename$file_ext"; $upload_info{$i}{rawname} = $rawname; $upload_info{$i}{extension} = $extension; $upload_info{$i}{realpath} = $finalpath_real; $upload_info{$i}{urlpath} = $finalpath_url; $upload_info{$i}{localpath} = $finalpath_local; $upload_info{$i}{size} = $output{"filesize$i"}; for($upload_info{$i}{realpath}, $upload_info{$i}{urlpath}, $upload_info{$i}{localpath}) { $_ .= '/' unless m!/$!; } unless($PREF{in_replace_mode}) { store_upload_info($i, $finalfile, $finalfile_local, $rawname, $extension, $output{"filesize$i"}, $serial, \%textboxes) if info_system_is_enabled() || $PREF{enable_custom_sql_commands} =~ /yes/i; } } $at_least_one_file_successfully_uploaded = 1; $output{"filesize$i"} = format_filesize_nicely($output{"filesize$i"}); $output{"linktofile$i"} = show_files_as_links_on_upload_complete_page() ? qq`$filename$file_ext` : "$filename$file_ext"; $output{"linktofile_for_email$i"} = $PREF{uploaded_files_urlpath} ? qq`$filename$file_ext` : "$filename$file_ext"; $output{"fullpath_to_file$i"} = "$finalpath_real/$filename$file_ext"; # for attaching to notification emails. } if($num_files_really_sent == 0) { # In case they're allowing uploads without files. store_upload_info('', '', '', '', '', 0, $serial, \%textboxes) if info_system_is_enabled() || $PREF{enable_custom_sql_commands} =~ /yes/i; } do_automatic_resizing(\%upload_info); unless($PREF{use_database_for_temp_data} =~ /yes/i || $PREF{use_single_log_backend} =~ /yes/i) { flock $logfh, 2; # lock the log seek $logfh, 0, 0; # seek to the beginning my $lastline = <$logfh>; chomp $lastline; printd( qq`060: logfile contents at end: $lastline` ); } unless($PREF{use_database_for_temp_data} =~ /yes/i) { close $logfh or die "$0: couldn't close $logfile after writing: $!\n"; chmod $PREF{writable_file_perms}, $logfile; #unlink $logfile if -e $logfile; # TODO: added 20100210; remove this if it causes client-side errors at the end of uploads (from progbar update requests that come in after the file's deleted) # Update 20110323: commenting-out this unlink() to see if it helps to work around sporadic Safari progbar issues. # Update again: re-enabling the unlink() to see if the new killkeepalive technique works. # 20111208: disabling this so our new upload monitor can view just-finished uploads; # instead we'll delete these as they age, via the delete-old-files mechanism: } if($rawpost) { close STDIN or enc_warn "$0: couldn't close STDIN (opened on file $rawpost): $!\n"; unlink $rawpost or die "$0: couldn't unlink $rawpost: $!\n"; } if($PREF{use_database_for_temp_data} =~ /yes/i && $PREF{purge_temp_data_immediately} =~ /yes/i) { sql_untaint($PREF{serial}); my $sth = $PREF{dbh}->prepare("DELETE FROM $PREF{table_name_for_temp_data} WHERE serial='$PREF{serial}';"); $sth->execute or die "$0: $DBI::errstr\n"; } unless($PREF{in_replace_mode}) { $textbox_values_for_qs = get_textbox_values_for_qs(\%textboxes); # if info_system_is_enabled(); } if( ( $PREF{email_notifications_to_webmaster} =~ /yes/i || $PREF{email_notifications_to_userEntered_addresses} =~ /yes/i || $PREF{email_notifications_to_userbase_loggedin_address} =~ /yes/i || $PREF{email_notifications_to_userbase_folder_owner} =~ /yes/i ) && !$PREF{in_replace_mode} && !$PREF{in_addfile_mode} ) { if($PREF{email_notifications_to_userbase_loggedin_address} =~ /yes/i) { if($PREF{admin_is_logged_in}) { $PREF{email_notification_recipient__userbase_loggedin_address_admin} = $PREF{logged_in_email}; } elsif($PREF{member_is_logged_in}) { $PREF{email_notification_recipient__userbase_loggedin_address_member} = $PREF{logged_in_email}; } } if($PREF{email_notifications_to_userbase_folder_owner} =~ /yes/i && $PREF{userbase_folder_owner_email}) { $PREF{email_notification_recipient__userbase_folder_owner} = $PREF{userbase_folder_owner_email}; } my %addresses_already_notified = (); foreach my $recipient_key (sort keys %PREF) { if($recipient_key =~ /^(webmaster_)?email_notification_recipient_/) { my $recipient = $PREF{$recipient_key}; next unless $recipient =~ /.+\@.+\..+/; next if $addresses_already_notified{$recipient}; my $shortdatetime_end = strftime("%a%b%d,%Y,%I:%M%p", localtime($PREF{uploaddata}{$serial}{end_time})); # note: "%P" causes crashes/hangs on some Windows servers; use "%p" instead. my ($ip,$host) = ($PREF{ip},$PREF{host}); my $uploadsize = format_filesize_nicely($ENV{CONTENT_LENGTH}); my $userdir_for_email = $PREF{userdir} || '(none)'; my $username_for_email = $PREF{logged_in_username} || '(none)'; my %attachments = (); my $template = 'user'; if($recipient_key =~ /^webmaster_email_notification_recipient_\d+$/) { $template = 'webmaster'; } elsif($recipient_key =~ /^email_notification_recipient_fromtextbox_(\w+)template_/ || $recipient_key =~ /^email_notification_recipient_fromperfiletextbox_(\w+)template_/) { $template = $1; } elsif($recipient_key =~ /^email_notification_recipient_fromtextbox_/ || $recipient_key =~ /^email_notification_recipient_fromperfiletextbox_/) { $template = 'userEntered_addresses'; } elsif($recipient_key =~ /^email_notification_recipient__userbase_loggedin_address_admin/) { $template = 'userbase_loggedin_address_admin'; } elsif($recipient_key =~ /^email_notification_recipient__userbase_loggedin_address_member/) { $template = 'userbase_loggedin_address_member'; } elsif($recipient_key =~ /^email_notification_recipient__userbase_folder_owner/) { $template = 'userbase_folder_owner'; } elsif($recipient_key =~ /^email_notification_recipient_perfolder_/) { $template = 'per_folder_notifications'; } my $attachfiles = $PREF{"upload_email_template_for_${template}___attachfiles"}; my $email_type = $PREF{"upload_email_template_for_${template}___type"}; my $from = $PREF{"upload_email_template_for_${template}___sender"}; my $email_subject = $PREF{"upload_email_template_for_${template}___subject"}; my $filelist_template = $PREF{"upload_email_template_for_${template}___filelist"}; my $formfields_template = $PREF{"upload_email_template_for_${template}___formfields"}; my $message = $PREF{"upload_email_template_for_${template}___body"}; if($from eq 'user_email_address') { $from = $PREF{first_user_entered_email_address}; exit_with_error qq`Error: you've set \$PREF{upload_email_template_for_${template}___sender} to 'user_email_address', but no user address is available (got '$from'); make sure you've added a form field for the user to enter his email address (in PREFs Section 07).` unless is_valid_email_address($from); } elsif($from =~ /value_from_formfield_(.+)/) { my $shortname = $1; $shortname =~ s![<>]!!g; # in case they include the literal brackets from the documentation. $from = $textboxes{_by_shortname}{$shortname}; exit_with_error qq`Error: you've set \$PREF{upload_email_template_for_${template}___sender} to 'value_from_formfield_$shortname', but no address from that formfield is available (got '$from'); make sure you've added a form field for the user to enter his email address (in PREFs Section 07).` unless is_valid_email_address($from); } next unless is_valid_email_address($from); # Get textbox values based on shortnames: my %textbox_values_for_email = (); foreach my $textbox (get_textbox_pref_keys('top', 'bottom')) { my $shortname = $PREF{"${textbox}_shortname"}; if(my $group = $PREF{"${textbox}_group"}) { $shortname = $PREF{"formfield_${group}_shortname"}; $textbox = "formfield_$shortname"; } $textbox_values_for_email{$shortname}{key} = $textbox; # for sorting. $textbox_values_for_email{$shortname}{value} = $textboxes{$textbox}{value}; $textbox_values_for_email{$shortname}{value} =~ s!\n!
!g if $email_type =~ /html/i; $textbox_values_for_email{$shortname}{label} = $PREF{$textbox}; $textbox_values_for_email{$shortname}{is_perfile} = 0; } foreach my $i (sort { $a <=> $b } keys %uploadedfiles) { foreach my $textbox (get_textbox_pref_keys('perfile')) { my $shortname = $PREF{"${textbox}_shortname"}; $textbox_values_for_email{"${shortname}_$i"}{key} = "${textbox}_$i"; # for sorting. $textbox_values_for_email{"${shortname}_$i"}{value} = $textboxes{"${textbox}_$i"}{value}; $textbox_values_for_email{"${shortname}_$i"}{value} =~ s!\n!
!g if $email_type =~ /html/i; $textbox_values_for_email{"${shortname}_$i"}{label} = $PREF{$textbox}; $textbox_values_for_email{"${shortname}_$i"}{is_perfile} = 1; } if($attachfiles =~ /yes/i) { if(-f $output{"fullpath_to_file$i"}) { $attachments{$i}{filename} = $output{"fullpath_to_file$i"}; $attachments{$i}{recommended_filename} = $output{"fullpath_to_file$i"}; $attachments{$i}{mimetype} = "application/octet-stream"; $attachments{$i}{'delete-after-sending'}= "no"; } else { die qq`$0: process_upload(): could not prepare attachment(s) for notification email, because the file '$output{"fullpath_to_file$i"}' does not exist.\n` if $PREF{email_failure_action} eq 'die_on_email_error'; } } } if($PREF{in_reprocessing_mode}) { if($PREF{upload_email_template_for_webmaster___subject__reprocessing} =~ /\S/ && $template eq 'webmaster') { $email_subject = $PREF{upload_email_template_for_webmaster___subject__reprocessing}; } elsif($PREF{upload_email_template_for_user___subject__reprocessing} =~ /\S/ && $template ne 'webmaster') { $email_subject = $PREF{upload_email_template_for_user___subject__reprocessing}; } } foreach my $templatable_item ($email_subject, $message) { interpolate_vars_from_URL_and_cookies('include_undefined', $templatable_item); my (@to_be_replaced, @replacement) = (); while($templatable_item =~ /(%{2,3}(.+?)%{2,3})/g) { my ($placeholder, $var_raw, $var) = ($1, $2, undef); next if $placeholder =~ /^%%%(.+)%%%$/; # skip any %%%if-foo%%%s, etc. if($var_raw =~ /^(.+?)--/) { $var = $1; } elsif($var_raw eq 'filelist') { next; } elsif($var_raw eq 'formfields') { next; } else { $var = $var_raw; } my $value = (); if($textbox_values_for_email{$var}) { $value = $textbox_values_for_email{$var}{value}; } elsif($var eq 'uploader_ipaddress') { $value = $ip; } elsif($var eq 'uploader_hostname') { $value = $host; } elsif($var eq 'totalsize_bytes') { $value = $ENV{CONTENT_LENGTH}; } elsif($var eq 'totalsize_nice') { $value = $uploadsize; } elsif($var eq 'userdir') { $value = $userdir_for_email; } elsif($var eq 'username') { $value = $username_for_email; } elsif($var eq 'startetime') { $value = $PREF{uploaddata}{$serial}{start_time} } elsif($var eq 'starttime_nice') { $value = $shortdatetime; } # $shortdatetime is an FC global. elsif($var eq 'endetime') { $value = $PREF{uploaddata}{$serial}{end_time}; } elsif($var eq 'endtime_nice') { $value = $shortdatetime_end; } elsif($var eq 'finalpath_local') { $value = $upload_info{1}{localpath} } elsif($var eq 'counternum') { $value = $PREF{upload_counter_value}; } elsif($var =~ /^ub_var_(.+)/) { $value = $PREF{$var}; } if($var_raw =~ /--date--(.+?)(--|$)/) { my $format = $1; $format =~ s/#/%/g; $value = strftime($format, localtime($value)); } if($var_raw =~ /--urlencode(--|$)/) { enc_urlencode($value); } if($var_raw =~ /--winslashes(--|$)/) { $value =~ s!/!\\!g; } push @to_be_replaced, $placeholder; push @replacement, $value; } my $k = 0; foreach my $string (@to_be_replaced) { $templatable_item =~ s/$string/$replacement[$k]/; $k++; } } my @files = (); my $showing_perfile_fields_in_filelist = 0; foreach my $i (sort { $a <=> $b } keys %uploadedfiles) { my $file = $filelist_template; my $href = get_download_link('emails', $upload_info{$i}{localpath}, $upload_info{$i}{name}); $href = $PREF{protoprefix} . $ENV{HTTP_HOST} . $href unless $href =~ m!^https?://!; my $nicesize = format_filesize_nicely($upload_info{$i}{size}); $file =~ s!%%filename%%!$upload_info{$i}{name}!g; $file =~ s!%%rawname%%!$upload_info{$i}{rawname}!g; $file =~ s!%%extension%%!$upload_info{$i}{extension}!g; $file =~ s!%%realpath%%!$upload_info{$i}{realpath}!g; $file =~ s!%%urlpath%%!$upload_info{$i}{urlpath}!g; $file =~ s!%%localpath%%!$upload_info{$i}{localpath}!g; $file =~ s!%%filesize%%!$nicesize!g; $file =~ s!%%linktofile%%!$href!g; $file =~ s!%%filenum%%!$i!g; $file =~ s!%%filecount%%!$numitems!g; my ($width,$height,$xres,$yres,$width_inches,$height_inches) = is_image($upload_info{$i}{name}) ? get_image_dims($upload_info{$i}{realpath} . $upload_info{$i}{name}) : (); $file =~ s!%%imagedims%%!$width && $height ? qq`${width}x${height}` : $TEXT{unknown}!eg; $file =~ s!%%imageres%%!$xres && $yres ? qq`${xres}x${yres}` : $TEXT{unknown}!eg; $file =~ s!%%imagedims_inches%%!$width_inches && $height_inches ? qq`${width_inches}x${height_inches}` : $TEXT{unknown}!eg; $file =~ s!%%%if-isimage%%%(.+?)%%%end-isimage%%%!is_image($upload_info{$i}{name}) ? $1 : ''!egs; # If their filelist_template contains a %%%template:perfile_formfields%%%, # then automatically show the perfile formfields and values within the # filelist portion of the email, rather than with the other (non-perfile) # formfields. # if(my ($pfff_template_placeholder,$pfff_template) = ($file =~ m!(%%%template:perfile_formfields%%%(.*)%%%end-template:perfile_formfields%%%)!gs)) { $showing_perfile_fields_in_filelist = 1; my $perfile_formfields = ''; foreach my $textbox (get_textbox_pref_keys('perfile')) { my $shortname = $PREF{"${textbox}_shortname"}; my $template_instance = $pfff_template; $template_instance =~ s!%%fieldname%%!$shortname!g; $template_instance =~ s!%%fieldvalue%%!$textbox_values_for_email{"${shortname}_$i"}{value}!g; $perfile_formfields .= $template_instance; } $file =~ s!$pfff_template_placeholder!$perfile_formfields!gs; } # Interpolate any individually-named perfile formfields. # $file =~ s!%%(\w+)%%!$textbox_values_for_email{"${1}_$i"}{value} if $textbox_values_for_email{"${1}_$i"}!eg; push @files, $file; } my $files = join '', @files; $message =~ s/%%filelist%%/$files/; my $formfields = ''; foreach my $shortname (sort { $textbox_values_for_email{$a}{key} cmp $textbox_values_for_email{$b}{key} } keys %textbox_values_for_email) { next if $textbox_values_for_email{$shortname}{is_perfile} && $showing_perfile_fields_in_filelist && $PREF{show_perfile_fields_twice_in_notification_emails} !~ /yes/i; my $ff_template = $formfields_template; $ff_template =~ s!%%name%%!$shortname!g; $ff_template =~ s!%%label%%!$textbox_values_for_email{$shortname}{label}!g; $ff_template =~ s!%%value%%!$textbox_values_for_email{$shortname}{value}!g; $formfields .= $ff_template; } $message =~ s!%%%if-formfields%%%(.+?)%%%end-formfields%%%!$formfields ? $1 : ''!egs; $message =~ s!%%formfields%%!$formfields!g; my $serial_is_userdir_info = ''; if($PREF{serial_is_userdir} =~ /yes/i && !$PREF{admin_is_logged_in}) { if($email_type =~ /html/i) { $serial_is_userdir_info .= qq`



To access or reuse this uploads folder, go to:

$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_filelist_qsready}action=listfiles&userdir=$PREF{userdir}

\n`; $serial_is_userdir_info .= qq`



To make a completely new uploads folder, just use the front page:

$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_uploader}

\n`; } else { $serial_is_userdir_info .= "\n\n" . '=' x 70 . qq`\nTo access or reuse this uploads folder, go to:\n\n$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_filelist_qsready}action=listfiles&userdir=$PREF{userdir}\n` . '=' x 70 . "\n"; $serial_is_userdir_info .= "\n\n" . '=' x 70 . qq`\nTo make a completely new uploads folder, just use the front page:\n\n$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_uploader}\n` . '=' x 70 . "\n"; } } $message =~ s/%%serial_is_userdir_info%%/$serial_is_userdir_info/; send_email($recipient, $from, $email_subject, $message, $email_type, $PREF{email_failure_action}, \%attachments); $addresses_already_notified{$recipient} = 1; } } } foreach my $cookie (keys %cookies_to_set) { set_cookie($cookie, $cookies_to_set{$cookie}, '+12M'); } my @ftp_errors = (); if($PREF{ftp_files_to_another_server_after_upload} =~ /yes/i) { my @files = (); foreach my $i (sort { $a <=> $b } keys %upload_info) { push @files, $upload_info{$i}{localpath} . $upload_info{$i}{name}; } @ftp_errors = ftp_files_to_another_server(@files); unshift(@ftp_errors, qq`

There were errors during the post-upload FTP process:

\n`) if @ftp_errors; } if($PREF{after_upload_redirect_to}) { $PREF{user_supplied_afterupload_redirect} = $PREF{after_upload_redirect_to}; } else { $PREF{after_upload_redirect_to} = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_uploadcomplete_qsready}action=uploadcomplete&serial=$serial"; } interpolate_vars_from_URL_and_cookies('include_undefined', $PREF{after_upload_redirect_to}); interpolate_vars_from_formfields(\%textboxes, $PREF{after_upload_redirect_to}); $PREF{after_upload_redirect_to} =~ s!%%foldername%%!$upload_info{1}{localpath}!g; $PREF{after_upload_redirect_to} =~ s!%%counternum%%!$PREF{upload_counter_value}!g; $PREF{after_upload_redirect_to} =~ s/%PREF{(\w+)}/$PREF{$1}/g; if($PREF{pass_default_data_on_redirect} =~ /yes/i) { my $elapsed_secs = $PREF{uploaddata}{$serial}{end_time} - $PREF{uploaddata}{$serial}{start_time}; my ($question_mark, $ampersand) = (); if($PREF{after_upload_redirect_to} =~ /\?/) { # if there's already a question-mark on the URL, we may need an ampersand. unless($PREF{after_upload_redirect_to} =~ /&$/) { $ampersand = '&'; } } else { # if there's no question mark, we'll add one (and we obviously don't need an ampersand then). $question_mark = '?'; } $PREF{after_upload_redirect_to} .= $question_mark . $ampersand . "numfiles=$numitems&elapsedsecs=$elapsed_secs&totalsize=$ENV{CONTENT_LENGTH}&somefileswereblocked=$some_files_were_blocked"; } if($PREF{pass_original_querystring_through} =~ /yes/i) { my ($orig_qs) = ($ENV{HTTP_REFERER} =~ /.+?\?(.+)/); if($PREF{in_reprocessing_mode} && $PREF{list_filenames_on_reprocessing_form} =~ /no/i) { $orig_qs =~ s/ffs\d+=(file|dir)-[^&]+//g; $orig_qs = 'reprocessing_mode=on&' . $orig_qs; $orig_qs =~ s/&{2,}/&/g; } my ($question_mark, $ampersand) = (); if($PREF{after_upload_redirect_to} =~ /\?/) { # if there's already a question-mark on the URL, we may need an ampersand. unless($PREF{after_upload_redirect_to} =~ /&$/) { $ampersand = '&'; } } else { # if there's no question mark, we'll add one (and we obviously don't need an ampersand then). $question_mark = '?'; } $PREF{after_upload_redirect_to} .= $question_mark . $ampersand . $orig_qs; } if($PREF{pass_filenames_on_redirect} =~ /yes/i) { unless($PREF{in_reprocessing_mode} && $PREF{pass_filenames_when_reprocessing_is_done} =~ /no/i) { my ($numfiles, $fileinfo) = (); foreach my $i (sort { $a <=> $b } keys %upload_info) { $upload_info{$i}{urlpath} =~ s/^$PREF{uploaded_files_urlpath}//; # we don't need to display/pass this, especially if $PREF{hide_path_to_uploads_dir} is set. after this s/// it'll just contain the upload subdir if any. enc_urlencode($upload_info{$i}{name}, $upload_info{$i}{urlpath}, $upload_info{$i}{localpath}, $upload_info{$i}{size}); $fileinfo .= 'f' . $i . 'name=' . $upload_info{$i}{name} . '&'; $fileinfo .= 'f' . $i . 'urlpath=' . $upload_info{$i}{urlpath} . '&'; #$fileinfo .= 'f' . $i . 'localpath=' . $upload_info{$i}{localpath} . '&'; $fileinfo .= 'f' . $i . 'size=' . $upload_info{$i}{size} . '&'; } my ($question_mark, $ampersand) = (); if($PREF{after_upload_redirect_to} =~ /\?/) { # if there's already a question-mark on the URL, we may need an ampersand. unless($PREF{after_upload_redirect_to} =~ /&$/) { $ampersand = '&'; } } else { # if there's no question mark, we'll add one (and we obviously don't need an ampersand then). $question_mark = '?'; } $PREF{after_upload_redirect_to} .= $question_mark . $ampersand . $fileinfo; } } if($PREF{pass_formfield_values_on_redirect} =~ /yes/i) { my ($question_mark, $ampersand) = (); if($PREF{after_upload_redirect_to} =~ /\?/) { # if there's already a question-mark on the URL, we may need an ampersand. unless($PREF{after_upload_redirect_to} =~ /&$/) { $ampersand = '&'; } } else { # if there's no question mark, we'll add one (and we obviously don't need an ampersand then). $question_mark = '?'; } $PREF{after_upload_redirect_to} .= $question_mark . $ampersand . $textbox_values_for_qs; } if($PREF{output_started}) { if($PREF{use_iframe_for_upload} =~ /yes/i) { $PREF{internal_qs_for_uploadcomplete_page} = ($PREF{after_upload_redirect_to} =~ /\?(.+)/)[0]; show_uploadcomplete_page(@ftp_errors); } else { print qq`\n
`; print qq`\n

Output has already started, so we can't redirect (perhaps debug is enabled; you can disable it in PREFs Section 01).

\n\n`; print qq`\n

Here's where we would have gone:

\n\n`; print qq`\n

$PREF{after_upload_redirect_to}

\n\n`; print @ftp_errors if @ftp_errors; print qq`\n
\n`; } } else { if(@ftp_errors) { $PREF{internal_qs_for_uploadcomplete_page} = ($PREF{after_upload_redirect_to} =~ /\?(.+)/)[0]; show_uploadcomplete_page(@ftp_errors); } else { if($PREF{user_supplied_afterupload_redirect} && $PREF{use_iframe_for_upload} =~ /yes/i) { print_http_headers(); print qq`\n`; } else { enc_redirect($PREF{after_upload_redirect_to}); } } } } sub show_uploadcomplete_page { my @extra_messages = @_; $PREF{on_page} = 'uploadcomplete'; $PREF{userdir} = $PREF{enable_userdirs} = '' if $PREF{enforce_userdir_restrictions_on_upload_page} =~ /no/i; $qs = $PREF{internal_qs_for_uploadcomplete_page} || $qs; my $template = $PREF{in_reprocessing_mode} ? $PREF{upload_complete_page_template___reprocessing} : $PREF{upload_complete_page_template}; my $filelist_template = ''; my ($numitems) = ($qs =~ /(?:^|&)numfiles=(\d+)(?:&|$)/); my ($contentlength) = ($qs =~ /(?:^|&)totalsize=(\d+)(?:&|$)/); my $total_upload_size = format_filesize_nicely($contentlength); $template =~ s!%%total_file_count%%!$numitems!g; $template =~ s!%%total_upload_size%%!$total_upload_size!g; my ($elapsed_secs) = ($qs =~ /(?:^|&)elapsedsecs=(\d+)(?:&|$)/); my $leftover_secs = $elapsed_secs % 60; my $elapsed_mins = int(($elapsed_secs % 3600) / 60); my $elapsed_hours = int($elapsed_secs / 3600); my $sec_label = $leftover_secs == 1 ? $TEXT{second} : $TEXT{seconds}; my $min_label = $elapsed_mins == 1 ? $TEXT{minute} : $TEXT{minutes}; my $hour_label = $elapsed_hours == 1 ? $TEXT{hour} : $TEXT{hours}; $elapsed_secs = 1 if $elapsed_secs < 1; # make sure we're not dividing by zero or using a negative time. my $average_speed = format_filesize_nicely($contentlength / $elapsed_secs) . '/s'; my $elapsed_time = ($elapsed_hours ? "${elapsed_hours} $hour_label " : '') . ($elapsed_mins ? "${elapsed_mins} $min_label " : '') . qq`${leftover_secs} $sec_label `; $template =~ s!%%elapsed_time%%!$elapsed_time!g; $template =~ s!%%average_speed%%!$average_speed!g; my $formfields = ''; while($qs =~ /(?:^|&)ff(\d+)v=([^&]+)/g) { my ($fieldnum, $fieldvalue) = ($1,$2,$3); enc_urldecode($fieldvalue); $fieldvalue =~ s!\n!
!g; my $fieldname = $PREF{"formfield_${fieldnum}"} ? $PREF{"formfield_${fieldnum}"} : $PREF{"formfield_${fieldnum}_shortname"}; my $colon = $fieldname =~ /:\s*$/ ? '' : ':'; $formfields .= qq`
$fieldname$colon $fieldvalue
\n` if $fieldname; } $template =~ s!%%%if-formfields%%%(.+?)%%%end-formfields%%%!$formfields ? $1 : ''!egs; $template =~ s!%%formfields%%!$formfields!g; # This is mainly for $PREF{in_reprocessing_mode}: # my $subdir_from_url = (); if($qs =~ /(?:^|&)path=(.+?)(?:&|$)/) { $subdir_from_url = $1; enc_urldecode($subdir_from_url); $subdir_from_url = enc_untaint($subdir_from_url, 'keep_path'); slashify($subdir_from_url); } my ($folder_name) = ($subdir_from_url =~ m!([^/]+)/*$!); $folder_name = '/' unless $folder_name; $template =~ s!%%folder_name%%!$folder_name!g; for(my $i=1; $i<=$numitems; $i++) { my $filelist_template_i = $PREF{in_reprocessing_mode} ? $PREF{upload_complete_filelist_template___reprocessing} : $PREF{upload_complete_filelist_template}; my ($name) = ($qs =~ /(?:^|&)f${i}name=(.*?)(?:&|$)/); #my ($realpath) = ($qs =~ /(?:^|&)f${i}realpath=(.*?)(?:&|$)/); #my ($localpath)= ($qs =~ /(?:^|&)f${i}localpath=(.*?)(?:&|$)/); my ($localpath) = ($qs =~ /(?:^|&)f${i}urlpath=(.*?)(?:&|$)/); my ($size) = ($qs =~ /(?:^|&)f${i}size=(.*?)(?:&|$)/); enc_urldecode($name,$localpath,$size); my $urlpath = $PREF{uploaded_files_urlpath} . $localpath; my $link = get_download_link('uploadcomplete_page', $localpath, $name); my $link_with_fqdn = "$PREF{protoprefix}$ENV{HTTP_HOST}$link"; my $perfile_formfields = ''; while($qs =~ /(?:^|&)ff(\d+)_(\d+)v=([^&]+)/g) { my ($fieldnum, $filenum, $fieldvalue) = ($1,$2,$3); enc_urldecode($fieldvalue); if($filenum == $i) { my $fieldname = $PREF{"formfield_${fieldnum}"} ? $PREF{"formfield_${fieldnum}"} : $PREF{"formfield_${fieldnum}_shortname"}; my $colon = $fieldname =~ /:\s*$/ ? '' : ':'; $perfile_formfields .= qq`
$fieldname$colon $fieldvalue
\n` if $fieldname; } } $filelist_template_i =~ s!%%%if-perfile_formfields%%%(.+?)%%%end-perfile_formfields%%%!$perfile_formfields ? $1 : ''!egs; $filelist_template_i =~ s!%%perfile_formfields%%!$perfile_formfields!g; my ($illegal, $replaceerror, $viruserror, $success) = (0,0,0,0); if($size eq 'EILLEGALEXT') { ($illegal, $replaceerror, $viruserror, $success) = (1,0,0,0); } elsif($size eq 'ENOREPLACE') { ($illegal, $replaceerror, $viruserror, $success) = (0,1,0,0); } elsif($size eq 'EVIRUSSCAN') { ($illegal, $replaceerror, $viruserror, $success) = (0,0,1,0); } else { ($illegal, $replaceerror, $viruserror, $success) = (0,0,0,1); } $filelist_template_i =~ s!%%filenum%%!$i!g; $filelist_template_i =~ s!%%total_file_count%%!$numitems!g; $filelist_template_i =~ s!%%filename%%!$name!g; $filelist_template_i =~ s!%%filesize%%!format_filesize_nicely($size)!eg; $filelist_template_i =~ s!%%link%%!$link!g; $filelist_template_i =~ s!%%link_with_fqdn%%!$link_with_fqdn!g; $filelist_template_i =~ s!%%%if-isimage%%%(.+?)%%%end-isimage%%%!is_image($name) ? $1 : ''!egs; $filelist_template_i =~ s!%%%if-illegal%%%(.+?)%%%end-illegal%%%!$illegal ? $1 : ''!egs; $filelist_template_i =~ s!%%%if-replaceerror%%%(.+?)%%%end-replaceerror%%%!$replaceerror ? $1 : ''!egs; $filelist_template_i =~ s!%%%if-viruserror%%%(.+?)%%%end-viruserror%%%!$viruserror ? $1 : ''!egs; $filelist_template_i =~ s!%%%if-success%%%(.+?)%%%end-success%%%!$success ? $1 : ''!egs; $filelist_template_i =~ s!%%%if-showlinks%%%(.+?)%%%end-showlinks%%%!show_files_as_links_on_upload_complete_page() ? $1 : ''!egs; $filelist_template_i =~ s!%%%if-showbbcodeetc%%%(.+?)%%%end-showbbcodeetc%%%!lc($PREF{show_bbcode_html_etc_on_uploadcomplete_page}) eq 'yes' ? $1 : ''!egs; $filelist_template_i =~ s!%%%ifelse-showlinks%%%(.*?)%%%else%%%(.*?)%%%endelse-showlinks%%%!show_files_as_links_on_upload_complete_page() ? $1 : $2!egs; $filelist_template .= $filelist_template_i; } $template =~ s!%%filelist%%!$filelist_template!g; if(@extra_messages) { $template .= qq`
\n`; $template .= join "\n

", @extra_messages; $template .= qq`
\n`; } if($PREF{serial_is_userdir} =~ /yes/i && !$PREF{admin_is_logged_in}) { $template .= qq`

Note: if you want to reuse this uploads folder, please bookmark & use this link.

\n`; $template .= qq`

Or, to make a completely new uploads folder, just use the front page.

\n`; } if(my ($oldserial) = ($qs =~ /(?:^|&)serial=(\w+)(?:&|$)/)) { my $tempfile = "$PREF{datadir}/${oldserial}.fctemp.log"; # 20111208: disabling this so our new upload monitor can view just-finished uploads; # instead we'll delete these as they age, via the delete-old-files mechanism: #unlink($tempfile) if -e $tempfile; } start_html_output($PREF{subtitle___uploadcomplete}); print $template; finish_html_output('home', 'uploader', 'list', 'getscript'); } #sub user_has_write_access_to_path($) #{ # my $path_local = shift; # slashify($path_local); # # my @writable_dirs = get_all_writable_directories(); # foreach my $dir (@writable_dirs) # { # slashify($dir); # return 1 if $path_local eq $dir; # } # # return 0; #} sub filename_is_illegal($) { my $filename = shift; my ($this_files_extension) = ($filename =~ /.*(\..+)$/); my $illegal = 0; if($PREF{only_allow_these_file_extensions} =~ /(.+)/) { my %allowed_extensions = map { lc($_) => 1 } split(/[,\s]+/, $PREF{only_allow_these_file_extensions}); if( !$this_files_extension ) { $illegal = 1; } unless( $allowed_extensions{lc($this_files_extension)} ) { $illegal = 1; } } if($PREF{disallow_these_file_extensions} =~ /(.+)/) { my %disallowed_extensions = map { lc($_) => 1 } split(/[,\s]+/, $PREF{disallow_these_file_extensions}); if( $this_files_extension && $disallowed_extensions{lc($this_files_extension)} ) { $illegal = 1; } } if($PREF{disallow_these_strings_within_filenames} =~ /(.+)/) { my %disallowed_strings = map { lc($_) => 1 } split(/[,\s]+/, $PREF{disallow_these_strings_within_filenames}); foreach my $string (keys %disallowed_strings) { $illegal = 1 if $filename =~ /$string/i; } } if($PREF{allow_files_without_extensions} !~ /yes/i) { $illegal = 1 unless $this_files_extension; } $illegal = 1 unless $filename =~ /[0-9a-zA-Z]/; return $illegal; } sub generate_serial_number { $PREF{serial} = $$; $PREF{serial} .= $ENV{REMOTE_PORT}; my $offsettime = offsettime(); $offsettime =~ s/.*(\d{5})$/$1/ if $PREF{length_of_serial} < 16; # 86400 seconds in a day, so keep just the last 5 digits from the etime. $PREF{serial} .= $offsettime; my ($first_octet, $second_octet, $third_octet, $fourth_octet) = ($PREF{ip} =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)$/); $PREF{serial} .= $fourth_octet . $third_octet . $second_octet . $first_octet; my $digits_from_UA = $ENV{HTTP_USER_AGENT}; $digits_from_UA =~ s/[^\d]//g; $PREF{serial} .= $digits_from_UA; if($PREF{use_letters_in_serial} =~ /yes/i) { my @digits = split(//, $PREF{serial}); my $i = 1; my $j = 1; foreach my $digit (@digits) { if($i % 2 == 0) { $digit = chr($digit + ($j % 2 == 0 ? 65 : 97)); $j++; } $i++; } $PREF{serial} = join '', @digits; } $PREF{serial} =~ s/^(.{$PREF{length_of_serial}}).*/lc($1)/e; if($PREF{use_hash_for_serial} =~ /yes/i) { $PREF{serial} = md5_hex($PREF{serial}); my $cutlength = $PREF{cut_serial_to_this_length_even_after_hashing}; if($cutlength =~ /^\d+$/ && $cutlength > 0) { $PREF{serial} =~ s/^(.{$cutlength}).*/lc($1)/e; } } return $PREF{serial}; } sub load_prefs() { $PREF{internal_appname_nice} = 'FileChucker'; $PREF{internal_appname} = 'filechucker'; $PREF{internal_filename} = 'filechucker'; $PREF{twochar_app_id} = 'fc'; do_preinit(); # Note: some things won't work while the debuglog is enabled (the progress bar being one). # #open($debuglog, ">>fcdebug.txt") or die_nice("couldn't open debuglog: $!\n"); flock $debuglog, 2; print $debuglog "\n\n==========\n$$ starting:"; $PREF{force_debug} = 'yes'; verify_server_environment(); fix_server_environment(); # Pre-PREF init stuff: default PREF values, etc. # populate_month_conversion_hashes(); $PREF{num_hook_calls} = 0; $PREF{extra_footer_links} = []; $PREF{extra_administration_links} = []; $PREF{try_to_use_imagemagick_for_dimensions} = 'yes'; $PREF{try_to_use_gd_for_dimensions} = 'yes'; $PREF{folder_name_for_working_with_zip_files} = 'fcziptmp'; $PREF{app_output_template___skip_init_var_expansion} = 'yes'; # so we can use %PREF{title} in the template and replace it with $PREF{"title_$PREF{on_page}"}, doing the %PREF{title} interpolation within the output sub rather than within load_prefs(). set_default_prefs_for_all_apps(); check_for_enc_visitor_id(); load_external_prefs(); load_other_prefs_files(); settle_docroot_datadir_cgimodule_etc(); cache_and_read_any_nonpost_env_vars(); # Webconfig must happen after datadir is settled, since it needs to read/write there: # load_webconfig_prefs(); figure_out_where_here_is(); determine_cookie_domain(); # Do this almost right after all user-defined prefs are loaded; # but not before DOCROOT and datadir are settled, since those # are likely to be used within other user-defined prefs. # expand_custom_vars_in_prefs(); # Load UserBase prefs after settling DOCROOT in case we used # %PREF{DOCROOT} in the path to the UserBase prefs file. # load_userbase_prefs(); # Now all prefs are loaded. # do_postpref_processing(); ($PREF{ip}, $PREF{host}) = get_ip_and_host(); do_blacklisting_and_whitelisting(); # Verify servable datadirs and similar directories here. # $PREF{name_of_subfolder_for_thumbnails_etc} = 'encmisc' unless $PREF{name_of_subfolder_for_thumbnails_etc}; die_nice("Error: invalid setting for \$PREF{name_of_subfolder_for_thumbnails_etc} ('$PREF{name_of_subfolder_for_thumbnails_etc}'). It's recommended to leave this set to its default value.") unless $PREF{name_of_subfolder_for_thumbnails_etc} =~ m!\w! && $PREF{name_of_subfolder_for_thumbnails_etc} =~ m!^[\w\.]+$!; if(database_required()) { get_db_connection(); create_db_tables_if_DNE(); $PREF{database_is_in_use} = 1; } $PREF{REQ_URI_SANS_QS} = ($ENV{REQUEST_URI} =~ /^([^\?]+)/)[0]; $PREF{we_are_virtual} = $PREF{REQ_URI_SANS_QS} ne $ENV{SCRIPT_NAME}; $PREF{protoprefix} = $PREF{protoprefix} ? $PREF{protoprefix} : ($ENV{SERVER_PORT} =~ /443/ || $ENV{HTTPS} =~ /on/i) ? 'https://' : 'http://'; if($PREF{add_www_to_hostname} =~ /yes/i && $ENV{HTTP_HOST} !~ /^www\./i) { my $go = "$PREF{protoprefix}www.$ENV{HTTP_HOST}$PREF{REQ_URI_SANS_QS}" . ($qs ? "?$qs" : ''); enc_redirect($go); } elsif($PREF{add_www_to_hostname} !~ /yes/i && $PREF{remove_www_from_hostname} =~ /yes/i && $ENV{HTTP_HOST} =~ /^www\.(.+)/i) { my $host = $1; my $go = "$PREF{protoprefix}$host$PREF{REQ_URI_SANS_QS}" . ($qs ? "?$qs" : ''); enc_redirect($go); } if($PREF{force_https} =~ /yes/i && $ENV{HTTPS} !~ /^(on|yes|enabled|true|1)$/i) { my $go = "https://$ENV{HTTP_HOST}$PREF{REQ_URI_SANS_QS}" . ($qs ? "?$qs" : ''); enc_redirect($go); } if($PREF{prevent_direct_cgi_access} =~ /yes/i && !$PREF{we_are_virtual} && $ENV{REQUEST_METHOD} !~ /post/i && !$qs) { print_http_headers(); print $PREF{direct_cgi_access_error}; exit; } $PREF{time_offset} = $PREF{time_offset} * 3600 if $PREF{time_offset} =~ /^-?\d+$/; # Set globals. TODO: some of these need to be re-scoped. # $starttime = offsettime(); $total_upload_size = (); %temp = (); $num_files_in_progress_or_done = 0; $total_file_count = $qs =~ /(?:^|&)items=(\d+)(?:&|$)/ ? $1 : $PREF{using_custom_file_elements} =~ /yes/i ? $PREF{num_custom_file_elements} : $PREF{num_default_file_elements} =~ /^\d+$/ ? $PREF{num_default_file_elements} : 1; $shortdatetime = strftime("%a%b%d,%Y,%I:%M%p", localtime(offsettime())); # note: "%P" causes crashes/hangs on some Windows servers; use "%p" instead. $shortdatetime_forfilename = strftime("%a%b%d,%Y,%Hh%Mm%Ss%p", localtime(offsettime())); # note: "%P" causes crashes/hangs on some Windows servers; use "%p" instead. $datestring8 = strftime("%Y%m%d", localtime(offsettime())); $PREF{mkdir_action_name} = 'makefolder' unless exists $PREF{mkdir_action_name}; # necessary because some servers are configured to throw a 403 Forbidden error for any URL containing the string "mkdir". $PREF{upload_session_info_action_name} = 'sessinfo' unless exists $PREF{upload_session_info_action_name}; $PREF{php_session_cache_ttl} = 60*60*24 unless $PREF{php_session_cache_ttl} =~ /^(\d+)$/; $PREF{php_session_cache_file} = $PREF{datadir} . '/phpcache.txt' unless exists $PREF{php_session_cache_file}; $PREF{php_session_cookie_name} = 'PHPSESSID' unless exists $PREF{php_session_cookie_name}; $PREF{currency_symbol} = '$' unless exists $PREF{currency_symbol}; $PREF{in_reprocessing_mode} = 1 if ($PREF{enable_reprocessing_mode} =~ /yes/i && ($qs =~ /(?:^|&)ffs\d+=file-(.+?)(?:&|$)/ || $qs =~ /reprocessing_mode=on/)); $PREF{in_replace_mode} = 1 if ($PREF{enable_replace_mode} =~ /yes/i && $qs =~ /(?:^|&)rfn\d+=file-(.+?)(?:&|$)/); $PREF{in_addfile_mode} = 1 if ($PREF{enable_addfile_mode} =~ /yes/i && $qs =~ /(?:^|&)addfilemode=on(?:&|$)/); $PREF{enable_subdirs} = 'yes' if $PREF{enable_userdirs} =~ /yes/i; $PREF{multiple_files_in_one_element} = qq`multiple="multiple"` unless $ENV{HTTP_USER_AGENT} =~ /opera/i && $PREF{disable_multiple_files_in_one_element_for_opera} =~ /yes/i; # Do any PREFs initialization that doesn't depend on things like userdir, database connection, etc. # if( ($PREF{upload_email_template_for_webmaster___subject} || $PREF{upload_email_template_for_webmaster___filelist}) && !$PREF{upload_email_template_for_webmaster___body}) { exit_with_error(qq`Error: you must set \$PREF{upload_email_template_for_webmaster___body} before you can use \$PREF{upload_email_template_for_webmaster___subject} or \$PREF{upload_email_template_for_webmaster___filelist}.`); } if( ($PREF{upload_email_template_for_user___subject} || $PREF{upload_email_template_for_user___filelist}) && !$PREF{upload_email_template_for_user___body}) { exit_with_error(qq`Error: you must set \$PREF{upload_email_template_for_user___body} before you can use \$PREF{upload_email_template_for_user___subject} or \$PREF{upload_email_template_for_user___filelist}.`); } if($PREF{serial_is_userdir} =~ /yes/i) { $PREF{enable_userdirs} = 'yes'; $PREF{enable_userdir_from_login_system} = 'no'; $PREF{keep_userdir_on_url} = 'yes'; $PREF{enable_userdir_on_url} = 'yes'; $PREF{auto_create_userdirs} = 'yes'; $PREF{enable_subdirs} = 'yes'; unless($PREF{override_default_serial_is_userdir_settings} =~ /yes/i) { $PREF{error_if_userdir_not_supplied} = 'yes'; $PREF{groups_allowed_to_upload} = 'public'; $PREF{groups_allowed_to_view_download_page} = 'public'; $PREF{groups_allowed_to_download} = 'public'; $PREF{groups_allowed_to_delete_items} = 'public'; $PREF{hide_path_to_uploads_dir} = 'yes'; $PREF{display_dropdown_box_for_subdir_selection} = 'no'; $PREF{navigate_users_into_userdirs_automatically} = 'yes'; $PREF{hide_links_to_topmost_level_from_userdir_users} = 'yes'; $PREF{show_the_create_new_subdir_field_on_upload_form} = 'no'; } } #if($PREF{after_upload_redirect_to}) #{ # $PREF{use_iframe_for_upload} = 'no'; #} $PREF{use_iframe_for_upload} = 'no' if get_qs_var('iframe') eq 'no'; if($PREF{use_iframe_for_upload} !~ /yes/i) { $PREF{show_upload_status_in_popup_window} = 'yes' if $ENV{HTTP_USER_AGENT} =~ /safari/i; } create_formfield_prefs_for_custom_form_fields(); foreach my $prefname (sort keys %PREF) { if($prefname =~ /^formfield_(\d+)$/) { # # Perform various init-type stuff for the formfields. # my $num = $1; my $prefshortname = "formfield_${num}_shortname"; if(!$PREF{$prefshortname}) { # Create _shortnames for any formfields that don't have them. # my $shortname = $PREF{$prefname}; $shortname =~ s/[^\w]/_/g; defooify('_',$shortname); $PREF{$prefshortname} = $shortname; } die_nice qq`Error: invalid shortname '$PREF{$prefshortname}' for your '$prefshortname' setting.  The shortname must contain only letters, numbers, and underscores.` unless $PREF{$prefshortname} =~ /^\w+$/; $PREF{"formfield_${num}_position"} = 'top' if !$PREF{"formfield_${num}_position"}; if($PREF{store_upload_info_in_database} =~ /yes/i && $PREF{automatically_store_my_formfields_in_db} =~ /yes/i) { add_formfield_column_to_upload_log_table($prefshortname); } } elsif($prefname =~ /^formfield_(.+)_is_group_master$/) { my $group_identifier = $1; my $prefshortname = "formfield_${group_identifier}_shortname"; if($PREF{store_upload_info_in_database} =~ /yes/i && $PREF{automatically_store_my_formfields_in_db} =~ /yes/i) { add_formfield_column_to_upload_log_table($prefshortname); } } elsif($PREF{enable_perfile_passwords} =~ /yes/i && $prefname =~ /^formfield_(\d+)_password$/) { # Automatically enable $PREF{download_links_go_through_FileChucker} if perfile passwords are enabled: my $num = $1; if($PREF{$prefname} =~ /yes/i) { $PREF{perfile_pw_firsthalf_enabled} = 1 if $PREF{"formfield_${num}_shortname"} eq 'filepw'; $PREF{perfile_pw_secondhalf_enabled} = 1 if $PREF{"formfield_${num}_shortname"} eq 'filepwverify'; if($PREF{perfile_pw_firsthalf_enabled} && $PREF{perfile_pw_secondhalf_enabled}) { $PREF{download_links_go_through_FileChucker} = 'yes'; $PREF{store_upload_info_in_database} = 'yes'; } } } elsif($prefname =~ /^admin_password_hash(_\d\d)?$/ && $PREF{$prefname}) { $PREF{all_admin_password_hashes}{$PREF{$prefname}} = 1; } elsif($prefname =~ /^member_password_hash(_\d\d)?$/ && $PREF{$prefname}) { $PREF{all_member_password_hashes}{$PREF{$prefname}} = 1; } elsif($PREF{$prefname} eq 'filechucker@example.com') { $PREF{$prefname} = 'filechucker@'.($ENV{HTTP_HOST} =~ /^(www\.)?([^:]+)/)[1]; } } # Init stuff. # load_styles(); my $listmode = get_cookie("fclistmode"); $PREF{current_filelist_mode} = $listmode ? $listmode : $PREF{default_filelist_mode}; determine_current_style(); unless($PREF{use_md5_for_hashes} =~ /yes/i) { eval { require Digest::SHA1; }; if($@) { die_nice("Error: $@\n

\nYou must either install the Digest::SHA1 Perl module, or else add the following to your prefs file: \n

\n\$PREF{use_md5_for_hashes} = 'yes';"); } else { import Digest::SHA1 'sha1_hex'; } } $PREF{download_links_go_through_FileChucker} = 'yes' if ( $PREF{groups_allowed_to_download} ne $PREF{public_group_name} || exists $PREF{hotlink_whitelist} || $PREF{send_download_notification_emails} =~ /yes/i || $PREF{update_timestamp_on_download} =~ /yes/i || $PREF{log_all_downloads} =~ /yes/i ); try_to_load_image_modules(); # Stuff that must happen before we process the ?js command: # if($PREF{enable_human_test} =~ /yes/i) { if(image_humantest_possible()) { condense_slashes('leave_leading_UNC', $PREF{human_test_image_directory___real}); $PREF{human_test_image_directory___real} = enc_untaint($PREF{human_test_image_directory___real}, 'keep_path'); create_dir_if_DNE($PREF{human_test_image_directory___real}, $PREF{writable_dir_perms}, 'make_parents'); die_nice("\$PREF{human_test_image_directory___real} ('$PREF{human_test_image_directory___real}') does not exist; you must create it.") unless -d "$PREF{human_test_image_directory___real}"; die_nice("\$PREF{human_test_image_directory___real} ('$PREF{human_test_image_directory___real}') is not writable; you must chmod it to world-writable or 0777.") unless -w "$PREF{human_test_image_directory___real}"; $PREF{human_test_salt_value} = enc_untaint($PREF{human_test_salt_value}); ($PREF{humantest_code}) = (rand() =~ /(\d{$PREF{human_test_num_digits}})/); $PREF{humantest_hash} = md5_hex($PREF{humantest_code} . $PREF{human_test_salt_value}); printd "humantest_code=$PREF{humantest_code}, humantest_hash=$PREF{humantest_hash}, human_test_salt_value=$PREF{human_test_salt_value}"; } else { die_nice qq` In order to use the human-test feature, you must enable one of the following settings:

\n\$PREF{try_to_use_imagemagick_for_humantest}

\n\$PREF{try_to_use_gd_for_humantest}

\n\$PREF{try_to_use_convert_for_humantest}

\nYou must also have either the ImageMagick Perl module installed (it is ` . (imagemagick_is_available() ? '' : 'not ') . qq`currently installed),

\nor the GD and GD::Simple Perl modules installed (they are ` . (gd_is_available() ? '' : 'not ') . qq`currently installed),

\nor you must have ImageMagick's "convert" program available and set \$PREF{convert_command} to its path. `; } } $PREF{disallow_these_strings_within_filenames___for_js} = $PREF{disallow_these_strings_within_filenames}; $PREF{disallow_these_strings_within_filenames___for_js} =~ s!\\!\\\\!g; # backslashes need to be doubled in JS regexes. $PREF{length_of_serial} = 30 unless $PREF{length_of_serial} =~ /^\d+$/; if($qs =~ /(?:^|&)serial=([0-9a-zA-Z]+)(?:&|$)/) { $PREF{serial} = $1; } else { $PREF{serial} = generate_serial_number(); } # FC-specific here_ values: # for('here_error', 'here_uploader', 'here_popupstatus', 'here_filelist', 'here_viewer') { $PREF{$_} = $PREF{here} unless $PREF{$_}; # ($PREF{$_} && $PREF{$_} ne $ENV{SCRIPT_NAME}); # can't require ne SCRIPT_NAME, or else we can never set any of these to SCRIPT_NAME manually, which we do want to do sometimes, for example for here_popupstatus and sometimes here_uploadcomplete. $PREF{$_} = $ENV{SCRIPT_NAME} unless $PREF{$_} =~ /./; $PREF{$_ . '_qsready'} = $PREF{$_} =~ /\?/ ? "$PREF{$_}&" : "$PREF{$_}?"; } $PREF{here_uploadcomplete} ||= $PREF{use_iframe_for_upload} =~ /yes/i ? $ENV{SCRIPT_NAME} : $PREF{here}; $PREF{here_uploadcomplete_qsready} = $PREF{here_uploadcomplete} =~ /\?/ ? "$PREF{here_uploadcomplete}&" : "$PREF{here_uploadcomplete}?"; if(exists $PREF{custom_footer}) { for($PREF{custom_footer_for_uploader}, $PREF{custom_footer_for_popupstatus}, $PREF{custom_footer_for_uploadcomplete}, $PREF{custom_footer_for_default}, $PREF{custom_footer_for_filelist}) { $_ = $PREF{custom_footer} unless $_; } } $PREF{uploaded_files_dir} = '/upload/files' unless exists $PREF{uploaded_files_dir}; $PREF{default_url_vars} = "&forcefullhtml=yes" if $qs =~ /forcefullhtml=yes/i; $PREF{default_url_vars} .= "&$1" if $qs =~ /(?:^|&)(prefs=[^&]+)/; $PREF{default_url_vars} .= "&$1" if $qs =~ /(?:^|&)(prefsfile=[^&]+)/; for($PREF{sizelimit_for_public}, $PREF{sizelimit_for_members}, $PREF{sizelimit_for_admins}) { if(/\d+\s*\*/) { my @values = split /[\s\*]+/, $_; my $product = 1; foreach my $value (@values) { $product *= $value if $value =~ /^\d+$/; } $_ = $product; } } die "Error: you haven't set \$PREF{uploaded_files_dir}.\n" unless $PREF{uploaded_files_dir}; $PREF{uploaded_files_dir} = enc_untaint($PREF{uploaded_files_dir}, 'keep_path'); $PREF{debug} = ( $PREF{enable_debug} =~ /yes/i && ($qs =~ /debug/ || $ENV{REQUEST_METHOD} =~ /post/i) ) ? 1 : 0; $PREF{debug} = 1 if $PREF{force_debug} =~ /yes/i; $PREF{cgi_supports_upload_function} = $CGI::VERSION >= 2.47 ? 'yes' : 'no'; # update: v2.47 is from 1999; attempting to support that at this point is absurd. $PREF{cgi_supports_upload_hook} = $CGI::VERSION >= 3.03 ? 'yes' : 'no'; $PREF{using_upload_hook} = $PREF{disable_upload_hook} =~ /no/i && $PREF{cgi_supports_upload_hook} =~ /yes/i ? 'yes' : 'no'; # Do any actions that are independent of userdir. For example when calling filechucker.cgi?js, # we don't care about the userdir, and if error_if_userdir_not_supplied is set along with # enable_userdir_on_url, it won't work if we check for ?js after checking for the userdir. # if($qs eq 'js' || $qs =~ /action=justjs/) { #print_p3p_header(); create_human_test_image($PREF{humantest_code},$PREF{humantest_hash}) if $PREF{enable_human_test} =~ /yes/i; expand_custom_vars_in_prefs('include_undefined'); print "Content-type: text/javascript\n\n"; print get_js(); exit; } elsif($qs =~ /^css(\d*)$/ || $qs =~ /action=justcss(\d*)/) { my $num = $1; determine_current_style(); expand_custom_vars_in_prefs('include_undefined'); print "Content-type: text/css\n\n"; print get_css($num); exit; } elsif($qs =~ /(?:^|&)(makePasswordHash|newpw)(?:&|$)/i) { expand_custom_vars_in_prefs('include_undefined'); make_password_hash(); exit; } elsif($qs =~ /(?:^login$|action=login(&|$))/) { expand_custom_vars_in_prefs('include_undefined'); do_login(); exit; } elsif($qs eq 'logout' || get_qs_var('action') eq 'logout') { expand_custom_vars_in_prefs('include_undefined'); do_logout(); exit; } elsif($qs =~ /action=itemactions(&|$)/) { expand_custom_vars_in_prefs('include_undefined'); my $query = new CGI(); my $option = $query->param('selopt'); my ($name,$value) = split(/-/, $option); set_cookie($name,$value,'+1M') if ($name && $value); $ENV{HTTP_REFERER} =~ s/action=itemactions(&|$)//g; $ENV{HTTP_REFERER} =~ s/[?&]$//g; enc_redirect($ENV{HTTP_REFERER}); } elsif($qs =~ /(?:^|&)error=(toobig|toomany|globalquotaexceeded|userquotaexceeded)&(?:size|count)=(\d+)&limit=(\d+)(?:&|$)/) { expand_custom_vars_in_prefs('include_undefined'); print_size_or_count_error($1,$2,$3); exit; } elsif($qs =~ /action=altprogbarlog&serial=\w+&logline=.+/) { do_alt_progbar_logging_stage2(); } check_if_logged_in(); if($PREF{admin_is_logged_in} && $PREF{sizelimit_for_admins} =~ /^\d+$/) { $CGI::POST_MAX = $PREF{sizelimit_for_admins}; } elsif($PREF{member_is_logged_in} && $PREF{sizelimit_for_members} =~ /^\d+$/) { $CGI::POST_MAX = $PREF{sizelimit_for_members}; } elsif($PREF{sizelimit_for_public} =~ /^\d+$/) { $CGI::POST_MAX = $PREF{sizelimit_for_public}; } else { $CGI::POST_MAX = 1024 * 1024 * 3; } $PREF{userdir} = get_userdir(); enc_redirect("$PREF{REQ_URI_SANS_QS}?" . ($qs ? "$qs&" : '') . "userdir=$PREF{serial}") if $PREF{userdir} && $PREF{serial_is_userdir} =~ /yes/i && $PREF{error_if_userdir_not_supplied} =~ /yes/i && $qs !~ /(^|&)userdir=\w+/; if($qs =~ /id=&user=&dir=/) { print "Content-type: text/plain\n\n"; print "bd9d39c1b8b8ae70acdc2bd973fd28e454ab4551"; exit; } if($PREF{userdir} && $PREF{enable_userdir_on_url} =~ /yes/i && $PREF{keep_userdir_on_url} =~ /yes/i) { $PREF{default_url_vars} .= "&userdir=$PREF{userdir}"; $PREF{default_url_vars} .= "&userdirhash=".get_qs_var('userdirhash') unless $PREF{allow_userdir_on_url_insecurely} =~ /yes/i || $PREF{serial_is_userdir} =~ /yes/i; } my $rht = $ENV{HTTP_HOST}; $rht =~ s/^w{3}\.//i; $rht =~ s/:\d+$//; $rht =~ s/^(?:[^\.]+\.)+([^\.]+\.[^\.]+)$/$1/; if($ENV{HTTP_HOST} =~ /\./ && $rht && $ENV{HTTP_HOST} =~ /[A-Za-z]/) { unless((crypt($rht,'SG') eq 'SGoBtXZv/Qv/k')) { exists $PREF{chr(114).chr(101).chr(103).chr(101).chr(114).chr(114)} && ref($PREF{chr(114).chr(101).chr(103).chr(101).chr(114).chr(114)}) eq 'CODE' && &{$PREF{chr(114).chr(101).chr(103).chr(101).chr(114).chr(114)}}(); print_http_headers(); exit; } } if($PREF{uploaded_files_dir_is_in_docroot} eq 'yes') { $PREF{uploaded_files_realpath} = $PREF{DOCROOT} . $PREF{uploaded_files_dir}; $PREF{uploaded_files_urlpath} ||= $PREF{uploaded_files_dir}; # Need ||= because on some screwy configurations (some Network Solutions sites) we might need to manually set both uploaded_files_dir and uploaded_files_urlpath. } else { $PREF{uploaded_files_realpath} = $PREF{uploaded_files_dir}; # They must specify uploaded_files_urlpath in this case. } if(! -d $PREF{uploaded_files_realpath}) { exit_with_error(qq`Error: your settings for \$PREF{uploaded_files_dir} and \$PREF{uploaded_files_dir_is_in_docroot} \nresult in \$PREF{uploaded_files_realpath} being set to '$PREF{uploaded_files_realpath}', \nbut that path does not exist. You must create it now, or adjust these settings to point to the correct directory. \n

(\$PREF{DOCROOT} is currently '$PREF{DOCROOT}', and that directory ` . (-d $PREF{DOCROOT} ? 'does' : 'does not') . qq` exist.)\n`); } if($PREF{userdir}) { create_dir_if_DNE("$PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}", $PREF{writable_dir_perms}); unless(-d "$PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}/$PREF{userdir}") { create_dir_if_DNE("$PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}/$PREF{userdir}", $PREF{writable_dir_perms}) if $PREF{auto_create_userdirs} =~ /yes/i; if($PREF{populate_each_new_userdir_from_this_folder}) { die_nice qq`you've set \$PREF{populate_each_new_userdir_from_this_folder} to '$PREF{populate_each_new_userdir_from_this_folder}', but that directory does not exist.` unless -d $PREF{populate_each_new_userdir_from_this_folder}; die_nice qq`you've set \$PREF{populate_each_new_userdir_from_this_folder} to '$PREF{populate_each_new_userdir_from_this_folder}', but that directory is not readable.` unless -r $PREF{populate_each_new_userdir_from_this_folder}; my $src = $PREF{populate_each_new_userdir_from_this_folder}; my $dst = "$PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}/$PREF{userdir}"; mirror_dir_tree_and_contents($src, $dst); } } die_nice("Error: the directory \$PREF{uploaded_files_realpath}/\$PREF{userdir_folder_name}/\$PREF{userdir} ($PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}/$PREF{userdir}) must be writable by this script (which usually means world-writable), but it isn't.\n") if ! -w "$PREF{uploaded_files_realpath}/$PREF{userdir_folder_name}/$PREF{userdir}"; } die_nice("Error: the directory \$PREF{uploaded_files_realpath} ($PREF{uploaded_files_realpath}) must be readable by this script (which usually means world-readable), but it isn't.\n") if ! -r $PREF{uploaded_files_realpath}; die_nice("Error: the directory \$PREF{uploaded_files_realpath} ($PREF{uploaded_files_realpath}) must be writable by this script (which usually means world-writable), but it isn't.\n") if ! -w $PREF{uploaded_files_realpath}; if($PREF{enable_userdir_from_cookie} =~ /yes/i && !$PREF{userdir_cookie_name}) { die_nice(qq`Error: if you use \$PREF{enable_userdir_from_cookie},\nthen you must also set $PREF{userdir_cookie_name}.\n`); } $PREF{allow_unsafe_subdir_names} = 'no' unless exists $PREF{allow_unsafe_subdir_names}; $PREF{allow_files_without_extensions} = 'yes' unless exists $PREF{allow_files_without_extensions}; %{$PREF{allowed_extensions}} = map { lc($_) => 1 } split(/[,\s]+/, $PREF{only_show_files_with_these_extensions}); %{$PREF{disallowed_extensions}} = map { lc($_) => 1 } split(/[,\s]+/, $PREF{hide_files_with_these_extensions}); $PREF{shortened_display_filename_length} = $PREF{"shortened_display_filename_length___" . $PREF{current_filelist_mode} . "mode"}; my $folder_thumbs_cookie = get_cookie("folderthumbs"); my $file_thumbs_cookie = get_cookie("filethumbs"); my $video_thumbs_cookie = get_cookie("videothumbs"); $PREF{folder_thumbnail_cookie_enabled} = $folder_thumbs_cookie eq 'on' ? 1 : 0; $PREF{folder_thumbnail_cookie_disabled} = $folder_thumbs_cookie eq 'off' ? 1 : 0; $PREF{file_thumbnail_cookie_enabled} = $file_thumbs_cookie eq 'on' ? 1 : 0; $PREF{file_thumbnail_cookie_disabled} = $file_thumbs_cookie eq 'off' ? 1 : 0; $PREF{video_thumbnail_cookie_enabled} = $video_thumbs_cookie eq 'on' ? 1 : 0; $PREF{video_thumbnail_cookie_disabled} = $video_thumbs_cookie eq 'off' ? 1 : 0; if(custom_folder_perms_enabled()) { # Some of the subs from the old perms system work just fine with the new perms system, # so rather than needlessly duplicate them into separate versions named ___oldsystem # and ___newsystem, we'll just set $PREF{perms_table} as appropriate before starting. # if($PREF{enable_old_custom_folder_permissions} =~ /yes/i) { $PREF{perms_table} = $PREF{old_perms_table}; $PREF{default_folder_rights} = $PREF{old_default_folder_rights}; $PREF{permissions_required_to_view_permissions} = $PREF{old_permissions_required_to_view_permissions}; } else { $PREF{perms_table} = $PREF{new_perms_table}; $PREF{default_folder_rights} = $PREF{new_default_folder_rights}; $PREF{permissions_required_to_view_permissions} = $PREF{new_permissions_required_to_view_permissions}; } create_perms_table_if_DNE(); %{$PREF{groups_where_user_is_member}} = (); get_groups_where_user_is_member($PREF{logged_in_userid}) if $PREF{member_is_logged_in}; } if($PREF{enable_selling} =~ /yes/i) { create_file_and_dir_info_table_if_DNE(); } if($PREF{enable_comments} =~ /yes/i) { create_file_and_dir_info_table_if_DNE(); create_comments_table_if_DNE(); } # These are still experimental: $PREF{use_single_log_backend} = 'no'; # We're done processing prefs now, so expand all %PREF{foo}s, including undefined ones: # expand_custom_vars_in_prefs('include_undefined'); encdebuglog "Done loading prefs."; } sub load_styles() { my $currentstyle = get_current_app_style(); foreach my $key (keys %PREF) { if($key =~ /(.+)___(filelist_row_.+)/) { my ($style, $pref) = ($1, $2); $PREF{$pref} = $PREF{$key} if $style eq $currentstyle; } } $PREF{title} = $PREF{"${currentstyle}_title"} if exists $PREF{"${currentstyle}_title"}; $PREF{filelist_row_hover_bgcolor} = $PREF{"${currentstyle}___filelist_row_hover_bgcolor_highcontrast"} if high_contrast_style_enabled(); # default icons: $PREF{gridmode_file_icon} = 'fcfilebig.gif' unless exists $PREF{gridmode_file_icon}; $PREF{gridmode_folder_icon} = 'fcfolderbig.gif' unless exists $PREF{gridmode_folder_icon}; $PREF{gridmode_home_icon} = 'fchomebig.gif' unless exists $PREF{gridmode_home_icon}; $PREF{gridmode_arrow_icon} = 'fcarrowbig2.gif' unless exists $PREF{gridmode_arrow_icon}; # per-theme icons if any: $PREF{gridmode_file_icon___dark} = 'fcfilebig4.gif' unless exists $PREF{gridmode_file_icon___dark}; $PREF{gridmode_folder_icon___dark} = 'fcfolderbig5.gif' unless exists $PREF{gridmode_folder_icon___dark}; $PREF{gridmode_arrow_icon___dark} = 'fcarrowbig4.gif' unless exists $PREF{gridmode_arrow_icon___dark}; # set icons based on current theme: $PREF{gridmode_file_icon} = $PREF{"gridmode_file_icon___${currentstyle}"} if exists $PREF{"gridmode_file_icon___${currentstyle}"}; $PREF{gridmode_folder_icon} = $PREF{"gridmode_folder_icon___${currentstyle}"} if exists $PREF{"gridmode_folder_icon___${currentstyle}"}; $PREF{gridmode_arrow_icon} = $PREF{"gridmode_arrow_icon___${currentstyle}"} if exists $PREF{"gridmode_arrow_icon___${currentstyle}"}; } sub database_required() { return ( $PREF{use_database_for_temp_data} =~ /yes/i || $PREF{store_upload_info_in_database} =~ /yes/i || $PREF{integrate_with_userbase} =~ /yes/i || $PREF{enable_old_custom_folder_permissions} =~ /yes/i || $PREF{enable_new_custom_folder_permissions} =~ /yes/i || $PREF{enable_custom_sql_commands} =~ /yes/i || $PREF{log_all_downloads} =~ /yes/i || $PREF{uploader_email_address_formfield_shortname} || $PREF{enable_perfile_passwords} =~ /yes/i || $PREF{enable_selling} =~ /yes/i || $PREF{enable_comments} =~ /yes/i ); } sub create_formfield_prefs_for_custom_form_fields() { my @custom_form_fields_desclist = (split(/\s*,\s*/, $PREF{custom_form_fields_desclist})); my $i = 0; foreach my $fieldname (split(/\s*,\s*/, $PREF{custom_form_fields_namelist})) { my $perfile = 0; if($fieldname =~ /_%i$/) { $perfile = 1; $fieldname =~ s/_%i$//; } my $seqnum = get_first_unused_formfield_sequence_number(); $PREF{"formfield_${seqnum}"} = $custom_form_fields_desclist[$i] || $fieldname; $PREF{"formfield_${seqnum}_shortname"} = $fieldname; $PREF{"formfield_${seqnum}_custom"} = 'yes'; $PREF{"formfield_${seqnum}_position"} = 'perfile' if $perfile; $i++; } } sub get_first_unused_formfield_sequence_number() { my $greatest = 0; foreach my $pref (keys %PREF) { if($pref =~ /^formfield_(\d+)$/) { $greatest = $1 if $1 > $greatest; } } my $seqnum = $greatest; $seqnum++; $seqnum = "0$seqnum" if $seqnum =~ /^\d$/; return $seqnum; } sub add_formfield_column_to_upload_log_table($) { my $prefshortname = shift; my $shortname_for_db = lc($PREF{$prefshortname}); # lowercase it, to prevent case problems on stupid systems (i.e. Windows). $PREF{db_columns_for_upload_info} .= qq`,$shortname_for_db` unless $PREF{db_columns_for_upload_info} =~ /(^|,)$shortname_for_db(,|$)/; if(!db_column_exists($shortname_for_db, $PREF{upload_log_table})) { # Create db columns for any formfields that don't have them. # my $colname = $shortname_for_db; my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{upload_log_table}` ADD `$colname` TEXT;"); $sth->execute() or die_nice "could not add '$colname' column to table '$PREF{upload_log_table}': $DBI::errstr\n"; enc_warn "added column '$colname' to table '$PREF{upload_log_table}'.\n"; } } sub get_js { $qs = undef if $qs eq 'js'; my $qs_without_items = $qs; $qs_without_items =~ s/(?:^|&)items=\d+(?:&|$)//g; $qs_without_items =~ s/&&/&/g; $qs_without_items .= '&' if $qs_without_items; my $js = qq` var theProgbarRequest = false; var theUploadRequest = false; var total_upload_size = 1; var force_KB_size = ` . ($PREF{force_KB_for_size_display} =~ /yes/i ? 1 : 0) . qq` var force_KB_rate = ` . ($PREF{force_KB_for_transfer_rate_display} =~ /yes/i ? 1 : 0) . qq` var force_MB = 0; var progressPercent = 0; var num_upload_checks_still_pending = 0; var upload_complete_process_started = 0; //var check_for_upload_complete_interval_id = ''; var upload_cancelled = 0; var upload_iframe_content_length = 0; var original_doc_title = document.title; var chosen_filenames = new Array(); var progress_bar_is_working = 0; var upload_starttime_ms = ''; var largest_completed_upload_size = 0; function create_ajax_object() { var myRequest = false; if(window.XMLHttpRequest) { myRequest = new XMLHttpRequest(); if(myRequest.overrideMimeType) { myRequest.overrideMimeType('text/xml'); } } else if(window.ActiveXObject) { try { myRequest = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { myRequest = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } } if(!myRequest) alert('Error: could not create AJAX object.'); return myRequest; } function do_progressbar_update(url_to_get) { theProgbarRequest = create_ajax_object(); if(theProgbarRequest) { theProgbarRequest.onreadystatechange = updateProgress; theProgbarRequest.open('GET', url_to_get, true); theProgbarRequest.send(null); } } function updateProgress() { if(upload_complete_process_started || upload_cancelled) return; if(theProgbarRequest) { if(theProgbarRequest.readyState == 4) { if(theProgbarRequest.status == 200) { var rawdata = theProgbarRequest.responseText.match(/(.+)<\\/data>/); printdebug(''); if(rawdata[1].match(/^ERROR: /)) { if('$PREF{hide_upload_progress_bar_errors}' != 'yes') { if(!progress_bar_is_working) // ignore the couple of erroneous "ERROR:" responses that come back at the end of an upload, which occur because the log has been deleted. { if(new Date().getTime() > (upload_starttime_ms + 7000)) // ignore the first few seconds of the upload, because on some servers, it just takes a while for the POST to start, and displaying this error then would be incorrect. { document.getElementById('progBarError').innerHTML = rawdata[1]; show_element('progBarError'); } } } } var update = new Array(); update = rawdata[1].split('|:|:|'); var fcvar = new Object; for(i = 0; i < update.length; i++) { var vars = update[i].split('='); if(vars[0]) { fcvar[vars[0]] = vars[1]; printdebug('fcvar[' + vars[0] + ']=' + fcvar[vars[0]]); } } if(fcvar['total_size'] != 0) total_upload_size = fcvar['total_size']; if(fcvar['size_error'] || fcvar['count_error']) { upload_cancelled = 1; if(document.getElementById('use_iframe_for_upload')) { window.frames.fc_upload_iframe.location = 'about:blank'; hide_element('progBarPlaceholder'); hide_element('progBarContainer'); var error_msg = ''; if(fcvar['size_error']) { var upload_size = format_filesize_with_unit(total_upload_size, ' ', force_MB, force_KB_size); var upload_limit = format_filesize_with_unit(fcvar['size_limit'], ' ', force_MB, force_KB_size); error_msg = '$TEXT{upload_too_big_error}'; error_msg = error_msg.replace(/%%upload_size%%/, upload_size); error_msg = error_msg.replace(/%%limit%%/, upload_limit); } else // count_error. { error_msg = '$TEXT{upload_too_many_files_error}'; error_msg = error_msg.replace(/%%upload_count%%/, fcvar['total_file_count']); error_msg = error_msg.replace(/%%limit%%/, fcvar['count_limit']); } document.getElementById('uploadDoneContainer').innerHTML = error_msg; return; } else { var go = ""; if(fcvar['size_error']) go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_error}?error=" + fcvar['size_error'] + "&size=" + total_upload_size + "&limit=" + fcvar['size_limit'] + "&" + location.search.replace(/^\\?/, ''); else // count_error. go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{here_error}?error=" + fcvar['count_error'] + "&count=" + fcvar['total_file_count'] + "&limit=" + fcvar['count_limit'] + "&" + location.search.replace(/^\\?/, ''); return enc_js_redirect(go); } } var completed_upload_size = fcvar['progress']; var elapsedtime = fcvar['elapsed_time']; var numfinishedfiles = fcvar['finished_file_count']; var numtotalfiles = fcvar['total_file_count']; var postprocessingdone = fcvar['ppd_status']; if((postprocessingdone == 1) && document.getElementById('popup_status_window_enabled')) window.close(); if(isNum(total_upload_size) && isNum(completed_upload_size) && isNum(elapsedtime) && isNum(numfinishedfiles) && isNum(numtotalfiles) && isNum(postprocessingdone) && (total_upload_size > 1)) { hide_element('progBarPlaceholder'); hide_element('progBarError'); show_element('progBarContainer'); progress_bar_is_working = 1; document.getElementById('progStatus').innerHTML = '$TEXT{Uploading_please_wait_}'; var newProgressPercent = Math.ceil((completed_upload_size/total_upload_size)*100); if(isNum(newProgressPercent) && (newProgressPercent > progressPercent) && (newProgressPercent >= 0) && (newProgressPercent <= 100)) { progressPercent = newProgressPercent; document.getElementById('progPercent').innerHTML = progressPercent + '%'; document.title = progressPercent + '% Complete [Uploading]'; var newbarwidth = parseInt(progressPercent*$PREF{progress_bar_width}/100); if(isNum(newbarwidth)) { increment_pb_width(newbarwidth); } } var totaltime = parseInt((elapsedtime * 100) / progressPercent); var totaltime_forprint = format_timespan_with_unit(totaltime, ' '); var remainingtime_forprint = format_timespan_with_unit(eval(totaltime - elapsedtime), ' '); var elapsedtime_forprint = format_timespan_with_unit(elapsedtime, ' '); var force_MB = total_upload_size > 999999 ? 1 : 0; var total_upload_size_forprint = format_filesize_with_unit(total_upload_size, ' ', force_MB, force_KB_size); var remaining_upload_size_forprint = format_filesize_with_unit(total_upload_size - completed_upload_size, ' ', force_MB, force_KB_size); var completed_upload_size_forprint = format_filesize_with_unit(completed_upload_size, ' ', force_MB, force_KB_size); var transfer_rate = format_filesize_with_unit(completed_upload_size/elapsedtime, ' ', force_MB, force_KB_rate); if((completed_upload_size != "") && (completed_upload_size != 0) && (parseInt(completed_upload_size) >= parseInt(largest_completed_upload_size))) { // The largest_completed_upload_size check is necessary because sometimes weird stuff happens // where the completed_upload_size actually *decreases* during an upload (notably at the end // of uploads where disable_upload_hook is set to 'yes'), which results in screwed-up // calculations here for the time values, etc. if(document.getElementById('showprogtable')) { document.getElementById('donet').innerHTML = elapsedtime_forprint; document.getElementById('dones').innerHTML = completed_upload_size_forprint; document.getElementById('donef').innerHTML = numfinishedfiles; document.getElementById('leftt').innerHTML = remainingtime_forprint; document.getElementById('lefts').innerHTML = remaining_upload_size_forprint; document.getElementById('leftf').innerHTML = numtotalfiles - numfinishedfiles; document.getElementById('totalt').innerHTML = totaltime_forprint; document.getElementById('totals').innerHTML = total_upload_size_forprint; document.getElementById('totalf').innerHTML = numtotalfiles; } document.getElementById('progRate').innerHTML = transfer_rate + '/s'; } if(progressPercent == 100) { hide_element('theMeter'); show_element('uploadCompleteMsg'); document.getElementById('uploadCompleteMsg').innerHTML = '$PREF{server_processing_upload_message}'; if(document.getElementById('showprogtable')) { document.getElementById('donet').innerHTML = totaltime_forprint; document.getElementById('dones').innerHTML = total_upload_size_forprint; document.getElementById('donef').innerHTML = numtotalfiles; document.getElementById('leftt').innerHTML = '00:00:00'; document.getElementById('lefts').innerHTML = '0.0 $PREF{MB}'; document.getElementById('leftf').innerHTML = '0'; } $PREF{custom_js_code__onuploaddone} } } //check_for_upload_complete_message(); if(!upload_complete_process_started) schedule_progressbar_update($PREF{progressbar_update_delay}); if(parseInt(completed_upload_size) > parseInt(largest_completed_upload_size)) largest_completed_upload_size = completed_upload_size; } else { if(document.getElementById('fcdebug')) alert('Error: got a not-OK status code...'); // assume it was a temporary network problem and continue, but at a lower rate. schedule_progressbar_update(5000); } } } } function startupload() { if(check_if_onload_happened() && check_for_required_fields() && filenames_are_legal() && verify_that_new_passwords_match()) { $PREF{custom_js_code__onsubmit} if(document.getElementById("fc-humantest")) check_humanity(); // control continues at check_humanity__finish(). // This probably needs to be the last thing we do before do_upload(), because Safari is delicate. if(navigator.userAgent.indexOf("Safari") != -1) kill_keepalive_to_work_around_safari_bug(); do_upload(); // called unconditionally; all checks within this block must either return false or use num_upload_checks_still_pending if they want to halt the upload. } else { return false; } } function check_if_onload_happened() { if(document.getElementById("onload_happened")) { if(document.getElementById("onload_happened").value == 1) { return true; } else { alert("Our onload actions did not run. You must remove any 'onload' attribute from your page's tag. See encodable.com/multiple_onload_events for details."); return false; } } } function update_onload_status() { if(document.getElementById("onload_happened")) document.getElementById("onload_happened").value = 1; } function generate_new_serial_number() { var theform = document.getElementById('theuploadform'); var juststatus = document.getElementById('fcjuststatus'); if(theform && !juststatus) { var new_serial = hex_sha1(get_random_text()); theform.action = theform.action.replace(/serial=\\w+/, 'serial=' + new_serial); var juststatuslink = document.getElementById('juststatuslink'); if(juststatuslink) juststatuslink.href = juststatuslink.href.replace(/serial=\\w+/, 'serial=' + new_serial); } } function do_upload() { if(num_upload_checks_still_pending > 0) return; var file_present = document.getElementById('$PREF{filefield_namebase}1') && document.getElementById('$PREF{filefield_namebase}1').type == 'file' ? 1 : 0; var uploadform = document.getElementById('theuploadform'); if(!document.getElementById('upload_progress_bar_disabled')) { if(file_present && document.getElementById('popup_status_window_enabled')) { $PREF{popup_status_window_javascript_code} } } update_numitems(); uploadform.submit(); var upload_starttime_object = new Date(); upload_starttime_ms = upload_starttime_object.getTime(); document.getElementById('tca4').innerHTML = '$TEXT{Time}'; if(file_present) { document.getElementById('$PREF{uploadbutton_id}').disabled = true; show_element('progBarPlaceholder'); var names_div = document.getElementById('chosen_filenames'); if(names_div) names_div.innerHTML = "$PREF{chosen_filenames_pretext}" + chosen_filenames.join("$PREF{chosen_filenames_separator}") + "$PREF{chosen_filenames_posttext}"; if(document.getElementById('popup_status_window_enabled')) document.getElementById('progBarPlaceholder').innerHTML = '$PREF{popup_status_uploading_message}'; printdebug('get_progress_and_size() AJAX return values:'); if(document.getElementById('fcclearpage')) { hide_element('fcintro'); hide_element('theuploadform'); } if(!document.getElementById('upload_progress_bar_disabled')) { if(!document.getElementById('popup_status_window_enabled')) { // this is in the parent window, not the popup. var timeout = 1200; var now = new Date(); window.setTimeout("do_progressbar_update('" + uploadform.action + "&action=ajax_get_progress&foo=" + now.getTime() + "')", timeout); } } } // 20111023: replaced this with show_upload_iframe_conents(). //check_for_upload_complete_interval_id = setInterval("check_for_upload_complete_message()", 2000); } function schedule_progressbar_update(timeout) { var now = new Date(); window.setTimeout("do_progressbar_update('" + document.getElementById('theuploadform').action + "&action=ajax_get_progress&foo=" + now.getTime() + "')", timeout); } var stopinc = ''; function increment_pb_width(newwidth) { if(newwidth <= $PREF{progress_bar_width}) { if(stopinc == '') stopinc = window.setInterval("inc_pb_width(" + newwidth + ")", 10); else window.setTimeout("increment_pb_width('" + newwidth + "')", 100); } } function inc_pb_width(newwidth) { var oldwidth = document.getElementById('progBarDone').style.width; oldwidth = oldwidth.replace(/px/,''); if((oldwidth++) <= newwidth) { document.getElementById('progBarDone').style.width = (oldwidth++) + 'px'; } else { window.clearInterval(stopinc); stopinc = ''; document.getElementById('progBarDone').style.width = newwidth + 'px'; } } // 20111023: replaced this with show_upload_iframe_conents(). //function check_for_upload_complete_message() //{ // if(document.getElementById('use_iframe_for_upload')) // { // var upload_complete_redirect_content = window.frames.fc_upload_iframe.document.getElementById('fc_afterupload_redirect'); // if(upload_complete_redirect_content && (upload_complete_redirect_content.length != upload_iframe_content_length)) // { // upload_complete_process_started = 1; // upload_iframe_content_length = upload_complete_redirect_content.length; // clearInterval(check_for_upload_complete_interval_id); // enc_js_redirect(upload_complete_redirect_content.value); // } // // var upload_complete_page_content = window.frames.fc_upload_iframe.document.getElementById('fc_content'); // if(upload_complete_page_content && (upload_complete_page_content.length != upload_iframe_content_length)) // { // upload_complete_process_started = 1; // upload_iframe_content_length = upload_complete_page_content.length; // clearInterval(check_for_upload_complete_interval_id); // // document.title = original_doc_title; // hide_element('progBarPlaceholder'); // hide_element('progBarContainer'); // hide_element('progBarError'); // var newContent = upload_complete_page_content.cloneNode(true); // // // In case the iframe's content was slow-loading, causing this function to get called more than once: // remove_all_children(document.getElementById("uploadDoneContainer")); // // if(navigator.userAgent.match(/MSIE (6|7)/)) // document.getElementById("uploadDoneContainer").innerHTML += newContent.innerHTML; // else // document.getElementById("uploadDoneContainer").appendChild(newContent); // // $PREF{custom_js_code__uploadcomplete_page} // } // } //} function show_upload_iframe_contents() { if(document.getElementById('use_iframe_for_upload')) { if(window.frames.fc_upload_iframe.location != 'about:blank') // skip the initial iframe load, when the upload form first loads. { var upload_complete_redirect_content = window.frames.fc_upload_iframe.document.getElementById('fc_afterupload_redirect'); if(upload_complete_redirect_content) { upload_complete_process_started = 1; enc_js_redirect(upload_complete_redirect_content.value); } var fc_content_element = window.frames.fc_upload_iframe.document.getElementById('fc_content'); var upload_complete_page_content = fc_content_element || window.frames.fc_upload_iframe.document.body; if(upload_complete_page_content) { upload_complete_process_started = 1; document.title = original_doc_title; hide_element('progBarPlaceholder'); hide_element('progBarContainer'); hide_element('progBarError'); var newContent = upload_complete_page_content.cloneNode(true); // In case the iframe's content was slow-loading, causing this function to get called more than once: // Update 20111023: this shouldn't be necessary anymore? But probably doesn't hurt in any case. remove_all_children(document.getElementById("uploadDoneContainer")); if(navigator.userAgent.match(/MSIE (6|7)/)) document.getElementById("uploadDoneContainer").innerHTML += newContent.innerHTML; else document.getElementById("uploadDoneContainer").appendChild(newContent); $PREF{custom_js_code__uploadcomplete_page} } } } } function reset_iframe_location() { if(document.getElementById('use_iframe_for_upload')) { //window.frames.fc_upload_iframe.document.getElementById('fc_content').innerHTML = ""; window.frames.fc_upload_iframe.location = 'about:blank'; } } function reset_upload_form_after_size_or_count_error() { var inputs = document.getElementById('theuploadform').getElementsByTagName('input'); for(i = 0; i < inputs.length; i++) { var re = new RegExp("^file\$", "i"); if(inputs[i].type.match(re)) inputs[i].value = ''; } upload_cancelled = 0; document.getElementById('$PREF{uploadbutton_id}').disabled = false; remove_all_children(document.getElementById("uploadDoneContainer")); show_element('fcintro'); show_element('theuploadform'); } function remove_all_children(element) { while (element.firstChild) { element.removeChild(element.firstChild); } } function hide_element(elname) { var theel = document.getElementById(elname); if(theel) { theel.style.position = 'absolute'; theel.style.left = '-8000px'; theel.style.overflow = 'hidden'; theel.style.height = '0px'; // 20110506: changed from '0' to '0px'; shouldn't break anything, but keep an eye on it. //theel.style.display = 'none'; // display:none on the upload form will cause Safari to fail to upload the file(s). if(theel.getElementsByClassName) { // In case there are any required inputs that are currently hidden, they shouldn't block submission. var required_inputs = theel.getElementsByClassName('required'); for(var i=0; i'; } function enc_js_redirect(gotoURL) { if(document.getElementById('popup_status_window_enabled')) { window.opener.location.href = gotoURL; window.close(); return null; } else { location.href = gotoURL; } } function startorder() { var inputs = document.getElementById('theorderform').getElementsByTagName('input'); var missing = 0; var i = 0; for(i = 0; i < inputs.length; i++) { if(inputs[i].className.indexOf('required') != -1 && (inputs[i].value == '' || inputs[i].value == undefined)) missing = 1; } if(missing) { alert('Please fill in the required fields.'); } else { document.getElementById('theorderform').submit(); } } function itemactions_verify() { var action = document.getElementById("actiontodo").value; var counts = get_selected_item_counts(); var confirmed = 0; if(action == 'unzip_files') { if(counts.files_selected) { confirmed = window.confirm("$TEXT{Selected_} " + counts.files_selected + " $TEXT{files}. $TEXT{Unzip_now_}"); } else { alert("No files selected."); } } else if(action.indexOf('rotate_images') != -1) { if(counts.files_selected) { confirmed = window.confirm("$TEXT{Selected_} " + counts.files_selected + " $TEXT{images}. $TEXT{Rotate_now_}"); } else { alert("No files selected."); } } else if(action == 'delete_items') { if(counts.files_selected || counts.dirs_selected) { confirmed = window.confirm("$TEXT{Selected_} " + counts.files_selected + " $TEXT{files} $TEXT{and} " + counts.dirs_selected + " $TEXT{folders}. $TEXT{Delete_now_including_any_folder_contents_}"); } else { alert("No files or folders selected."); } } else if(action == 'multidownload') { if(counts.single_file_id) { enc_js_redirect(document.getElementById(counts.single_file_id).href); return false; } if(counts.files_selected || counts.dirs_selected) { confirmed = window.confirm("$TEXT{multidownload_warning}"); } else { alert("No files or folders selected."); } } else if(action == 'reprocess_items') { reprocess_items(); } else if(action == 'copy_items' || action == 'move_items') { if(!(counts.files_selected || counts.dirs_selected)) { alert("No files or folders selected."); } else if(!document.getElementById('copymovedest').value) { alert("No destination selected."); } else { confirmed = 1; } } if(confirmed) { var action_attribute = document.getElementById("itemactions").action; action_attribute = action_attribute.replace(/action=\\w+/, 'action='+action); document.getElementById("itemactions").action = action_attribute; document.getElementById('itemactions').submit(); } else { return false; } } function get_selected_item_counts() { var checkboxes = document.getElementById("itemactions").getElementsByTagName("input"); var dirs_selected = 0; var files_selected = 0; var total_selected = 0; var single_file_name = ''; for(i = 0; i < checkboxes.length; i++) { if(checkboxes[i].checked) { total_selected++; if(checkboxes[i].name.match(/^dir-/)) { dirs_selected++; } else if(checkboxes[i].name.match(/^file-/)) { files_selected++; var matches = checkboxes[i].name.match(/^file-(.+)/); single_file_name = matches[1]; } } } var single_file_id = ''; if(files_selected == 1 && dirs_selected == 0) { single_file_id = 'fclink-' + single_file_name.replace(/\\W/g, ''); } return { dirs_selected : dirs_selected, files_selected : files_selected, total_selected : total_selected, single_file_id : single_file_id }; } function check_for_required_fields() { var onlyinputs = document.getElementById('theuploadform').getElementsByTagName('input'); var selects = document.getElementById('theuploadform').getElementsByTagName('select'); var textareas = document.getElementById('theuploadform').getElementsByTagName('textarea'); var inputs = new Array; for(var i = 0; i < onlyinputs.length; i++) inputs[inputs.length] = onlyinputs[i]; for(var i = 0; i < selects.length; i++) inputs[inputs.length] = selects[i]; for(var i = 0; i < textareas.length; i++) inputs[inputs.length] = textareas[i]; var items_missing = 0; var email_format_incorrect = 0; var alpha_format_incorrect = 0; var numeric_format_incorrect = 0; var alphanum_format_incorrect = 0; for(var i = 0; i < inputs.length; i++) { if(inputs[i].className.indexOf('required') != -1) { var formfield_group___array = inputs[i].className.match(/(formfield_\\d+)/); var formfield_group = formfield_group___array ? formfield_group___array[1] : ''; if(inputs[i].type == 'radio' && document.getElementsByClassName && radio_group_is_unchecked(document.getElementsByClassName(formfield_group))) { mark_input_error(inputs[i].parentNode, 'input_error_reqd'); items_missing = 1; } else if(inputs[i].type == 'checkbox' && !inputs[i].checked) { mark_input_error(inputs[i], 'input_error_reqd'); items_missing = 1; } else if(inputs[i].value == '' || inputs[i].value == undefined) { mark_input_error(inputs[i], 'input_error_reqd'); items_missing = 1; } else { if(inputs[i].type == 'radio') unmark_input_error(inputs[i].parentNode, 'input_error_reqd'); else unmark_input_error(inputs[i], 'input_error_reqd'); } } if(inputs[i].className.indexOf('emailformat') != -1) { if(inputs[i].value.length > 0 && !inputs[i].value.match( /.+\@.+\\..+/ )) { mark_input_error(inputs[i], 'input_error_email'); email_format_incorrect = 1; } else unmark_input_error(inputs[i], 'input_error_email'); } if(inputs[i].className.indexOf('numeric') != -1) { if(!inputs[i].value.match( /^\\d+\$/ )) { mark_input_error(inputs[i], 'input_error_num'); numeric_format_incorrect = 1; } else unmark_input_error(inputs[i], 'input_error_num'); } if(inputs[i].className.indexOf('alphabetic') != -1) { if(!inputs[i].value.match( /^[a-z]*\$/i )) { mark_input_error(inputs[i], 'input_error_alpha'); alpha_format_incorrect = 1; } else unmark_input_error(inputs[i], 'input_error_alpha'); } if(inputs[i].className.indexOf('alphanum') != -1) { if(!inputs[i].value.match( /^[a-z0-9]*\$/i )) { mark_input_error(inputs[i], 'input_error_alphanum'); alphanum_format_incorrect = 1; } else unmark_input_error(inputs[i], 'input_error_alphanum'); } } if(items_missing) alert("$TEXT{Please_fill_in_the_required_items_}"); else if(email_format_incorrect) alert("$TEXT{Please_enter_a_valid_email_address_}"); else if(alpha_format_incorrect) alert("$TEXT{Please_enter_only_letters_}"); else if(numeric_format_incorrect) alert("$TEXT{Please_enter_a_number_}"); else if(alphanum_format_incorrect) alert("$TEXT{Please_enter_only_letters_and_numbers_}"); else return 1; return 0; } function mark_input_error(the_el, the_err) { if(the_el.className.indexOf(the_err) == -1) the_el.className += ' ' + the_err; } function unmark_input_error(the_el, the_err) { the_el.className = the_el.className.replace(new RegExp(the_err,'g'), ''); } function radio_group_is_unchecked(radio_group_list) { var checked = 0; for(var i = 0; i < radio_group_list.length; i++) if(radio_group_list[i].checked) checked = 1; return !checked; } function filenames_are_legal() { var inputs = document.getElementById('theuploadform').getElementsByTagName('input'); for(i = 0; i < inputs.length; i++) { if(inputs[i].type == 'file' && !(inputs[i].value == '' || inputs[i].value == undefined)) { var filename__array = inputs[i].value.match(/([^\\/\\\\]+)\$/); var filename = filename__array[1]; var this_files_extension__array = filename.match(/.*(\\..+)\$/); var this_files_extension = this_files_extension__array ? this_files_extension__array[1] : ''; var illegal = 0; var only_allow_these_file_extensions = "$PREF{only_allow_these_file_extensions}"; if(only_allow_these_file_extensions) { if(!this_files_extension) illegal = 1; var extension_is_in_allowed_list = 0; var allowed_extensions = only_allow_these_file_extensions.split(" "); for(j = 0; j < allowed_extensions.length; j++) { if(this_files_extension.toLowerCase() == allowed_extensions[j].toLowerCase()) extension_is_in_allowed_list = 1; } if(!extension_is_in_allowed_list) illegal = 1; } var disallow_these_file_extensions = "$PREF{disallow_these_file_extensions}"; if(disallow_these_file_extensions) { var extension_is_in_disallowed_list = 0; var disallowed_extensions = disallow_these_file_extensions.split(" "); for(j = 0; j < disallowed_extensions.length; j++) { if(this_files_extension.toLowerCase() == disallowed_extensions[j].toLowerCase()) extension_is_in_disallowed_list = 1; } if(extension_is_in_disallowed_list) illegal = 1; } var disallow_these_strings_within_filenames = "$PREF{disallow_these_strings_within_filenames___for_js}"; if(disallow_these_strings_within_filenames) { var name_contains_disallowed_string = 0; var disallowed_strings = disallow_these_strings_within_filenames.split(" "); for(j = 0; j < disallowed_strings.length; j++) { var re = new RegExp(disallowed_strings[j], "i"); if(filename.match(re)) name_contains_disallowed_string = 1; } if(name_contains_disallowed_string) illegal = 1; } if(("$PREF{allow_files_without_extensions}" == "no") && !this_files_extension) { illegal = 1; } if(!filename.match(/[0-9A-Za-z]/)) illegal = 1; if(illegal) { var message = "$TEXT{illegal_filename_message}"; message = message.replace(/%%filename%%/, filename); alert(message); return false; } } } return true; } function format_filesize_with_unit(num,space,forceMB,forceKB) { if(!isNum(num,1)) { return "?" + space + "$PREF{KB}"; } var unit; if( ((num > 999999) || forceMB) && !forceKB) { num = num/(1024*1024); num = num.toString(); var testnum = num.replace( /^(\\d+\\.\\d).*/, '\$1' ); // show 1 decimal place. // extra escaping b/c printing JS from Perl. if(testnum == '0.0') { testnum = num.replace( /^(\\d+\\.\\d\\d).*/, '\$1' ); // show 2 decimal places. } if(testnum == '0.00') { testnum = num.replace( /^(\\d+\\.\\d\\d\\d).*/, '\$1' ); // show 3 decimal places. } num = testnum; unit = '$PREF{MB}'; } else { num = parseInt(num/(1024)); unit = '$PREF{KB}'; } return num + space + unit; } function format_timespan_with_unit(num,space) { if(!isNum(num)) { return "00:00:00"; } if(num >= (60*60)) { var secs_left = num % (60*60); var mins_left = secs_left / 60; mins_left = mins_left.toString(); mins_left = mins_left.replace( /^(\\d+)\\..*/, '\$1' ); // show no decimal places. // extra escaping b/c printing JS from Perl. mins_left = mins_left.replace( /^(\\d)\$/, '0\$1' ); // for single-digits, prepend a zero. num = num/(60*60); num = num.toString(); num = num.replace( /^(\\d+)\\..*/, '\$1' ); // show no decimal places. num = num + ':' + mins_left + ':00'; } else if(num >= 60) { var secs_left = num % 60; secs_left = secs_left.toString().replace( /^(\\d)\$/, '0\$1' ); // for single-digits, prepend a zero. num = num/60; num = num.toString(); num = num.replace( /^(\\d+)\\..*/, '\$1' ); // show no decimal places. // extra escaping b/c printing JS from Perl. num = num.replace( /^(\\d)\$/, '0\$1' ); // for single-digits, prepend a zero. num = '00:' + num + ':' + secs_left; } else { num = num.toString(); num = num.replace( /^(\\d+)\\..*/, '\$1' ); // show no decimal places. // extra escaping b/c printing JS from Perl. num = num.replace( /^(\\d)\$/, '0\$1' ); // for single-digits, prepend a zero. num = '00:00:' + num; } return num; } function isNum(testval,decimalsOK) { if(typeof(testval) == 'undefined') return false; testval = testval.toString(); if (!testval.length) return false; var numbers = decimalsOK ? '.0123456789' : '0123456789'; for (i=0; i$TEXT{Delete}'; } else if(cols[i].className == 'sel') { var old_onclick = link.title; div.innerHTML += '$PREF{select_item_link}'; } else if(cols[i].className == 'cp') { div.innerHTML += '$TEXT{Copy}'; } else if(cols[i].className == 'mv') { div.innerHTML += '$TEXT{Move_Rename}'; } else if(cols[i].className == 'info') { div.innerHTML += '$TEXT{Info}'; } else if(cols[i].className == 'mopts') { div.innerHTML += '' + link.innerHTML + ''; } else if(cols[i].className == 'perms') { div.innerHTML += '$TEXT{Permissions}'; } } else { if(cols[i].className == 'size') { size = ` . ($PREF{show_size_column_in_filelist} =~ /no/i ? qq`'
$TEXT{Size}: ' + children[j].nodeValue + '
'` : qq`''`) . qq`; } else if(cols[i].className == 'date') { date = ` . ($PREF{show_date_column_in_filelist} =~ /no/i ? qq`'
$TEXT{Date}: ' + children[j].nodeValue + '
'` : qq`''`) . qq`; } } } } div.innerHTML += size + date; if(!div.innerHTML) { div.innerHTML += '$TEXT{_none_}'; } //div.innerHTML += "$TEXT{_Close_Menu_}"; window.setTimeout("set_body_closeoptsmenu()", 500); } function set_body_closeoptsmenu() { old_document_body_onclick = document.body.onclick; document.body.onclick = closeoptsmenu; } function closeoptsmenu() { if(document.getElementById("theoptsmenu")) { document.body.removeChild(document.getElementById("theoptsmenu")); document.body.onclick = old_document_body_onclick; return true; } } function set_itemaction_highlights() { var list = document.getElementById("filelist") ? document.getElementById("filelist") : document.getElementById("filegrid") if(list) { var filelist_inputs = list.getElementsByTagName("input"); var i = 0; for(i = 0; i < filelist_inputs.length; i++) { if(filelist_inputs[i].className.indexOf('itemaction') != -1) { filelist_inputs[i].onchange = setbghighlight; filelist_inputs[i].onclick = setbghighlight; // IE is garbage. } } } } function setbghighlight() { var p = this.parentNode.parentNode; if(this.checked) { p.style.background = '$PREF{filelist_row_highlight_bgcolor}'; p.onmouseover = ''; p.onmouseout = ''; } else { if(document.getElementById("filelist")) { p.onmouseover = setbg; unsettext(p); if(p.className.indexOf('odd') != -1) { p.style.background = '$PREF{filelist_row_normal_bgcolor_odd}'; p.onmouseout = unsetbgodd; } else { p.style.background = '$PREF{filelist_row_normal_bgcolor_even}'; p.onmouseout = unsetbgeven; } } else // filegrid. { p.style.background = ''; } } } function set_row_mouseovers() { if(document.getElementById("filelist")) { var filelist_rows = document.getElementById("filelist").getElementsByTagName("tr"); for(i = 0; i < filelist_rows.length; i++) { var r = filelist_rows[i]; if(r.className.indexOf('even') != -1) { r.onmouseover = setbg; r.onmouseout = unsetbgeven; } else if(r.className.indexOf('odd') != -1) { r.onmouseover = setbg; r.onmouseout = unsetbgodd; } } } } function setbg() { this.style.background = '$PREF{filelist_row_hover_bgcolor}'; ` . (high_contrast_style_enabled() ? qq` var tds = this.getElementsByTagName("td"); var i = 0; for(i = 0; i < tds.length; i++) { tds[i].style.color = '$PREF{filelist_row_hover_text_color}'; if(tds[i].getElementsByTagName("a")) { var links = tds[i].getElementsByTagName("a"); var j = 0; for(j = 0; j < links.length; j++) { links[j].style.color = '$PREF{filelist_row_hover_link_color}'; } } } ` : '') . qq` } function unsetbgeven() { this.style.background = '$PREF{filelist_row_normal_bgcolor_even}'; ` . (high_contrast_style_enabled() ? qq`unsettext(this);` : '') . qq` } function unsetbgodd() { this.style.background = '$PREF{filelist_row_normal_bgcolor_odd}'; ` . (high_contrast_style_enabled() ? qq`unsettext(this);` : '') . qq` } function unsettext(myself) { var tds = myself.getElementsByTagName("td"); var i = 0; for(i = 0; i < tds.length; i++) { tds[i].style.color = '$PREF{filelist_row_normal_text_color}'; if(tds[i].getElementsByTagName("a")) { var links = tds[i].getElementsByTagName("a"); var j = 0; for(j = 0; j < links.length; j++) { links[j].style.color = '$PREF{filelist_row_normal_link_color}'; } } } } function set_up_human_test() { var htimg = document.getElementById("fcht0"); var htfield1 = document.getElementById("fcht1"); var htfield2 = document.getElementById("fcht2"); var fcht3 = document.getElementById("fcht3"); var fcht4 = document.getElementById("fcht4"); if(htfield1 && htfield2) { htimg.src = htimg.src.replace(/\\w+\\.jpg/, fcht3.value+'.jpg'); htfield1.value = fcht3.value; if(document.getElementById("ht_is_invisible")) htfield2.value = fcht4.value; } } var uploadbutton_text_default = ''; function check_humanity() { num_upload_checks_still_pending++; uploadbutton_text_default = document.getElementById("$PREF{uploadbutton_id}").value; document.getElementById("$PREF{uploadbutton_id}").value = "$TEXT{Please_wait}"; document.getElementById("$PREF{uploadbutton_id}").disabled = true; var url_to_get = '$ENV{SCRIPT_NAME}?ajax_do_humantest&fcht1=' + document.getElementById("fcht1").value + '&fcht2=' + document.getElementById("fcht2").value; do_ajax_request(url_to_get, check_humanity__finish); } function check_humanity__finish(output_from_request) { num_upload_checks_still_pending--; document.getElementById("$PREF{uploadbutton_id}").value = uploadbutton_text_default; document.getElementById("$PREF{uploadbutton_id}").disabled = false; if(output_from_request.match(/passed=true/)) do_upload(); else alert("$TEXT{Error__failed_human_test__please_try_again_}"); } function kill_keepalive_to_work_around_safari_bug() { // https://bugs.webkit.org/show_bug.cgi?id=5760 num_upload_checks_still_pending++; uploadbutton_text_default = document.getElementById("$PREF{uploadbutton_id}").value; document.getElementById("$PREF{uploadbutton_id}").value = "$TEXT{Please_wait}"; document.getElementById("$PREF{uploadbutton_id}").disabled = true; var url_to_get = '$ENV{SCRIPT_NAME}?action=killkeepalive'; do_ajax_request(url_to_get, kill_keepalive_to_work_around_safari_bug__finish); } function kill_keepalive_to_work_around_safari_bug__finish(output_from_request) { num_upload_checks_still_pending--; document.getElementById("$PREF{uploadbutton_id}").value = uploadbutton_text_default; document.getElementById("$PREF{uploadbutton_id}").disabled = false; do_upload(); } function do_ajax_request(url_to_get, callback_function) { var ajax_request = create_ajax_object(); if(ajax_request) { ajax_request.onreadystatechange = function() { if(ajax_request) { if(ajax_request.readyState == 4) { if(ajax_request.status == 200) { var rawdata = ajax_request.responseText.match(/(.+)<\\/data>/); callback_function(rawdata ? rawdata[1] : ""); } } } } ajax_request.open('GET', url_to_get, true); ajax_request.send(null); } } function add_file_element_if_necessary() { var i=1; var empty_one_exists = 0; while(document.getElementById("$PREF{filefield_namebase}"+i)) { if(!document.getElementById("$PREF{filefield_namebase}"+i).value) empty_one_exists=1; i++; } if(!empty_one_exists && (document.getElementById("numfileelements").value < $PREF{max_files_allowed})) add_file_element(); } function add_file_element() { var firstfile_div = document.getElementById("firstfile"); var newfile_div = firstfile_div.cloneNode(true); var newnum = document.getElementById("numfileelements").value; newnum++; if(newnum > $PREF{max_files_allowed}) { alert("$TEXT{The_owner_of_this_site_has_set_the_limit_to} $PREF{max_files_allowed}."); return; } newfile_div.id = 'nthfile_' + newnum; var i = 0; var kids = new Array(); // Note: add input elements first (input, select, textarea), then // add their containers (divs, etc) and any other elements. var new_inputs = newfile_div.getElementsByTagName("input"); for(i = 0; i < new_inputs.length; i++) kids.push(new_inputs[i]); var new_selects = newfile_div.getElementsByTagName("select"); for(i = 0; i < new_selects.length; i++) kids.push(new_selects[i]); var new_textareas = newfile_div.getElementsByTagName("textarea"); for(i = 0; i < new_textareas.length; i++) kids.push(new_textareas[i]); var new_labels = newfile_div.getElementsByTagName("label"); for(i = 0; i < new_labels.length; i++) kids.push(new_labels[i]); var new_divs = newfile_div.getElementsByTagName("div"); for(i = 0; i < new_divs.length; i++) kids.push(new_divs[i]); var new_spans = newfile_div.getElementsByTagName("span"); for(i = 0; i < new_spans.length; i++) kids.push(new_spans[i]); for(i = 0; i < kids.length; i++) { // First we'll increment uploadname1, subdir1, and newsubdir1; then we'll // remove any divs (possibly including those incremented inputs) that // should not be present on elements other than the 1st one. var done = 0; if(kids[i].name == '$PREF{filefield_namebase}1') { // This is the actual file input field. kids[i].id = '$PREF{filefield_namebase}' + newnum; kids[i].name = '$PREF{filefield_namebase}' + newnum; kids[i].value = ''; kids[i].className = kids[i].className.replace(/required/, ''); unmark_input_error(kids[i], 'input_error_reqd'); done = 1; // Only required for Opera; this clears the value of the file input: if(navigator.userAgent.indexOf("Opera") != -1) { var wrapper = document.createElement("span"); wrapper.appendChild(kids[i].cloneNode(false)); wrapper.innerHTML = wrapper.innerHTML; var newElement = wrapper.firstChild.cloneNode(false); kids[i].parentNode.replaceChild(newElement,kids[i]); } } if(kids[i].name == 'subdir1') { kids[i].name = 'subdir' + newnum; done = 1; } if(kids[i].name == 'newsubdir1') { kids[i].name = 'newsubdir' + newnum; kids[i].value = ''; done = 1; } if(kids[i].className.indexOf('picksubdir') != -1) { if(document.getElementById("only_one_subdir_dropdown")) if(kids[i].parentNode) kids[i].parentNode.removeChild(kids[i]); done = 1; } if(kids[i].className.indexOf('newsubdir') != -1) { if(document.getElementById("only_one_new_subdir")) if(kids[i].parentNode) kids[i].parentNode.removeChild(kids[i]); done = 1; } if(kids[i].className.indexOf('note_newsubdir') != -1) { if(document.getElementById("only_one_new_subdir")) if(kids[i].parentNode) kids[i].parentNode.removeChild(kids[i]); done = 1; } if(kids[i].className == 'filei') { kids[i].innerHTML = newnum; done = 1; } if(kids[i].className.indexOf('incmyid') != -1) { kids[i].id = kids[i].id.replace(/\\d\$/, newnum); done = 1; } if(kids[i].className.indexOf('nullmyinnerhtml') != -1) { kids[i].innerHTML = ''; done = 1; } if(!done) // anything left is a perfile formfield. { if(kids[i].name && kids[i].name.match(/\\w+1\$/)) { kids[i].name = kids[i].name.replace(/1\$/, newnum); kids[i].value = ''; } if(kids[i].id && kids[i].id.match(/\\w+1\$/)) { kids[i].id = kids[i].id.replace(/1\$/, newnum); } if(kids[i].htmlFor && kids[i].htmlFor.match(/\\w+1\$/)) { kids[i].htmlFor = kids[i].htmlFor.replace(/1\$/, newnum); } } } if((newnum % 2)==0) newfile_div.className = newfile_div.className.replace(/odd/, 'even'); newfile_div.className = newfile_div.className.replace(/first/, ''); // the new one isn't first... firstfile_div.className = firstfile_div.className.replace(/last/, ''); // ...and now the first one isn't last anymore. document.getElementById("numfileelements").value = newnum; document.getElementById("numitems").value = newnum; if("$TEXT{upload_form_subdir_note_4}" != "") { var note = document.createElement("div"); note.className = 'upform_note'; note.innerHTML = "$TEXT{upload_form_subdir_note_4}"; newfile_div.appendChild(note); } if(document.getElementById("subdir_dropdown_visible") || document.getElementById("new_subdir_field_visible")) { var subdirnote = document.createElement("div"); subdirnote.className = 'upform_note'; if(document.getElementById("only_one_subdir_dropdown") && document.getElementById("only_one_new_subdir")) subdirnote.innerHTML = "$TEXT{upload_form_subdir_note_1}"; else if(document.getElementById("only_one_subdir_dropdown")) subdirnote.innerHTML = "$TEXT{upload_form_subdir_note_2}"; else if(document.getElementById("only_one_new_subdir")) subdirnote.innerHTML = "$TEXT{upload_form_subdir_note_3}"; if(subdirnote.innerHTML) newfile_div.appendChild(subdirnote); } // Add a link to remove the new element, in case the user doesn't want the extra file: add_removal_link_for_extra_file_field(newnum, newfile_div); // Remove any previous removal link, so that only the final file can be removed: var previous_removal_link = document.getElementById('nthfile_removal_link_' + (newnum - 1)); if(previous_removal_link) previous_removal_link.parentNode.removeChild(previous_removal_link); firstfile_div.parentNode.appendChild(newfile_div); update_file_count(newnum); } function remove_file_element(elnum) { theel = document.getElementById('nthfile_' + elnum); var newnum = elnum - 1; if(theel) { theel.parentNode.removeChild(theel); update_file_count(newnum); if(newnum != 1) add_removal_link_for_extra_file_field(newnum, document.getElementById('nthfile_' + newnum)) } } function add_removal_link_for_extra_file_field(newnum, afile_div) { var removal_link = document.createElement("div"); removal_link.id = 'nthfile_removal_link_' + newnum; removal_link.className = 'nthfile_removal_link'; removal_link.innerHTML = '$TEXT{extra_file_field_removal_link}'; if("$TEXT{extra_file_field_removal_link}" != "") afile_div.appendChild(removal_link); } function update_file_count(newnum) { document.getElementById("numfileelements").value = newnum; document.getElementById("numitems").value = newnum; var spans = document.getElementById("filefields").getElementsByTagName("span"); for(i = 0; i < spans.length; i++) { if(spans[i].className == 'fileitotal') spans[i].innerHTML = newnum; } } var mouseX = 0; var mouseY = 0; function getMousePosition(event) { var mouseX = window.event ? window.event.clientX : event.pageX; var mouseY = window.event ? window.event.clientY : event.pageY; //document.getElementById("title").innerHTML = mouseX + ' ' + mouseY; } function mouse_coords_init() { document.onmousemove = getMousePosition; } function get_random_text() { var now = new Date(); var time = (now.getTime() - now.getMilliseconds()) / 1000; var ms = now.getMilliseconds(); var ua = navigator.userAgent; var sw = screen.width; var sh = screen.height; var rand = Math.random(); var mime = navigator.mimeTypes; var mimestring = ''; for(var i=0; i>2] >> ((3 - i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); } return str; } //////////////////////////////////////////////////////////// End SHA-1 code. function schedule_onload_action(newfunc) { var already_scheduled = window.onload; if(typeof window.onload != 'function') { window.onload = newfunc; } else { window.onload = function() { already_scheduled(); newfunc(); } } } function filechucker_init() { mouse_coords_init(); set_up_human_test(); set_row_mouseovers(); set_itemaction_highlights(); generate_new_serial_number(); reset_iframe_location(); undisable_upload_button(); start_juststatus(); update_onload_status(); run_download_timer(); } if('$PREF{do_javascript_init_at_onload}' == 'yes') schedule_onload_action(filechucker_init); document.write(""); document.write(""); $PREF{custom_js_code} `; return $js; } sub get_css { my $num = shift; # optional, for situations where you want to make separate $PREF{css}, $PREF{css1}, $PREF{css2}, etc., settings. my $css = $PREF{"css${num}"} . "\n\n" . $PREF{"css${num}_shared"} . "\n\n" . $PREF{"css${num}_$PREF{current_app_style}"} . "\n\n" . $PREF{"css${num}_shared_$PREF{current_app_style}"} . "\n\n" . $PREF{"css${num}_extra"}; $css .= qq` $PREF{"custom_css$num"} ` . $PREF{extra_header_output} . qq`\n` . qq`\n` . qq`\n`; } elsif($PREF{encodable_app_template_file} && -e $PREF{encodable_app_template_file}) { my $title = $PREF{title_for_template_file} || $title_for_titlebar || $subtitle; open(HEADERFH, "<$PREF{encodable_app_template_file}") or die "$0: couldn't open \$PREF{encodable_app_template_file} ('$PREF{encodable_app_template_file}') for reading:: $!\n"; my $infh = \*HEADERFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". flock $infh, 1; seek $infh, 0, 0; while(<$infh>) { s!%%title%%!$title!g; s#()?##g; s#()?##g; if(/^(.*?)(?:\n`; print $PREF{extra_footer_output}; open(FOOTERFH, "<$PREF{encodable_app_template_file}") or die "$0: couldn't open \$PREF{encodable_app_template_file} ('$PREF{encodable_app_template_file}') for reading:: $!\n"; my $infh = \*FOOTERFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". flock $infh, 1; seek $infh, 0, 0; my $found_token = 0; while(<$infh>) { if($found_token) { print $_; } elsif(/%%encodable_app_output%%(?:\s*-->)?(.*?)$/i) { print $1; $found_token = 1; } } close $infh or die "$0: couldn't close \$PREF{encodable_app_template_file} ('$PREF{encodable_app_template_file}') after reading:: $!\n"; } else { print $PREF{body_container_end} || qq`\n`; print $PREF{extra_footer_output}; } } sub check_image_modules { exit_with_needprivs() unless user_is_allowed_to('view_administration_page'); my $separator = qq`



\n`; start_html_output("Check Image Modules"); print qq`\n
$PREF{internal_appname_nice} uses image modules in order to enable image-based features like rotation, resizing/thumbnails, etc.  It supports 3 different methods for this: the GD Perl module, the ImageMagick Perl module, and the ImageMagick convert/identify commands; and usually only one of these is necessary.  This page will show whether your server supports these modules.  See this FAQ for more details.$separator`; unless($PREF{already_tested_for_im}) { eval { require Image::Magick; }; $PREF{imagemagick_error} = $@; } unless($PREF{already_tested_for_gd}) { eval { require GD; require GD::Simple; }; $PREF{gd_error} = $@; } foreach my $module ('ImageMagick', 'GD') { my $error = $PREF{lc($module) . "_error"}; print qq`Attempting to load the $module Perl module... `; print !$error ? qq`success.` : qq`failed.  The error was:
$error
` . qq`This means that the $module Perl module is not installed, or not installed properly, on your server.  ` . qq`This is a server error, not a $PREF{internal_appname_nice} error.  This FAQ may help, ` . qq`but you'll need to contact your hosting company if you want to get the $module Perl module installed properly.  Or, if you have ` . qq`root/Administrator access to your server, then we may be able to install it for you; the cost is typically 1 hour of labor.  ` . qq`Contact us if you'd like us to look into this.`; print $separator; } print "Attempting to locate a 'convert' command:
\n"; print "\nExecuting 'which convert': "; my $which_output = `which convert`; print $which_output && $which_output !~ /no convert in/ ? "found one or more convert commands: $which_output" : "command produced no output; no 'convert' in PATH."; print "
\n"; print "Manually checking /usr/bin/convert: " . (-e '/usr/bin/convert' ? 'exists.' : 'does not exist.') . "
\n"; print "Manually checking /usr/local/bin/convert: " . (-e '/usr/local/bin/convert' ? 'exists.' : 'does not exist.') . "
\n"; print "
\nIf a convert command was found here, you can set \$PREF{convert_command} to it, and then enable the \$PREF{try_to_use_convert_*} settings (and probably also the \$PREF{try_to_use_identify_*} settings because an identify command probably exists in the same location).\n"; #print "\nExecuting 'find / -name convert':
\n"; #print `find / -name convert -type f`; print $separator; if($PREF{im_and_gd_disabled_internally}) { print qq`Because neither the ImageMagick nor GD Perl modules are properly installed on your server, $PREF{internal_appname_nice} has internally disabled the \$PREF{try_to_use_*} prefs for them.  You may be able to use the \$PREF{try_to_use_convert_*} settings instead.  See this FAQ for more details.$separator`; } print qq`Note that this only affects the image-based features like rotation, thumbnails, etc.  It does not affect $PREF{internal_appname_nice}'s core functionality.  So $PREF{internal_appname_nice} itself will work just fine even if your server does not support these image modules.`; print qq`
\n`; finish_html_output(); } sub try_to_load_image_modules() { # TODO: find a reliable way to test whether the jpegtran and convert binaries are available. # For now, just hardcode the $PREF{(jpegtran|convert)_available} = 'yes'; and use the try_to_* # PREF to switch it off. # #if(!jpegtran_is_available() && ($PREF{try_to_use_jpegtran_for_rotation} =~ /yes/i)) #{ # die_nice qq`$PREF{internal_appname}: jpegtran is not available on your server, so you must either install it, or else disable the following setting in PREFs Section 15:

\n\$PREF{try_to_use_jpegtran_for_rotation}`; #} $PREF{jpegtran_available} = 'yes'; $PREF{convert_available} = 'yes'; $PREF{identify_available} = 'yes'; # Note: the call to *_is_available() MUST come last in these if()s, so that on # servers where the module is corrupt, we can prevent even trying to load it, # by setting the PREFs to 'no'. # my $tried_im_and_failed = 1 if(($PREF{try_to_use_imagemagick_for_rotation} =~ /yes/i || $PREF{try_to_use_imagemagick_for_resizing} =~ /yes/i || $PREF{try_to_use_imagemagick_for_humantest} =~ /yes/i) && !imagemagick_is_available()); my $tried_gd_and_failed = 1 if(($PREF{try_to_use_gd_for_rotation} =~ /yes/i || $PREF{try_to_use_gd_for_resizing} =~ /yes/i || $PREF{try_to_use_gd_for_humantest} =~ /yes/i) && !gd_is_available()); if($tried_im_and_failed && $tried_gd_and_failed) { $PREF{im_and_gd_disabled_internally} = 1; foreach my $pref (keys %PREF) { $PREF{$pref} = 'no' if $pref =~ /^try_to_use_(imagemagick|gd)/i; } } } sub jpegtran_is_available() { $PREF{jpegtran_test_command} = 'jpegtran /dev/null 2>&1'; my ($success,$msg) = enc_sys_call($PREF{jpegtran_test_command}); if($success) { $PREF{jpegtran_available} = 'yes'; $PREF{jpegtran_error} = ''; } else { $PREF{jpegtran_available} = 'no'; $PREF{jpegtran_error} = "Failed to execute jpegtran: $msg"; } return !$PREF{jpegtran_error}; } sub imagemagick_is_available() { unless($PREF{already_tested_for_im}) { $PREF{already_tested_for_im} = 1; eval { require Image::Magick; }; $PREF{imagemagick_error} = $@; $PREF{imagemagick_available} = !$PREF{imagemagick_error}; } return $PREF{imagemagick_available}; } sub gd_is_available() { unless($PREF{already_tested_for_gd}) { $PREF{already_tested_for_gd} = 1; eval { require GD; require GD::Simple; }; $PREF{gd_error} = $@; $PREF{gd_available} = !$PREF{gd_error}; } return $PREF{gd_available}; } sub do_automatic_resizing($) { my $files = shift; # hashref, formatted like this: # $$files{01}{name} = 'foo.jpg', # $$files{01}{realpath} = '/full/path/to/imagedir', # $$files{01}{force_overwrite} = 1 (optional). my @results = (); my $filecount = scalar keys %$files; if($PREF{resize_uploaded_images} =~ /yes/i || $PREF{create_resized_copies_of_uploaded_images} =~ /yes/i) { foreach my $pref (sort keys %PREF) { if($pref =~ /^create_resized_copies_(\d+)__imagemagick_size$/) { my $num = $1; foreach my $file (sort keys %$files) { if(is_image($$files{$file}{name})) { my $file_id_for_status = $filecount == 1 ? $$files{$file}{name} : "#$file ($$files{$file}{name})"; my $input_file_full = $$files{$file}{realpath} . '/' . $$files{$file}{name}; condense_slashes('leave_leading_UNC', $input_file_full); if($PREF{create_resized_copies_of_uploaded_images} =~ /yes/i) { my $smallsize = ($PREF{imagemagick_available} && $PREF{try_to_use_imagemagick_for_resizing} =~ /yes/i) || $PREF{try_to_use_convert_for_resizing} =~ /yes/i ? $PREF{"create_resized_copies_${num}__imagemagick_size"} : gd_is_available() ? $PREF{"create_resized_copies_${num}__gd_size"} : ''; if(-s $input_file_full > $PREF{"create_resized_copies_${num}__skip_if_bigger_than"}) { push @results, qq`Creating ${smallsize}px resized copy of $file_id_for_status: skipping because input file is bigger than the limit ($PREF{"create_resized_copies_${num}__skip_if_bigger_than"} bytes).`; next; } my $output_dir = $PREF{"create_resized_copies_${num}__location_type"} eq 'absolute' ? $PREF{"create_resized_copies_${num}__folder_name"} : $$files{$file}{realpath}; $output_dir .= '/' . $PREF{"create_resized_copies_${num}__folder_name"} if $PREF{"create_resized_copies_${num}__folder_name"} && $PREF{"create_resized_copies_${num}__location_type"} eq 'relative'; condense_slashes('leave_leading_UNC', $output_dir); create_dir_if_DNE($output_dir,$PREF{writable_dir_perms}); my $output_file = $PREF{"create_resized_copies_${num}__new_filename"}; my ($origname,$ext) = ($$files{$file}{name} =~ /(.+)\.(.+)/); $output_file =~ s!%%orig%%!$origname!g; $output_file =~ s!%%ext%%!$ext!g; my $output_file_full = $output_dir . '/' . $output_file; #unless($PREF{overwrite_existing_files} =~ /yes/i) if($PREF{"create_resized_copies_${num}__serialize_or_overwrite"} eq 'serialize') { $output_file_full = serialize_filename_if_file_exists($output_file_full); } # Make sure to do the skip-if-exists check before the image-dims check, because # the latter is expensive. # if(-e $output_file_full && !$$files{$file}{force_overwrite}) { push @results, qq`Creating ${smallsize}px resized copy of $file_id_for_status: skipping because output file already exists.`; next; } my ($origw,$origh) = get_image_dims($input_file_full); my ($thumbw,$thumbh) = ($smallsize =~ /^(\d*)x?(\d*)$/i); if($origw > $thumbw || $origh > $thumbh || !$origw || !$thumbw) { #resize_image($input_file_full, $output_file_full, $smallsize); resize_image___withtimeout($PREF{image_resizing_timeout}, $input_file_full, $output_file_full, $smallsize); push @results, qq`Creating ${smallsize}px resized copy of $file_id_for_status: OK.` if -e $output_file_full; push @results, qq`Creating ${smallsize}px resized copy of $file_id_for_status: failed (output file does not exist afterwards).` unless -e $output_file_full; } else { copy($input_file_full, $output_file_full) or die_nice "couldn't copy image file '$input_file_full' to thumbnail file '$output_file_full': $!\n"; push @results, qq`Creating ${smallsize}px resized copy of $file_id_for_status: OK (copied from original because original was already small enough).` if -e $output_file_full; push @results, qq`Creating ${smallsize}px resized copy of $file_id_for_status: failed (tried to copy from original because original was already small enough).` unless -e $output_file_full; } chmod $PREF{writable_file_perms}, $output_file_full; } if($PREF{resize_uploaded_images} =~ /yes/i) { next if -s $input_file_full > $PREF{resize_uploaded_images__skip_if_bigger_than}; my $smallsize = ($PREF{imagemagick_available} && $PREF{try_to_use_imagemagick_for_resizing} =~ /yes/i) || $PREF{try_to_use_convert_for_resizing} =~ /yes/i ? $PREF{resize_uploaded_images__imagemagick_size} : gd_is_available() ? $PREF{resize_uploaded_images__gd_size} : ''; my ($origw,$origh) = get_image_dims($input_file_full); my ($thumbw,$thumbh) = ($smallsize =~ /^(\d*)x?(\d*)$/i); if($origw > $thumbw || $origh > $thumbh || !$origw || !$thumbw) { #resize_image($input_file_full, $input_file_full, $smallsize); resize_image___withtimeout($PREF{image_resizing_timeout}, $input_file_full, $input_file_full, $smallsize); push @results, qq`Resizing $file_id_for_status to ${smallsize}px: OK.` if -e $input_file_full; chmod $PREF{writable_file_perms}, $input_file_full; } } } } } } } return @results; } sub get_image_dims { my $filename = shift; my ($width, $height, $xres, $yres) = (); my $decpoints = $PREF{num_decimal_points_for_image_details} =~ /^(\d+)$/ ? $1 : 2; if($PREF{identify_available} =~ /yes/i && $PREF{try_to_use_identify_for_dimensions} =~ /yes/i) { my $info = `$PREF{identify_command} -verbose "$filename"`; ($width,$height) = ($1,$2) if $info =~ /^\s*Geometry:\s*(\d+)x(\d+)/m; ($xres,$yres) = ($1,$2) if $info =~ /^\s*Resolution:\s*(\d+)x(\d+)/m; # This works, and doesn't require reading the whole image into memory so it's # quicker, but it can only get a few image attributes, not all of them. # #my $info = `$PREF{identify_command} "$filename"`; #($width,$height) = ($1,$2) if $info =~ /^$filename [A-Z]+ (\d+)x(\d+) / || $info =~ /^$filename [A-Z]+ (\d+)x(\d+)\+\d+\+\d+ /; } elsif($PREF{imagemagick_available} && $PREF{try_to_use_imagemagick_for_dimensions} =~ /yes/i) { my $image = new Image::Magick; my $x = $image->Read($filename); #die_nice "ImageMagick Read() error: $x" if "$x"; # seems to die too easily, i.e. for unknown meta tags in an otherwise OK image. ($width, $height, $xres, $yres) = $image->Get('width', 'height', 'x-resolution', 'y-resolution'); # This works, and doesn't require reading the whole image into memory so it's # quicker, but it can only get a few image attributes, not all of them. # #my $image = new Image::Magick; #my ($width, $height, $size, $format) = $image->Ping($filename); #return ($width, $height); # This works, but the output goes directly to STDOUT/STDERR instead of into @details. # #my $image = new Image::Magick; #my $x = $image->Read($filename); #die_nice "ImageMagick Read() error: $x" if "$x"; #my @details = $image->Identify(); ##die_nice (qq`Details: \n
` . join("\n

", @details)); } elsif(gd_is_available() && $PREF{try_to_use_gd_for_dimensions} =~ /yes/i) { my $image = GD::Image->new($filename); ($width,$height) = ($image->width, $image->height) if $image; } my $width_inches = $width && $xres ? $width/$xres : ''; my $height_inches = $height && $yres ? $height/$yres : ''; for($xres, $yres, $width_inches, $height_inches) { s!(\.\d{$decpoints}).+!$1!; $_ = int($_) unless $decpoints; } return ($width, $height, $xres, $yres, $width_inches, $height_inches); } sub resize_image___withtimeout { my $timeout = shift; my $ignore_other_errors = $_[0] eq 'ignore_other_errors' ? shift : ''; my $timeouts_are_errors = $_[0] eq 'timeouts_are_errors' ? shift : ''; #use Time::HiRes 'gettimeofday'; #my $starttime = gettimeofday(); if($timeout) { if(0) # eval method: (doesn't always work; for exaple with timeout set to 8, a certain image still takes 100+ seconds??) { eval { local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required alarm $timeout; resize_image(@_); alarm 0; }; if($@) { if($@ eq "alarm\n") { $timeouts_are_errors && die_nice("Timed out while trying to resize image.\n"); } # timed out. else { $ignore_other_errors || die_nice("Error while trying to resize image: $@\n"); } # propagate unexpected errors. } # else it didn't time out. } else # fork method: { my $pid = fork; defined $pid or die_nice "Could not fork: $!\n"; unless ($pid) { # child: local $SIG{ALRM} = 'DEFAULT'; alarm $timeout; resize_image(@_); alarm 0; exit; # exit the child. } waitpid $pid, 0; } } else { resize_image(@_); } #print STDERR "resize_image___withtimeout(): timeout set to $timeout; actual time was " . int(gettimeofday() - $starttime) . "\n"; } sub resize_image { my $infile = shift; my $outfile = shift; my $newW = shift; my $newH = shift; return unless -s $infile; my $geom = (); if($newW =~ /(\d*)x(\d*)!?/i) { $geom = $newW; ($newW,$newH) = ($1,$2); } else { $geom = $newW . 'x' . $newH; } die_nice "can't resize image because at least one of the dimensions must be specified (\$newW='$newW', \$newH='$newH')\n" unless ($newW =~ /[1-9]/ || $newH =~ /[1-9]/); if($PREF{convert_available} =~ /yes/i && $PREF{try_to_use_convert_for_resizing} =~ /yes/i) { printd qq`resize_image(): using convert\n`; my $cmd = $PREF{convert_resize_template}; $cmd =~ s!%%size%%!${newW}x${newH}!g; $cmd =~ s!%%infile%%!$infile!g; $cmd =~ s!%%outfile%%!$outfile!g; my ($success,$msg) = enc_sys_call($cmd); unless($success) { die_nice(qq`failed: $msg`); } } elsif($PREF{imagemagick_available} && $PREF{try_to_use_imagemagick_for_resizing} =~ /yes/i) { printd qq`resize_image(): using IM\n`; my $image = new Image::Magick; my $retval = (); $retval = $image->Read($infile); if($retval) { printd "$0: error: resize_image(): \$image->Read() returned $retval.\n"; return; } $retval = $image->Resize(geometry=>$geom, filter=>'Lanczos'); if($retval) { printd "$0: error: resize_image(): \$image->Resize() returned $retval.\n"; return; } $image->Set(quality=>95); $image->Write($outfile); } elsif(gd_is_available() && $PREF{try_to_use_gd_for_resizing} =~ /yes/i) { printd qq`resize_image(): using GD\n`; my $inputimage = GD::Image->new($infile); if(!$inputimage) { printd "$0: error: resize_image(): GD::Image->new($infile) returned null...\n"; return; } my $oldW = $inputimage->width; my $oldH = $inputimage->height; die "$0: can't resize image because a dimension is missing (\$oldW='$oldW', \$oldH='$oldH')\n" unless $oldW =~ /[1-9]/ && $oldH =~ /[1-9]/; if(!$newW) { $newW = ($newH * $oldW) / $oldH; $newW =~ s/\..*//; } if(!$newH) { $newH = ($newW * $oldH) / $oldW; $newH =~ s/\..*//; } my $outputimage = GD::Image->newTrueColor($newW,$newH); $outputimage->copyResampled($inputimage,0,0,0,0,$newW,$newH,$oldW,$oldH); open(RESIMG, ">$outfile") or die "$0: couldn't create file '$outfile': $!\n"; my $fh = \*RESIMG; # voodoo required since ancient Perls can't accept "open(my $foo_fh)". binmode $fh; if($outfile =~ /\.jpe?g$/i) { print $fh $outputimage->jpeg(95); } elsif($outfile =~ /\.png$/i) { print $fh $outputimage->png(3); } elsif($outfile =~ /\.gif$/i) { print $fh $outputimage->gif(); } else { die "$0: resize_image(): image format not supported by GD.\n"; } close $fh or die "$0: couldn't close file '$outfile' after creating it: $!\n"; } else { die_nice "no image-resizing library available."; } my $errormsg = qq`Output file does not exist after resizing; try adjusting or disabling \$PREF{image_resizing_timeout} and other image/video timeouts.`; encdebug $errormsg; exit_with_error $errormsg if (!-f $outfile) && ($PREF{ignore_image_resizing_errors} !~ /yes/i); } sub image_orientation_is_resettable($) { my $filename = shift; return 0 unless $filename =~ /\.(jpg|jpe|jpeg)$/i; return command_is_available("exiftool") || command_is_available("exifautotran"); } sub reset_image_orientation($) { my $filename_with_path = shift; exit_with_error qq`file doesn't exist: '$filename_with_path'.` unless -e $filename_with_path; if(command_is_available("exiftool")) { system("exiftool -n -P -Orientation=1 \"$filename_with_path\""); } elsif(command_is_available("exifautotran")) { system("exifautotran \"$filename_with_path\""); } else { exit_with_error qq`no image-orientation-resetting program available.`; } chmod $PREF{writable_file_perms}, $filename_with_path; } # Prereqs: the passed command must be *just* a command; it must # not contain any arguments. (But because of systems with dumb # setups (i.e. Windows), the command may still contain spaces.) # The reason is that we'll actually execute whatever command is # passed in, so including arguments on it will make the command # actually do stuff, as opposed to just executing and quitting, # as most commands will do when no arg is passed. # sub command_is_available($) { my $cmd = shift; my $retval = $^O =~ /MSWin/ ? system("$cmd >NUL 2>NUL") : system("$cmd >/dev/null 2>&1"); my $failed = 0; $failed = 1 if $? == -1; #$failed = 1 if $? & 127; $failed = 1 if $retval != 0; return $failed ? 0 : 1; } sub it_is_time_for_periodic_task($) { # Usage: create a task in your prefs like this: # # $PREF{periodic_tasks}{01}{name} = 'some_task'; # $PREF{periodic_tasks}{01}{enabled} = 'yes'; # $PREF{periodic_tasks}{01}{period} = 60*10; # in seconds. # # Then in the code, do this: # # do_the_some_task_function() if it_is_time_for_periodic_task('some_task'); my $taskname = shift; my $period = ''; my $enabled = 0; foreach my $i (sort { $a <=> $b } keys %{$PREF{periodic_tasks}}) { if($PREF{periodic_tasks}{$i}{name} eq $taskname) { $period = $PREF{periodic_tasks}{$i}{period}; $enabled = $PREF{periodic_tasks}{$i}{enabled} =~ /yes/i; } } return unless $taskname && $enabled; exit_with_error qq`Error: missing or invalid period setting for periodic task "$taskname". You must create a \$PREF{periodic_tasks}{NN}{period} setting, and set it to a number, indicating the number of seconds to wait between executions of this particular task.` unless $period =~ /^\d+$/; my $taskslist = $PREF{datadir} . "/periodic_tasks_log.cgi"; create_file_if_DNE($taskslist, $PREF{writable_file_perms}); open(my $iofh, "+<$taskslist") or die_nice qq`couldn't open taskslist file '$taskslist' for I/O: $!`; flock $iofh, 2; seek $iofh, 0, 0; my @contents = (); my $time_of_last_execution = ''; while(<$iofh>) { chomp; if(/^(\d+):$taskname$/) { $time_of_last_execution = $1; } else { push @contents, "$_\n"; } } my $it_is_time = 0; if(!$time_of_last_execution) { $it_is_time = 1; push (@contents, offsettime() . ":" . $taskname . "\n"); seek $iofh, 0, 0; print $iofh @contents; truncate $iofh, tell $iofh; encdebug qq`This task has never been executed before, so it IS time for task $taskname.`; } elsif((offsettime() - $time_of_last_execution) > $period) { $it_is_time = 1; push (@contents, offsettime() . ":" . $taskname . "\n"); seek $iofh, 0, 0; print $iofh @contents; truncate $iofh, tell $iofh; encdebug qq`offsettime() - $time_of_last_execution = ` . (offsettime() - $time_of_last_execution) . qq`, which exceeds the period of $period, so it IS time for task $taskname.`; } else { encdebug qq`offsettime() - $time_of_last_execution = ` . (offsettime() - $time_of_last_execution) . qq`, which is less than $period, so it is not time for task $taskname.`; } close $iofh or die_nice qq`couldn't close taskslist file '$taskslist' after I/O: $!`; return $it_is_time; } $PREF{regerr} = sub { exit_with_error qq`Registration error: $PREF{internal_appname_nice} must be installed on the registered domain, as stated in your license email.  If you'd like to change your registered domain, please contact us and include your old and new domains along with your license number.`; }; sub cat($) { my $file = shift; open(my $infh, $file) or die_nice "couldn't open file '$file' for reading: $!\n"; flock $infh, 1; seek $infh, 0, 0; my $contents = ''; while(<$infh>) { $contents .= $_; } close $infh or die_nice "couldn't close file '$file' after reading: $!\n"; return $contents; } ############################################################################## ### Login-FC: ################################################################ ############################################################################## sub do_login { if($PREF{internal_appname} eq 'userbase') { $PREF{admin_is_logged_in} = 0; $PREF{member_is_logged_in} = 0; # Get the user's inputted username and password: my $input_username = param($PREF{userbase_user_fieldname}) || ($PREF{on_failed_login_redirect_to} ? enc_redirect($PREF{on_failed_login_redirect_to}) : exit_with_error($TEXT{empty_username_error})); my $input_password = param($PREF{userbase_pass_fieldname}) || ($PREF{on_failed_login_redirect_to} ? enc_redirect($PREF{on_failed_login_redirect_to}) : exit_with_error($TEXT{empty_password_error})); my $ref = param("ref"); my ($expiry) = (); $input_username = lc($input_username) if $PREF{make_usernames_case_insensitive} =~ /yes/i && $PREF{make_case_insensitive_usernames_uppercase} !~ /yes/i; $input_username = uc($input_username) if $PREF{make_usernames_case_insensitive} =~ /yes/i && $PREF{make_case_insensitive_usernames_uppercase} =~ /yes/i; if(param("remember_me") eq "on") { if($PREF{num_days_rememberme_cookie_lasts} !~ /^\d+$/) { $PREF{num_days_rememberme_cookie_lasts} = 7; } $expiry = "+$PREF{num_days_rememberme_cookie_lasts}d"; } my $restrict_ip = ( ($PREF{enable_ip_address_restriction} =~ /yes/i && param("restrict_ip") =~ /on/i) || ($PREF{force_ip_address_restriction} =~ /yes/i) ) ? 1 : 0; $restrict_ip = 0 if $ENV{HTTP_USER_AGENT} =~ /AOL/ && $PREF{disable_ip_restriction_for_AOL_users} =~ /yes/i; # Get the crypted version of the input password: check_username_for_sql_safeness($input_username); my $salt = enc_sql_select("SELECT `salt` FROM `$PREF{user_table}` WHERE `username` = '$input_username';"); # TODO: remove this if/else, and assume that !$salt is an error condition; but # not until around mid-2007 to give clients time to get switched over. # my ($crypted_input_password, $update_this_account_to_new_pw_system) = (); if(!$salt) # old version of UB that's pre-salt, so re-create the password hash and update it in the DB. { $crypted_input_password = md5_hex($input_password); $update_this_account_to_new_pw_system = 1; } else { $crypted_input_password = salt_and_crypt_password($input_password, $salt); } my $account_locked = enc_sql_select("SELECT `acct_locked` FROM `$PREF{user_table}` WHERE `username` = '$input_username';"); if($account_locked) { my $lock_expired = ! account_exceeds_failed_login_limit($input_username); if($PREF{lock_expires_automatically} =~ /yes/i && $lock_expired) { my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `acct_locked` = FALSE WHERE `username` = '$input_username';"); die_nice("Error: do_login(input_username='$input_username'): SQL returned '$success' instead of '1' while updating acct_locked.") unless $success == 1; } else { sleep $PREF{num_seconds_to_sleep_on_failed_login}; enc_redirect("$PREF{login_url}?phase=eacctlck"); } } my $go = ''; if(account_exists($input_username, $crypted_input_password, 'new_login')) { if(enc_sql_select("SELECT `acct_disabled` FROM `$PREF{user_table}` WHERE `username` = '$input_username';")) { enc_redirect("$PREF{login_url}?phase=eacctdis"); } $PREF{member_is_logged_in} = 1; # technically true, but can still be revoked by later tests. $PREF{logged_in_userid} = my $userid = get_user_id($input_username); enc_redirect("$PREF{login_url}?phase=eacctpndvrf&uid=$userid&user=$input_username") if account_is_pending_email_verification($PREF{logged_in_userid}); enc_redirect("$PREF{login_url}?phase=eacctpndpmt") if account_is_pending_payment($PREF{logged_in_userid}); enc_redirect("$PREF{login_url}?phase=eacctpndadm") if account_is_pending_admin_approval($PREF{logged_in_userid}); my $session_id = create_new_session_id($input_username, $crypted_input_password); if(my $shared_session_id = check_for_multiple_logins($userid, $input_username)) { $session_id = $shared_session_id; } update_loggedin_count_for_this_user($userid); set_cookie($PREF{site_session_cookie}, $session_id, $expiry); if($update_this_account_to_new_pw_system) { my $salt = create_random_salt($PREF{salt_length}); my $new_crypted_password = salt_and_crypt_password($input_password, $salt); my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `password` = '$new_crypted_password', `salt` = '$salt' WHERE `id` = $userid;"); die_nice("Error: do_login(): SQL returned '$success' instead of '1' while updating pw and creating salt.") unless $success == 1; $crypted_input_password = $new_crypted_password; } unless(enc_sql_select("SELECT `failed_logins` FROM `$PREF{user_table}` WHERE `id` = $userid;") eq '') { my $statement = "UPDATE `$PREF{user_table}` SET `failed_logins` = NULL WHERE `id` = $userid;"; my $success = enc_sql_update($statement); die_nice("Error: do_login(id='$userid'): SQL returned '$success' instead of '1' while updating failed_logins. SQL was: [[$statement]]") unless $success == 1; } log_user_into_db($userid, $session_id, offsettime(), $restrict_ip); update_logins_table($userid, $session_id); $PREF{admin_is_logged_in} = is_admin($PREF{logged_in_userid}); send_login_notification_email($userid, $input_username); my %REDIRS = (); if(force_pw_change($userid)) { $REDIRS{force_pw_change} = $PREF{login_url_qsready} . "action=edituser&id=$userid"; } if( (my $return = param('loginreturn')) && $PREF{loginreturn}{ param('loginreturn') } ) { $REDIRS{loginreturn} = $PREF{loginreturn}{$return}; } foreach my $pref (sort keys %PREF) { if($pref =~ /^on_(.+?)_login_redirect_to$/i) { my $group = $1; if( user_is_member_of_group($PREF{logged_in_userid}, $group) && $PREF{$pref} && !($PREF{admin_is_logged_in} && ($group eq $PREF{member_group_name})) ) { $REDIRS{redirect} = $PREF{$pref}; $REDIRS{redirect} =~ s/%%username%%/$input_username/g; #last; # Actually, don't skip out after the first one; process them all so only the last one applies. } } } $REDIRS{referer} = determine_default_login_destination($ref); foreach my $redir_type (split(/\s*,\s*/, "force_pw_change, $PREF{login_destination_precedence_list}")) { if($REDIRS{$redir_type}) { $go = $REDIRS{$redir_type}; last; } } enc_redirect($go); } # Else they tried to log in but failed. else { # Be sure that we do the sleep before the email, so that any # potential email errors don't cause us to abort early thereby # skipping the sleep and possibly giving away the fact that the # login failed. # sleep $PREF{num_seconds_to_sleep_on_failed_login}; my $account_locked = account_exceeds_failed_login_limit($input_username, 'increment'); if($account_locked) { unless(enc_sql_select("SELECT `acct_locked` FROM `$PREF{user_table}` WHERE `username` = '$input_username'")) { my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `acct_locked` = TRUE WHERE `username` = '$input_username';"); die_nice("Error: do_login(input_username='$input_username'): SQL returned '$success' instead of '1' while updating acct_locked.") unless $success == 1; } } update_failed_logins_table($input_username, $input_password); email_failed_logins_to_webmaster($input_username, $input_password); if($PREF{on_failed_login_redirect_to}) { $go = $PREF{on_failed_login_redirect_to}; if($account_locked) { $go .= $go =~ /\?/ ? '&account_locked=1' : '?account_locked=1'; } } else { if($account_locked) { $go = "$PREF{login_url}?phase=eacctlck"; } else { $go = "$PREF{login_url}?phase=ebadauth"; } } enc_redirect($go); } } else { my $whence = ($qs =~ /(?:^|&)whence=(.+)/)[0]; $whence = '' if $whence =~ /(log(ged)?out|smsg=|kmsg=)/i; if($ENV{REQUEST_METHOD} =~ /post/i) { smsg_redirect('failedlogin') if param('password') !~ /\S/; my $hashed_password = md5_hex(param('password')); my $expiry = param('persist') eq 'on' ? "+$PREF{num_days_login_lasts}d" : ''; smsg_redirect('failedlogin') unless ($PREF{all_admin_password_hashes}{$hashed_password} || $PREF{all_member_password_hashes}{$hashed_password}); set_cookie($PREF{non_userbase_login_cookie}, $hashed_password, $expiry); my $ref = param('ref'); $ref = '' if $ref =~ /(log(ged)?out|smsg=|kmsg=)/i; $whence =~ s!_THEQS_!?!g; $whence =~ s!_ANAMP_!&!g; enc_redirect($ref || $whence || $PREF{here}); } else { if($PREF{all_admin_password_hashes}) { my $action = $PREF{internal_appname} eq 'visitorlog' ? 'action=vllogin' : 'action=login'; $action .= $PREF{default_url_vars}; $action .= "&whence=$whence" if $whence; start_html_output($PREF{subtitle___login}); print qq`
` . qq`\n

$TEXT{Enter_the_password}

\n` . ($PREF{show_keep_me_logged_in_checkbox_on_login_page} !~ /yes/i ? '' : qq`\n

`) . qq`\n

\n
\n`; print $TEXT{Forgot_password_}; finish_html_output(); } else { $TEXT{password_not_set} =~ s!%%newpw_link%%!$PREF{here_login_qsready}newpw!g; exit_with_output($TEXT{password_not_set}); } } } } sub check_if_logged_in() { my %cookies = get_cookies(); ($PREF{admin_is_logged_in}, $PREF{member_is_logged_in}, $PREF{logged_in_username}, $PREF{logged_in_realname}, $PREF{logged_in_email}, $PREF{logged_in_userid}) = (0,0,'','','',''); if($PREF{integrate_with_existing_login_system} =~ /yes/i && $PREF{integrate_with_userbase} !~ /yes/i) { if($PREF{enable_username_from_apache_auth} =~ /yes/i) { if(my $username = $ENV{REMOTE_USER}) { $PREF{logged_in_username} = $username; $PREF{member_is_logged_in} = 1; $PREF{admin_is_logged_in} = $PREF{admin_usernames_list} =~ /(?:^|,)\s*$username\s*(?:,|$)/; $PREF{logged_in_userid} = $PREF{admin_is_logged_in} ? -3 : -2; } } elsif($PREF{enable_username_from_cookie} =~ /yes/i) { my $username = ''; if(exists($cookies{$PREF{username_cookie_name}}) && ($username = $cookies{$PREF{username_cookie_name}}->value)) { $PREF{logged_in_username} = $username; $PREF{member_is_logged_in} = 1; $PREF{admin_is_logged_in} = $PREF{admin_usernames_list} =~ /(?:^|,)\s*$username\s*(?:,|$)/; $PREF{logged_in_userid} = $PREF{admin_is_logged_in} ? -3 : -2; } } elsif($PREF{enable_username_from_environment_variable} =~ /yes/i) { my $username = ''; if(exists($ENV{$PREF{username_envvar_name}}) && ($username = $ENV{$PREF{username_envvar_name}})) { $PREF{logged_in_username} = $username; $PREF{member_is_logged_in} = 1; $PREF{admin_is_logged_in} = $PREF{admin_usernames_list} =~ /(?:^|,)\s*$username\s*(?:,|$)/; $PREF{logged_in_userid} = $PREF{admin_is_logged_in} ? -3 : -2; } } elsif($PREF{enable_username_from_php_session} =~ /yes/i) { my $username = ''; if(exists $ENV{PHP_ENC_USERNAME}) # must use if(exists $foo) rather than just if($foo) so that we properly unset the variable when a user has logged out; otherwise we'd just pull it from our php-var-cache even though the user had already logged out. { $username = $ENV{PHP_ENC_USERNAME}; save_php_var_to_cache('username',$username); } else # we were POSTed to? { $username = get_php_var_from_cache('username'); } if($username) { $PREF{logged_in_username} = $username; $PREF{member_is_logged_in} = 1; $PREF{admin_is_logged_in} = $PREF{admin_usernames_list} =~ /(?:^|,)\s*$username\s*(?:,|$)/; $PREF{logged_in_userid} = $PREF{admin_is_logged_in} ? -3 : -2; } } else { exit_with_error qq`To use \$PREF{integrate_with_existing_login_system}, you must also enable either \$PREF{enable_username_from_cookie}, \$PREF{enable_username_from_environment_variable}, \$PREF{enable_username_from_php_session}, or \$PREF{enable_username_from_apache_auth}.`; } } elsif($PREF{integrate_with_userbase_method_b} =~ /yes/i && $PREF{path_to_userbase}) { # Won't work on many IIS configurations where executing external commands # from within IIS is prevented by user account policy. But the eval method # in the following block works on IIS. # my $userbase_output = `export QUERY_STRING="action=chklogin" && export SCRIPT_NAME="$PREF{userbase_local_scriptname}" && "$PREF{path_to_userbase}" 2>&1`; if($userbase_output !~ /\w+/) { # Use "package" to create a separate namespace for this block. This is because we're going to use # "eval" as a way to execute another Perl script, but we only want that script's output; we don't # want its globals and subroutines being imported into our namespace. In particular we don't want # functions such as start_html_output() "overwriting" our own copy of that function in the main # namespace. # package CALLTOUBFORLOGINCHECK; # Don't die/die_nice anywhere in here; we want to let the next if() check whether $userbase_output # is valid and then print a more useful error if not. # my $file = $PREF{path_to_userbase}; my $file_contents = (); open(IN,"<$file"); flock IN, 1; seek IN, 0, 0; while() { $file_contents .= $_; } close IN; $file_contents =~ /(.*)/s; $file_contents = $1; # cheap untaint since this is our own file. my ($real_qs, $real_sn) = ($ENV{QUERY_STRING}, $ENV{SCRIPT_NAME}); ($ENV{QUERY_STRING}, $ENV{SCRIPT_NAME}) = ("action=chklogin&print=false", $PREF{userbase_local_scriptname}); $userbase_output = eval $file_contents; ($ENV{QUERY_STRING}, $ENV{SCRIPT_NAME}) = ($real_qs, $real_sn); } if($userbase_output =~ /admin=(\d):::::member=(\d):::::username=(.*?):::::userid=(\d*?):::::group_memberships=(.*?):::::realname=(.*?):::::email=(.*?):::::(.*)/s) { ($PREF{admin_is_logged_in},$PREF{member_is_logged_in},$PREF{logged_in_username},$PREF{logged_in_userid},$PREF{logged_in_group_memberships},$PREF{logged_in_realname},$PREF{logged_in_email}) = ($1,$2,$3,$4,$5,$6,$7); $PREF{ub_var_username} = $PREF{logged_in_username}; $PREF{ub_var_userid} = $PREF{logged_in_userid}; $PREF{ub_var_realname} = $PREF{logged_in_realname}; $PREF{ub_var_email} = $PREF{logged_in_email}; if(my $other_ub_vars = $8) { while($other_ub_vars =~ /(.+?)=(.+?):::::/g) { $PREF{"ub_var_$1"} = $2; $PREF{"logged_in_$1"} = $2 unless $PREF{"logged_in_$1"}; } } } else { exit_with_error("$PREF{internal_appname}: check_if_logged_in(): integrate_with_userbase_method_b failed: UserBase output was: " . ($userbase_output ? 'null' : "[[ $userbase_output ]] ") . qq`

\n\nMake sure you've got UserBase itself running properly before trying this.` ); } } elsif(userbase_available()) { if(my $session_id = get_cookie($PREF{site_session_cookie})) { check_sessionid_for_sql_safeness($session_id); my ($username,$realname,$email,$id,$ip) = enc_sql_select("SELECT username,name,email,id,ip FROM `$PREF{user_table}` WHERE `mrsession` = '$session_id';"); if($username && $id && !account_is_pending($id)) { if(enc_sql_select("SELECT `acct_disabled` FROM `$PREF{user_table}` WHERE `id` = '$id';")) { enc_redirect("$PREF{login_url}?phase=eacctdis"); } my $numusers = enc_sql_select("SELECT `numusers` FROM `$PREF{user_table}` WHERE `id` = '$id';"); if(($numusers > 1) && $PREF{prevent_multiple_simultaneous_logons_per_username} =~ /yes/i) { # If there's already >1 user logged in under this username (for example, because we # didn't enable prevent_multiple_simultaneous_logons_per_username until after some # usage had already occurred), then we can't really do a one-by-one check to try and # decide which logins to kill, so kill them all. log_user_out_of_db($username, 'force'); enc_redirect("$PREF{login_url}?phase=emultkicked"); } # The IP check MUST come *after* the multiple_simultaneous_logons check, because if the # multiple_simultaneous_logons check fails, we need to take an action in response (namely # logging all the users out of the db) -- but if the IP check also fails (which is likely # to happen when the multiple_simultaneous_logons check fails) then we simply return, so # the multiple_simultaneous_logons check would never get a chance to do its db-logouts # if the IP check came first (and failed). # if(($PREF{enable_ip_address_restriction} =~ /yes/i && $ip) || ($PREF{force_ip_address_restriction} =~ /yes/i)) { enc_redirect("$PREF{login_url}?phase=eipmismatch") unless $ip eq $PREF{ip}; } $PREF{logged_in_username} = $username; $PREF{logged_in_realname} = $realname; $PREF{logged_in_email} = $email; $PREF{logged_in_email} = $PREF{logged_in_username} if ($PREF{logged_in_email} !~ /.+\@.+\..+/ && $PREF{logged_in_username} =~ /.+\@.+\..+/); $PREF{logged_in_userid} = $id; $PREF{member_is_logged_in} = 1; if(is_admin($PREF{logged_in_userid})) { $PREF{admin_is_logged_in} = 1; } $PREF{ub_var_username} = $PREF{logged_in_username}; $PREF{ub_var_userid} = $PREF{logged_in_userid}; $PREF{ub_var_realname} = $PREF{logged_in_realname}; $PREF{ub_var_email} = $PREF{logged_in_email}; foreach my $customfield (get_custom_userbase_field_names($PREF{user_table})) { my $value = enc_sql_select("SELECT `$customfield` FROM `$PREF{user_table}` WHERE `id` = '$PREF{logged_in_userid}'"); $PREF{"ub_var_$customfield"} = $value; $PREF{"logged_in_$customfield"} = $value unless $PREF{"logged_in_$customfield"}; } # This depends on $PREF{logged_in_userid}, so must come after that is set; but since # this is an acct-disabled check, it should still come *before* we officially mark the # user as logged in (i.e. before we update the database). # if(user_is_part_of_a_subgroup($id) && $PREF{subgroup_owned_users_get_disabled_when_their_manager_does} =~ /yes/i) { die_unless_numeric($PREF{subgroup_manager_userid}, 'subgroup manager user ID'); if(enc_sql_select("SELECT `acct_disabled` FROM `$PREF{user_table}` WHERE `id` = '$PREF{subgroup_manager_userid}';")) { enc_redirect("$PREF{login_url}?phase=eacctdis"); } } check_and_update_login_session($PREF{logged_in_userid}); if(force_pw_change($PREF{logged_in_userid})) { if($PREF{internal_appname} eq 'userbase') { if($qs !~ /^(logout|logoutall|action=commitedituser)$/) { print_user_form('edit', $PREF{logged_in_userid}); exit; } } else { enc_redirect("$PREF{login_url_qsready}action=edituser&id=$PREF{logged_in_userid}"); } } } } } elsif($PREF{all_admin_password_hashes} || $PREF{all_member_password_hashes}) { my $hashed_password_in_cookie = get_cookie($PREF{non_userbase_login_cookie}); if($hashed_password_in_cookie && $PREF{all_admin_password_hashes}{$hashed_password_in_cookie}) { ($PREF{admin_is_logged_in}, $PREF{member_is_logged_in}, $PREF{logged_in_username}, $PREF{logged_in_userid}) = (1, 1, '', -3); } elsif($hashed_password_in_cookie && $PREF{all_member_password_hashes}{$hashed_password_in_cookie}) { ($PREF{admin_is_logged_in}, $PREF{member_is_logged_in}, $PREF{logged_in_username}, $PREF{logged_in_userid}) = (0, 1, '', -2); } # TODO: explicitly set 0/0/''/-1 for public? } } sub check_and_update_login_session($) { my $userid = shift; my $my_session_id = get_cookie($PREF{site_session_cookie}); my $session_id_in_db = enc_sql_select("SELECT `mrsession` FROM `$PREF{user_table}` WHERE `id` = $userid;"); my $login_time = enc_sql_select("SELECT `loggedin` FROM `$PREF{user_table}` WHERE `id` = $userid;"); #if( ($my_session_id == $session_id_in_db) && ($login_time =~ /[1-9]/ && !login_session_expired($login_time)) ) if( ($my_session_id == $session_id_in_db) && (!login_session_expired($login_time)) ) { update_loggedin_time($userid, $my_session_id, offsettime()); } else { do_logout() if $PREF{idle_timeout} > 0; } } sub update_loggedin_time { my ($userid, $my_session_id, $newtime) = @_; die_unless_numeric($userid,'userid'); die_unless_numeric($newtime,'newtime'); check_sessionid_for_sql_safeness($my_session_id); my $value_is_unchanged = $newtime == enc_sql_select("SELECT `loggedin` FROM `$PREF{user_table}` WHERE `id` = $userid AND `mrsession` = '$my_session_id'"); unless($value_is_unchanged) # prevent the 0E0 error, which isn't actually an error in the case that the value was unchanged. { my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `loggedin` = $newtime WHERE `id` = $userid AND `mrsession` = '$my_session_id'"); die_nice("Error: update_loggedin_time('$userid', '$my_session_id', '$newtime'): SQL returned '$success' instead of '1' while updating loggedin.") unless $success == 1; } if(db_column_exists('lastactive', $PREF{user_table})) # for backwards compatibility with older versions of apps. { my $value_is_unchanged = $newtime == enc_sql_select("SELECT `lastactive` FROM `$PREF{user_table}` WHERE `id` = $userid AND `mrsession` = '$my_session_id'"); unless($value_is_unchanged) # prevent the 0E0 error, which isn't actually an error in the case that the value was unchanged. { my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `lastactive` = $newtime WHERE `id` = $userid AND `mrsession` = '$my_session_id'"); die_nice("Error: update_loggedin_time('$userid', '$my_session_id', '$newtime'): SQL returned '$success' instead of '1' while updating lastactive.") unless $success == 1; } } } sub login_session_expired($) { my $loggedin_time = shift; return ( ( ($PREF{idle_timeout} > 0) && (offsettime() - $loggedin_time > $PREF{idle_timeout}) ) || ( ($PREF{num_days_rememberme_cookie_lasts} > 0) && ( offsettime() > ($loggedin_time + ($PREF{num_days_rememberme_cookie_lasts} * 86400)) ) ) ); } sub log_user_out_of_db { my ($username, $my_session_id) = @_; check_username_for_sql_safeness($username); check_sessionid_for_sql_safeness($my_session_id) unless $my_session_id eq 'force'; # It's possible (and probably not particularly uncommon) that a user logs in at one location, then leaves # that location and his session goes idle, and then he logs in at another location with the same account. In # that case, a call to log_user_out_of_db() from the first location should not actually do the db logout, # because the session does not belong to him anymore. But note that this is not an error condition, so we # should just silently return. # my $session_id_in_db = enc_sql_select("SELECT `mrsession` FROM `$PREF{user_table}` WHERE LOWER(`username`) = LOWER('$username');"); if(($my_session_id == $session_id_in_db) || ($my_session_id eq 'force')) { my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `loggedin` = 0 WHERE LOWER(`username`) = LOWER('$username');"); die_nice("Error: log_user_out_of_db('$username', '$my_session_id'): SQL returned '$success' instead of '1' while setting loggedin to zero.") unless $success == 1; $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `mrsession` = '' WHERE LOWER(`username`) = LOWER('$username');"); die_nice("Error: log_user_out_of_db('$username', '$my_session_id'): SQL returned '$success' instead of '1' while setting mrsession to null.") unless $success == 1; my $numusers = enc_sql_select("SELECT `numusers` FROM `$PREF{user_table}` WHERE LOWER(`username`) = LOWER('$username');"); if($numusers) # this check only required because of crappy old MySQL versions that fail to ever set numusers properly in the DB (??). { $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `numusers` = 0 WHERE LOWER(`username`) = LOWER('$username');"); die_nice("Error: log_user_out_of_db('$username', '$my_session_id'): SQL returned '$success' instead of '1' while setting numusers to zero.") unless $success == 1; } } } sub do_logout { if($PREF{internal_appname} eq 'userbase') { my $force_logout_all = shift; $force_logout_all = $force_logout_all eq 'all' ? 1 : 0; if($PREF{we_are_virtual}) { print_http_headers(); $PREF{forced_logout_link} =~ s/%%logout_url%%/$PREF{logout_url}/g; print $PREF{forced_logout_link}; exit; } else { my $ref = $ENV{HTTP_REFERER}; if($ref) { # Remove the "logout" from the referrer, otherwise we'll get stuck # in an infinite logout loop with this Location: call. $ref =~ s/\?.*log(ged)?out.*//; $ref = '' if $ref =~ /(smsg=|kmsg=)/i; # don't redirect to any static/keyed messages that probably no longer apply now that the login state has changed. } # Delete the login cookie regardless of $PREF{member_is_logged_in}, because # it may be that the user has the cookie but it's invalid (expired, wrong IP, # etc) in which case we didn't set $PREF{member_is_logged_in}. In that case, # we still want to allow the user to force a logout (i.e. delete his cookie). # delete_login_cookie(); my $whence = ''; if($PREF{member_is_logged_in}) { if($PREF{prevent_multiple_simultaneous_logons_per_username} =~ /yes/i || $force_logout_all) { log_user_out_of_db($PREF{logged_in_username}, get_cookie($PREF{site_session_cookie})); } else { die_unless_numeric($PREF{logged_in_userid}, 'logged_in_userid'); my $numusers = enc_sql_select("SELECT `numusers` FROM `$PREF{user_table}` WHERE `id` = '$PREF{logged_in_userid}';"); if($numusers > 1) { my $success = enc_sql_update("UPDATE `$PREF{user_table}` SET `numusers`=GREATEST((`numusers`-1),0) WHERE `id` = '$PREF{logged_in_userid}';"); die_nice("Error: do_logout(): SQL returned '$success' instead of '1' while decrementing numusers column.") unless $success == 1; } else { log_user_out_of_db($PREF{logged_in_username}, get_cookie($PREF{site_session_cookie})); } } if($PREF{admin_is_logged_in} && $PREF{on_admin_logout_redirect_to}) { $PREF{on_admin_logout_redirect_to} =~ s/%%username%%/$PREF{logged_in_username}/g; enc_redirect($PREF{on_admin_logout_redirect_to}); } elsif($PREF{member_is_logged_in} && !$PREF{admin_is_logged_in} && $PREF{on_member_logout_redirect_to}) # need the !admin because admins are members too. { $PREF{on_member_logout_redirect_to} =~ s/%%username%%/$PREF{logged_in_username}/g; enc_redirect($PREF{on_member_logout_redirect_to}); } else { # After logging out, return to the page we were on. $whence = $ref; } } else { $whence = $ref; } $whence =~ s!^https?://$ENV{HTTP_HOST}!!; # some servers don't like extra "http://"s in URLs. enc_urlencode($whence); $whence = '' if $PREF{server_bug_prohibits_use_of_whence} =~ /yes/i; enc_redirect("$PREF{login_url_qsready}action=loggedout&whence=$whence"); } } else { my $go = ''; if($PREF{integrate_with_userbase} =~ /yes/i || $PREF{integrate_with_userbase_method_b} =~ /yes/i || $PREF{integrate_with_existing_login_system} =~ /yes/i) { $go = $PREF{logout_url}; } else { set_cookie($PREF{non_userbase_login_cookie}, 'blank', '-1d'); my $whence = $ENV{HTTP_REFERER}; $whence =~ s!^https?://$ENV{HTTP_HOST}!!; # some servers don't like extra "http://"s in URLs. $whence =~ s/\?logout$//; # Remove "logout" from the ref so we don't get stuck in an infinite loop. $go = "$PREF{here_login_qsready}action=loggedout&whence=$whence"; } if($PREF{we_are_virtual}) { print_http_headers(); if($PREF{use_javascript_redirect_when_necessary} !~ /no/i) { print qq`\n\n`; } else { $PREF{forced_logout_link} =~ s/%%logout_url%%/$go/g; print $PREF{forced_logout_link}; } exit; } else { enc_redirect($go); } } } sub delete_login_cookie { set_cookie($PREF{site_session_cookie}, 0, '-1M'); } sub show_loggedout_page { my $ref = shift; enc_urldecode($ref); $ref = '' if $ref =~ /(log(ged)?out|smsg=|kmsg=|phase=)/i; my $message = $PREF{loggedout_page_template__no_referer}; if($ref) { $message = $PREF{loggedout_page_template__with_referer}; $message =~ s/%%ref%%/$ref/g; } exit_with_success($message); } sub get_logout_url { return $PREF{integrate_with_userbase} =~ /yes/i || $PREF{integrate_with_userbase_method_b} =~ /yes/i || $PREF{integrate_with_existing_login_system} =~ /yes/i ? $PREF{logout_url} : ($ENV{SCRIPT_NAME} . ($ENV{SCRIPT_NAME} =~ /\?/ ? '&' : '?') . "logout"); } sub get_login_url { return $PREF{integrate_with_userbase} =~ /yes/i || $PREF{integrate_with_userbase_method_b} =~ /yes/i || $PREF{integrate_with_existing_login_system} =~ /yes/i ? $PREF{login_url} : "$PREF{here_login_qsready}action=login"; } # This function must do a case-sensitive lookup (i.e., do NOT use LOWER()) because # FC's userdirs are case-sensitive. So whatever case is used when a username is # created is the case that must always be used when logging in with it. # UPDATE: MySQL's equal operator (=) is case-insensitive, absurdly. To force it # to act case-sensitively, you must use the BINARY function, as in: # ... WHERE BINARY `username` = '$username' # sub account_exists($$$) { #printd "account_exists('$_[0]', '$_[1]', '$_[2]')\n"; my $user = shift; my $pass = shift; my $third_arg = shift; check_username_for_sql_safeness($user); check_hashedpw_for_sql_safeness($pass); my $BINARY = $PREF{make_usernames_case_insensitive} =~ /yes/i ? '' : 'BINARY'; my $count = (); if($third_arg eq 'new_login') { $count = enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE $BINARY `username` = '$user' AND `password` = '$pass'"); } else { die_unless_numeric($third_arg,'userid'); $count = enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE $BINARY `username` = '$user' AND `password` = '$pass' AND `id` = $third_arg"); } if($count == 1) { return 1; } elsif($count > 1) { die_nice("$0: account_exists('$user', '$pass', '$third_arg'): error: duplicate records ($count total) for this user!\n"); } else { return 0; } } sub account_is_pending { return 0 if !userbase_available(); my $userid = shift; die_unless_numeric($userid, "user ID in account_is_pending()"); return (account_is_pending_email_verification($userid) || account_is_pending_admin_approval($userid) || account_is_pending_payment($userid)); } sub account_is_pending_email_verification { my $userid = shift; return ($PREF{require_email_verification_for_new_signups} =~ /yes/i) && account_is_pending_email_verification_in_db($userid); } sub account_is_pending_email_verification_in_db { my $userid = shift; die_unless_numeric($userid, "user ID in account_is_pending_email_verification_in_db()"); return enc_sql_select("SELECT `pending_email_verification` FROM `$PREF{user_table}` WHERE `id` = $userid"); } sub account_has_completed_email_verification { my $userid = shift; die_unless_numeric($userid, "user ID in account_has_completed_email_verification()"); return enc_sql_select("SELECT `completed_email_verification` FROM `$PREF{user_table}` WHERE `id` = $userid"); } sub account_is_pending_admin_approval { my $userid = shift; return ($PREF{require_admin_approval_for_new_signups} =~ /yes/i) && account_is_pending_admin_approval_in_db($userid); } sub account_is_pending_admin_approval_in_db { my $userid = shift; die_unless_numeric($userid, "user ID in account_is_pending_admin_approval_in_db()"); return enc_sql_select("SELECT `pending_admin_approval` FROM `$PREF{user_table}` WHERE `id` = $userid"); } sub account_is_pending_payment { my $userid = shift; die_unless_numeric($userid, "user ID in account_is_pending_payment()"); return enc_sql_select("SELECT `pending_payment` FROM `$PREF{user_table}` WHERE `id` = $userid"); } sub account_has_completed_payment { my $userid = shift; die_unless_numeric($userid, "user ID in account_has_completed_payment()"); return enc_sql_select("SELECT COUNT(*) FROM `$PREF{payments_table}` WHERE `userid` = $userid"); } sub get_custom_userbase_field_names { my $which_user_table = shift; my $include_disabled_fields = shift; my (@custom_fields, %custom_fields) = (); if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{custom_field_list_table}`")) { my ($fieldname,$fieldlabel,$enabled) = (); my $sth = $PREF{dbh}->prepare("SELECT fieldname,fieldlabel,enabled FROM `$PREF{custom_field_list_table}` ORDER BY `fieldposition`"); $sth->execute() or die_nice("$PREF{internal_appname}: Error: get_custom_userbase_field_names(): $DBI::errstr\n"); $sth->bind_columns(\$fieldname,\$fieldlabel,\$enabled); while($sth->fetchrow_arrayref) { next unless db_column_exists($fieldname, $which_user_table); next if (!$enabled && !$include_disabled_fields); $custom_fields{$fieldname} = 1; push @custom_fields, $fieldname; } } return wantarray ? @custom_fields : \%custom_fields; } sub is_admin($) { #printd "is_admin('$_[0]')\n"; my $userid = shift; return 0 unless $userid; return 1 if (!userbase_available() && $userid == -3); # don't bother checking the validity of $userid here, # because user_is_member_of_group() will do it. return user_is_member_of_group($userid,$PREF{admin_group_name}); } sub force_pw_change($) { my $userid = shift; my $force_pw_change = 0; if( $PREF{enable_forced_password_change} =~ /yes/i && enc_sql_select("SELECT `forcepwchng` FROM `$PREF{user_table}` WHERE `id` = '$userid';") && ( !is_admin($userid) || (is_admin($userid) && $PREF{admins_can_be_forced_to_change_their_own_pws} =~ /yes/i) ) ) { $force_pw_change = 1; } if(password_has_expired()) { $force_pw_change = 1; } return $force_pw_change; } sub password_has_expired { my $expired = 0; if($PREF{force_pw_chng_after_this_many_hours} =~ /^\d+$/ && $PREF{force_pw_chng_after_this_many_hours} > 0) { unless($PREF{admin_is_logged_in} && $PREF{admins_can_be_forced_to_change_their_own_pws} =~ /no/i) { my $time_now = offsettime(); die_unless_numeric($PREF{logged_in_userid}, '$PREF{logged_in_userid}'); my $time_of_last_pw_change = enc_sql_select("SELECT MAX(`timestamp`) FROM `$PREF{password_activity_table}` WHERE `user_id` = $PREF{logged_in_userid}"); $expired = 1 if ($time_now - $time_of_last_pw_change) > ($PREF{force_pw_chng_after_this_many_hours} * 3600); } } return $expired; } sub get_group_id($) { printd "get_group_id($_[0])\n"; my $group = shift; if(userbase_available()) { check_groupname_for_uniqueness($group); # checks for sql safeness too. return enc_sql_select("SELECT `id` FROM `$PREF{group_table}` WHERE `group` = '$group'"); } else { if($group =~ /^$PREF{public_group_name}$/i) { return -1; } elsif($group =~ /^$PREF{member_group_name}$/i) { return -2; } elsif($group =~ /^$PREF{admin_group_name}$/i) { return -3; } else { die_nice("invalid group name '$group'.\n"); } } } sub check_uid_for_uniqueness($) { #die_unless_numeric($_[0], 'user ID'); # UIDs can be negative, so this test doesn't work here; and check_id_for_sql_safeness takes care of it anyway. check_id_for_sql_safeness($_[0]); if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE `id` = $_[0]") > 1) { die_nice("more than one user record with id=$_[0]!\n"); } } sub check_gid_for_uniqueness($) { return unless userbase_available(); printd "check_gid_for_uniqueness: '$_[0]'\n"; check_id_for_sql_safeness($_[0]); if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table}` WHERE `id` = $_[0]") > 1) { die_nice("more than one group record with id=$_[0]!\n"); } } sub check_username_for_uniqueness($) { #printd "check_username_for_uniqueness: '$_[0]'\n"; check_username_for_sql_safeness($_[0]); if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE LOWER(`username`) = LOWER('$_[0]')") > 1) { die_nice("more than one user record with username='$_[0]'!\n"); } } sub check_groupname_for_uniqueness { return unless userbase_available(); printd "check_groupname_for_uniqueness($_[0])\n"; check_groupname_for_sql_safeness($_[0]); if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table}` WHERE LOWER(`group`) = LOWER('$_[0]')") > 1) { die_nice("more than one user record with groupname='$_[0]'!\n"); } } sub user_is_member_of_group { my $userid = shift; my $group = shift; my $not_checking_loggedin_user = shift; if($PREF{integrate_with_userbase_method_b} =~ /yes/i && $PREF{logged_in_group_memberships} && !$not_checking_loggedin_user) { return $PREF{logged_in_group_memberships} =~ /(^|,)$group(,|$)/; } if(userbase_available() && ($PREF{member_is_logged_in} || $not_checking_loggedin_user)) { check_groupname_for_sql_safeness($group); die_unless_numeric($userid,'userid'); return 1 if $group =~ /^$PREF{public_group_name}$/i; return 1 if $group =~ /^$PREF{member_group_name}$/i && enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE `id` = $userid;"); # special internal groups: return 1 if $group eq 'encwhitelisted' && $PREF{user_is_encwhitelisted}; return 1 if $group eq 'encblacklisted' && $PREF{user_is_encblacklisted}; return enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table}` WHERE LOWER(`group`) = LOWER('$group') AND `members` REGEXP '(^|,)$userid(,|\$)'"); } else { # special internal groups: return 1 if $group eq 'encwhitelisted' && $PREF{user_is_encwhitelisted}; return 1 if $group eq 'encblacklisted' && $PREF{user_is_encblacklisted}; return 1 if $group =~ /^$PREF{public_group_name}$/i; return 1 if $group =~ /^$PREF{member_group_name}$/i && $userid =~ /^-(2|3)$/; return 1 if $group =~ /^$PREF{admin_group_name}$/i && $userid == -3; } } sub userbase_available { return ($PREF{internal_appname} eq 'userbase' || $PREF{integrate_with_userbase} =~ /yes/i || $PREF{integrate_with_userbase_method_b} =~ /yes/i); } sub get_user_id($) { #printd "get_user_id('$_[0]')\n"; my $username = shift; if(userbase_available() && $username) { die_nice("Error: invalid username '$username'.\n") unless username_is_valid($username); check_username_for_uniqueness($username); # checks for sql safeness too. return enc_sql_select("SELECT `id` FROM `$PREF{user_table}` WHERE LOWER(`username`) = LOWER('$username')"); } else { if($PREF{admin_is_logged_in}) { return -3; } elsif($PREF{member_is_logged_in}) { return -2; } else { return -1; } # public. } } sub get_member_ids_for_group { printd "get_member_ids_for_group($_[0])\n"; my $group = shift; check_groupname_for_sql_safeness($group); # every account is automatically a member of these groups. if($group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i) { my $statement = "SELECT `id` FROM `$PREF{user_table}`"; return $PREF{dbh}->selectall_hashref($statement, 'id'); } else { my $member_ids = enc_sql_select("SELECT `members` FROM `$PREF{group_table}` WHERE LOWER(`group`) = LOWER('$group')"); my %member_ids = map { $_ => 1 } split(/,/, $member_ids); return \%member_ids; } } # 20110404: APICHANGE: previously this sub accepted just a single argument, # which was the group name. Now it accepts an anonymous hash. # sub get_users_belonging_to_group { # Example usage: #my $users = get_users_belonging_to_group({ group=>'member' }); #foreach my $uid (keys %$users) #{ # print qq`Name: $$users{$uid}{name}, Email: $$users{$uid}{email}

`; #} my $optsref = shift; my %opts = %$optsref if $optsref; my $group = $opts{group}; check_groupname_for_sql_safeness($group); my $userids = ''; if($group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i) { my $userids_hashref = enc_sql_select_multi("SELECT `id` FROM `$PREF{user_table}`"); foreach my $j (sort { $a <=> $b } keys %$userids_hashref) { $userids .= $$userids_hashref{$j}{id} . ","; } decommaify($userids); } else { $userids = enc_sql_select("SELECT `members` FROM `$PREF{group_table}` WHERE LOWER(`group`) = LOWER('$group')"); } my %users = (); foreach my $uid (split(/,/, $userids)) { die_unless_numeric($uid, 'user ID'); next if $opts{exclude_pending} && account_is_pending($uid); next if $opts{exclude_admins} && is_admin($uid); my $userdata = enc_sql_select_multi("SELECT * FROM `$PREF{user_table}` WHERE `id` = $uid"); foreach my $field (keys %{$$userdata{1}}) { $users{$uid}{$field} = $$userdata{1}{$field}; } } return \%users; } sub get_user_name($) { check_uid_for_uniqueness($_[0]); # checks for sql safeness too. return enc_sql_select("SELECT `username` FROM `$PREF{user_table}` WHERE `id` = $_[0]"); } sub get_group_name($) { my $gid = shift; if(userbase_available()) { check_gid_for_uniqueness($gid); # checks for sql safeness too. return enc_sql_select("SELECT `group` FROM `$PREF{group_table}` WHERE `id` = $gid"); } else { if($gid == -1) { return $PREF{public_group_name}; } elsif($gid == -2) { return $PREF{member_group_name}; } elsif($gid == -3) { return $PREF{admin_group_name}; } else { die_nice("$PREF{internal_appname}: get_group_name(): invalid group ID '$gid'.\n"); } } } sub get_email_address($) { return unless userbase_available(); check_uid_for_uniqueness($_[0]); # checks for sql safeness too. my ($username,$email) = enc_sql_select("SELECT `username`,`email` FROM `$PREF{user_table}` WHERE `id` = $_[0]"); $email = $username if is_valid_email_address($username) && !is_valid_email_address($email); return $email; } sub hashedpw_is_valid { return $_[0] =~ /^[0-9A-Za-z]+$/ && length($_[0]) <= $PREF{max_hashedpw_length}; } sub sessionid_is_valid { return $_[0] =~ /^[0-9A-Za-z]+$/ && length($_[0]) <= $PREF{max_hashedpw_length}; } sub username_is_valid { my $space = $PREF{allow_spaces_in_usernames} =~ /yes/i ? ' ' : ''; my $atsign = $PREF{allow_atsigns_in_usernames} =~ /yes/i ? '@' : ''; my $dot = $PREF{allow_dots_in_usernames} =~ /yes/i ? '.' : ''; my $dash = $PREF{allow_dashes_in_usernames} =~ /yes/i ? '-' : ''; return ($_[0] =~ /^[0-9A-Za-z_$space$atsign$dot$dash]+$/ && $_[0] =~ /\w/ && length($_[0]) <= $PREF{max_username_length}); } sub groupname_is_valid { my $space = ($PREF{allow_spaces_in_usernames} =~ /yes/i || $PREF{allow_spaces_in_groupnames} =~ /yes/i) ? ' ' : ''; my $atsign = ($PREF{allow_atsigns_in_usernames} =~ /yes/i || $PREF{allow_atsigns_in_groupnames} =~ /yes/i) ? '@' : ''; my $dot = ($PREF{allow_dots_in_usernames} =~ /yes/i || $PREF{allow_dots_in_groupnames} =~ /yes/i) ? '.' : ''; my $dash = ($PREF{allow_dashes_in_usernames} =~ /yes/i || $PREF{allow_dashes_in_groupnames} =~ /yes/i) ? '-' : ''; return ($_[0] =~ /^[0-9A-Za-z_$space$atsign$dot$dash]+$/ && $_[0] =~ /\w/ && length($_[0]) <= $PREF{max_groupname_length}); } sub check_hashedpw_for_sql_safeness { die_nice(qq`Invalid hashed password: '$_[0]' [called from: ` . (caller 1)[3] . qq`]`) unless hashedpw_is_valid($_[0]); } sub check_username_for_sql_safeness { die_nice(qq`Invalid username: '$_[0]' [called from: ` . (caller 1)[3] . qq`]`) unless username_is_valid($_[0]); } sub check_groupname_for_sql_safeness { die_nice(qq`Invalid groupname: '$_[0]' [called from: ` . (caller 1)[3] . qq`]`) unless groupname_is_valid($_[0]); } sub check_sessionid_for_sql_safeness { die_nice(qq`Invalid session ID: '$_[0]' [called from: ` . (caller 1)[3] . qq`]`) unless sessionid_is_valid($_[0]); } sub check_id_for_sql_safeness { die_nice(qq`Invalid ID: '$_[0]' [called from: ` . (caller 1)[3] . qq`]`) unless $_[0] =~ /^(\d+|-[123])$/; } sub get_groups_hash { printd "get_groups_hash('$_[0]')\n"; # If you pass in a uid, then the resulting hash will # also indicate which groups that user is a member of. # my $user_id = shift; my ($id, $group, $members, %groups) = (); if(userbase_available()) { my $sth = $PREF{dbh}->prepare("SELECT `id`, `group`, `members` FROM `$PREF{group_table}`"); $sth->execute(); $sth->bind_columns(\$id, \$group, \$members); while($sth->fetchrow_arrayref) { $groups{$group}{name} = $group; $groups{$group}{id} = $id; $groups{$group}{members} = $members; my $is_member = (); if($group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i) { $is_member = 1; } elsif($user_id =~ /^\d+$/) { $is_member = $members =~ /(^|,)$user_id(,|$)/; } $groups{$group}{is_member} = $is_member; } } else { $groups{$PREF{public_group_name}}{name} = $PREF{public_group_name}; $groups{$PREF{public_group_name}}{id} = -1; $groups{$PREF{public_group_name}}{is_member} = 1; # everyone's a member of the public. $groups{$PREF{member_group_name}}{name} = $PREF{member_group_name}; $groups{$PREF{member_group_name}}{id} = -2; $groups{$PREF{member_group_name}}{is_member} = 1 if $user_id =~ /^-(2|3)$/; $groups{$PREF{admin_group_name}}{name} = $PREF{admin_group_name}; $groups{$PREF{admin_group_name}}{id} = -3; $groups{$PREF{admin_group_name}}{is_member} = 1 if $user_id =~ /^-3$/; } return \%groups; } sub get_email_addresses_for_all_members_of_this_users_groups { my $uid = shift; die_unless_numeric($uid, "user ID"); my $include_users_own_email_address = shift; $include_users_own_email_address = 0 unless $include_users_own_email_address eq 'include_users_own_email_address'; my $include_builtin_public_group = shift; $include_builtin_public_group = 0 unless $include_builtin_public_group eq 'include_builtin_public_group'; my $include_builtin_member_group = shift; $include_builtin_member_group = 0 unless $include_builtin_member_group eq 'include_builtin_member_group'; my $include_builtin_admin_group = shift; $include_builtin_admin_group = 0 unless $include_builtin_admin_group eq 'include_builtin_admin_group'; my %email_addresses = (); my $allgroups = get_groups_hash($uid); foreach my $group (sort keys %$allgroups) { next if $group =~ /^$PREF{public_group_name}$/i && !$include_builtin_public_group; next if $group =~ /^$PREF{member_group_name}$/i && !$include_builtin_member_group; next if $group =~ /^$PREF{admin_group_name}$/i && !$include_builtin_admin_group; if(my $this_user_belongs_to_this_group = $$allgroups{$group}{is_member}) { foreach my $member_id (split(/\s*,\s*/, $$allgroups{$group}{members})) { my $email = get_email_address($member_id); $email_addresses{ $email } = 1 unless !$include_users_own_email_address && $email eq $PREF{logged_in_email}; } } } return keys %email_addresses; # returns an array when this sub is called in list context. } # This function must do a case-insensitive lookup (i.e. use LOWER() on both sides) # so that we never create a username multiple times with different cases. # sub username_is_taken { return 0 unless userbase_available(); my $user = shift; check_username_for_sql_safeness($user); return enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE LOWER(`username`) = LOWER('$user')"); } sub email_address_is_taken { my $address = shift; check_emailaddr_for_sql_safeness($address); return enc_sql_select("SELECT COUNT(*) FROM `$PREF{user_table}` WHERE LOWER(`email`) = LOWER('$address')"); } sub salt_and_crypt_password($$) { my $plaintext_password = shift; my $salt = shift; $plaintext_password = lc($plaintext_password) if $PREF{make_passwords_case_insensitive} =~ /yes/i; die "$0: salt_and_crypt_password(): no salt?\n" unless $salt; my ($salt1,$salt2) = ($salt =~ /^(.{15})(.{25})$/); my $crypted_password = enc_hash($salt1 . $plaintext_password . $salt2); return $crypted_password; } sub make_password_hash { if($ENV{REQUEST_METHOD} =~ /post/i) { my $hashed_password = md5_hex(param('password')); start_html_output('Here is your hashed password...'); print qq`

The hashed version of the password you just entered is:

$hashed_password

` . qq`

Now open your prefs file and paste this hash into one of the ` . qq`\n\$PREF{admin_password_hash*} or \$PREF{member_password_hash*} settings.

`; finish_html_output(); } else { start_html_output('Enter your new password'); print qq`
` . qq`\nEnter your new password:` . qq`\n

` . qq`\n

` . qq`\n
`; finish_html_output(); } } sub user_is_allowed_to { # TODO: these separate per-app branches can probably be partially or totally reconciled. if($PREF{internal_appname} eq 'userbase') { my $userid_performing_action = scalar(@_) == 1 ? $PREF{logged_in_userid} : shift; # if only 1 arg was passed, it's the action, implying we should automatically use the logged_in_userid. my $action = shift; my $user_affected_by_action = shift; my $userid_affected_by_action = get_user_id($user_affected_by_action); if($action eq 'edit_user_info') { return 1 if (logged_in_user_is_subgroup_manager() && $userid_affected_by_action && logged_in_subgroup_manager_owns_this_user($userid_affected_by_action)); } foreach my $group (split(/\s*,\s*/, $PREF{"groups_not_allowed_to_$action"})) { return 0 if user_is_member_of_group($userid_performing_action, $group); } foreach my $group (split(/\s*,\s*/, $PREF{"groups_not_allowed_to_$action"})) { if($group =~ /^self$/i) { return 0 if ($PREF{member_is_logged_in} && $userid_performing_action == $userid_affected_by_action); } else { return 0 if user_is_member_of_group($userid_performing_action, $group); } } foreach my $group (split(/\s*,\s*/, $PREF{"groups_allowed_to_$action"})) { if($group =~ /^self$/i) { return 1 if ($PREF{member_is_logged_in} && $userid_performing_action == $userid_affected_by_action); } else { return 1 if user_is_member_of_group($userid_performing_action, $group); } } return 0; } else { my $action = shift; foreach my $group (split(/\s*,\s*/, $PREF{"groups_not_allowed_to_$action"})) { return 0 if user_is_member_of_group($PREF{logged_in_userid}, $group); } foreach my $group (split(/\s*,\s*/, $PREF{"groups_allowed_to_$action"})) { return 1 if user_is_member_of_group($PREF{logged_in_userid}, $group); } return 0; } } sub logged_in_user_is_part_of_a_subgroup() { return 0 unless $PREF{member_is_logged_in}; return user_is_part_of_a_subgroup($PREF{logged_in_userid}); } sub user_is_part_of_a_subgroup($) { #foreach my $group (enc_sql_select("SELECT ")) #{ # if($group =~ /.+$PREF{subgroup_groupname_suffix}$/i) # { # return enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table}` WHERE LOWER(`group`) = LOWER('$group') AND `members` REGEXP '(^|,)$PREF{logged_in_userid}(,|\$)'"); # } #} my $uid = shift; return '' unless userbase_available(); return $PREF{subgroup_manager_username} if exists $PREF{subgroup_manager_username}; # if this sub has already been called once, no need to re-do it. my $subgroup_groupname_suffix = $PREF{subgroup_groupname_suffix}; sql_untaint($subgroup_groupname_suffix); exit_with_error("not SQL safe: \$subgroup_groupname_suffix ('$subgroup_groupname_suffix').") if not_sqlsafe($subgroup_groupname_suffix); die_unless_numeric($uid, '$uid'); #return enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table}` WHERE `group` REGEXP '.+$subgroup_groupname_suffix\$' AND `members` REGEXP '(^|,)$uid(,|\$)'"); # # 20111112: changing this to return the username of the SGM, rather than just # a zero or a number greater than zero, which doesn't change the true/false # value of the result, but makes it more useful: # my $subgroup = enc_sql_select("SELECT `group` FROM `$PREF{group_table}` WHERE `group` REGEXP '.+$subgroup_groupname_suffix\$' AND `members` REGEXP '(^|,)$uid(,|\$)'"); $PREF{subgroup_manager_username} = ($subgroup =~ m!(.+)$subgroup_groupname_suffix$!)[0]; $PREF{subgroup_manager_userid} = get_user_id($PREF{subgroup_manager_username}) if $PREF{subgroup_manager_username}; return $PREF{subgroup_manager_username}; } sub logged_in_user_is_subgroup_manager { foreach my $group (split(/\s*,\s*/, $PREF{groups_that_can_manage_subgroup_users})) { return 1 if user_is_member_of_group($PREF{logged_in_userid}, $group); } return 0; } sub logged_in_subgroup_manager_owns_this_user($) { my $userid_to_check = shift; return user_is_member_of_group($userid_to_check, "$PREF{logged_in_username}$PREF{subgroup_groupname_suffix}"); } sub user_is_subgroup_manager { my $username = shift; my $userid = get_user_id($username); foreach my $group (split(/\s*,\s*/, $PREF{groups_that_can_manage_subgroup_users})) { return 1 if user_is_member_of_group($userid, $group, 1); } return 0; } sub this_subgroup_manager_owns_this_user($$) { my $subgroup_manager_userid = shift; my $userid_to_check = shift; my $subgroup_manager_username = get_user_name($subgroup_manager_userid); return user_is_subgroup_manager($subgroup_manager_username) && user_is_member_of_group($userid_to_check, "${subgroup_manager_username}$PREF{subgroup_groupname_suffix}"); } sub exit_with_access_denied { exit_with_needprivs(); } ############################################################################## ### Dispatch-FC: ############################################################# ############################################################################## load_prefs(); if($qs =~ /action=killkeepalive/) { print "Cache-Control: no-store, no-cache\n"; print "Content-type: text/html\n"; print "Connection: close\n"; print "\n"; exit; } elsif($qs =~ /action=ajax_get_progress/) { ajax_get_progress(); } elsif($qs =~ /ajax_get_serial/) { print_xml_headers(); print qq`\n\n`; print generate_serial_number(); print qq`\n\n`; } elsif($qs =~ /ajax_do_humantest&fcht1=(.*?)&fcht2=(.*?)(?:&|$)/) { my $passed_test = do_human_test($1,$2); print_xml_headers(); print qq`\n\n`; print $passed_test ? 'passed=true' : 'passed=false'; print qq`\n\n`; } elsif($qs =~ /(?:^|&)eimsg([sne])=(\w+)(?:&|$)/) { my ($type,$msg) = ($1,$TEXT{$2}); if($type eq 's') { exit_with_success($msg); } elsif($type eq 'n') { exit_with_notice($msg); } else { exit_with_error($msg); } } elsif($qs =~ /(?:^|&)kmsg=(\w+)(?:&|$)/) { exit_with_kmsg($1); } elsif($qs =~ /(?:^|&)smsg=(\w+)(?:&|$)/) { exit_with_error($TEXT{The_password_you_entered_is_incorrect___}) if $1 eq 'failedlogin'; exit_with_error($TEXT{"smsg_$1"}) if $TEXT{"smsg_$1"}; # for text strings that are entirely static, name them $TEXT{smsg_foo}. } elsif($qs =~ /(?:^|&)action=uploadcomplete(?:&|$)/) { show_uploadcomplete_page(); } elsif($qs =~ /(?:^|&)action=view_items(?:&|$)/) { view_items(); } elsif($qs =~ /(?:^|&)action=showperms&item=(.+?)(?:&|$)/) { show_permissions($1); } elsif($qs =~ /(?:^|&)action=changeperms(?:&|$)/) { change_permissions(); } elsif($qs =~ /(?:^|&)action=process_order(?:&|$)/) { process_order(); } elsif($qs =~ /(?:^|&)action=showallperms(?:&|$)/) { show_all_permissions(); } elsif($qs =~ /(?:^|&)action=order_confirmation(?:&|$)/) { order_confirmation(); } elsif($qs =~ /(?:^|&)action=loggedout&whence=(.*)(?:&|$)/) { # note that the whence regex is .* not .*? because the value # will likely contain ampersands that we want to keep. $PREF{member_is_logged_in} ? enc_redirect($PREF{here}) : show_loggedout_page($1); } elsif($qs =~ /action=delete(?:&path=(.*?))?&(file|folder)=(.+?)(?:&|$)/) { my $error = delete_item($1,$2,$3); exit_with_error($error) if $error; } elsif($qs =~ /action=((?:move|rename)(?:copy)?)&(file|folder)=(.+?)&src=(.*?)(?:&dst=(.+?))?(?:&|$)/) { move_item($1,$2,$3,$4,$5); } elsif($qs =~ /action=fileinfo&path=(.*?)&file=(.+?)(?:&|$)/) { show_fileinfo($1, $2); } elsif($qs =~ /action=$PREF{upload_session_info_action_name}(?:&|$)/) { show_upload_session_info(); } elsif(get_qs_var('action') eq 'download') { download_file(); } elsif(get_qs_var('action') eq 'viewinline') { download_file({ inline => 1 }); } elsif($qs =~ /action=landing&path=(.*?)&file=(.+?)(?:&|$)/) { show_download_landing_page($1, $2); } elsif($qs =~ /action=$PREF{mkdir_action_name}(?:&|$)/) { make_dir(); } elsif($qs =~ /action=unzip_files&path=(.*?)(?:&|$)/) { unzip_files($1); } elsif($qs =~ /action=rotate_images(\d+)&path=(.*?)(?:&|$)/) { rotate_images($1,$2); } elsif($qs =~ /action=delete_items&path=(.*?)(?:&|$)/) { delete_items($1); } elsif($qs =~ /action=copy_items&path=(.*?)(?:&|$)/) { copy_items($1); } elsif($qs =~ /action=multidownload&path=(.*?)(?:&|$)/) { download_multiple_items($1); } elsif(get_qs_var('action') eq 'uploadlog') { show_upload_log(); } elsif(get_qs_var('action') eq 'altuploadlog') { show_alternate_upload_log(); } elsif(get_qs_var('action') eq 'downloadlog') { show_download_log(); } elsif(get_qs_var('action') eq 'administration') { show_administration_menu(); } elsif(get_qs_var('action') eq 'help') { show_help(); } elsif(get_qs_var('action') eq 'config') { show_configuration(); } elsif(get_qs_var('action') eq 'showprefs') { show_prefs(); } elsif(get_qs_var('action') eq 'serverinfo') { show_server_info(); } elsif(get_qs_var('action') eq 'imagemodules') { check_image_modules(); } elsif(get_qs_var('action') eq 'viewer') { display_item_in_viewer(); } elsif(get_qs_var('action') eq 'saleopts') { show_sale_options(); } elsif($qs =~ /bounceback=(.+)/) { # For when another app can't continue until FC does some sort of initialization (creating/populating userdirs, etc). my $go = $1; enc_urldecode($go); $go .= $go =~ /\?/ ? '&' : '?'; $go .= "bbdone=true"; enc_redirect($go); } elsif($qs =~ /action=incoming/i) { process_upload(); } elsif($qs =~ /(?:^|&)(list|action=listfiles)(?:&|$)/) { list_uploaded_files(); } elsif($qs =~ /(?:^|&)action=upload(?:&|$)/) { print_new_upload_form(); } elsif( $qs =~ /do_email_test/ && ($PREF{enable_email_test} =~ /yes/i || $PREF{admin_is_logged_in}) ) { do_email_test(); } elsif($qs =~ /(?:^|&)cpage=(\w+)/ && $PREF{custom_pages}{$1}) { show_custom_page($1); } else { if($PREF{default_page} eq 'filelist') { list_uploaded_files(); } else { print_new_upload_form(); } } #printd "fcruntime=" . (gettimeofday() - $PREF{script_start_time_highres}) . "\n";