--- sites/all/modules/private_upload/private_upload.module	2008-07-26 03:49:36.000000000 +0300
+++ sites/all/modules/private_upload/private_upload_rc4.module	2009-07-22 17:45:08.000000000 +0300
@@ -10,16 +10,14 @@
  * slow. 
  * 
  * This module addresses these problems: 
- * It creates a secure subdirectory inside fo the standard, public files 
+ * It creates a secure subdirectory inside of the standard, public files
  *   directory (which can be set at admin/settings/private_upload).
- * It warns you if files in the private directory are accessable.
- * It offers to place a .htaccess file in that directory that will prevent 
- *   files from being directly accessed on Apache webservers.
- * It proveds a per-file checkbox for specifying a particular file as public
+ * It provides a per-file checkbox for specifying a particular file as public
  *   or private.
  * It allows you to set the default method to public or private.
  * It only allows access to files to users who have access to the node it is 
  *   attached to.
+ * It is now compatible with Upload Path (see http://drupal.org/node/197590#comment-1176337).
  * 
  * Definition of private: If a user has permission to view the node, they can 
  *  download the attached private file.  If they do not, then even if the have
@@ -62,11 +60,6 @@ function private_upload_menu() {
     'access arguments' => array('administer site configuration'),
     'type' => MENU_NORMAL_ITEM,
   );
-  $items['admin/private_upload/add_htaccess'] = array( 
-    'access arguments' => array('administer site configuration'),
-    'page callback' => '_private_upload_add_htacess',
-    'type' => MENU_CALLBACK,
-  ); 
   $items['admin/private_upload/migrate_private'] = array( 
     'access arguments' => array('administer site configuration'),
     'page callback' => '_private_upload_migrate_private',
@@ -101,74 +94,9 @@ function _private_upload_requirements($p
   $t = get_t();   // Ensure translations don't break at install time.
   $requirements = array();
   
-  if(module_exists('uploadpath')) {
-    $requirements['private_upload_conflict'] = array(
-      'title' => t('Private Upload'),
-      'severity' => REQUIREMENT_ERROR,
-      'value' => $t('Conflict with Upload Path'),
-      'description' => $t('Private Upload will not work if Upload Path is installed'),
-    );  
-  }
-  
   $public = file_directory_path();
   $private_path = _private_upload_path();
   
-  // Attempt to create the directory if it doesn't already exist.
-  if (!file_check_directory($private_path, FILE_CREATE_DIRECTORY)) {
-    $requirements['private_upload_writable'] = array(
-      'title' => t('Private Upload'),
-      'severity' => REQUIREMENT_WARNING,
-      'value' => $t('Private Downloads directory is not writable'),
-      'description' => $t('Please make sure directory !dir exists and is writable.', array('!dir'=>$private_path)),
-    );  
-    $status[] = '<div class="error">'. $requirements['private_upload_writable']['description'] .'</div>';
-  }
-  else {
-    $status[] = "<div class='ok'>$private_path exists and is writable. Great.</div>";
-  }
-    
-  // Write out a .htaccess file if one doesn't already exist in the private folder.
-  if (!file_exists($private_path.'/.htaccess')) {
-    _private_upload_add_htacess();
-  }
-  else {
-    $status[] = '<div class="ok">'. $t("You have an .htaccess file in private folder. Great.") ."</div>";  
-  }  
-  
-  // Write a test file to the private folder to test public access.
-  $test_file = $private_path.'/privacy_test.txt';  
-  if (!file_exists($test_file)) {
-    $test_path = file_create_path($test_file);  
-    file_save_data( "This is just a test.", $test_path, FILE_EXISTS_REPLACE );
-    drupal_set_message("Added test file: $test_path.");
-  }
-  
-  if (file_exists($test_file)) {
-    $url = $GLOBALS['base_url'] .'/'. $test_file;
-    if ( !_private_upload_is_url_protected( $url )) {
-      $requirements['private_upload_readable'] = array(
-        'title' => t('Private Upload'),
-        'severity' => REQUIREMENT_WARNING,
-        'value' => $t('Private directory is publically accessable!'),
-        'description' => $t('Very bad! Your private files are not private!'),
-      ); 
-      $status[] = '<div class="error">'. $requirements['private_upload_readable']['description']. '</div>';
-    }
-    else {
-      $secure = true; // good can't read files in private folder
-      $status[] = '<div class="ok">'. $t("Your private folder is not accessable. Great!"). "</div>";
-    }
-  }
-  else { // unable to write the test file
-    $requirements['private_upload_testfile'] = array(
-      'title' => t('Private Upload'),
-      'severity' => REQUIREMENT_WARNING,
-      'value' => $t('Unable to write test file.'),
-      'description' => $t( "Unable to add test file to your private folder. Unable to test security of your private folder!"),
-    );
-    $status[] = '<div class="error">'. $requirements['private_upload_testfile']['description'] .'</div>';
-  }
- 
   if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {  
     $requirements['private_upload_method'] = array(
       'title' => t('Private Upload'),
@@ -197,6 +125,16 @@ function _private_upload_requirements($p
     $status[] = '<div class="ok">'. $t("There are no old-style private files hanging around. Great.") . '</div>';
   }
   
+  if( module_exists("uploadpath") ) {
+    $requirements['upload_path_exists'] = array(
+      'title' => t('Private Upload'),
+      'severity' => REQUIREMENT_INFO,
+      'value' => $t('Private Downloads'),
+      'description' => $t("You have 'Upload Path' enabled. Private dirs are created relative to each upload path.")
+    );
+    $status[] = '<div class="ok">'. $requirements['upload_path_exists']['description'] .'</div>';
+  }
+
   if( count($requirements) ) { // failed
     if ($phase == 'runtime') {
       foreach( $requirements as $key => $req ) {
@@ -261,42 +199,37 @@ function private_upload_nodeapi(&$node, 
       if (user_access('upload files')) {
         if(is_array($node->files)) {
           foreach ($node->files as $fid => $file) {
+            debug_message('<pre>PRIVATE UPLOAD BEFORE: '.print_r($node->files[$fid],1).'</pre>');
             $file = (object)$file;  // Convert file to object for compatibility
             $fid = $file->fid; // for the cases where we have temp fid for uploaded files
             $success = false;
-	          $filepath = $file->filepath; // need copy if file_move fails.
-	          $public = file_directory_path();
-            $private_path = _private_upload_path(); // actual path of private files
+            $filepath = $file->filepath; // need copy if file_move fails.
+            $file_directory_path = file_directory_path();
+            if (substr($filepath, 0, strlen($file_directory_path)) != $file_directory_path) {
+              $filepath = $file_directory_path .'/'. $filepath; // For new attachments $file->filepath does not contain the file directory path.
+            }
+            debug_message("Filepath: $filepath -> dir: ".dirname($filepath));
             $file_is_private = _private_upload_is_file_private($filepath);
-            if( $file->private && !$file_is_private ) { 
-	            // private flag is set, but file NOT yet listed as being in private repo,
+
+            if( $file->private && !$file_is_private ) {
+            
+ 	          // private flag is set, but file NOT yet listed as being in private repo,
               //   so try and move it from public area to private repo
-              if( file_move($filepath, $private_path, FILE_EXISTS_REPLACE) ) {
-                $success = true;          
-              }
-              else {
-                drupal_set_message( "Could not move the file ($file->filepath) to the private directory ($private_path).", 'error' );
-		          }	                         
+              debug_message("not private -> private");
+
+              _private_upload_file_move($node->nid, $fid, $filepath, true);
             }
             else if (!$file->private && $file_is_private) { 
+
               // private flag is false, but file IS g in private repo
               // so try and move it from private repo to public area
-              if (file_move($filepath, $public, FILE_EXISTS_REPLACE)) {
-                $success = true;
-	            }
-		          else {
-		            drupal_set_message( "Could not move the file ($file->filepath) to the public directory ($public).", 'error' );
-		          }	            
+              debug_message("private -> not private");
+
+              _private_upload_file_move($node->nid, $fid, $filepath, false);
             }
             
-            if( $success ) { // we were able to move the file, so update filepath in db.
-              _private_upload_update_filepath($filepath, $fid);
-              $row_count = db_affected_rows();
-              if( $row_count != 1 ) {
-                drupal_set_message( "Error: Unable to make uploaded file private! (". $row_count .")", 'error'  );
-              }
-            }
-                     
+            debug_message('<pre>PRIVATE UPLOAD AFTER: '.print_r($node->files[$fid],1).'</pre>');
+
           } // Done with all the files.
         }        
       }
@@ -318,6 +251,75 @@ function private_upload_nodeapi(&$node, 
 }
 
 /**
+ * _create_dirname($file, $to_private)
+ *
+ * Create the name of the appropriate directory
+ *
+ * @param $filepath: complete path to the file to be moved
+ * @param $to_private: destination should be private or not
+ */
+
+function _create_dirname($filepath, $to_private) {
+	$private_upload_path = _private_upload_path();
+	
+    if ($to_private) {
+       $dir = dirname($filepath)."/$private_upload_path";
+    } else {
+       $dir = str_replace("/".$private_upload_path, "", dirname($filepath));
+    }
+
+    return $dir;
+}
+
+/**
+ * _private_upload_file_move(&$node, $fid, $file, $destination_path, $to_private)
+ *
+ * Move the file to the destination and create directories when needed
+ *
+ * @param $source_path: complete path to the file to be moved
+ * @param $destination_path: path to the destination
+ * @param $to_private: destination should be private or not
+*/
+
+function _private_upload_file_move($nid, $fid, $file, $to_private) {
+   debug_message("_private_upload_file_move($nid, $fid, $file, $to_private)");
+    
+   $destination_path = _create_dirname($file, $to_private);
+   debug_message("Destination: $destination_path");
+
+   if( !file_check_directory( $destination_path ) ) {
+     debug_message("Create $destination_path");
+     file_check_directory($destination_path, FILE_CREATE_DIRECTORY);
+   }
+
+  // Write out a .htaccess file if one doesn't already exist in the private folder.
+  if ($to_private && !file_exists($destination_path.'/.htaccess')) {
+     debug_message("Create '/.htaccess'");
+     _private_upload_add_htaccess($destination_path);
+  }
+
+  // Move the file
+  if ( file_move($file, $destination_path, FILE_EXISTS_REPLACE) ) {
+    debug_message("File (".basename($file).") moved to the directory ($destination_path).");
+    $success = true;
+  } else {
+    drupal_set_message( "Could not move the file (".basename($file).") to the directory ($destination_path).", 'error' );
+  }
+
+  if ( $success ) { // we were able to move the file, so update filepath in db.
+     debug_message("_private_upload_update_filepath($file, $fid)");
+     _private_upload_update_filepath($file, $fid);
+     $row_count = db_affected_rows();
+     if( $row_count != 1 ) {
+        drupal_set_message( "Error: Unable to make uploaded file private! (". $row_count .")", 'error'  );
+        $success = false;
+     }
+  }
+
+  return $success;
+}
+
+/**
  * hook_form_alter().
  * 
  * Inject the 'private' checkboxes into the upload form.
@@ -495,38 +497,21 @@ function private_upload_admin() {
   _private_upload_requirements('runtime', $status);  
     
   // loop through all the file in private folder & collect stats.
-  $private_file_count = 0;
-  $d = opendir( $private_path );
-  if( $d ) {
-    while( $f = readdir($d) ) {
-      if( $f != '.' && $f != '..' && $f != '.htaccess' && $f != 'privacy_test.txt') {
-        $private_file_count++;
-      }
-    }
-    closedir( $d );
-   
-    $db_private_file_count = db_result( db_query("SELECT COUNT(fid) FROM {files} WHERE filepath REGEXP '^%s'", $private_path) );
-    $output = t( "There are '!fs_count' files in the private folder, ".
+  $private_file_count = count(get_private_files( $public ));
+
+  $db_private_file_count = db_result( db_query("SELECT COUNT(fid) FROM {files} WHERE filepath REGEXP '%s'", $private_path) );
+  $output = t( "There are '!fs_count' files in the private folder(s), ".
                  " and the DB thinks there are '!db_count' private files.", 
                  array( '!fs_count'=>$private_file_count, '!db_count'=>$db_private_file_count) );
-    if( $db_private_file_count != $private_file_count ) {
+  if( $db_private_file_count != $private_file_count ) {
       $status[] = '<div class="error">'. $output .'</div>';  
     }
     else {
       $status[] = '<div class="ok">'. $output . t(" Great.") . '</div>';
     }
-  }
-  else { // unable to open folder!
-    $status[] = '<div class="error">'. 
-                t("'!private_path' is not a valid directory (!is).", 
-                  array('!private_path'=>$private_path, '!is'=>is_dir($private_path))) .
-                '</div>';  
-  }
-  
+
   // check for public files attached to private nodes.
-  $count = db_result( db_query('SELECT COUNT(DISTINCT(f.fid)) '. 
-                     'FROM {files} f INNER JOIN {upload} u ON f.fid=u.fid INNER JOIN {node_access} na ON u.nid=na.nid '.
-                     'WHERE na.gid != 0 AND f.filepath NOT REGEXP "^%s"', $private_path));
+  $count = db_result( db_query('SELECT COUNT(fid) FROM {files} WHERE filepath NOT REGEXP "%s"', $private_path));
   if( $count ) {
     $status[] = t("There are !count public files attached to private nodes. ", array('!count'=>$count)).
                 l( 'Click here to make them all private.', 'admin/private_upload/migrate_private');
@@ -547,31 +532,22 @@ function private_upload_admin() {
   return system_settings_form($form);
 }
 
-/**
- * Make sure the new private_upload_path can be created and writen to.
- */
-function private_upload_admin_validate($form_id, $form_values) {
-  variable_set('private_upload_path', $form_values['private_upload_path'] );
-  $private_upload_path = file_create_path($form_values['private_upload_path']);
-  
-  // FILE_CREATE_DIRECTORY and FILE_MODIFY_PERMISSIONS
-  if (!file_check_directory($private_upload_path, TRUE, 'private_upload_path')) {
-    return false;
-  }
-}
-
 // *****************************************************************************
 // Callback Functions **********************************************************
 // *****************************************************************************
 
 /**
  * Callback to inject an .htaccess file into the private_upload_path folder
+ * @param path: specifies path in which file should be made
  */
-function _private_upload_add_htacess() {
-  $path = file_create_path( _private_upload_path() .'/.htaccess' );  
-  file_save_data( "SetHandler This_is_a_Drupal_security_line_do_not_remove
-Deny from all", $path, FILE_EXISTS_REPLACE );
-  drupal_set_message("Added .htaccess file at $path");
+
+function _private_upload_add_htaccess($path) {
+  $file = file_create_path( $path .'/.htaccess' );
+  if (!file_exists($file)) {
+    file_save_data( "SetHandler This_is_a_Drupal_security_line_do_not_remove
+Deny from all", $file, FILE_EXISTS_REPLACE );
+    debug_message("Added .htaccess file to $path");
+  }
 }
 
 /**
@@ -582,19 +558,18 @@ Deny from all", $path, FILE_EXISTS_REPLA
 function _private_upload_migrate_private() {
   $private_path = _private_upload_path();
   $result = db_query('SELECT f.* FROM {files} f, {node_access} na '.
-                     ' WHERE f.nid = na.nid AND na.gid != 0 AND f.filepath NOT REGEXP "^%s" '.
+                     ' WHERE f.nid = na.nid AND na.gid != 0 AND f.filepath NOT REGEXP "%s" '.
                      ' GROUP BY f.fid', $private_path  );
   while( $file = db_fetch_object($result) ) {
     // file is attached to a private node, but is a public file, so move it.
     $filepath = $file->filepath;
-    if( file_move($filepath, $private_path, FILE_EXISTS_REPLACE) ) {
-      $output .= t("Making !filename private", array('!filename'=>$file->filename)). "<br>";     
-      _private_upload_update_filepath($filepath, $file->fid);
+    if( _private_upload_file_move($file->nid, $file->fid, $filepath, true) ) {
+      $output .= t("Making !filename private", array('!filename'=>$file->filename)). "<br>";
     }
     else {
-      $output .= t("Could not move %filepath to private directory (fid: %fid attached to node: nid).", 
-                   array('%filepath'=>$file->filepath, '%fid'=>$file->fid, 'nid'=>$file->nid)). "<br>";          
-    }                        
+      $output .= t("Could not move %filepath to private directory (fid: %fid attached to node: nid).",
+                   array('%filepath'=>$file->filepath, '%fid'=>$file->fid, 'nid'=>$file->nid)). "<br>";
+    }
   }
   return $output;
 }
@@ -603,10 +578,10 @@ function _private_upload_migrate_private
  * Set the filepath for the file in the db.
  *
  * @param string $filepath
- * @param int $fid: Unique id for file. 
+ * @param int $fid: Unique id for file.
  */
 function _private_upload_update_filepath($filepath, $fid) {
-  db_query("UPDATE {files} SET filepath = '%s' WHERE fid=%d", $filepath, $fid); 
+  db_query("UPDATE {files} SET filepath = '%s' WHERE fid=%d", $filepath, $fid);
 }
 
 // *****************************************************************************
@@ -681,13 +656,65 @@ function theme_private_upload_attachment
 // Utility functions ***********************************************************
 // *****************************************************************************
 
+function get_private_files($root_dir, $all_data=array()) {
+    // make any specific files you wish to be excluded
+    $ignore_files = array(".htaccess");
+    $ignore_regex = '/^_/';
+    // skip these directories
+    $ignore_dirs = array(".", "..");
+    $private_dir = _private_upload_path();
+
+    // run through content of root directory
+    $dir_content = scandir($root_dir);
+    foreach($dir_content as $key => $content)
+    {
+      $path = $root_dir.'/'.$content;
+      if(is_file($path) && is_readable($path) && strpos($path, $private_dir))
+      {
+        // skip ignored files
+        if(!in_array($content, $ignore_files))
+        {
+          if (preg_match($ignore_regex,$content) == 0)
+          {
+            $content_chunks = explode(".",$content);
+            $ext = $content_chunks[count($content_chunks) - 1];
+            // save file name with path
+            debug_message("Private file $path"); // this outputs all files regarded as private
+            $all_data[] = $path;
+          }
+        }
+      }
+      // if content is a directory and readable, add path and name
+      elseif(is_dir($path) && is_readable($path))
+      {
+        // skip any ignored dirs
+        if(!in_array($content, $ignore_dirs))
+        {
+          // recursive callback to open new directory
+          $all_data = get_private_files($path, $all_data);
+        }
+      }
+    } // end foreach
+    return $all_data;
+} // end get_private_files()
+
 /**
- * Returns the system path for the private folder,
- * Or false if the folder is invalid.
- * 
- */
+ *  debug_message: display debug message
+ *  @param $string - string to display when debugging
+*/
+function debug_message ( $string ) {
+    $debugging = false; // set this to true to get detailed logging
+
+    if ( $debugging) {
+      drupal_set_message($string);
+    }
+
+    return;
+} // end debug_message
+
 function _private_upload_path() {
-  return file_create_path( variable_get('private_upload_path', 'private') );
+  //return file_create_path( variable_get('private_upload_path', 'private') );
+  return variable_get('private_upload_path', 'private');
 }
 
 /**
@@ -714,8 +741,8 @@ function _private_upload_replace_start_w
 function _private_upload_is_file_private( $filepath ) {
 //  $private_prefix = 'private_upload'; // fake path of private file (for public consumption and menu)
   $private_path = _private_upload_path();
-  $is_in = _private_upload_starts_with($filepath, $private_path );
-  return $is_in;              
+  $is_in = strpos($filepath, $private_path );
+  return $is_in;
 }
 
 
