function test() { return; } * php > var_dump(test()); * NULL * * - Check if a failed function returns false, e.g., * php > function orwell() { return (2 + 2 === 5); } * php > var_dump(orwell()); * bool(false) * * todo: Support form ID checks * todo: Number and date validation */ class Validate { /** * title * * Check if a torrent title is valid. * If so, return the sanitized title. * If not, return an error. */ public function textInput($String) { # Previously a constant $MinLength = 10; $MaxLength = 255; # Does it exist and is it valid? if (!$String || !is_string($String)) { error('No or invalid $String parameter.'); } # Is it too long or short? if (count($String)) { } } /** * Torrent errors * * Responsible for the red error messages on bad upload attemps. * todo: Test $this->TorrentError() on new file checker functions */ public function TorrentError($Suspect) { global $Err; if (!$Suspect) { error('No error source :^)'); } switch (false) { case $this->HasExtensions($Suspect, 1): return $Err = "The torrent has one or more files without extensions:\n" . display_str($Suspect); case $this->CruftFree($Suspect): return $Err = "The torrent has one or more junk files:\n" . display_str($Suspect); case $this->SafeCharacters($Suspect): $BadChars = $this->SafeCharacters('', true); return $Err = "One or more files has the forbidden characters $BadChars:\n" . display_str($Suspect); default: return null; } return null; } /** * Check if a file has no extension and return false. * Otherwise, return an array of the last $x extensions. */ private function HasExtensions($FileName, $x) { if (!is_int($x) || $x <= 0) { error('Requested number of extensions must be <= 0'); } if (!strstr('.', $FileName)) { return false; } $Extensions = array_slice(explode('.', strtolower($FileName)), -$x, $x); return (!empty($Extensions)) ? $Extensions : false; } /** * Check if a file is junk according to a filename blacklist. * todo: Change $Keywords into an array of regexes */ public function CruftFree($FileName) { $Keywords = [ 'ahashare.com', 'demonoid.com', 'demonoid.me', 'djtunes.com', 'h33t', 'housexclusive.net', 'limetorrents.com', 'mixesdb.com', 'mixfiend.blogstop', 'mixtapetorrent.blogspot', 'plixid.com', 'reggaeme.com', 'scc.nfo', 'thepiratebay.org', 'torrentday', ]; # $Keywords match foreach ($Keywords as &$Value) { if (strpos(strtolower($FileName), $Value) !== false) { return false; } } # Incomplete data if (preg_match('/INCOMPLETE~\*/i', $FileName)) { return false; } return true; } /* * These characters are invalid on Windows NTFS: * : ? / < > \ * | " * * If no $FileName, return the list of bad characters. * If $FileName contains, a bad character, return false. * Otherwise, return true. * * todo: Add "/" to the blacklist. This causes problems with nested dirs, apparently * todo: Make possible preg_match($AllBlockedChars, $Name, $Matches) */ public function SafeCharacters($FileName, $Pretty = false) { $InvalidChars = ':?<>\*|"'; if (empty($FileName)) { return (!$Pretty) ? $InvalidChars : implode(' ', str_split($InvalidChars)); } # todo: Regain functionality to return the invalid character if (preg_match(implode('\\', str_split($InvalidChars)), $Name, $Matches)) { return false; } return true; } /** * Extension Parser * * Takes an associative array of file types and extension, e.g., * $Archives = [ * '7z' => ['7z'], * 'bzip2' => ['bz2', 'bzip2'], * 'gzip' => ['gz', 'gzip', 'tgz', 'tpz'], * ... * ]; * * Then it finds all the extensions in a torrent file list, * organizes them by file size, and returns the heaviest match. * * That way, you can have, e.g., 5 GiB FASTQ sequence data in one file, * and 100 other small files, and get the format of the actual data. */ public function ParseExtensions($FileList, $Category, $FileTypes) { # Sort $Tor->file_list() output by size $UnNested = array_values($FileList[1]); $Sorted = (usort($UnNested, function ($a, $b) { return $b <=> $a; })) ? $UnNested : null; # Ternary wrap because ↑ returns true # Harvest the wheat # todo: Entries seem duplicated here $Heaviest = array_slice($Sorted, 0, 20); $Matches = []; # Distill the file format $FileTypes = $FileTypes[$Category]; $FileTypeNames = array_keys($FileTypes); foreach ($Heaviest as $Heaviest) { # Collect the last 2 period-separated tokens $Extensions = array_slice(explode('.', strtolower($Heaviest[1])), -2, 2); $Matches = array_merge($Extensions); # todo: Reduce nesting by one level foreach ($Matches as $Match) { $Match = strtolower($Match); foreach ($FileTypeNames as $FileTypeName) { $SearchMe = [ $FileTypeName, $FileTypes[$FileTypeName] ]; if (in_array($Match, $SearchMe[1])) { return $SearchMe[0]; break; } } # Return the last element (Other or None) return array_key_last($FileTypes); } } } /** * Make input * https://www.w3schools.com/html/html_form_input_types.asp * * Generate a secure HTML input element. * Takes $Type, $Name (also used for id), and $Classes. * Outputs a hardened input with the requested attributes. * * todo: See if this could work, one ugly switch for all forms in sections/ */ public function MakeInput($Type = 'text', $Name = '', $Classes = []) { if (!is_str($Type) || !is_str($Name)) { error('$Type and $Name must be strings'); } # Intentionally double quoted PHP constructing $HTML # Fields[$FieldName]['Type'] = strtolower($FieldType); $this->Fields[$FieldName]['Required'] = $Required; $this->Fields[$FieldName]['ErrorMessage'] = $ErrorMessage; if (!empty($Options['maxlength'])) { $this->Fields[$FieldName]['MaxLength'] = $Options['maxlength']; } if (!empty($Options['minlength'])) { $this->Fields[$FieldName]['MinLength'] = $Options['minlength']; } if (!empty($Options['comparefield'])) { $this->Fields[$FieldName]['CompareField'] = $Options['comparefield']; } if (!empty($Options['allowperiod'])) { $this->Fields[$FieldName]['AllowPeriod'] = $Options['allowperiod']; } if (!empty($Options['allowcomma'])) { $this->Fields[$FieldName]['AllowComma'] = $Options['allowcomma']; } if (!empty($Options['inarray'])) { $this->Fields[$FieldName]['InArray'] = $Options['inarray']; } if (!empty($Options['regex'])) { $this->Fields[$FieldName]['Regex'] = $Options['regex']; } } public function ValidateForm($ValidateArray) { reset($this->Fields); foreach ($this->Fields as $FieldKey => $Field) { $ValidateVar = $ValidateArray[$FieldKey]; # todo: Change this to a switch statement if ($ValidateVar !== '' || !empty($Field['Required']) || $Field['Type'] === 'date') { if ($Field['Type'] === 'string') { if (isset($Field['MaxLength'])) { $MaxLength = $Field['MaxLength']; } else { $MaxLength = 255; } if (isset($Field['MinLength'])) { $MinLength = $Field['MinLength']; } else { $MinLength = 1; } if (strlen($ValidateVar) > $MaxLength) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) < $MinLength) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'number') { if (isset($Field['MaxLength'])) { $MaxLength = $Field['MaxLength']; } else { $MaxLength = ''; } if (isset($Field['MinLength'])) { $MinLength = $Field['MinLength']; } else { $MinLength = 0; } $Match = '0-9'; if (isset($Field['AllowPeriod'])) { $Match .= '.'; } if (isset($Field['AllowComma'])) { $Match .= ','; } if (preg_match('/[^'.$Match.']/', $ValidateVar) || strlen($ValidateVar) < 1) { return $Field['ErrorMessage']; } elseif ($MaxLength !== '' && $ValidateVar > $MaxLength) { return $Field['ErrorMessage'].'!!'; } elseif ($ValidateVar < $MinLength) { return $Field['ErrorMessage']."$MinLength"; } } elseif ($Field['Type'] === 'email') { if (isset($Field['MaxLength'])) { $MaxLength = $Field['MaxLength']; } else { $MaxLength = 255; } if (isset($Field['MinLength'])) { $MinLength = $Field['MinLength']; } else { $MinLength = 6; } if (!preg_match("/^".EMAIL_REGEX."$/i", $ValidateVar)) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) > $MaxLength) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) < $MinLength) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'link') { if (isset($Field['MaxLength'])) { $MaxLength = $Field['MaxLength']; } else { $MaxLength = 255; } if (isset($Field['MinLength'])) { $MinLength = $Field['MinLength']; } else { $MinLength = 10; } if (!preg_match('/^'.URL_REGEX.'$/i', $ValidateVar)) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) > $MaxLength) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) < $MinLength) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'username') { if (isset($Field['MaxLength'])) { $MaxLength = $Field['MaxLength']; } else { $MaxLength = 20; } if (isset($Field['MinLength'])) { $MinLength = $Field['MinLength']; } else { $MinLength = 1; } if (!preg_match(USERNAME_REGEX, $ValidateVar)) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) > $MaxLength) { return $Field['ErrorMessage']; } elseif (strlen($ValidateVar) < $MinLength) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'checkbox') { if (!isset($ValidateArray[$FieldKey])) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'compare') { if ($ValidateArray[$Field['CompareField']] !== $ValidateVar) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'inarray') { if (array_search($ValidateVar, $Field['InArray']) === false) { return $Field['ErrorMessage']; } } elseif ($Field['Type'] === 'regex') { if (!preg_match($Field['Regex'], $ValidateVar)) { return $Field['ErrorMessage']; } } } } // while } // function } // class /** * File checker stub class * * Not technically part of the Validate class (yet). * Useful torrent file functions such as finding disallowed characters. * This will eventually move inside Validate for upload_handle.php. */ $Keywords = array( 'ahashare.com', 'demonoid.com', 'demonoid.me', 'djtunes.com', 'h33t', 'housexclusive.net', 'limetorrents.com', 'mixesdb.com', 'mixfiend.blogstop', 'mixtapetorrent.blogspot', 'plixid.com', 'reggaeme.com' , 'scc.nfo', 'thepiratebay.org', 'torrentday'); function check_file($Type, $Name) { check_name($Name); check_extensions($Type, $Name); } function check_name($Name) { global $Keywords; $NameLC = strtolower($Name); foreach ($Keywords as &$Value) { if (strpos($NameLC, $Value) !== false) { forbidden_error($Name); } } if (preg_match('/INCOMPLETE~\*/i', $Name)) { forbidden_error($Name); } /* * These characters are invalid in NTFS on Windows systems: * : ? / < > \ * | " * * todo: Add "/" to the blacklist. This causes problems with nested dirs, apparently * todo: Make possible preg_match($AllBlockedChars, $Name, $Matches) * * Only the following characters need to be escaped (see the link below): * \ - ^ ] * * http://www.php.net/manual/en/regexp.reference.character-classes.php */ $AllBlockedChars = ' : ? < > \ * | " '; if (preg_match('/[\\:?<>*|"]/', $Name, $Matches)) { character_error($Matches[0], $AllBlockedChars); } } function check_extensions($Type, $Name) { # todo: Make generic or subsume into Validate->ParseExtensions() /* if (!isset($MusicExtensions[get_file_extension($Name)])) { invalid_error($Name); } */ } function get_file_extension($FileName) { return strtolower(substr(strrchr($FileName, '.'), 1)); } /** * Error functions * * Responsible for the red error messages on bad upload attemps. * todo: Make one function, e.g., Validate->error($type) */ function invalid_error($Name) { global $Err; $Err = 'The torrent contained one or more invalid files (' . display_str($Name) . ')'; } function forbidden_error($Name) { global $Err; $Err = 'The torrent contained one or more forbidden files (' . display_str($Name) . ')'; } function character_error($Character, $AllBlockedChars) { global $Err; $Err = "One or more of the files or folders in the torrent has a name that contains the forbidden character '$Character'. Please rename the files as necessary and recreate the torrent.

\nNote: The complete list of characters that are disallowed are shown below:
\n\t\t$AllBlockedChars"; }