upload($postIndex, $acceptable_file_types, $default_extension); if($my_uploader->error != ""){ motive_fs_error("Error uploading: ".$my_uploader->error); } else { $origFileName = $my_uploader->getTempName(); $retVal = motive_fs_commitFile($origFileName, $my_uploader, true, $postIndex); } return $retVal; } // end motive_fs_saveFile(...) //// // Commits a file to Motive FS and deletes the original. This should be // called to add files that were already on the server... uploads should be // done using motive_fs_saveFile() (which will call this function from // inside itself). 'postIndex' is only needed for uploads and is ignored // otherwise. // // Returns a string containing the final filename of the file committed. //// function motive_fs_commitFile($origFileName, $my_uploader='', $deleteOriginal=true, $postIndex=''){ $retVal = ""; GLOBAL $MOTIVE_FS_FILES_PER_DIR; GLOBAL $MOTIVE_FS_ROOT_DIR; GLOBAL $MOTIVE_FS_MODIFIED_PATH; $isUpload = is_object($my_uploader); $contentDirectory = ""; // not needed, we roll the dir into the filename $default_extension = ""; $fileName = ""; // TODO: CHECK FOR BLACKLISTED FILES AND RED-FLAG THEM. // TODO: CHECK FOR BLACKLISTED FILES AND RED-FLAG THEM. // Hash and compare to the db. motive_fs_connect(); GLOBAL $motive_fs_db; $md5 = md5_file($origFileName); $sha1 = sha1_file($origFileName); $queryString = "SELECT * FROM motive_fs_files WHERE hash_md5='$md5' AND hash_sha1='$sha1' LIMIT 1"; if($result = mysql_query($queryString,$motive_fs_db)){ if(($numRows = mysql_num_rows($result)) && ($numRows > 0)){ // File is a repeat, increment the num of references, and return the fileName_fs $cnt=0; $retVal = mysql_result($result, $cnt, "fileName"); $id = mysql_result($result, $cnt, "id"); $queryString = "UPDATE motive_fs_files SET numRefs=numRefs+1 WHERE id=$id"; if(false === mysql_query($queryString, $motive_fs_db)){ motive_fs_error("Error with query:\n$queryString\n".mysql_error()); } // SWC 20070404 - If told to, still need to delete original even if this is a repeated file (this was leaking extra images). if($deleteOriginal){ if(!@unlink($origFileName)){ motive_fs_error("Couldn't delete file while committing: \"$origFileName\""); } } } else { // File is unique. Rename and store it, store the info to the db, and return the fileName_fs. $currRoot = motive_fs_simpleQuery("SELECT value FROM motive_fs_map WHERE keyName='CURR_ROOT_DIR'"); $currSub = motive_fs_simpleQuery("SELECT value FROM motive_fs_map WHERE keyName='CURR_SUB_DIR'"); $fileName = motive_fs_simpleQuery("SELECT value FROM motive_fs_map WHERE keyName='NEXT_FILE'"); // This needs to be done as quickly as possible after fetching the value in order to prevent multiple requests // in short succession from getting the same value. $nextFile = motive_fs_nextName($fileName); $queryString = "UPDATE motive_fs_map SET value=\"$nextFile\" WHERE keyName='NEXT_FILE'"; if(false === mysql_query($queryString, $motive_fs_db)){ motive_fs_error("Error with query:\n$queryString\n".mysql_error()); } // TODO: REMEMBER TO makedir() EVERYWHERE BELOW WHEN CHANGING DIRECTORIES!!!! // TODO: REMEMBER TO makedir() EVERYWHERE BELOW WHEN CHANGING DIRECTORIES!!!! // TODO: CHECK TO MAKE SURE THE DIRECTORY EXISTS AND IS WRITABLE, OTHERWISE WARN THAT MOTIVE FS IS CONFIGURED INCORRECTLY. // TODO: CHECK TO MAKE SURE THE DIRECTORY EXISTS AND IS WRITABLE, OTHERWISE WARN THAT MOTIVE FS IS CONFIGURED INCORRECTLY. // Calculate the directory. $fileNum = motive_fs_nameToDecimal($fileName); if($fileNum == $MOTIVE_FS_FILES_PER_DIR){ $currSub = motive_fs_nextName($currSub); // Afterwards, save it as fast as possible. $queryString = "UPDATE motive_fs_map SET value=\"$currSub\" WHERE keyName='CURR_SUB_DIR'"; mysql_query($queryString, $motive_fs_db); // Calculate the directory $subNum = motive_fs_nameToDecimal($currSub); if($subNum == $MOTIVE_FS_FILES_PER_DIR){ $nextRoot = motive_fs_nextName($currRoot); $nextRootNum = motive_fs_nameToDecimal($nextRoot); // If the root would spill over, keep same root but add a directory in the subdir if($nextRootNum >= $MOTIVE_FS_FILES_PER_DIR){ // TODO: IMPLEMENT THIS BEFORE THERE ARE 32k FILES ON SERVER. // TODO: IMPLEMENT THIS BEFORE THERE ARE 32k FILES ON SERVER. } } else if($subNum > $MOTIVE_FS_FILES_PER_DIR){ // TODO: IMPLEMENT THIS BEFORE THERE ARE 32k FILES ON SERVER. // TODO: IMPLEMENT THIS BEFORE THERE ARE 32k FILES ON SERVER. } // TODO: MAKE SURE BOTH THE CURR_SUB_DIR AND CURR_ROOT_DIR WERE SAVED AFTER THE LAST BATCH OF CHANGES IF NEEDED // TODO: MAKE SURE BOTH THE CURR_SUB_DIR AND CURR_ROOT_DIR WERE SAVED AFTER THE LAST BATCH OF CHANGES IF NEEDED } if($isUpload){ $extension = $my_uploader->getExtention(); if($extension == ""){ if(isset($_FILES[$postIndex]['name'])){ $clientFileName = $_FILES[$postIndex]['name']; } $index = strrpos($clientFileName, "."); if($index !== false){ $extension = substr($clientFileName, $index); } } } else { $extension = ""; $index = strrpos($origFileName, "."); if($index !== false){ $extension = substr($origFileName, $index); } } // Actually move the file to its final location. if($isUpload){ $retVal = $MOTIVE_FS_ROOT_DIR."$currRoot/$currSub/$fileName$extension"; $my_uploader->save_file($contentDirectory, UPLOAD_MODE_RENAME, $retVal); if($my_uploader->error != ""){ motive_fs_error($my_uploader->error); $retVal = ""; $finalFileName = ""; } else { $finalFileName = $contentDirectory.$my_uploader->file['name']; } } else { $retVal = $MOTIVE_FS_ROOT_DIR."$currRoot/$currSub/$fileName"; $suffix = ""; $num = 1; // If the destination filename already exists, keep incrementing the suffix until a unique name is found. while(file_exists($contentDirectory.$retVal.$suffix.$extension)){ $suffix = "_copy$num"; $num++; } $finalFileName = $contentDirectory.$retVal.$suffix.$extension; $retVal .= $extension; // Actually move (or copy) the file. if($deleteOriginal){ if(!rename($origFileName, $MOTIVE_FS_MODIFIED_PATH.$finalFileName)){ $retVal = ""; } } else { //if(getVal($_SERVER, 'PHP_SELF') == "the300.php"){ // print "trying: copy($origFileName, $MOTIVE_FS_MODIFIED_PATH$finalFileName)\n"; //} if(!copy($origFileName, $MOTIVE_FS_MODIFIED_PATH.$finalFileName)){ $retVal = ""; } //if(getVal($_SERVER, 'PHP_SELF') == "the300.php"){ //if($retVal == ""){ // print "Copy didn't work.\n"; //} else { // print "Copy worked.\n"; //} //return; //} } } if(($finalFileName != $retVal) && ($retVal != "")){ $errorString = "Duplicate naming during file upload. "; $errorString.= "May be due to too many uploads in rapid succession. "; $errorString.= "(\"$retVal\" renamed to \"$finalFileName\")"; motive_fs_error($errorString); $retVal = $finalFileName; } // Store the info in the database $queryString = "INSERT INTO motive_fs_files (fileName, hash_md5, hash_sha1) VALUES ('$retVal', '$md5', '$sha1')"; if(false === mysql_query($queryString, $motive_fs_db)){ motive_fs_error("Error with query:\n$queryString\n".mysql_error()); } } } return $retVal; } // end motive_fs_commitFile() //// // Deletes a reference to a file. If this was the last reference, // then the file will be deleted from the file system. // // If the recycle-bin is turned on (MOTIVE_FS_RECYCLE_BIN_DAYS > 0), // then the file will be moved to the bin and only deleted after the // specified duration. // // Returns a boolean, true on success, false on failure. //// function motive_fs_deleteFile($fileName_fs){ $retVal = false; GLOBAL $MOTIVE_FS_RECYCLE_BIN_DAYS; if($fileName_fs != ""){ motive_fs_connect(); GLOBAL $motive_fs_db; motive_fs_sendQuery("UPDATE motive_fs_files SET numRefs=numRefs-1 WHERE fileName='$fileName_fs'"); $numRefs = motive_fs_simpleQuery("SELECT SUM(numRefs) FROM motive_fs_files WHERE fileName='$fileName_fs'"); $retVal = true; if($numRefs <= 0){ if($MOTIVE_FS_RECYCLE_BIN_DAYS <= 0){ if((!file_exists($fileName_fs)) || (unlink($fileName_fs))){ // physically deletes the file from the disk motive_fs_sendQuery("DELETE FROM motive_fs_files WHERE fileName='$fileName_fs'"); } else { motive_fs_error("There was an error when attempting to delete \"$fileName_fs\""); $retVal = false; } } else { // TODO: IMPLEMENT MOVING TO RECYCLE BIN HERE logEvent("Tried to use MOTIVE_FS_RECYCLE_BIN which has not been implemented yet!",2); // TODO: IMPLEMENT MOVING TO RECYCLE BIN HERE } } } return $retVal; } // end motive_fs_deleteFile(...) //// // Deletes the items in the recycle bin that have expired. If forceEmpty // is set to true, then it will delete the entire contents of the bin // regardless of the expiration date. //// function motive_fs_emptyRecycleBin(){ // TODO: IMPLEMENT // TODO: IMPLEMENT } // end motive_fs_emptyRecycleBin() //// // Logs the error to the configured error function if one is provided. // Passes this function one parameter: the error string. //// function motive_fs_error($errorString){ GLOBAL $MOTIVE_FS_ERROR_FUNC; if(function_exists($MOTIVE_FS_ERROR_FUNC)){ call_user_func($MOTIVE_FS_ERROR_FUNC, $errorString); } } // end motive_fs_error(...) //// // Connects to the database using the configuration settings from the top of // this file. //// function motive_fs_connect(){ GLOBAL $MOTIVE_FS_DB_HOST; GLOBAL $MOTIVE_FS_DB_USER; GLOBAL $MOTIVE_FS_DB_PASS; GLOBAL $MOTIVE_FS_DB_NAME; GLOBAL $motive_fs_db; if(!isset($motive_fs_db)){ // only connect once. error_reporting(0); $motive_fs_db = mysql_connect($MOTIVE_FS_DB_HOST, $MOTIVE_FS_DB_USER, $MOTIVE_FS_DB_PASS) or die ("Could not connect Motive FS to database"); mysql_select_db($MOTIVE_FS_DB_NAME, $motive_fs_db) or die ("Could not select database in Motive FS"); error_reporting(E_ALL ^ E_NOTICE); } } // end motive_fs_connect() //// // Returns the result of a query which has just one row and one column. // If there are no rows in the result, an empty string is returned. //// function motive_fs_simpleQuery($queryString){ motive_fs_connect(); GLOBAL $motive_fs_db; $retVal = ""; if($result = mysql_query($queryString,$motive_fs_db)){ if(mysql_num_rows($result) > 0){ if($myRow = mysql_fetch_row($result)){ $retVal = $myRow[0]; } } } return $retVal; } // end motive_fs_simpleQuery(...) //// // Sends a query which does not expect a result (an insert, update , etc.) // Logs any errors to the configured error function (MOTIVE_FS_ERROR_FUNC). //// function motive_fs_sendQuery($queryString){ motive_fs_connect(); GLOBAL $motive_fs_db; if(false === mysql_query($queryString, $motive_fs_db)){ motive_fs_error("Error with query:\n$queryString\n".mysql_error()); } } // end motive_fs_sendQuery(...) //// // Given a filename, returns the next filename // in base 36. In this implementation, 0 through 9 are the lowest, then // the lowercase letters a through z are used to represent 10 through 35. //// function motive_fs_nextName($fileName, $index=-2){ if($index == -2){ $index = strlen($fileName)-1; } if($index == -1){ // Start at 1 to allow for shorter names to be prepended with // a 0 later if a single length is desired. $fileName = "1$fileName"; } else { $currChar = strtolower($fileName[$index]); if($currChar == "z"){ $fileName[$index] = "0"; $fileName = motive_fs_nextName($fileName, ($index-1)); } else if($currChar == "9"){ $fileName[$index] = "a"; } else { $currChar++; // if it's not a special-case, let PHP do the counting. $fileName[$index] = $currChar; } } return $fileName; } // end motive_fs_nextName() //// // Returns the integer equivalent of the fileName which is assumed // to be in base-36 as used above. //// function motive_fs_nameToDecimal($fileName){ $retVal = 0; $BASE = 36; $fileName = strtolower($fileName); $maxIndex = strlen($fileName) - 1; for($cnt=0; $cnt= '0') && ($currChar <= '9')){ $digit = ord($currChar) - ord('0'); } else { $digit = ord($currChar) - ord('a') + 10; } $retVal += ($digit * (pow($BASE, ($maxIndex - $cnt)))); } return $retVal; } // end motive_fs_nameToDecimal(...) //// // Will create the .htaccess file to protect that directory from direct access. // Throws errors when attempting to protect directories that don't exist. //// function motive_fs_protectDir($directory){ if(substr($directory, -1) != "/"){ $directory .= "/"; } $content = "IndexIgnore *\n\norder allow,deny\ndeny from all\n"; if(file_exists($directory.".htaccess")){ $currContent= file_get_contents($directory.".htaccess"); if(strpos($currContent, $content) === false){ $currFile = fopen($directory.".htaccess", "a"); fwrite($currFile, $content); fclose($currFile); } } else { $currFile = fopen($directory.".htaccess", "a"); fwrite($currFile, $content); fclose($currFile); } } // end motive_fs_protectDir(...) //// // Should be called once to install Motive FS according to the configuration // set at the top of the file. //// function motive_fs_install(){ GLOBAL $MOTIVE_FS_PROTECT_DIRS; GLOBAL $MOTIVE_FS_ROOT_DIR; // TODO: CREATE THE DATABASE // TODO: CREATE THE DATABASE // TODO: INSTALL THE .SQL FILE // TODO: INSTALL THE .SQL FILE // Protect the contents of the directories from direct access if desired. if($MOTIVE_FS_PROTECT_DIRS){ // .htaccess will protect all sub-directories automatically. motive_fs_protectDir($MOTIVE_FS_ROOT_DIR); } } // end motive_fs_install() ?>