diff options
Diffstat (limited to 'lib')
110 files changed, 7169 insertions, 2417 deletions
diff --git a/lib/MDB2/Driver/Function/sqlite3.php b/lib/MDB2/Driver/Function/sqlite3.php index a013aea165a..1af262fd7a7 100644 --- a/lib/MDB2/Driver/Function/sqlite3.php +++ b/lib/MDB2/Driver/Function/sqlite3.php @@ -134,4 +134,3 @@ class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common // }}} } -?> diff --git a/lib/MDB2/Driver/Manager/sqlite3.php b/lib/MDB2/Driver/Manager/sqlite3.php index 8f4e1312eb8..10255a3619a 100644 --- a/lib/MDB2/Driver/Manager/sqlite3.php +++ b/lib/MDB2/Driver/Manager/sqlite3.php @@ -1360,4 +1360,3 @@ class MDB2_Driver_Manager_sqlite3 extends MDB2_Driver_Manager_Common // }}} } -?> diff --git a/lib/MDB2/Driver/sqlite3.php b/lib/MDB2/Driver/sqlite3.php index 39d3fb6727d..6bfccadad9a 100644 --- a/lib/MDB2/Driver/sqlite3.php +++ b/lib/MDB2/Driver/sqlite3.php @@ -1221,7 +1221,7 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common return $affected_rows; } - $result =& $this->db->_wrapResult($result, $this->result_types, + $result = $this->db->_wrapResult($result, $this->result_types, $result_class, $result_wrap_class, $this->limit, $this->offset); $this->db->debug($this->query, 'execute', array('is_manip' => $this->is_manip, 'when' => 'post', 'result' => $result)); return $result; @@ -1332,5 +1332,3 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common $this->free(); } } - -?> diff --git a/lib/app.php b/lib/app.php index f9292b331bb..74315903467 100644..100755 --- a/lib/app.php +++ b/lib/app.php @@ -27,7 +27,6 @@ * upgrading and removing apps. */ class OC_App{ - static private $apps = array(); static private $activeapp = ''; static private $navigation = array(); static private $settingsForms = array(); @@ -36,6 +35,7 @@ class OC_App{ static private $appInfo = array(); static private $appTypes = array(); static private $loadedApps = array(); + static private $checkedApps = array(); /** * @brief loads all apps @@ -49,15 +49,7 @@ class OC_App{ * if $types is set, only apps of those types will be loaded */ public static function loadApps($types=null){ - // Our very own core apps are hardcoded - foreach( array( 'settings') as $app ){ - if(is_null($types) && !in_array($app, self::$loadedApps)){ - require( $app.'/appinfo/app.php' ); - self::$loadedApps[] = $app; - } - } - - // The rest comes here + // Load the enabled apps here $apps = self::getEnabledApps(); // prevent app.php from printing output ob_start(); @@ -69,6 +61,19 @@ class OC_App{ } ob_end_clean(); + if (!defined('DEBUG') || !DEBUG){ + if (is_null($types)) { + OC_Util::$core_scripts = OC_Util::$scripts; + OC_Util::$scripts = array(); + OC_Util::$core_styles = OC_Util::$styles; + OC_Util::$styles = array(); + + if (!OC_AppConfig::getValue('core', 'remote_core.css', false)) { + OC_AppConfig::setValue('core', 'remote_core.css', '/core/minimizer.php'); + OC_AppConfig::setValue('core', 'remote_core.js', '/core/minimizer.php'); + } + } + } // return return true; } @@ -78,7 +83,8 @@ class OC_App{ * @param string app */ public static function loadApp($app){ - if(is_file(OC::$APPSROOT.'/apps/'.$app.'/appinfo/app.php')){ + if(is_file(self::getAppPath($app).'/appinfo/app.php')){ + self::checkUpgrade($app); require_once( $app.'/appinfo/app.php' ); } } @@ -138,6 +144,8 @@ class OC_App{ * get all enabled apps */ public static function getEnabledApps(){ + if(!OC_Config::getValue('installed', false)) + return array(); $apps=array('files'); $query = OC_DB::prepare( 'SELECT `appid` FROM `*PREFIX*appconfig` WHERE `configkey` = \'enabled\' AND `configvalue`=\'yes\'' ); $result=$query->execute(); @@ -175,7 +183,7 @@ class OC_App{ if(!OC_Installer::isInstalled($app)){ // check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string if(!is_numeric($app)){ - OC_Installer::installShippedApp($app); + $app = OC_Installer::installShippedApp($app); }else{ $download=OC_OCSClient::getApplicationDownload($app,1); if(isset($download['downloadlink']) and $download['downloadlink']!='') { @@ -187,7 +195,7 @@ class OC_App{ // check if the app is compatible with this version of ownCloud $info=OC_App::getAppInfo($app); $version=OC_Util::getVersion(); - if(!isset($info['require']) or ($version[0]>$info['require'])){ + if(!isset($info['require']) or ($version[0]>$info['require'])){ OC_Log::write('core','App "'.$info['name'].'" can\'t be installed because it is not compatible with this version of ownCloud',OC_Log::ERROR); return false; }else{ @@ -197,6 +205,7 @@ class OC_App{ }else{ return false; } + return $app; } /** @@ -212,36 +221,6 @@ class OC_App{ } /** - * @brief makes owncloud aware of this app - * @param $data array with all information - * @returns true/false - * - * This function registers the application. $data is an associative array. - * The following keys are required: - * - id: id of the application, has to be unique ('addressbook') - * - name: Human readable name ('Addressbook') - * - version: array with Version (major, minor, bugfix) ( array(1, 0, 2)) - * - * The following keys are optional: - * - order: integer, that influences the position of your application in - * a list of applications. Lower values come first. - * - */ - public static function register( $data ){ - OC_App::$apps[] = $data; - } - - /** - * @brief returns information of all apps - * @return array with all information - * - * This function returns all data it got via register(). - */ - public static function get(){ - return OC_App::$apps; - } - - /** * @brief adds an entry to the navigation * @param $data array containing the data * @returns true/false @@ -320,10 +299,15 @@ class OC_App{ // settings menu $settings[]=array( "id" => "settings", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "settings.php" ), "name" => $l->t("Settings"), "icon" => OC_Helper::imagePath( "settings", "settings.svg" )); - // if the user is an admin - if(OC_Group::inGroup( $_SESSION["user_id"], "admin" )) { + //SubAdmins are also allowed to access user management + if(OC_SubAdmin::isSubAdmin($_SESSION["user_id"]) || OC_Group::inGroup( $_SESSION["user_id"], "admin" )){ // admin users menu $settings[] = array( "id" => "core_users", "order" => 2, "href" => OC_Helper::linkTo( "settings", "users.php" ), "name" => $l->t("Users"), "icon" => OC_Helper::imagePath( "settings", "users.svg" )); + } + + + // if the user is an admin + if(OC_Group::inGroup( $_SESSION["user_id"], "admin" )) { // admin apps menu $settings[] = array( "id" => "core_apps", "order" => 3, "href" => OC_Helper::linkTo( "settings", "apps.php" ).'?installed', "name" => $l->t("Apps"), "icon" => OC_Helper::imagePath( "settings", "apps.svg" )); @@ -353,16 +337,65 @@ class OC_App{ } /** + * Get the path where to install apps + */ + public static function getInstallPath() { + if(OC_Config::getValue('appstoreenabled', true)==false) { + return false; + } + + foreach(OC::$APPSROOTS as $dir) { + if(isset($dir['writable']) && $dir['writable']===true) + return $dir['path']; + } + + OC_Log::write('core','No application directories are marked as writable.',OC_Log::ERROR); + return null; + } + + + protected static function findAppInDirectories($appid) { + static $app_dir = array(); + if (isset($app_dir[$appid])) { + return $app_dir[$appid]; + } + foreach(OC::$APPSROOTS as $dir) { + if(file_exists($dir['path'].'/'.$appid)) { + return $app_dir[$appid]=$dir; + } + } + } + /** + * Get the directory for the given app. + * If the app is defined in multiple directory, the first one is taken. (false if not found) + */ + public static function getAppPath($appid) { + if( ($dir = self::findAppInDirectories($appid)) != false) { + return $dir['path'].'/'.$appid; + } + } + + /** + * Get the path for the given app on the access + * If the app is defined in multiple directory, the first one is taken. (false if not found) + */ + public static function getAppWebPath($appid) { + if( ($dir = self::findAppInDirectories($appid)) != false) { + return OC::$WEBROOT.$dir['url'].'/'.$appid; + } + } + + /** * get the last version of the app, either from appinfo/version or from appinfo/info.xml */ public static function getAppVersion($appid){ - $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/version'; + $file= self::getAppPath($appid).'/appinfo/version'; $version=@file_get_contents($file); if($version){ - return $version; + return trim($version); }else{ $appData=self::getAppInfo($appid); - return $appData['version']; + return isset($appData['version'])? $appData['version'] : ''; } } @@ -379,7 +412,7 @@ class OC_App{ if(isset(self::$appInfo[$appid])){ return self::$appInfo[$appid]; } - $file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml'; + $file= self::getAppPath($appid).'/appinfo/info.xml'; } $data=array(); $content=@file_get_contents($file); @@ -470,21 +503,21 @@ class OC_App{ * register a settings form to be shown */ public static function registerSettings($app,$page){ - self::$settingsForms[]='apps/'.$app.'/'.$page.'.php'; + self::$settingsForms[]= $app.'/'.$page.'.php'; } /** * register an admin form to be shown */ public static function registerAdmin($app,$page){ - self::$adminForms[]='apps/'.$app.'/'.$page.'.php'; + self::$adminForms[]= $app.'/'.$page.'.php'; } /** * register a personal form to be shown */ public static function registerPersonal($app,$page){ - self::$personalForms[]='apps/'.$app.'/'.$page.'.php'; + self::$personalForms[]= $app.'/'.$page.'.php'; } /** @@ -492,32 +525,33 @@ class OC_App{ */ public static function getAllApps(){ $apps=array(); - $dh=opendir(OC::$APPSROOT.'/apps'); - while($file=readdir($dh)){ - if($file[0]!='.' and is_file(OC::$APPSROOT.'/apps/'.$file.'/appinfo/app.php')){ - $apps[]=$file; + foreach(OC::$APPSROOTS as $apps_dir) { + $dh=opendir($apps_dir['path']); + while($file=readdir($dh)){ + if($file[0]!='.' and is_file($apps_dir['path'].'/'.$file.'/appinfo/app.php')){ + $apps[]=$file; + } } } return $apps; } /** - * check if any apps need updating and update those + * check if the app need updating and update when needed */ - public static function updateApps(){ + public static function checkUpgrade($app) { + if (in_array($app, self::$checkedApps)) { + return; + } + self::$checkedApps[] = $app; $versions = self::getAppVersions(); - //ensure files app is installed for upgrades - if(!isset($versions['files'])){ - $versions['files']='0'; - } - foreach( $versions as $app=>$installedVersion ){ - $currentVersion=OC_App::getAppVersion($app); - if ($currentVersion) { - if (version_compare($currentVersion, $installedVersion, '>')) { - OC_Log::write($app, 'starting app upgrade from '.$installedVersion.' to '.$currentVersion,OC_Log::DEBUG); - OC_App::updateApp($app); - OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); - } + $currentVersion=OC_App::getAppVersion($app); + if ($currentVersion) { + $installedVersion = $versions[$app]; + if (version_compare($currentVersion, $installedVersion, '>')) { + OC_Log::write($app, 'starting app upgrade from '.$installedVersion.' to '.$currentVersion,OC_Log::DEBUG); + OC_App::updateApp($app); + OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); } } } @@ -544,9 +578,13 @@ class OC_App{ } /** - * get the installed version of all papps + * get the installed version of all apps */ public static function getAppVersions(){ + static $versions; + if (isset($versions)) { // simple cache, needs to be fixed + return $versions; // when function is used besides in checkUpgrade + } $versions=array(); $query = OC_DB::prepare( 'SELECT `appid`, `configvalue` FROM `*PREFIX*appconfig` WHERE `configkey` = \'installed_version\'' ); $result = $query->execute(); @@ -561,24 +599,24 @@ class OC_App{ * @param string appid */ public static function updateApp($appid){ - if(file_exists(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml')){ - OC_DB::updateDbFromStructure(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/database.xml'); + if(file_exists(self::getAppPath($appid).'/appinfo/database.xml')){ + OC_DB::updateDbFromStructure(self::getAppPath($appid).'/appinfo/database.xml'); } if(!self::isEnabled($appid)){ return; } - if(file_exists(OC::$APPSROOT.'/apps/'.$appid.'/appinfo/update.php')){ + if(file_exists(self::getAppPath($appid).'/appinfo/update.php')){ self::loadApp($appid); - include OC::$APPSROOT.'/apps/'.$appid.'/appinfo/update.php'; + include self::getAppPath($appid).'/appinfo/update.php'; } //set remote/public handelers $appData=self::getAppInfo($appid); foreach($appData['remote'] as $name=>$path){ - OCP\CONFIG::setAppValue('core', 'remote_'.$name, '/apps/'.$appid.'/'.$path); + OCP\CONFIG::setAppValue('core', 'remote_'.$name, $appid.'/'.$path); } foreach($appData['public'] as $name=>$path){ - OCP\CONFIG::setAppValue('core', 'public_'.$name, '/apps/'.$appid.'/'.$path); + OCP\CONFIG::setAppValue('core', 'public_'.$name, $appid.'/'.$path); } self::setAppTypes($appid); diff --git a/lib/appconfig.php b/lib/appconfig.php index 20fbb5313b8..372cded9a5f 100644 --- a/lib/appconfig.php +++ b/lib/appconfig.php @@ -4,7 +4,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/lib/archive.php b/lib/archive.php index 113f92e9604..fabd7cc7a51 100644 --- a/lib/archive.php +++ b/lib/archive.php @@ -112,4 +112,25 @@ abstract class OC_Archive{ * @return resource */ abstract function getStream($path,$mode); + /** + * add a folder and all it's content + * @param string $path + * @param string source + * @return bool + */ + function addRecursive($path,$source){ + if($dh=opendir($source)){ + $this->addFolder($path); + while($file=readdir($dh)){ + if($file=='.' or $file=='..'){ + continue; + } + if(is_dir($source.'/'.$file)){ + $this->addRecursive($path.'/'.$file,$source.'/'.$file); + }else{ + $this->addFile($path.'/'.$file,$source.'/'.$file); + } + } + } + } } diff --git a/lib/archive/tar.php b/lib/archive/tar.php index 944a0ac4ba4..f6efd6d0ecc 100644 --- a/lib/archive/tar.php +++ b/lib/archive/tar.php @@ -56,14 +56,21 @@ class OC_Archive_TAR extends OC_Archive{ * @return bool */ function addFolder($path){ - $tmpBase=get_temp_dir().'/'; + $tmpBase=OC_Helper::tmpFolder(); if(substr($path,-1,1)!='/'){ $path.='/'; } if($this->fileExists($path)){ return false; } - mkdir($tmpBase.$path); + $parts=explode('/',$path); + $folder=$tmpBase; + foreach($parts as $part){ + $folder.='/'.$part; + if(!is_dir($folder)){ + mkdir($folder); + } + } $result=$this->tar->addModify(array($tmpBase.$path),'',$tmpBase); rmdir($tmpBase.$path); $this->fileList=false; @@ -79,7 +86,7 @@ class OC_Archive_TAR extends OC_Archive{ if($this->fileExists($path)){ $this->remove($path); } - if(file_exists($source)){ + if($source and $source[0]=='/' and file_exists($source)){ $header=array(); $dummy=''; $this->tar->_openAppend(); diff --git a/lib/archive/zip.php b/lib/archive/zip.php index 6631a649b16..c1a5c35738b 100644 --- a/lib/archive/zip.php +++ b/lib/archive/zip.php @@ -11,7 +11,6 @@ class OC_Archive_ZIP extends OC_Archive{ * @var ZipArchive zip */ private $zip=null; - private $success=false; private $path; function __construct($source){ @@ -37,7 +36,7 @@ class OC_Archive_ZIP extends OC_Archive{ * @return bool */ function addFile($path,$source=''){ - if(file_exists($source)){ + if($source and $source[0]=='/' and file_exists($source)){ $result=$this->zip->addFile($source,$path); }else{ $result=$this->zip->addFromString($path,$source); @@ -74,8 +73,7 @@ class OC_Archive_ZIP extends OC_Archive{ * @return int */ function mtime($path){ - $stat=$this->zip->statName($path); - return $stat['mtime']; + return filemtime($this->path); } /** * get the files in a folder diff --git a/lib/backgroundjob/queuedtask.php b/lib/backgroundjob/queuedtask.php new file mode 100644 index 00000000000..68ba97c1e39 --- /dev/null +++ b/lib/backgroundjob/queuedtask.php @@ -0,0 +1,104 @@ +<?php +/** +* ownCloud +* +* @author Jakob Sack +* @copyright 2012 Jakob Sack owncloud@jakobsack.de +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * This class manages our queued tasks. + */ +class OC_BackgroundJob_QueuedTask{ + /** + * @brief Gets one queued task + * @param $id ID of the task + * @return associative array + */ + public static function find( $id ){ + $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*queuedtasks WHERE id = ?' ); + $result = $stmt->execute(array($id)); + return $result->fetchRow(); + } + + /** + * @brief Gets all queued tasks + * @return array with associative arrays + */ + public static function all(){ + // Array for objects + $return = array(); + + // Get Data + $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*queuedtasks' ); + $result = $stmt->execute(array()); + while( $row = $result->fetchRow()){ + $return[] = $row; + } + + return $return; + } + + /** + * @brief Gets all queued tasks of a specific app + * @param $app app name + * @return array with associative arrays + */ + public static function whereAppIs( $app ){ + // Array for objects + $return = array(); + + // Get Data + $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*queuedtasks WHERE app = ?' ); + $result = $stmt->execute(array($app)); + while( $row = $result->fetchRow()){ + $return[] = $row; + } + + // Und weg damit + return $return; + } + + /** + * @brief queues a task + * @param $app app name + * @param $klass class name + * @param $method method name + * @param $parameters all useful data as text + * @return id of task + */ + public static function add( $app, $klass, $method, $parameters ){ + $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*queuedtasks (app, klass, method, parameters) VALUES(?,?,?,?)' ); + $result = $stmt->execute(array($app, $klass, $method, $parameters )); + + return OC_DB::insertid(); + } + + /** + * @brief deletes a queued task + * @param $id id of task + * @return true/false + * + * Deletes a report + */ + public static function delete( $id ){ + $stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*queuedtasks WHERE id = ?' ); + $result = $stmt->execute(array($id)); + + return true; + } +} diff --git a/lib/backgroundjob/regulartask.php b/lib/backgroundjob/regulartask.php new file mode 100644 index 00000000000..53bd4eb5e9b --- /dev/null +++ b/lib/backgroundjob/regulartask.php @@ -0,0 +1,52 @@ +<?php +/** +* ownCloud +* +* @author Jakob Sack +* @copyright 2012 Jakob Sack owncloud@jakobsack.de +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * This class manages the regular tasks. + */ +class OC_BackgroundJob_RegularTask{ + static private $registered = array(); + + /** + * @brief creates a regular task + * @param $klass class name + * @param $method method name + * @return true + */ + static public function register( $klass, $method ){ + // Create the data structure + self::$registered["$klass-$method"] = array( $klass, $method ); + + // No chance for failure ;-) + return true; + } + + /** + * @brief gets all regular tasks + * @return associative array + * + * key is string "$klass-$method", value is array( $klass, $method ) + */ + static public function all(){ + return self::$registered; + } +} diff --git a/lib/backgroundjob/worker.php b/lib/backgroundjob/worker.php new file mode 100644 index 00000000000..b4f0429b319 --- /dev/null +++ b/lib/backgroundjob/worker.php @@ -0,0 +1,118 @@ +<?php +/** +* ownCloud +* +* @author Jakob Sack +* @copyright 2012 Jakob Sack owncloud@jakobsack.de +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * This class does the dirty work. + * + * TODO: locking in doAllSteps + */ +class OC_BackgroundJob_Worker{ + /** + * @brief executes all tasks + * @return boolean + * + * This method executes all regular tasks and then all queued tasks. + * This method should be called by cli scripts that do not let the user + * wait. + */ + public static function doAllSteps(){ + // Do our regular work + $lasttask = OC_Appconfig::getValue( 'core', 'backgroundjobs_task', '' ); + + $regular_tasks = OC_BackgroundJob_RegularTask::all(); + ksort( $regular_tasks ); + foreach( $regular_tasks as $key => $value ){ + if( strcmp( $key, $lasttask ) > 0 ){ + // Set "restart here" config value + OC_Appconfig::setValue( 'core', 'backgroundjobs_task', $key ); + call_user_func( $value ); + } + } + // Reset "start here" config value + OC_Appconfig::setValue( 'core', 'backgroundjobs_task', '' ); + + // Do our queued tasks + $queued_tasks = OC_BackgroundJob_QueuedTask::all(); + foreach( $queued_tasks as $task ){ + OC_BackgroundJob_QueuedTask::delete( $task['id'] ); + call_user_func( array( $task['klass'], $task['method'] ), $task['parameters'] ); + } + + return true; + } + + /** + * @brief does a single task + * @return boolean + * + * This method executes one task. It saves the last state and continues + * with the next step. This method should be used by webcron and ajax + * services. + */ + public static function doNextStep(){ + $laststep = OC_Appconfig::getValue( 'core', 'backgroundjobs_step', 'regular_tasks' ); + + if( $laststep == 'regular_tasks' ){ + // get last app + $lasttask = OC_Appconfig::getValue( 'core', 'backgroundjobs_task', '' ); + + // What's the next step? + $regular_tasks = OC_BackgroundJob_RegularTask::all(); + ksort( $regular_tasks ); + $done = false; + + // search for next background job + foreach( $regular_tasks as $key => $value ){ + if( strcmp( $key, $lasttask ) > 0 ){ + OC_Appconfig::setValue( 'core', 'backgroundjobs_task', $key ); + $done = true; + call_user_func( $value ); + break; + } + } + + if( $done == false ){ + // Next time load queued tasks + OC_Appconfig::setValue( 'core', 'backgroundjobs_step', 'queued_tasks' ); + } + } + else{ + $tasks = OC_BackgroundJob_QueuedTask::all(); + if( count( $tasks )){ + $task = $tasks[0]; + // delete job before we execute it. This prevents endless loops + // of failing jobs. + OC_BackgroundJob_QueuedTask::delete($task['id']); + + // execute job + call_user_func( array( $task['klass'], $task['method'] ), $task['parameters'] ); + } + else{ + // Next time load queued tasks + OC_Appconfig::setValue( 'core', 'backgroundjobs_step', 'regular_tasks' ); + OC_Appconfig::setValue( 'core', 'backgroundjobs_task', '' ); + } + } + + return true; + } +} diff --git a/lib/base.php b/lib/base.php index 67f8e7702fc..b4f3e667133 100644 --- a/lib/base.php +++ b/lib/base.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -31,30 +31,18 @@ class OC{ */ public static $CLASSPATH = array(); /** - * $_SERVER['DOCUMENTROOT'] but without symlinks - */ - public static $DOCUMENTROOT = ''; - /** * The installation path for owncloud on the server (e.g. /srv/http/owncloud) */ public static $SERVERROOT = ''; /** * the current request path relative to the owncloud root (e.g. files/index.php) */ - public static $SUBURI = ''; + private static $SUBURI = ''; /** * the owncloud root path for http requests (e.g. owncloud/) */ public static $WEBROOT = ''; /** - * the folder that stores that data files for the filesystem of the user (e.g. /srv/http/owncloud/data/myusername/files) - */ - public static $CONFIG_DATADIRECTORY = ''; - /** - * the folder that stores the data for the root filesystem (e.g. /srv/http/owncloud/data) - */ - public static $CONFIG_DATADIRECTORY_ROOT = ''; - /** * The installation path of the 3rdparty folder on the server (e.g. /srv/http/owncloud/3rdparty) */ public static $THIRDPARTYROOT = ''; @@ -63,13 +51,9 @@ class OC{ */ public static $THIRDPARTYWEBROOT = ''; /** - * The installation path of the apps folder on the server (e.g. /srv/http/owncloud) - */ - public static $APPSROOT = ''; - /** - * the root path of the apps folder for http requests (e.g. owncloud) + * The installation path array of the apps folder on the server (e.g. /srv/http/owncloud) 'path' and web path in 'url' */ - public static $APPSWEBROOT = ''; + public static $APPSROOTS = array(); /* * requested app */ @@ -79,11 +63,19 @@ class OC{ */ public static $REQUESTEDFILE = ''; /** + * check if owncloud runs in cli mode + */ + public static $CLI = false; + /** * SPL autoload */ public static function autoload($className){ if(array_key_exists($className,OC::$CLASSPATH)){ - require_once OC::$CLASSPATH[$className]; + /** @TODO: Remove this when necessary + Remove "apps/" from inclusion path for smooth migration to mutli app dir + */ + $path = preg_replace('/apps\//','', OC::$CLASSPATH[$className]); + require_once $path; } elseif(strpos($className,'OC_')===0){ require_once strtolower(str_replace('_','/',substr($className,3)) . '.php'); @@ -91,6 +83,9 @@ class OC{ elseif(strpos($className,'OCP\\')===0){ require_once 'public/'.strtolower(str_replace('\\','/',substr($className,3)) . '.php'); } + elseif(strpos($className,'OCA\\')===0){ + require_once 'apps/'.strtolower(str_replace('\\','/',substr($className,3)) . '.php'); + } elseif(strpos($className,'Sabre_')===0) { require_once str_replace('_','/',$className) . '.php'; } @@ -99,34 +94,8 @@ class OC{ } } - /** - * autodetects the formfactor of the used device - * default -> the normal desktop browser interface - * mobile -> interface for smartphones - * tablet -> interface for tablets - * standalone -> the default interface but without header, footer and sidebar. just the application. useful to ue just a specific app on the desktop in a standalone window. - */ - public static function detectFormfactor(){ - // please add more useragent strings for other devices - if(isset($_SERVER['HTTP_USER_AGENT'])){ - if(stripos($_SERVER['HTTP_USER_AGENT'],'ipad')>0) { - $mode='tablet'; - }elseif(stripos($_SERVER['HTTP_USER_AGENT'],'iphone')>0){ - $mode='mobile'; - }elseif((stripos($_SERVER['HTTP_USER_AGENT'],'N9')>0) and (stripos($_SERVER['HTTP_USER_AGENT'],'nokia')>0)){ - $mode='mobile'; - }else{ - $mode='default'; - } - }else{ - $mode='default'; - } - return($mode); - } - public static function initPaths(){ - // calculate the documentroot - OC::$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']); + // calculate the root directories OC::$SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-13)); OC::$SUBURI= str_replace("\\","/",substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen(OC::$SERVERROOT))); $scriptName=$_SERVER["SCRIPT_NAME"]; @@ -140,10 +109,8 @@ class OC{ OC::$SUBURI=OC::$SUBURI.'index.php'; } } - OC::$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen(OC::$SUBURI)); - // try a new way to detect the WEBROOT which is simpler and also works with the app directory outside the owncloud folder. let´s see if this works for everybody -// OC::$WEBROOT=substr(OC::$SERVERROOT,strlen(OC::$DOCUMENTROOT)); + OC::$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen(OC::$SUBURI)); if(OC::$WEBROOT!='' and OC::$WEBROOT[0]!=='/'){ OC::$WEBROOT='/'.OC::$WEBROOT; @@ -169,29 +136,36 @@ class OC{ echo("3rdparty directory not found! Please put the ownCloud 3rdparty folder in the ownCloud folder or the folder above. You can also configure the location in the config.php file."); exit; } - // search the apps folder - if(OC_Config::getValue('appsroot', '')<>''){ - OC::$APPSROOT=OC_Config::getValue('appsroot', ''); - OC::$APPSWEBROOT=OC_Config::getValue('appsurl', ''); + $config_paths = OC_Config::getValue('apps_paths', array()); + if(! empty($config_paths)){ + foreach($config_paths as $paths) { + if( isset($paths['url']) && isset($paths['path'])) { + $paths['url'] = rtrim($paths['url'],'/'); + $paths['path'] = rtrim($paths['path'],'/'); + OC::$APPSROOTS[] = $paths; + } + } }elseif(file_exists(OC::$SERVERROOT.'/apps')){ - OC::$APPSROOT=OC::$SERVERROOT; - OC::$APPSWEBROOT=OC::$WEBROOT; + OC::$APPSROOTS[] = array('path'=> OC::$SERVERROOT.'/apps', 'url' => '/apps', 'writable' => true); }elseif(file_exists(OC::$SERVERROOT.'/../apps')){ - OC::$APPSROOT=rtrim(dirname(OC::$SERVERROOT), '/'); - OC::$APPSWEBROOT=rtrim(dirname(OC::$WEBROOT), '/'); - }else{ + OC::$APPSROOTS[] = array('path'=> rtrim(dirname(OC::$SERVERROOT), '/').'/apps', 'url' => '/apps', 'writable' => true); + } + + if(empty(OC::$APPSROOTS)){ echo("apps directory not found! Please put the ownCloud apps folder in the ownCloud folder or the folder above. You can also configure the location in the config.php file."); exit; } + $paths = array(); + foreach( OC::$APPSROOTS as $path) + $paths[] = $path['path']; // set the right include path set_include_path( OC::$SERVERROOT.'/lib'.PATH_SEPARATOR. OC::$SERVERROOT.'/config'.PATH_SEPARATOR. OC::$THIRDPARTYROOT.'/3rdparty'.PATH_SEPARATOR. - OC::$APPSROOT.PATH_SEPARATOR. - OC::$APPSROOT.'/apps'.PATH_SEPARATOR. + implode($paths,PATH_SEPARATOR).PATH_SEPARATOR. get_include_path().PATH_SEPARATOR. OC::$SERVERROOT ); @@ -200,8 +174,10 @@ class OC{ public static function checkInstalled() { // Redirect to installer if not installed if (!OC_Config::getValue('installed', false) && OC::$SUBURI != '/index.php') { - $url = 'http://'.$_SERVER['SERVER_NAME'].OC::$WEBROOT.'/index.php'; - header("Location: $url"); + if(!OC::$CLI){ + $url = 'http://'.$_SERVER['SERVER_NAME'].OC::$WEBROOT.'/index.php'; + header("Location: $url"); + } exit(); } } @@ -210,8 +186,8 @@ class OC{ // redirect to https site if configured if( OC_Config::getValue( "forcessl", false )){ ini_set("session.cookie_secure", "on"); - if(OC_Helper::serverProtocol()<>'https') { - $url = "https://". OC_Helper::serverHost() . $_SERVER['REQUEST_URI']; + if(OC_Request::serverProtocol()<>'https' and !OC::$CLI) { + $url = "https://". OC_Request::serverHost() . $_SERVER['REQUEST_URI']; header("Location: $url"); exit(); } @@ -238,22 +214,13 @@ class OC{ OC_Config::setValue('version',implode('.',OC_Util::getVersion())); OC_App::checkAppsRequirements(); + // load all apps to also upgrade enabled apps + OC_App::loadApps(); } - - OC_App::updateApps(); } } public static function initTemplateEngine() { - // if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation - if(!isset($_SESSION['formfactor'])){ - $_SESSION['formfactor']=OC::detectFormfactor(); - } - // allow manual override via GET parameter - if(isset($_GET['formfactor'])){ - $_SESSION['formfactor']=$_GET['formfactor']; - } - // Add the stuff we need always OC_Util::addScript( "jquery-1.7.2.min" ); OC_Util::addScript( "jquery-ui-1.8.16.custom.min" ); @@ -266,6 +233,13 @@ class OC{ OC_Util::addScript( "config" ); //OC_Util::addScript( "multiselect" ); OC_Util::addScript('search','result'); + + if( OC_Config::getValue( 'installed', false )){ + if( OC_Appconfig::getValue( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax' ){ + OC_Util::addScript( 'backgroundjobs' ); + } + } + OC_Util::addStyle( "styles" ); OC_Util::addStyle( "multiselect" ); OC_Util::addStyle( "jquery-ui-1.8.16.custom" ); @@ -276,64 +250,32 @@ class OC{ ini_set('session.cookie_httponly','1;'); session_start(); } - - public static function loadapp(){ - if(file_exists(OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/index.php')){ - require_once(OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/index.php'); - }else{ - trigger_error('The requested App was not found.', E_USER_ERROR);//load default app instead? - } - } - - public static function loadfile(){ - if(file_exists(OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/' . OC::$REQUESTEDFILE)){ - if(substr(OC::$REQUESTEDFILE, -3) == 'css'){ - $appswebroot = (string) OC::$APPSWEBROOT; - $webroot = (string) OC::$WEBROOT; - $filepath = OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/' . OC::$REQUESTEDFILE; - header('Content-Type: text/css'); - OC_Response::enableCaching(); - OC_Response::setLastModifiedHeader(filemtime($filepath)); - $cssfile = file_get_contents($filepath); - $cssfile = str_replace('%appswebroot%', $appswebroot, $cssfile); - $cssfile = str_replace('%webroot%', $webroot, $cssfile); - OC_Response::setETagHeader(md5($cssfile)); - header('Content-Length: '.strlen($cssfile)); - echo $cssfile; - exit; - }elseif(substr(OC::$REQUESTEDFILE, -3) == 'php'){ - require_once(OC::$APPSROOT . '/apps/' . OC::$REQUESTEDAPP . '/' . OC::$REQUESTEDFILE); - } - }else{ - header('HTTP/1.0 404 Not Found'); - exit; - } - } public static function init(){ // register autoloader spl_autoload_register(array('OC','autoload')); setlocale(LC_ALL, 'en_US.UTF-8'); - + // set some stuff //ob_start(); error_reporting(E_ALL | E_STRICT); if (defined('DEBUG') && DEBUG){ ini_set('display_errors', 1); } + self::$CLI=(php_sapi_name() == 'cli'); date_default_timezone_set('UTC'); ini_set('arg_separator.output','&'); - // try to switch magic quotes off. - if(function_exists('set_magic_quotes_runtime')) { - @set_magic_quotes_runtime(false); - } + // try to switch magic quotes off. + if(function_exists('set_magic_quotes_runtime')) { + @set_magic_quotes_runtime(false); + } //try to configure php to enable big file uploads. //this doesn´t work always depending on the webserver and php configuration. //Let´s try to overwrite some defaults anyways - + //try to set the maximum execution time to 60min @set_time_limit(3600); @ini_set('max_execution_time',3600); @@ -363,7 +305,7 @@ class OC{ $_SERVER['PHP_AUTH_USER'] = strip_tags($name); $_SERVER['PHP_AUTH_PW'] = strip_tags($password); } - + self::initPaths(); // register the stream wrappers @@ -374,20 +316,6 @@ class OC{ self::checkInstalled(); self::checkSSL(); - - // CSRF protection - if(isset($_SERVER['HTTP_REFERER'])) $referer=$_SERVER['HTTP_REFERER']; else $referer=''; - $refererhost=parse_url($referer); - if(isset($refererhost['host'])) $refererhost=$refererhost['host']; else $refererhost=''; - $server=OC_Helper::serverHost(); - $serverhost=explode(':',$server); - $serverhost=$serverhost['0']; - if(($_SERVER['REQUEST_METHOD']=='POST') and ($refererhost<>$serverhost)) { - $url = OC_Helper::serverProtocol().'://'.$server.OC::$WEBROOT.'/index.php'; - header("Location: $url"); - exit(); - } - self::initSession(); self::initTemplateEngine(); self::checkUpgrade(); @@ -398,28 +326,14 @@ class OC{ exit; } - // TODO: we should get rid of this one, too - // WARNING: to make everything even more confusing, - // DATADIRECTORY is a var that changes and DATADIRECTORY_ROOT - // stays the same, but is set by "datadirectory". - // Any questions? - OC::$CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - // User and Groups if( !OC_Config::getValue( "installed", false )){ $_SESSION['user_id'] = ''; } - - OC_User::useBackend( OC_Config::getValue( "userbackend", "database" )); + OC_User::useBackend(new OC_User_Database()); OC_Group::useBackend(new OC_Group_Database()); - // Set up file system unless forbidden - global $RUNTIME_NOSETUPFS; - if(!$RUNTIME_NOSETUPFS ){ - OC_Util::setupFS(); - } - // Load Apps // This includes plugins for users and filesystems as well global $RUNTIME_NOAPPS; @@ -431,19 +345,19 @@ class OC{ OC_App::loadApps(); } } - + // Check for blacklisted files OC_Hook::connect('OC_Filesystem','write','OC_Filesystem','isBlacklisted'); OC_Hook::connect('OC_Filesystem', 'rename', 'OC_Filesystem', 'isBlacklisted'); //make sure temporary files are cleaned up register_shutdown_function(array('OC_Helper','cleanTmp')); - + //parse the given parameters self::$REQUESTEDAPP = (isset($_GET['app']) && trim($_GET['app']) != '' && !is_null($_GET['app'])?str_replace(array('\0', '/', '\\', '..'), '', strip_tags($_GET['app'])):OC_Config::getValue('defaultapp', 'files')); if(substr_count(self::$REQUESTEDAPP, '?') != 0){ $app = substr(self::$REQUESTEDAPP, 0, strpos(self::$REQUESTEDAPP, '?')); - $param = substr(self::$REQUESTEDAPP, strpos(self::$REQUESTEDAPP, '?') + 1); + $param = substr($_GET['app'], strpos($_GET['app'], '?') + 1); parse_str($param, $get); $_GET = array_merge($_GET, $get); self::$REQUESTEDAPP = $app; @@ -459,8 +373,8 @@ class OC{ $_GET['getfile'] = $file; } if(!is_null(self::$REQUESTEDFILE)){ - $subdir = OC::$APPSROOT . '/apps/' . self::$REQUESTEDAPP . '/' . self::$REQUESTEDFILE; - $parent = OC::$APPSROOT . '/apps/' . self::$REQUESTEDAPP; + $subdir = OC_App::getAppPath(OC::$REQUESTEDAPP) . '/' . self::$REQUESTEDFILE; + $parent = OC_App::getAppPath(OC::$REQUESTEDAPP); if(!OC_Helper::issubdirectory($subdir, $parent)){ self::$REQUESTEDFILE = null; header('HTTP/1.0 404 Not Found'); @@ -468,12 +382,168 @@ class OC{ } } } + + /** + * @brief Handle the request + */ + public static function handleRequest() { + if (!OC_Config::getValue('installed', false)) { + // Check for autosetup: + $autosetup_file = OC::$SERVERROOT."/config/autoconfig.php"; + if( file_exists( $autosetup_file )){ + OC_Log::write('core','Autoconfig file found, setting up owncloud...',OC_Log::INFO); + include( $autosetup_file ); + $_POST['install'] = 'true'; + $_POST = array_merge ($_POST, $AUTOCONFIG); + unlink($autosetup_file); + } + OC_Util::addScript('setup'); + require_once('setup.php'); + exit(); + } + // Handle WebDAV + if($_SERVER['REQUEST_METHOD']=='PROPFIND'){ + header('location: '.OC_Helper::linkToRemote('webdav')); + return; + } + // Handle app css files + if(substr(OC::$REQUESTEDFILE,-3) == 'css') { + self::loadCSSFile(); + return; + } + // Someone is logged in : + if(OC_User::isLoggedIn()) { + OC_App::loadApps(); + if(isset($_GET["logout"]) and ($_GET["logout"])) { + OC_User::logout(); + header("Location: ".OC::$WEBROOT.'/'); + }else{ + $app = OC::$REQUESTEDAPP; + $file = OC::$REQUESTEDFILE; + if(is_null($file)) { + $file = 'index.php'; + } + $file_ext = substr($file, -3); + if ($file_ext != 'php' + || !self::loadAppScriptFile($app, $file)) { + header('HTTP/1.0 404 Not Found'); + } + } + return; + } + // Not handled and not logged in + self::handleLogin(); + } + + protected static function loadAppScriptFile($app, $file) { + $app_path = OC_App::getAppPath($app); + $file = $app_path . '/' . $file; + unset($app, $app_path); + if (file_exists($file)) { + require_once($file); + return true; + } + return false; + } + + protected static function loadCSSFile() { + $app = OC::$REQUESTEDAPP; + $file = OC::$REQUESTEDFILE; + $app_path = OC_App::getAppPath($app); + if (file_exists($app_path . '/' . $file)) { + $app_web_path = OC_App::getAppWebPath($app); + $filepath = $app_web_path . '/' . $file; + $minimizer = new OC_Minimizer_CSS(); + $info = array($app_path, $app_web_path, $file); + $minimizer->output(array($info), $filepath); + } + } + + protected static function handleLogin() { + OC_App::loadApps(array('prelogin')); + $error = false; + // remember was checked after last login + if (OC::tryRememberLogin()) { + // nothing more to do + + // Someone wants to log in : + } elseif (OC::tryFormLogin()) { + $error = true; + + // The user is already authenticated using Apaches AuthType Basic... very usable in combination with LDAP + } elseif (OC::tryBasicAuthLogin()) { + $error = true; + } + OC_Util::displayLoginPage($error); + } + + protected static function tryRememberLogin() { + if(!isset($_COOKIE["oc_remember_login"]) + || !isset($_COOKIE["oc_token"]) + || !isset($_COOKIE["oc_username"]) + || !$_COOKIE["oc_remember_login"]) { + return false; + } + OC_App::loadApps(array('authentication')); + if(defined("DEBUG") && DEBUG) { + OC_Log::write('core','Trying to login from cookie',OC_Log::DEBUG); + } + // confirm credentials in cookie + if(isset($_COOKIE['oc_token']) && OC_User::userExists($_COOKIE['oc_username']) && + OC_Preferences::getValue($_COOKIE['oc_username'], "login", "token") === $_COOKIE['oc_token']) { + OC_User::setUserId($_COOKIE['oc_username']); + OC_Util::redirectToDefaultPage(); + } + else { + OC_User::unsetMagicInCookie(); + } + return true; + } + + protected static function tryFormLogin() { + if(!isset($_POST["user"]) + || !isset($_POST['password']) + || !isset($_SESSION['sectoken']) + || !isset($_POST['sectoken']) + || ($_SESSION['sectoken']!=$_POST['sectoken']) ) { + return false; + } + OC_App::loadApps(); + if(OC_User::login($_POST["user"], $_POST["password"])) { + if(!empty($_POST["remember_login"])){ + if(defined("DEBUG") && DEBUG) { + OC_Log::write('core','Setting remember login to cookie', OC_Log::DEBUG); + } + $token = md5($_POST["user"].time().$_POST['password']); + OC_Preferences::setValue($_POST['user'], 'login', 'token', $token); + OC_User::setMagicInCookie($_POST["user"], $token); + } + else { + OC_User::unsetMagicInCookie(); + } + OC_Util::redirectToDefaultPage(); + } + return true; + } + + protected static function tryBasicAuthLogin() { + if (!isset($_SERVER["PHP_AUTH_USER"]) + || !isset($_SERVER["PHP_AUTH_PW"])){ + return false; + } + OC_App::loadApps(array('authentication')); + if (OC_User::login($_SERVER["PHP_AUTH_USER"],$_SERVER["PHP_AUTH_PW"])) { + //OC_Log::write('core',"Logged in with HTTP Authentication",OC_Log::DEBUG); + OC_User::unsetMagicInCookie(); + $_REQUEST['redirect_url'] = (isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:''); + OC_Util::redirectToDefaultPage(); + } + return true; + } + } // define runtime variables - unless this already has been done -if( !isset( $RUNTIME_NOSETUPFS )){ - $RUNTIME_NOSETUPFS = false; -} if( !isset( $RUNTIME_NOAPPS )){ $RUNTIME_NOAPPS = false; } @@ -490,7 +560,7 @@ if(!function_exists('get_temp_dir')) { return dirname($temp); } if( $temp=sys_get_temp_dir()) return $temp; - + return null; } } diff --git a/lib/cache.php b/lib/cache.php new file mode 100644 index 00000000000..55f189a5da8 --- /dev/null +++ b/lib/cache.php @@ -0,0 +1,147 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Cache { + /** + * @var OC_Cache $user_cache + */ + static protected $user_cache; + /** + * @var OC_Cache $global_cache + */ + static protected $global_cache; + /** + * @var OC_Cache $global_cache_fast + */ + static protected $global_cache_fast; + /** + * @var OC_Cache $user_cache_fast + */ + static protected $user_cache_fast; + static protected $isFast=null; + + /** + * get the global cache + * @return OC_Cache + */ + static public function getGlobalCache($fast=false) { + if (!self::$global_cache) { + self::$global_cache_fast = null; + if (!self::$global_cache_fast && function_exists('xcache_set')) { + self::$global_cache_fast = new OC_Cache_XCache(true); + } + if (!self::$global_cache_fast && function_exists('apc_store')) { + self::$global_cache_fast = new OC_Cache_APC(true); + } + + self::$global_cache = new OC_Cache_FileGlobal(); + if (self::$global_cache_fast) { + self::$global_cache = new OC_Cache_Broker(self::$global_cache_fast, self::$global_cache); + } + } + if($fast){ + if(self::$global_cache_fast){ + return self::$global_cache_fast; + }else{ + return false; + } + } + return self::$global_cache; + } + + /** + * get the user cache + * @return OC_Cache + */ + static public function getUserCache($fast=false) { + if (!self::$user_cache) { + self::$user_cache_fast = null; + if (!self::$user_cache_fast && function_exists('xcache_set')) { + self::$user_cache_fast = new OC_Cache_XCache(); + } + if (!self::$user_cache_fast && function_exists('apc_store')) { + self::$user_cache_fast = new OC_Cache_APC(); + } + + self::$user_cache = new OC_Cache_File(); + if (self::$user_cache_fast) { + self::$user_cache = new OC_Cache_Broker(self::$user_cache_fast, self::$user_cache); + } + } + + if($fast){ + if(self::$user_cache_fast){ + return self::$user_cache_fast; + }else{ + return false; + } + } + return self::$user_cache; + } + + /** + * get a value from the user cache + * @return mixed + */ + static public function get($key) { + $user_cache = self::getUserCache(); + return $user_cache->get($key); + } + + /** + * set a value in the user cache + * @return bool + */ + static public function set($key, $value, $ttl=0) { + if (empty($key)) { + return false; + } + $user_cache = self::getUserCache(); + return $user_cache->set($key, $value, $ttl); + } + + /** + * check if a value is set in the user cache + * @return bool + */ + static public function hasKey($key) { + $user_cache = self::getUserCache(); + return $user_cache->hasKey($key); + } + + /** + * remove an item from the user cache + * @return bool + */ + static public function remove($key) { + $user_cache = self::getUserCache(); + return $user_cache->remove($key); + } + + /** + * clear the user cache of all entries starting with a prefix + * @param string prefix (optional) + * @return bool + */ + static public function clear($prefix='') { + $user_cache = self::getUserCache(); + return $user_cache->clear($prefix); + } + + /** + * check if a fast memory based cache is available + * @return true + */ + static public function isFast() { + if(is_null(self::$isFast)){ + self::$isFast=function_exists('xcache_set') || function_exists('apc_store'); + } + return self::$isFast; + } + +} diff --git a/lib/cache/apc.php b/lib/cache/apc.php new file mode 100644 index 00000000000..c192fe2f196 --- /dev/null +++ b/lib/cache/apc.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Cache_APC { + protected $prefix; + + public function __construct($global = false) { + $this->prefix = OC_Util::getInstanceId().'/'; + if (!$global) { + $this->prefix .= OC_User::getUser().'/'; + } + } + + /** + * entries in APC gets namespaced to prevent collisions between owncloud instances and users + */ + protected function getNameSpace() { + return $this->prefix; + } + + public function get($key) { + $result = apc_fetch($this->getNamespace().$key, $success); + if (!$success) { + return null; + } + return $result; + } + + public function set($key, $value, $ttl=0) { + return apc_store($this->getNamespace().$key, $value, $ttl); + } + + public function hasKey($key) { + return apc_exists($this->getNamespace().$key); + } + + public function remove($key) { + return apc_delete($this->getNamespace().$key); + } + + public function clear($prefix=''){ + $ns = $this->getNamespace().$prefix; + $cache = apc_cache_info('user'); + foreach($cache['cache_list'] as $entry) { + if (strpos($entry['info'], $ns) === 0) { + apc_delete($entry['info']); + } + } + return true; + } +} +if(!function_exists('apc_exists')) { + function apc_exists($keys) + { + $result; + apc_fetch($keys, $result); + return $result; + } +} diff --git a/lib/cache/broker.php b/lib/cache/broker.php new file mode 100644 index 00000000000..c2aceabaf53 --- /dev/null +++ b/lib/cache/broker.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Cache_Broker { + protected $fast_cache; + protected $slow_cache; + + public function __construct($fast_cache, $slow_cache) { + $this->fast_cache = $fast_cache; + $this->slow_cache = $slow_cache; + } + + public function get($key) { + if ($r = $this->fast_cache->get($key)) { + return $r; + } + return $this->slow_cache->get($key); + } + + public function set($key, $value, $ttl=0) { + if (!$this->fast_cache->set($key, $value, $ttl)) { + if ($this->fast_cache->hasKey($key)) { + $this->fast_cache->remove($key); + } + return $this->slow_cache->set($key, $value, $ttl); + } + return true; + } + + public function hasKey($key) { + if ($this->fast_cache->hasKey($key)) { + return true; + } + return $this->slow_cache->hasKey($key); + } + + public function remove($key) { + if ($this->fast_cache->remove($key)) { + return true; + } + return $this->slow_cache->remove($key); + } + + public function clear($prefix=''){ + $this->fast_cache->clear($prefix); + $this->slow_cache->clear($prefix); + } +} diff --git a/lib/cache/file.php b/lib/cache/file.php new file mode 100644 index 00000000000..562c3d17167 --- /dev/null +++ b/lib/cache/file.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +class OC_Cache_File{ + protected function getStorage() { + if(OC_User::isLoggedIn()){ + $subdir = 'cache'; + $view = new OC_FilesystemView('/'.OC_User::getUser()); + if(!$view->file_exists($subdir)) { + $view->mkdir($subdir); + } + return new OC_FilesystemView('/'.OC_User::getUser().'/'.$subdir); + }else{ + OC_Log::write('core','Can\'t get cache storage, user not logged in', OC_Log::ERROR); + return false; + } + } + + public function get($key) { + if ($this->hasKey($key)) { + $storage = $this->getStorage(); + return $storage->file_get_contents($key); + } + return null; + } + + public function set($key, $value, $ttl=0) { + $storage = $this->getStorage(); + if ($storage and $storage->file_put_contents($key, $value)) { + if ($ttl === 0) { + $ttl = 86400; // 60*60*24 + } + return $storage->touch($key, time() + $ttl); + } + return false; + } + + public function hasKey($key) { + $storage = $this->getStorage(); + if ($storage && $storage->is_file($key)) { + $mtime = $storage->filemtime($key); + if ($mtime < time()) { + $storage->unlink($key); + return false; + } + return true; + } + return false; + } + + public function remove($key) { + $storage = $this->getStorage(); + if(!$storage){ + return false; + } + return $storage->unlink($key); + } + + public function clear($prefix=''){ + $storage = $this->getStorage(); + if($storage and $storage->is_dir('/')){ + $dh=$storage->opendir('/'); + while($file=readdir($dh)){ + if($file!='.' and $file!='..' and ($prefix==='' || strpos($file, $prefix) === 0)){ + $storage->unlink('/'.$file); + } + } + } + return true; + } +} diff --git a/lib/cache/fileglobal.php b/lib/cache/fileglobal.php new file mode 100644 index 00000000000..1c2c9bdc82d --- /dev/null +++ b/lib/cache/fileglobal.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +class OC_Cache_FileGlobal{ + protected function getCacheDir() { + $cache_dir = get_temp_dir().'/owncloud-'.OC_Util::getInstanceId().'/'; + if (!is_dir($cache_dir)) { + mkdir($cache_dir); + } + return $cache_dir; + } + + protected function fixKey($key) { + return str_replace('/', '_', $key); + } + + public function get($key) { + $key = $this->fixKey($key); + if ($this->hasKey($key)) { + $cache_dir = $this->getCacheDir(); + return file_get_contents($cache_dir.$key); + } + return null; + } + + public function set($key, $value, $ttl=0) { + $key = $this->fixKey($key); + $cache_dir = $this->getCacheDir(); + if ($cache_dir and file_put_contents($cache_dir.$key, $value)) { + if ($ttl === 0) { + $ttl = 86400; // 60*60*24 + } + return touch($cache_dir.$key, time() + $ttl); + } + return false; + } + + public function hasKey($key) { + $key = $this->fixKey($key); + $cache_dir = $this->getCacheDir(); + if ($cache_dir && is_file($cache_dir.$key)) { + $mtime = filemtime($cache_dir.$key); + if ($mtime < time()) { + unlink($cache_dir.$key); + return false; + } + return true; + } + return false; + } + + public function remove($key) { + $cache_dir = $this->getCacheDir(); + if(!$cache_dir){ + return false; + } + $key = $this->fixKey($key); + return unlink($cache_dir.$key); + } + + public function clear(){ + $cache_dir = $this->getCacheDir(); + if($cache_dir and is_dir($cache_dir)){ + $dh=opendir($cache_dir); + while($file=readdir($dh)){ + if($file!='.' and $file!='..'){ + unlink($cache_dir.$file); + } + } + } + } +} diff --git a/lib/cache/xcache.php b/lib/cache/xcache.php new file mode 100644 index 00000000000..951f9b47545 --- /dev/null +++ b/lib/cache/xcache.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Cache_XCache { + protected $prefix; + + public function __construct($global = false) { + $this->prefix = OC_Util::getInstanceId().'/'; + if (!$global) { + $this->prefix .= OC_User::getUser().'/'; + } + } + + /** + * entries in XCache gets namespaced to prevent collisions between owncloud instances and users + */ + protected function getNameSpace() { + return $this->prefix; + } + + public function get($key) { + return xcache_get($this->getNamespace().$key); + } + + public function set($key, $value, $ttl=0) { + if($ttl>0){ + return xcache_set($this->getNamespace().$key,$value,$ttl); + }else{ + return xcache_set($this->getNamespace().$key,$value); + } + } + + public function hasKey($key) { + return xcache_isset($this->getNamespace().$key); + } + + public function remove($key) { + return xcache_unset($this->getNamespace().$key); + } + + public function clear($prefix=''){ + xcache_unset_by_prefix($this->getNamespace().$prefix); + return true; + } +} diff --git a/lib/config.php b/lib/config.php index ad1cd18fa15..266d559126c 100644 --- a/lib/config.php +++ b/lib/config.php @@ -4,7 +4,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -152,32 +152,25 @@ class OC_Config{ * * Saves the config to the config file. * - * Known flaws: Strings are not escaped properly */ public static function writeData(){ // Create a php file ... - $content = "<?php\n\$CONFIG = array(\n"; - - foreach( self::$cache as $key => $value ){ - if( is_bool( $value )){ - $value = $value ? 'true' : 'false'; - $content .= "\"$key\" => $value,\n"; - } - else{ - $value = str_replace( "'", "\\'", $value ); - $content .= "\"$key\" => '$value',\n"; - } - } - $content .= ");\n?>\n"; + $content = "<?php\n\$CONFIG = "; + $content .= var_export(self::$cache, true); + $content .= ";\n"; + $filename = OC::$SERVERROOT."/config/config.php"; // Write the file - $result=@file_put_contents( OC::$SERVERROOT."/config/config.php", $content ); + $result=@file_put_contents( $filename, $content ); if(!$result) { $tmpl = new OC_Template( '', 'error', 'guest' ); $tmpl->assign('errors',array(1=>array('error'=>"Can't write into config directory 'config'",'hint'=>"You can usually fix this by giving the webserver user write access to the config directory in owncloud"))); $tmpl->printPage(); exit; } + // Prevent others not to read the config + @chmod($filename, 0640); + return true; } } diff --git a/lib/connector/sabre/client.php b/lib/connector/sabre/client.php new file mode 100644 index 00000000000..7e8f21264f9 --- /dev/null +++ b/lib/connector/sabre/client.php @@ -0,0 +1,173 @@ +<?php
+
+/**
+ * ownCloud
+ *
+ * @author Bjoern Schiessle
+ * @copyright 2012 Bjoern Schiessle <schiessle@owncloud.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+class OC_Connector_Sabre_Client extends Sabre_DAV_Client {
+
+ protected $trustedCertificates;
+
+ /**
+ * Add trusted root certificates to the webdav client.
+ *
+ * The parameter certificates should be a absulute path to a file which contains
+ * all trusted certificates
+ *
+ * @param string $certificates
+ */
+ public function addTrustedCertificates($certificates) {
+ $this->trustedCertificates = $certificates;
+ }
+
+ /**
+ * Copied from SabreDAV with some modification to use user defined curlSettings
+ * Performs an actual HTTP request, and returns the result.
+ *
+ * If the specified url is relative, it will be expanded based on the base
+ * url.
+ *
+ * The returned array contains 3 keys:
+ * * body - the response body
+ * * httpCode - a HTTP code (200, 404, etc)
+ * * headers - a list of response http headers. The header names have
+ * been lowercased.
+ *
+ * @param string $method
+ * @param string $url
+ * @param string $body
+ * @param array $headers
+ * @return array
+ */
+ public function request($method, $url = '', $body = null, $headers = array()) {
+
+ $url = $this->getAbsoluteUrl($url);
+
+ $curlSettings = array(
+ CURLOPT_RETURNTRANSFER => true,
+ // Return headers as part of the response
+ CURLOPT_HEADER => true,
+ CURLOPT_POSTFIELDS => $body,
+ // Automatically follow redirects
+ CURLOPT_FOLLOWLOCATION => true,
+ CURLOPT_MAXREDIRS => 5,
+ );
+
+ if($this->trustedCertificates) {
+ $curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates;
+ }
+
+ switch ($method) {
+ case 'HEAD' :
+
+ // do not read body with HEAD requests (this is neccessary because cURL does not ignore the body with HEAD
+ // requests when the Content-Length header is given - which in turn is perfectly valid according to HTTP
+ // specs...) cURL does unfortunately return an error in this case ("transfer closed transfer closed with
+ // ... bytes remaining to read") this can be circumvented by explicitly telling cURL to ignore the
+ // response body
+ $curlSettings[CURLOPT_NOBODY] = true;
+ $curlSettings[CURLOPT_CUSTOMREQUEST] = 'HEAD';
+ break;
+
+ default:
+ $curlSettings[CURLOPT_CUSTOMREQUEST] = $method;
+ break;
+
+ }
+
+ // Adding HTTP headers
+ $nHeaders = array();
+ foreach($headers as $key=>$value) {
+
+ $nHeaders[] = $key . ': ' . $value;
+
+ }
+ $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
+
+ if ($this->proxy) {
+ $curlSettings[CURLOPT_PROXY] = $this->proxy;
+ }
+
+ if ($this->userName && $this->authType) {
+ $curlType = 0;
+ if ($this->authType & self::AUTH_BASIC) {
+ $curlType |= CURLAUTH_BASIC;
+ }
+ if ($this->authType & self::AUTH_DIGEST) {
+ $curlType |= CURLAUTH_DIGEST;
+ }
+ $curlSettings[CURLOPT_HTTPAUTH] = $curlType;
+ $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
+ }
+
+ list(
+ $response,
+ $curlInfo,
+ $curlErrNo,
+ $curlError
+ ) = $this->curlRequest($url, $curlSettings);
+
+ $headerBlob = substr($response, 0, $curlInfo['header_size']);
+ $response = substr($response, $curlInfo['header_size']);
+
+ // In the case of 100 Continue, or redirects we'll have multiple lists
+ // of headers for each separate HTTP response. We can easily split this
+ // because they are separated by \r\n\r\n
+ $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
+
+ // We only care about the last set of headers
+ $headerBlob = $headerBlob[count($headerBlob)-1];
+
+ // Splitting headers
+ $headerBlob = explode("\r\n", $headerBlob);
+
+ $headers = array();
+ foreach($headerBlob as $header) {
+ $parts = explode(':', $header, 2);
+ if (count($parts)==2) {
+ $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
+ }
+ }
+
+ $response = array(
+ 'body' => $response,
+ 'statusCode' => $curlInfo['http_code'],
+ 'headers' => $headers
+ );
+
+ if ($curlErrNo) {
+ throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')');
+ }
+
+ if ($response['statusCode']>=400) {
+ switch ($response['statusCode']) {
+ case 404:
+ throw new Sabre_DAV_Exception_NotFound('Resource ' . $url . ' not found.');
+ break;
+
+ default:
+ throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
+ }
+ }
+
+ return $response;
+
+ }
+}
\ No newline at end of file diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index e74d832cb00..09c65f19b80 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -26,17 +26,45 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa /** * Creates a new file in the directory * - * data is a readable stream resource + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After succesful creation of the file, you may choose to return the ETag + * of the new file here. + * + * The returned ETag must be surrounded by double-quotes (The quotes should + * be part of the actual string). + * + * If you cannot accurately determine the ETag, you should not return it. + * If you don't store the file exactly as-is (you're transforming it + * somehow) you should also not return an ETag. + * + * This means that if a subsequent GET to this new file does not exactly + * return the same contents of what was submitted here, you are strongly + * recommended to omit the ETag. * * @param string $name Name of the file - * @param resource $data Initial payload - * @return void + * @param resource|string $data Initial payload + * @return null|string */ public function createFile($name, $data = null) { + if (isset($_SERVER['HTTP_OC_CHUNKED'])) { + $info = OC_FileChunking::decodeName($name); + $chunk_handler = new OC_FileChunking($info); + $chunk_handler->store($info['index'], $data); + if ($chunk_handler->isComplete()) { + $newPath = $this->path . '/' . $info['name']; + $f = OC_Filesystem::fopen($newPath, 'w'); + $chunk_handler->assemble($f); + return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath); + } + } else { + $newPath = $this->path . '/' . $name; + OC_Filesystem::file_put_contents($newPath,$data); + return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath); + } - $newPath = $this->path . '/' . $name; - OC_Filesystem::file_put_contents($newPath,$data); - + return null; } /** @@ -59,22 +87,23 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * @throws Sabre_DAV_Exception_FileNotFound * @return Sabre_DAV_INode */ - public function getChild($name) { + public function getChild($name, $info = null) { $path = $this->path . '/' . $name; + if (is_null($info)) { + $info = OC_FileCache::get($path); + } - if (!OC_Filesystem::file_exists($path)) throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); - - if (OC_Filesystem::is_dir($path)) { - - return new OC_Connector_Sabre_Directory($path); + if (!$info) throw new Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); + if ($info['mimetype'] == 'httpd/unix-directory') { + $node = new OC_Connector_Sabre_Directory($path); } else { - - return new OC_Connector_Sabre_File($path); - + $node = new OC_Connector_Sabre_File($path); } + $node->setFileinfoCache($info); + return $node; } /** @@ -84,18 +113,32 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function getChildren() { - $nodes = array(); - // foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); - if( OC_Filesystem::is_dir($this->path . '/')){ - $dh = OC_Filesystem::opendir($this->path . '/'); - while(( $node = readdir($dh)) !== false ){ - if($node!='.' && $node!='..'){ - $nodes[] = $this->getChild($node); - } + $folder_content = OC_FileCache::getFolderContent($this->path); + $paths = array(); + foreach($folder_content as $info) { + $paths[] = $this->path.'/'.$info['name']; + } + $properties = array_fill_keys($paths, array()); + if(count($paths)>0){ + $placeholders = join(',', array_fill(0, count($paths), '?')); + $query = OC_DB::prepare( 'SELECT * FROM *PREFIX*properties WHERE userid = ?' . ' AND propertypath IN ('.$placeholders.')' ); + array_unshift($paths, OC_User::getUser()); // prepend userid + $result = $query->execute( $paths ); + while($row = $result->fetchRow()) { + $propertypath = $row['propertypath']; + $propertyname = $row['propertyname']; + $propertyvalue = $row['propertyvalue']; + $properties[$propertypath][$propertyname] = $propertyvalue; } } - return $nodes; + $nodes = array(); + foreach($folder_content as $info) { + $node = $this->getChild($info['name'], $info); + $node->setPropertyCache($properties[$this->path.'/'.$info['name']]); + $nodes[] = $node; + } + return $nodes; } /** @@ -131,7 +174,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * @return array */ public function getQuotaInfo() { - $rootInfo=OC_FileCache::get(''); + $rootInfo=OC_FileCache_Cached::get(''); return array( $rootInfo['size'], OC_Filesystem::free_space() @@ -139,5 +182,25 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } + /** + * Returns a list of properties for this nodes.; + * + * The properties list is a list of propertynames the client requested, + * encoded as xmlnamespace#tagName, for example: + * http://www.example.org/namespace#author + * If the array is empty, all properties should be returned + * + * @param array $properties + * @return void + */ + public function getProperties($properties) { + $props = parent::getProperties($properties); + if (in_array(self::GETETAG_PROPERTYNAME, $properties) + && !isset($props[self::GETETAG_PROPERTYNAME])) { + $props[self::GETETAG_PROPERTYNAME] = + OC_Connector_Sabre_Node::getETagPropertyForPath($this->path); + } + return $props; + } } diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php index 3ba1b3355f2..9d571fceb0d 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -26,13 +26,28 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D /** * Updates the data * + * The data argument is a readable stream resource. + * + * After a succesful put operation, you may choose to return an ETag. The + * etag must always be surrounded by double-quotes. These quotes must + * appear in the actual string you're returning. + * + * Clients may use the ETag from a PUT request to later on make sure that + * when they update the file, the contents haven't changed in the mean + * time. + * + * If you don't plan to store the file byte-by-byte, and you return a + * different object on a subsequent GET you are strongly recommended to not + * return an ETag, and just return null. + * * @param resource $data - * @return void + * @return string|null */ public function put($data) { OC_Filesystem::file_put_contents($this->path,$data); + return OC_Connector_Sabre_Node::getETagPropertyForPath($this->path); } /** @@ -42,7 +57,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function get() { - return OC_Filesystem::fopen($this->path,'r'); + return OC_Filesystem::fopen($this->path,'rb'); } @@ -63,8 +78,8 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return int */ public function getSize() { - $this->stat(); - return $this->stat_cache['size']; + $this->getFileinfoCache(); + return $this->fileinfo_cache['size']; } @@ -79,9 +94,20 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return mixed */ public function getETag() { + $properties = $this->getProperties(array(self::GETETAG_PROPERTYNAME)); + if (isset($properties[self::GETETAG_PROPERTYNAME])) { + return $properties[self::GETETAG_PROPERTYNAME]; + } + return $this->getETagPropertyForPath($this->path); + } - return null; - + /** + * Creates a ETag for this path. + * @param string $path Path of the file + * @return string|null Returns null if the ETag can not effectively be determined + */ + static protected function createETag($path) { + return OC_Filesystem::hash('md5', $path); } /** @@ -92,6 +118,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return mixed */ public function getContentType() { + if (isset($this->fileinfo_cache['mimetype'])) { + return $this->fileinfo_cache['mimetype']; + } return OC_Filesystem::getMimeType($this->path); diff --git a/lib/connector/sabre/locks.php b/lib/connector/sabre/locks.php index a12f2a54406..0ddc8b18d2f 100644 --- a/lib/connector/sabre/locks.php +++ b/lib/connector/sabre/locks.php @@ -41,9 +41,10 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract { // NOTE: the following 10 lines or so could be easily replaced by // pure sql. MySQL's non-standard string concatination prevents us // from doing this though. - // Fix: sqlite does not insert time() as a number but as text, making - // the equation returning false all the time - $query = 'SELECT * FROM `*PREFIX*locks` WHERE `userid` = ? AND (`created` + `timeout`) > '.time().' AND ((`uri` = ?)'; + // NOTE: SQLite requires time() to be inserted directly. That's ugly + // but otherwise reading locks from SQLite Databases will return + // nothing + $query = 'SELECT * FROM `*PREFIX*locks` WHERE `userid` = ? AND (`created` + `timeout`) > '.time().' AND (( `uri` = ?)'; $params = array(OC_User::getUser(),$uri); // We need to check locks for every part in the uri. @@ -72,8 +73,8 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract { } $query.=')'; - $stmt = OC_DB::prepare($query); - $result = $stmt->execute($params); + $stmt = OC_DB::prepare( $query ); + $result = $stmt->execute( $params ); $lockList = array(); while( $row = $result->fetchRow()){ @@ -110,7 +111,7 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract { $locks = $this->getLocks($uri,false); $exists = false; - foreach($locks as $k=>$lock) { + foreach($locks as $lock) { if ($lock->token == $lockInfo->token) $exists = true; } diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index ce5cc022085..b9bf474a041 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -22,6 +22,7 @@ */ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties { + const GETETAG_PROPERTYNAME = '{DAV:}getetag'; /** * The path to the current node @@ -30,10 +31,15 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr */ protected $path; /** - * file stat cache + * node fileinfo cache * @var array */ - protected $stat_cache; + protected $fileinfo_cache; + /** + * node properties cache + * @var array + */ + protected $property_cache = null; /** * Sets up the node, expects a full path name @@ -82,23 +88,38 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } + public function setFileinfoCache($fileinfo_cache) + { + $this->fileinfo_cache = $fileinfo_cache; + } + /** - * Set the stat cache + * Make sure the fileinfo cache is filled. Uses OC_FileCache or a direct stat */ - protected function stat() { - if (!isset($this->stat_cache)) { - $this->stat_cache = OC_Filesystem::stat($this->path); + protected function getFileinfoCache() { + if (!isset($this->fileinfo_cache)) { + if ($fileinfo_cache = OC_FileCache::get($this->path)) { + } else { + $fileinfo_cache = OC_Filesystem::stat($this->path); + } + + $this->fileinfo_cache = $fileinfo_cache; } } + public function setPropertyCache($property_cache) + { + $this->property_cache = $property_cache; + } + /** * Returns the last modification time, as a unix timestamp * * @return int */ public function getLastModified() { - $this->stat(); - return $this->stat_cache['mtime']; + $this->getFileinfoCache(); + return $this->fileinfo_cache['mtime']; } @@ -144,37 +165,91 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } } + $this->setPropertyCache(null); return true; } /** * Returns a list of properties for this nodes.; * - * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author + * The properties list is a list of propertynames the client requested, + * encoded as xmlnamespace#tagName, for example: + * http://www.example.org/namespace#author * If the array is empty, all properties should be returned * * @param array $properties * @return void */ - function getProperties($properties) { - // At least some magic in here :-) - $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?' ); - $result = $query->execute( array( OC_User::getUser(), $this->path )); - - $existing = array(); - while( $row = $result->fetchRow()){ - $existing[$row['propertyname']] = $row['propertyvalue']; + public function getProperties($properties) { + if (is_null($this->property_cache)) { + $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?' ); + $result = $query->execute( array( OC_User::getUser(), $this->path )); + + $this->property_cache = array(); + while( $row = $result->fetchRow()){ + $this->property_cache[$row['propertyname']] = $row['propertyvalue']; + } } + // if the array was empty, we need to return everything if(count($properties) == 0){ - return $existing; + return $this->property_cache; } - // if the array was empty, we need to return everything $props = array(); foreach($properties as $property) { - if (isset($existing[$property])) $props[$property] = $existing[$property]; + if (isset($this->property_cache[$property])) $props[$property] = $this->property_cache[$property]; } return $props; } + + /** + * Creates a ETag for this path. + * @param string $path Path of the file + * @return string|null Returns null if the ETag can not effectively be determined + */ + static protected function createETag($path) { + return uniqid('', true); + } + + /** + * Returns the ETag surrounded by double-quotes for this path. + * @param string $path Path of the file + * @return string|null Returns null if the ETag can not effectively be determined + */ + static public function getETagPropertyForPath($path) { + $tag = self::createETag($path); + if (empty($tag)) { + return null; + } + $etag = '"'.$tag.'"'; + $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties` (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' ); + $query->execute( array( OC_User::getUser(), $path, self::GETETAG_PROPERTYNAME, $etag )); + return $etag; + } + + /** + * Remove the ETag from the cache. + * @param string $path Path of the file + */ + static public function removeETagPropertyForPath($path) { + // remove tags from this and parent paths + $paths = array(); + while ($path != '/' && $path != '') { + $paths[] = $path; + $path = dirname($path); + } + if (empty($paths)) { + return; + } + $paths[] = $path; + $path_placeholders = join(',', array_fill(0, count($paths), '?')); + $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`' + .' WHERE `userid` = ?' + .' AND `propertyname` = ?' + .' AND `propertypath` IN ('.$path_placeholders.')' + ); + $vals = array( OC_User::getUser(), self::GETETAG_PROPERTYNAME ); + $query->execute(array_merge( $vals, $paths )); + } } diff --git a/lib/db.php b/lib/db.php index 0d272f63037..208e70f4c35 100644 --- a/lib/db.php +++ b/lib/db.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -33,8 +33,6 @@ class OC_DB { static private $MDB2=false; static private $PDO=false; static private $schema=false; - static private $affected=0; - static private $result=false; static private $inTransaction=false; static private $prefix=null; static private $type=null; @@ -44,18 +42,18 @@ class OC_DB { * @return BACKEND_MDB2 or BACKEND_PDO */ private static function getDBBackend(){ - if(class_exists('PDO') && OC_Config::getValue('installed', false)){//check if we can use PDO, else use MDB2 (instalation always needs to be done my mdb2) + if(class_exists('PDO') && OC_Config::getValue('installed', false)){//check if we can use PDO, else use MDB2 (installation always needs to be done my mdb2) $type = OC_Config::getValue( "dbtype", "sqlite" ); - if($type=='oci') { //oracle also always needs mdb2 - return self::BACKEND_MDB2; - } + if($type=='oci') { //oracle also always needs mdb2 + return self::BACKEND_MDB2; + } if($type=='sqlite3') $type='sqlite'; $drivers=PDO::getAvailableDrivers(); if(array_search($type,$drivers)!==false){ return self::BACKEND_PDO; } } - return self::BACKEND_MDB2; + return self::BACKEND_MDB2; } /** @@ -131,15 +129,23 @@ class OC_DB { }else{ $dsn='pgsql:dbname='.$name.';host='.$host; } + /** + * Ugly fix for pg connections pbm when password use spaces + */ + $e_user = addslashes($user); + $e_password = addslashes($pass); + $pass = $user = null; + $dsn .= ";user='$e_user';password='$e_password'"; + /** END OF FIX***/ break; - case 'oci': // Oracle with PDO is unsupported - if ($port) { - $dsn = 'oci:dbname=//' . $host . ':' . $port . '/' . $name; - } else { - $dsn = 'oci:dbname=//' . $host . '/' . $name; - } - break; - } + case 'oci': // Oracle with PDO is unsupported + if ($port) { + $dsn = 'oci:dbname=//' . $host . ':' . $port . '/' . $name; + } else { + $dsn = 'oci:dbname=//' . $host . '/' . $name; + } + break; + } try{ self::$PDO=new PDO($dsn,$user,$pass,$opts); }catch(PDOException $e){ @@ -214,18 +220,18 @@ class OC_DB { 'database' => $name ); break; - case 'oci': - $dsn = array( - 'phptype' => 'oci8', - 'username' => $user, - 'password' => $pass, - ); - if ($host != '') { - $dsn['hostspec'] = $host; - $dsn['database'] = $name; - } else { // use dbname for hostspec - $dsn['hostspec'] = $name; - } + case 'oci': + $dsn = array( + 'phptype' => 'oci8', + 'username' => $user, + 'password' => $pass, + ); + if ($host != '') { + $dsn['hostspec'] = $host; + $dsn['database'] = $name; + } else { // use dbname for hostspec + $dsn['hostspec'] = $name; + } break; } @@ -256,32 +262,30 @@ class OC_DB { * SQL query via MDB2 prepare(), needs to be execute()'d! */ static public function prepare( $query , $limit=null, $offset=null ){ - - if (!is_null($limit)) { - if (self::$backend == self::BACKEND_MDB2) { - //MDB2 uses or emulates limits & offset internally - self::$MDB2->setLimit($limit, $offset); - } else { - //PDO does not handle limit and offset. - //FIXME: check limit notation for other dbs - //the following sql thus might needs to take into account db ways of representing it - //(oracle has no LIMIT / OFFSET) - $limitsql = ' LIMIT ' . $limit; - if (!is_null($offset)) { - $limitsql .= ' OFFSET ' . $offset; - } - //insert limitsql - if (substr($query, -1) == ';') { //if query ends with ; - $query = substr($query, 0, -1) . $limitsql . ';'; - } else { - $query.=$limitsql; - } - } - } - - - - // Optimize the query + + if (!is_null($limit)) { + if (self::$backend == self::BACKEND_MDB2) { + //MDB2 uses or emulates limits & offset internally + self::$MDB2->setLimit($limit, $offset); + } else { + //PDO does not handle limit and offset. + //FIXME: check limit notation for other dbs + //the following sql thus might needs to take into account db ways of representing it + //(oracle has no LIMIT / OFFSET) + $limitsql = ' LIMIT ' . $limit; + if (!is_null($offset)) { + $limitsql .= ' OFFSET ' . $offset; + } + //insert limitsql + if (substr($query, -1) == ';') { //if query ends with ; + $query = substr($query, 0, -1) . $limitsql . ';'; + } else { + $query.=$limitsql; + } + } + } + + // Optimize the query $query = self::processQuery( $query ); self::connect(); @@ -395,17 +399,17 @@ class OC_DB { $file2 = 'static://db_scheme'; $content = str_replace( '*dbname*', $CONFIG_DBNAME, $content ); $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); - /* FIXME: REMOVE this commented code - * actually mysql, postgresql, sqlite and oracle support CUURENT_TIMESTAMP - * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html - * http://www.postgresql.org/docs/8.1/static/functions-datetime.html - * http://www.sqlite.org/lang_createtable.html - * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm - - if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't - $content = str_replace( '<default>0000-00-00 00:00:00</default>', '<default>CURRENT_TIMESTAMP</default>', $content ); - } - */ + /* FIXME: REMOVE this commented code + * actually mysql, postgresql, sqlite and oracle support CURRENT_TIMESTAMP + * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html + * http://www.postgresql.org/docs/8.1/static/functions-datetime.html + * http://www.sqlite.org/lang_createtable.html + * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm + + if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't + $content = str_replace( '<default>0000-00-00 00:00:00</default>', '<default>CURRENT_TIMESTAMP</default>', $content ); + } + */ file_put_contents( $file2, $content ); // Try to create tables @@ -418,20 +422,17 @@ class OC_DB { if( $definition instanceof MDB2_Schema_Error ){ die( $definition->getMessage().': '.$definition->getUserInfo()); } -// if(OC_Config::getValue('dbtype','sqlite')=='sqlite'){ -// $definition['overwrite']=true;//always overwrite for sqlite -// } - if(OC_Config::getValue('dbtype','sqlite')==='oci'){ - unset($definition['charset']); //or MDB2 tries SHUTDOWN IMMEDIATE - $oldname = $definition['name']; - $definition['name']=OC_Config::getValue( "dbuser", $oldname ); - } - + if(OC_Config::getValue('dbtype','sqlite')==='oci'){ + unset($definition['charset']); //or MDB2 tries SHUTDOWN IMMEDIATE + $oldname = $definition['name']; + $definition['name']=OC_Config::getValue( "dbuser", $oldname ); + } + $ret=self::$schema->createDatabase( $definition ); // Die in case something went wrong if( $ret instanceof MDB2_Error ){ - echo (self::$MDB2->getDebugOutput()); + echo (self::$MDB2->getDebugOutput()); die ($ret->getMessage() . ': ' . $ret->getUserInfo()); } @@ -462,16 +463,16 @@ class OC_DB { $file2 = 'static://db_scheme'; $content = str_replace( '*dbname*', $previousSchema['name'], $content ); $content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content ); - /* FIXME: REMOVE this commented code - * actually mysql, postgresql, sqlite and oracle support CUURENT_TIMESTAMP - * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html - * http://www.postgresql.org/docs/8.1/static/functions-datetime.html - * http://www.sqlite.org/lang_createtable.html - * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm + /* FIXME: REMOVE this commented code + * actually mysql, postgresql, sqlite and oracle support CUURENT_TIMESTAMP + * http://dev.mysql.com/doc/refman/5.0/en/timestamp-initialization.html + * http://www.postgresql.org/docs/8.1/static/functions-datetime.html + * http://www.sqlite.org/lang_createtable.html + * http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't $content = str_replace( '<default>0000-00-00 00:00:00</default>', '<default>CURRENT_TIMESTAMP</default>', $content ); } - */ + */ file_put_contents( $file2, $content ); $op = self::$schema->updateDatabase($file2, $previousSchema, array(), false); @@ -581,8 +582,9 @@ class OC_DB { // Delete our temporary file unlink( $file2 ); - foreach($definition['tables'] as $name=>$table){ - self::dropTable($name); + $tables=array_keys($definition['tables']); + foreach($tables as $table){ + self::dropTable($table); } } @@ -590,15 +592,14 @@ class OC_DB { * @brief replaces the owncloud tables with a new set * @param $file string path to the MDB2 xml db export file */ - public static function replaceDB( $file ){ - + public static function replaceDB( $file ){ $apps = OC_App::getAllApps(); self::beginTransaction(); // Delete the old tables self::removeDBStructure( OC::$SERVERROOT . '/db_structure.xml' ); foreach($apps as $app){ - $path = OC::$SERVERROOT.'/apps/'.$app.'/appinfo/database.xml'; + $path = OC_App::getAppPath($app).'/appinfo/database.xml'; if(file_exists($path)){ self::removeDBStructure( $path ); } diff --git a/lib/eventsource.php b/lib/eventsource.php index cf10660b94c..95af2e471bc 100644 --- a/lib/eventsource.php +++ b/lib/eventsource.php @@ -36,12 +36,15 @@ class OC_EventSource{ header('Cache-Control: no-cache'); $this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true'; if($this->fallback){ - $fallBackId=$_GET['fallback_id']; + $this->fallBackId=$_GET['fallback_id']; header("Content-Type: text/html"); echo str_repeat('<span></span>'.PHP_EOL,10); //dummy data to keep IE happy }else{ header("Content-Type: text/event-stream"); } + if( !OC_Util::isCallRegistered()){ + exit(); + } flush(); } diff --git a/lib/filecache.php b/lib/filecache.php index 719f419b37c..e85d6747f90 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -28,15 +28,13 @@ * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache */ class OC_FileCache{ - private static $savedData=array(); - /** * get the filesystem info from the cache * @param string path * @param string root (optional) * @return array * - * returns an assiciative array with the following keys: + * returns an associative array with the following keys: * - size * - mtime * - ctime @@ -44,29 +42,15 @@ class OC_FileCache{ * - encrypted * - versioned */ - public static function get($path,$root=''){ - if(self::isUpdated($path,$root)){ - if(!$root){//filesystem hooks are only valid for the default root + public static function get($path,$root=false){ + if(OC_FileCache_Update::hasUpdated($path,$root)){ + if($root===false){//filesystem hooks are only valid for the default root OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path)); }else{ - self::fileSystemWatcherWrite(array('path'=>$path),$root); + OC_FileCache_Update::update($path,$root); } } - if(!$root){ - $root=OC_Filesystem::getRoot(); - } - if($root=='/'){ - $root=''; - } - $path=$root.$path; - $query=OC_DB::prepare('SELECT `ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - $result=$query->execute(array(md5($path)))->fetchRow(); - if(is_array($result)){ - return $result; - }else{ - OC_Log::write('files','get(): file not found in cache ('.$path.')',OC_Log::DEBUG); - return false; - } + return OC_FileCache_Cached::get($path,$root); } /** @@ -77,35 +61,31 @@ class OC_FileCache{ * * $data is an assiciative array in the same format as returned by get */ - public static function put($path,$data,$root=''){ - if(!$root){ + public static function put($path,$data,$root=false){ + if($root===false){ $root=OC_Filesystem::getRoot(); } - if($root=='/'){ - $root=''; - } $fullpath=$root.$path; $parent=self::getParentId($fullpath); - $id=self::getFileId($fullpath); - if(isset(OC_FileCache::$savedData[$fullpath])){ - $data=array_merge(OC_FileCache::$savedData[$fullpath],$data); - unset(OC_FileCache::$savedData[$fullpath]); + $id=self::getId($fullpath,''); + if(isset(OC_FileCache_Cached::$savedData[$fullpath])){ + $data=array_merge(OC_FileCache_Cached::$savedData[$fullpath],$data); + unset(OC_FileCache_Cached::$savedData[$fullpath]); } - - // add parent directory to the file cache if it does not exist yet.
- if ($parent == -1 && $fullpath != $root) {
- $parentDir = substr(dirname($path), 0, strrpos(dirname($path), DIRECTORY_SEPARATOR));
- self::scanFile($parentDir);
- $parent = self::getParentId($fullpath);
- } - if($id!=-1){ self::update($id,$data); return; } + // add parent directory to the file cache if it does not exist yet. + if ($parent == -1 && $fullpath != $root) { + $parentDir = substr(dirname($path), 0, strrpos(dirname($path), DIRECTORY_SEPARATOR)); + self::scanFile($parentDir); + $parent = self::getParentId($fullpath); + } + if(!isset($data['size']) or !isset($data['mtime'])){//save incomplete data for the next time we write it - self::$savedData[$fullpath]=$data; + OC_FileCache_Cached::$savedData[$fullpath]=$data; return; } if(!isset($data['encrypted'])){ @@ -126,6 +106,10 @@ class OC_FileCache{ if(OC_DB::isError($result)){ OC_Log::write('files','error while writing file('.$fullpath.') to cache',OC_Log::ERROR); } + + if($cache=OC_Cache::getUserCache(true)){ + $cache->remove('fileid/'.$fullpath);//ensure we don't have -1 cached + } } /** @@ -157,7 +141,7 @@ class OC_FileCache{ $query=OC_DB::prepare($sql); $result=$query->execute($arguments); if(OC_DB::isError($result)){ - OC_Log::write('files','error while updating file id('.$id.') in cache',OC_Log::ERROR); + OC_Log::write('files','error while updating file('.$id.') in cache',OC_Log::ERROR); } } @@ -167,19 +151,21 @@ class OC_FileCache{ * @param string newPath * @param string root (optional) */ - public static function move($oldPath,$newPath,$root=''){ - if(!$root){ + public static function move($oldPath,$newPath,$root=false){ + if($root===false){ $root=OC_Filesystem::getRoot(); } - if($root=='/'){ - $root=''; - } $oldPath=$root.$oldPath; $newPath=$root.$newPath; $newParent=self::getParentId($newPath); $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `parent`=? ,`name`=?, `path`=?, `path_hash`=? WHERE `path_hash`=?'); $query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath))); + if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$oldPath)){ + $cache->set('fileid/'.$newPath,$cache->get('fileid/'.$oldPath)); + $cache->remove('fileid/'.$oldPath); + } + $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `path` LIKE ?'); $oldLength=strlen($oldPath); $updateQuery=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `path`=?, `path_hash`=? WHERE `path_hash`=?'); @@ -187,33 +173,31 @@ class OC_FileCache{ $old=$row['path']; $new=$newPath.substr($old,$oldLength); $updateQuery->execute(array($new,md5($new),md5($old))); + + if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$old)){ + $cache->set('fileid/'.$new,$cache->get('fileid/'.$old)); + $cache->remove('fileid/'.$old); + } } } /** * delete info from the cache - * @param string/int $file + * @param string path * @param string root (optional) */ - public static function delete($file,$root=''){ - if(!is_numeric($file)){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - } - if($root=='/'){ - $root=''; - } - $path=$root.$file; - self::delete(self::getFileId($path)); - }elseif($file!=-1){ - $query=OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `parent`=?'); - $result=$query->execute(array($file)); - while($child=$result->fetchRow()){ - self::delete(intval($child['id'])); - } - $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE `id`=?'); - $query->execute(array($file)); + public static function delete($path,$root=false){ + if($root===false){ + $root=OC_Filesystem::getRoot(); } + $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE `path_hash`=?'); + $query->execute(array(md5($root.$path))); + + //delete everything inside the folder + $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE `path` LIKE ?'); + $query->execute(array($root.$path.'/%')); + + OC_Cache::remove('fileid/'.$root.$path); } /** @@ -223,13 +207,10 @@ class OC_FileCache{ * @param string root (optional) * @return array of filepaths */ - public static function search($search,$returnData=false,$root=''){ - if(!$root){ + public static function search($search,$returnData=false,$root=false){ + if($root===false){ $root=OC_Filesystem::getRoot(); } - if($root=='/'){ - $root=''; - } $rootLen=strlen($root); if(!$returnData){ $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `name` LIKE ? AND `user`=?'); @@ -264,29 +245,11 @@ class OC_FileCache{ * - encrypted * - versioned */ - public static function getFolderContent($path,$root='',$mimetype_filter=''){ - if(self::isUpdated($path,$root,true)){ - self::updateFolder($path,$root); - } - if(!$root){ - $root=OC_Filesystem::getRoot(); - } - if($root=='/'){ - $root=''; - } - $path=$root.$path; - $parent=self::getFileId($path); - if($parent==-1){ - return array(); - } - $query=OC_DB::prepare('SELECT `name`,`ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `parent`=? AND (`mimetype` LIKE ? OR `mimetype` = ?)'); - $result=$query->execute(array($parent, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll(); - if(is_array($result)){ - return $result; - }else{ - OC_Log::write('files','getFolderContent(): file not found in cache ('.$path.')',OC_Log::DEBUG); - return false; + public static function getFolderContent($path,$root=false,$mimetype_filter=''){ + if(OC_FileCache_Update::hasUpdated($path,$root,true)){ + OC_FileCache_Update::updateFolder($path,$root); } + return OC_FileCache_Cached::getFolderContent($path,$root,$mimetype_filter); } /** @@ -295,58 +258,44 @@ class OC_FileCache{ * @param string root (optional) * @return bool */ - public static function inCache($path,$root=''){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - } - if($root=='/'){ - $root=''; - } - $path=$root.$path; - return self::getFileId($path)!=-1; + public static function inCache($path,$root=false){ + return self::getId($path,$root)!=-1; } /** * get the file id as used in the cache - * unlike the public getId, full paths are used here (/usename/files/foo instead of /foo) - * @param string $path + * @param string path + * @param string root (optional) * @return int */ - private static function getFileId($path){ - $query=OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - if(OC_DB::isError($query)){ - OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR); - return -1; + public static function getId($path,$root=false){ + if($root===false){ + $root=OC_Filesystem::getRoot(); + } + + $fullPath=$root.$path; + if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$fullPath)){ + return $cache->get('fileid/'.$fullPath); } - $result=$query->execute(array(md5($path))); + + $query=OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); + $result=$query->execute(array(md5($fullPath))); if(OC_DB::isError($result)){ OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR); return -1; } + $result=$result->fetchRow(); if(is_array($result)){ - return $result['id']; + $id=$result['id']; }else{ - OC_Log::write('files','getFileId(): file not found in cache ('.$path.')',OC_Log::DEBUG); - return -1; + $id=-1; } - } - - /** - * get the file id as used in the cache - * @param string path - * @param string root (optional) - * @return int - */ - public static function getId($path,$root=''){ - if(!$root){ - $root=OC_Filesystem::getRoot(); + if($cache=OC_Cache::getUserCache(true)){ + $cache->set('fileid/'.$fullPath,$id); } - if($root=='/'){ - $root=''; - } - $path=$root.$path; - return self::getFileId($path); + + return $id; } /** @@ -379,154 +328,23 @@ class OC_FileCache{ if($path=='/'){ return -1; }else{ - return self::getFileId(dirname($path)); - } - } - - /** - * called when changes are made to files - * @param array $params - * @param string root (optional) - */ - public static function fileSystemWatcherWrite($params,$root=''){ - if(!$root){ - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView(($root=='/')?'':$root); - } - - $path=$params['path']; - $fullPath=$view->getRoot().$path; - $mimetype=$view->getMimeType($path); - $dir=$view->is_dir($path.'/'); - //dont use self::get here, we don't want inifinte loops when a file has changed - $cachedSize=self::getCachedSize($path,$root); - $size=0; - if($dir){ - if(self::inCache($path,$root) && $path != '/Shared'){ - $parent=self::getFileId($fullPath); - $query=OC_DB::prepare('SELECT `size` FROM `*PREFIX*fscache` WHERE `parent`=?'); - $result=$query->execute(array($parent)); - while($row=$result->fetchRow()){ - $size+=$row['size']; - } - $mtime=$view->filemtime($path); - $ctime=$view->filectime($path); - $writable=$view->is_writable($path); - self::put($path,array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype,'writable'=>$writable)); - }else{ - $count=0; - self::scan($path,null,$count,$root); - } - }else{ - $size=self::scanFile($path,$root); + return self::getId(dirname($path),''); } - self::increaseSize(dirname($fullPath),$size-$cachedSize); } - public static function getCached($path,$root=''){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - }else{ - if($root=='/'){ - $root=''; - } - } - $path=$root.$path; - $query=OC_DB::prepare('SELECT `ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - $result=$query->execute(array(md5($path)))->fetchRow(); - if(is_array($result)){ - if(isset(self::$savedData[$path])){ - $result=array_merge($result,self::$savedData[$path]); - } - return $result; - }else{ - OC_Log::write('files','getChached(): file not found in cache ('.$path.')',OC_Log::DEBUG); - if(isset(self::$savedData[$path])){ - return self::$savedData[$path]; - }else{ - return array(); - } - } - } - - private static function getCachedSize($path,$root){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - }else{ - if($root=='/'){ - $root=''; - } - } - $path=$root.$path; - $query=OC_DB::prepare('SELECT `size` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - $result=$query->execute(array(md5($path))); - if($row=$result->fetchRow()){ - return $row['size']; - }else{//file not in cache - return 0; - } - } - - /** - * called when files are deleted - * @param array $params - * @param string root (optional) - */ - public static function fileSystemWatcherDelete($params,$root=''){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - } - if($root=='/'){ - $root=''; - } - $path=$params['path']; - $fullPath=$root.$path; - if(self::getFileId($fullPath)==-1){ - return; - } - $size=self::getCachedSize($path,$root); - self::increaseSize(dirname($fullPath),-$size); - self::delete($path); - } - - /** - * called when files are deleted - * @param array $params - * @param string root (optional) - */ - public static function fileSystemWatcherRename($params,$root=''){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - } - if($root=='/'){ - $root=''; - } - $oldPath=$params['oldpath']; - $newPath=$params['newpath']; - $fullOldPath=$root.$oldPath; - $fullNewPath=$root.$newPath; - if(($id=self::getFileId($fullOldPath))!=-1){ - $oldSize=self::getCachedSize($oldPath,$root); - }else{ - return; - } - $size=OC_Filesystem::filesize($newPath); - self::increaseSize(dirname($fullOldPath),-$oldSize); - self::increaseSize(dirname($fullNewPath),$oldSize); - self::move($oldPath,$newPath); - } - /** * adjust the size of the parent folders * @param string $path * @param int $sizeDiff + * @param string root (optinal) */ - private static function increaseSize($path,$sizeDiff){ + public static function increaseSize($path,$sizeDiff, $root=false){ if($sizeDiff==0) return; - while(($id=self::getFileId($path))!=-1){//walk up the filetree increasing the size of all parent folders + $id=self::getId($path,$root); + while($id!=-1){//walk up the filetree increasing the size of all parent folders $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `size`=`size`+? WHERE `id`=?'); $query->execute(array($sizeDiff,$id)); + $id=self::getParentId($path); $path=dirname($path); } } @@ -536,17 +354,21 @@ class OC_FileCache{ * @param string $path * @param OC_EventSource $enventSource (optional) * @param int count (optional) - * @param string root (optionak) + * @param string root (optional) */ - public static function scan($path,$eventSource=false,&$count=0,$root=''){ + public static function scan($path,$eventSource=false,&$count=0,$root=false){ if($eventSource){ $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); } $lastSend=$count; - if(!$root){ + // NOTE: Ugly hack to prevent shared files from going into the cache (the source already exists somewhere in the cache) + if (substr($path, 0, 7) == '/Shared') { + return; + } + if($root===false){ $view=OC_Filesystem::getView(); }else{ - $view=new OC_FilesystemView(($root=='/')?'':$root); + $view=new OC_FilesystemView($root); } self::scanFile($path,$root); $dh=$view->opendir($path.'/'); @@ -568,8 +390,9 @@ class OC_FileCache{ } } } - self::cleanFolder($path,$root); - self::increaseSize($view->getRoot().$path,$totalSize); + + OC_FileCache_Update::cleanFolder($path,$root); + self::increaseSize($path,$totalSize,$root); } /** @@ -578,11 +401,15 @@ class OC_FileCache{ * @param string root (optional) * @return int size of the scanned file */ - public static function scanFile($path,$root=''){ - if(!$root){ + public static function scanFile($path,$root=false){ + // NOTE: Ugly hack to prevent shared files from going into the cache (the source already exists somewhere in the cache) + if (substr($path, 0, 7) == '/Shared') { + return; + } + if($root===false){ $view=OC_Filesystem::getView(); }else{ - $view=new OC_FilesystemView(($root=='/')?'':$root); + $view=new OC_FilesystemView($root); } if(!$view->is_readable($path)) return; //cant read, nothing we can do clearstatcache(); @@ -615,11 +442,9 @@ class OC_FileCache{ * seccond mimetype part can be ommited * e.g. searchByMime('audio') */ - public static function searchByMime($part1,$part2=null,$root=null){ - if(!$root){ + public static function searchByMime($part1,$part2=null,$root=false){ + if($root===false){ $root=OC_Filesystem::getRoot(); - }elseif($root=='/'){ - $root=''; } $rootLen=strlen($root); $root .= '%'; @@ -639,104 +464,6 @@ class OC_FileCache{ } /** - * check if a file or folder is updated outside owncloud - * @param string path - * @param string root (optional) - * @param bool folder (optional) - * @return bool - */ - public static function isUpdated($path,$root='',$folder=false){ - if(!$root){ - $root=OC_Filesystem::getRoot(); - $view=OC_Filesystem::getView(); - }else{ - if($root=='/'){ - $root=''; - } - $view=new OC_FilesystemView($root); - } - if(!$view->file_exists($path)){ - return false; - } - $mtime=$view->filemtime($path.(($folder)?'/':'')); - $isDir=$view->is_dir($path); - $fullPath=$root.$path; - $query=OC_DB::prepare('SELECT `mtime` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - $result=$query->execute(array(md5($fullPath))); - if($row=$result->fetchRow()){ - $cachedMTime=$row['mtime']; - return ($mtime>$cachedMTime); - }else{//file not in cache, so it has to be updated - if($path=='/' or $path==''){//dont auto update the root folder, it will be scanned - return false; - } - return true; - } - } - - /** - * update the cache according to changes in the folder - * @param string path - * @param string root (optional) - */ - private static function updateFolder($path,$root=''){ - if(!$root){ - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView(($root=='/')?'':$root); - } - $dh=$view->opendir($path.'/'); - if($dh){//check for changed/new files - while (($filename = readdir($dh)) !== false) { - if($filename != '.' and $filename != '..'){ - $file=$path.'/'.$filename; - if(self::isUpdated($file,$root)){ - if(!$root){//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$file)); - }else{ - self::fileSystemWatcherWrite(array('path'=>$file),$root); - } - } - } - } - } - - self::cleanFolder($path,$root); - - //update the folder last, so we can calculate the size correctly - if(!$root){//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path)); - }else{ - self::fileSystemWatcherWrite(array('path'=>$path),$root); - } - } - - /** - * delete non existing files from the cache - */ - private static function cleanFolder($path,$root=''){ - if(!$root){ - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView(($root=='/')?'':$root); - } - //check for removed files, not using getFolderContent to prevent loops - $parent=self::getFileId($view->getRoot().$path); - $query=OC_DB::prepare('SELECT `name` FROM `*PREFIX*fscache` WHERE `parent`=?'); - $result=$query->execute(array($parent)); - while($row=$result->fetchRow()){ - $file=$path.'/'.$row['name']; - if(!$view->file_exists($file)){ - if(!$root){//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem','post_delete',array('path'=>$file)); - }else{ - self::fileSystemWatcherDelete(array('path'=>$file),$root); - } - } - } - } - - /** * clean old pre-path_hash entries */ public static function clean(){ @@ -746,6 +473,6 @@ class OC_FileCache{ } //watch for changes and try to keep the cache up to date -OC_Hook::connect('OC_Filesystem','post_write','OC_FileCache','fileSystemWatcherWrite'); -OC_Hook::connect('OC_Filesystem','post_delete','OC_FileCache','fileSystemWatcherDelete'); -OC_Hook::connect('OC_Filesystem','post_rename','OC_FileCache','fileSystemWatcherRename'); +OC_Hook::connect('OC_Filesystem','post_write','OC_FileCache_Update','fileSystemWatcherWrite'); +OC_Hook::connect('OC_Filesystem','post_delete','OC_FileCache_Update','fileSystemWatcherDelete'); +OC_Hook::connect('OC_Filesystem','post_rename','OC_FileCache_Update','fileSystemWatcherRename'); diff --git a/lib/filecache/cached.php b/lib/filecache/cached.php new file mode 100644 index 00000000000..17a792a23d4 --- /dev/null +++ b/lib/filecache/cached.php @@ -0,0 +1,70 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +/** + * get data from the filecache without checking for updates + */ +class OC_FileCache_Cached{ + public static $savedData=array(); + + public static function get($path,$root=false){ + if($root===false){ + $root=OC_Filesystem::getRoot(); + } + $path=$root.$path; + $query=OC_DB::prepare('SELECT path,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?'); + $result=$query->execute(array(md5($path)))->fetchRow(); + if(is_array($result)){ + if(isset(self::$savedData[$path])){ + $result=array_merge($result,self::$savedData[$path]); + } + return $result; + }else{ + if(isset(self::$savedData[$path])){ + return self::$savedData[$path]; + }else{ + return array(); + } + } + } + + /** + * get all files and folders in a folder + * @param string path + * @param string root (optional) + * @return array + * + * returns an array of assiciative arrays with the following keys: + * - path + * - name + * - size + * - mtime + * - ctime + * - mimetype + * - encrypted + * - versioned + */ + public static function getFolderContent($path,$root=false,$mimetype_filter=''){ + if($root===false){ + $root=OC_Filesystem::getRoot(); + } + $parent=OC_FileCache::getId($path,$root); + if($parent==-1){ + return array(); + } + $query=OC_DB::prepare('SELECT path,name,ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE parent=? AND (mimetype LIKE ? OR mimetype = ?)'); + $result=$query->execute(array($parent, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll(); + if(is_array($result)){ + return $result; + }else{ + OC_Log::write('files','getFolderContent(): file not found in cache ('.$path.')',OC_Log::DEBUG); + return false; + } + } +}
\ No newline at end of file diff --git a/lib/filecache/update.php b/lib/filecache/update.php new file mode 100644 index 00000000000..0b5ff8e2446 --- /dev/null +++ b/lib/filecache/update.php @@ -0,0 +1,214 @@ +<?php +/** + * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +/** + * handles updating the filecache according to outside changes + */ +class OC_FileCache_Update{ + /** + * check if a file or folder is updated outside owncloud + * @param string path + * @param string root (optional) + * @param boolean folder + * @return bool + */ + public static function hasUpdated($path,$root=false,$folder=false){ + if($root===false){ + $view=OC_Filesystem::getView(); + }else{ + $view=new OC_FilesystemView($root); + } + if(!$view->file_exists($path)){ + return false; + } + $cachedData=OC_FileCache_Cached::get($path,$root); + if(isset($cachedData['mtime'])){ + $cachedMTime=$cachedData['mtime']; + if($folder){ + return $view->hasUpdated($path.'/',$cachedMTime); + }else{ + return $view->hasUpdated($path,$cachedMTime); + } + }else{//file not in cache, so it has to be updated + if(($path=='/' or $path=='') and $root===false){//dont auto update the home folder, it will be scanned + return false; + } + return true; + } + } + + /** + * delete non existing files from the cache + */ + public static function cleanFolder($path,$root=false){ + if($root===false){ + $view=OC_Filesystem::getView(); + }else{ + $view=new OC_FilesystemView($root); + } + + $cachedContent=OC_FileCache_Cached::getFolderContent($path,$root); + foreach($cachedContent as $fileData){ + $path=$fileData['path']; + $file=$view->getRelativePath($path); + if(!$view->file_exists($file)){ + if($root===false){//filesystem hooks are only valid for the default root + OC_Hook::emit('OC_Filesystem','post_delete',array('path'=>$file)); + }else{ + self::delete($file,$root); + } + } + } + } + + /** + * update the cache according to changes in the folder + * @param string path + * @param string root (optional) + */ + public static function updateFolder($path,$root=false){ + if($root===false){ + $view=OC_Filesystem::getView(); + }else{ + $view=new OC_FilesystemView($root); + } + $dh=$view->opendir($path.'/'); + if($dh){//check for changed/new files + while (($filename = readdir($dh)) !== false) { + if($filename != '.' and $filename != '..'){ + $file=$path.'/'.$filename; + if(self::hasUpdated($file,$root)){ + if($root===false){//filesystem hooks are only valid for the default root + OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$file)); + }else{ + self::update($file,$root); + } + } + } + } + } + + self::cleanFolder($path,$root); + + //update the folder last, so we can calculate the size correctly + if($root===false){//filesystem hooks are only valid for the default root + OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path)); + }else{ + self::update($path,$root); + } + } + + /** + * called when changes are made to files + * @param array $params + * @param string root (optional) + */ + public static function fileSystemWatcherWrite($params){ + $path=$params['path']; + self::update($path); + } + + /** + * called when files are deleted + * @param array $params + * @param string root (optional) + */ + public static function fileSystemWatcherDelete($params){ + $path=$params['path']; + self::delete($path); + } + + /** + * called when files are deleted + * @param array $params + * @param string root (optional) + */ + public static function fileSystemWatcherRename($params){ + $oldPath=$params['oldpath']; + $newPath=$params['newpath']; + self::rename($oldPath,$newPath); + } + + /** + * update the filecache according to changes to the fileysystem + * @param string path + * @param string root (optional) + */ + public static function update($path,$root=false){ + if($root===false){ + $view=OC_Filesystem::getView(); + }else{ + $view=new OC_FilesystemView($root); + } + + $mimetype=$view->getMimeType($path); + + $size=0; + $cached=OC_FileCache_Cached::get($path,$root); + $cachedSize=isset($cached['size'])?$cached['size']:0; + + if($view->is_dir($path.'/')){ + if(OC_FileCache::inCache($path,$root)){ + $cachedContent=OC_FileCache_Cached::getFolderContent($path,$root); + foreach($cachedContent as $file){ + $size+=$file['size']; + } + $mtime=$view->filemtime($path.'/'); + $ctime=$view->filectime($path.'/'); + $writable=$view->is_writable($path.'/'); + OC_FileCache::put($path,array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype,'writable'=>$writable)); + }else{ + $count=0; + OC_FileCache::scan($path,null,$count,$root); + return; //increaseSize is already called inside scan + } + }else{ + $size=OC_FileCache::scanFile($path,$root); + } + OC_FileCache::increaseSize(dirname($path),$size-$cachedSize,$root); + } + + /** + * update the filesystem after a delete has been detected + * @param string path + * @param string root (optional) + */ + public static function delete($path,$root=false){ + $cached=OC_FileCache_Cached::get($path,$root); + if(!isset($cached['size'])){ + return; + } + $size=$cached['size']; + OC_FileCache::increaseSize(dirname($path),-$size,$root); + OC_FileCache::delete($path,$root); + } + + /** + * update the filesystem after a rename has been detected + * @param string oldPath + * @param string newPath + * @param string root (optional) + */ + public static function rename($oldPath,$newPath,$root=false){ + if(!OC_FileCache::inCache($oldPath,$root)){ + return; + } + if($root===false){ + $view=OC_Filesystem::getView(); + }else{ + $view=new OC_FilesystemView($root); + } + + $cached=OC_FileCache_Cached::get($oldPath,$root); + $oldSize=$cached['size']; + OC_FileCache::increaseSize(dirname($oldPath),-$oldSize,$root); + OC_FileCache::increaseSize(dirname($newPath),$oldSize,$root); + OC_FileCache::move($oldPath,$newPath); + } +}
\ No newline at end of file diff --git a/lib/filechunking.php b/lib/filechunking.php new file mode 100644 index 00000000000..d03af226d8b --- /dev/null +++ b/lib/filechunking.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +class OC_FileChunking { + protected $info; + protected $cache; + + static public function decodeName($name) { + preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches); + return $matches; + } + + public function __construct($info) { + $this->info = $info; + } + + public function getPrefix() { + $name = $this->info['name']; + $transferid = $this->info['transferid']; + + return $name.'-chunking-'.$transferid.'-'; + } + + protected function getCache() { + if (!isset($this->cache)) { + $this->cache = new OC_Cache_File(); + } + return $this->cache; + } + + public function store($index, $data) { + $cache = $this->getCache(); + $name = $this->getPrefix().$index; + $cache->set($name, $data); + } + + public function isComplete() { + $prefix = $this->getPrefix(); + $parts = 0; + $cache = $this->getCache(); + for($i=0; $i < $this->info['chunkcount']; $i++) { + if ($cache->hasKey($prefix.$i)) { + $parts ++; + } + } + return $parts == $this->info['chunkcount']; + } + + public function assemble($f) { + $cache = $this->getCache(); + $prefix = $this->getPrefix(); + for($i=0; $i < $this->info['chunkcount']; $i++) { + $chunk = $cache->get($prefix.$i); + $cache->remove($prefix.$i); + fwrite($f,$chunk); + } + fclose($f); + } + + public function signature_split($orgfile, $input) { + $info = unpack('n', fread($input, 2)); + $blocksize = $info[1]; + $this->info['transferid'] = mt_rand(); + $count = 0; + $needed = array(); + $cache = $this->getCache(); + $prefix = $this->getPrefix(); + while (!feof($orgfile)) { + $new_md5 = fread($input, 16); + if (feof($input)) { + break; + } + $data = fread($orgfile, $blocksize); + $org_md5 = md5($data, true); + if ($org_md5 == $new_md5) { + $cache->set($prefix.$count, $data); + } else { + $needed[] = $count; + } + $count++; + } + return array( + 'transferid' => $this->info['transferid'], + 'needed' => $needed, + 'count' => $count, + ); + } +} diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 9cda94c1c2b..ec04faa9bc5 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -27,14 +27,17 @@ * Manipulation happens by using 2 kind of proxy operations, pre and post proxies * that manipulate the filesystem call and the result of the call respectively * - * A pre-proxy recieves the filepath as arugments (or 2 filespaths in case of operations like copy or move) and return a boolean - * If a pre-proxy returnes false the file operation will be canceled + * A pre-proxy recieves the filepath as arugments (or 2 filespaths in case of + * operations like copy or move) and return a boolean + * If a pre-proxy returns false the file operation will be canceled * All filesystem operations have a pre-proxy * * A post-proxy recieves 2 arguments, the filepath and the result of the operation. - * The return calue of the post-proxy will be used as the new result of the operation - * The operations that have a post-proxy are - * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, fileatime, filemtime, filectime, file_get_contents, getMimeType, hash, fopen, free_space and search + * The return value of the post-proxy will be used as the new result of the operation + * The operations that have a post-proxy are: + * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, + * is_writable, fileatime, filemtime, filectime, file_get_contents, + * getMimeType, hash, fopen, free_space and search */ class OC_FileProxy{ diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php index 1d2a1ffcf0b..7316224cc61 100644 --- a/lib/fileproxy/quota.php +++ b/lib/fileproxy/quota.php @@ -54,10 +54,10 @@ class OC_FileProxy_Quota extends OC_FileProxy{ * @return int */ private function getFreeSpace(){ - $rootInfo=OC_FileCache::get(''); + $rootInfo=OC_FileCache_Cached::get(''); // TODO Remove after merge of share_api if (OC_FileCache::inCache('/Shared')) { - $sharedInfo=OC_FileCache::get('/Shared'); + $sharedInfo=OC_FileCache_Cached::get('/Shared'); } else { $sharedInfo = null; } diff --git a/lib/files.php b/lib/files.php index bb128a8e44f..1a1fffa0a50 100644 --- a/lib/files.php +++ b/lib/files.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -30,16 +30,45 @@ class OC_Files { /** * get the content of a directory - * @param dir $directory + * @param dir $directory path under datadirectory */ - public static function getDirectoryContent($directory, $mimetype_filter = ''){ - if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){ - $directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY)); + public static function getDirectoryContent($directory, $mimetype_filter = ''){ + $directory=OC_Filesystem::normalizePath($directory); + if($directory=='/'){ + $directory=''; } - $files=OC_FileCache::getFolderContent($directory, '', $mimetype_filter); - foreach($files as &$file){ - $file['directory']=$directory; - $file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file'; + $files = array(); + if (($directory == '/Shared' || substr($directory, 0, 8) == '/Shared/') && OC_App::isEnabled('files_sharing')) { + if ($directory == '/Shared') { + $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter)); + } else { + $pos = strpos($directory, '/', 8); + // Get shared folder name + if ($pos !== false) { + $itemTarget = substr($directory, 7, $pos - 7); + } else { + $itemTarget = substr($directory, 7); + } + $files = OCP\Share::getItemSharedWith('folder', $itemTarget, OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter)); + } + } else { + $files = OC_FileCache::getFolderContent($directory, false, $mimetype_filter); + foreach ($files as &$file) { + $file['directory'] = $directory; + $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $permissions = OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE; + if ($file['type'] == 'dir' && $file['writable']) { + $permissions |= OCP\Share::PERMISSION_CREATE; + } + if ($file['writable']) { + $permissions |= OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE; + } + $file['permissions'] = $permissions; + } + if ($directory == '' && OC_App::isEnabled('files_sharing')) { + // Add 'Shared' folder + $files = array_merge($files, OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT)); + } } usort($files, "fileCmp");//TODO: remove this once ajax is merged return $files; @@ -107,8 +136,7 @@ class OC_Files { header('Content-Type: application/zip'); header('Content-Length: ' . filesize($filename)); }else{ - $fileData=OC_FileCache::get($filename); - header('Content-Type: ' . $fileData['mimetype']); + header('Content-Type: '.OC_Filesystem::getMimeType($filename)); } }elseif($zip or !OC_Filesystem::file_exists($filename)){ header("HTTP/1.0 404 Not Found"); @@ -170,10 +198,12 @@ class OC_Files { * @param file $target */ public static function move($sourceDir,$source,$targetDir,$target){ - if(OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared')){ + if(OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared') && !OC_Filesystem::file_exists($targetDir.'/'.$target)){ $targetFile=self::normalizePath($targetDir.'/'.$target); $sourceFile=self::normalizePath($sourceDir.'/'.$source); return OC_Filesystem::rename($sourceFile,$targetFile); + } else { + return false; } } @@ -373,10 +403,12 @@ class OC_Files { } } - //supress errors in case we don't have permissions for it - if(@file_put_contents(OC::$SERVERROOT.'/.htaccess', $htaccess)) { - return OC_Helper::computerFileSize($size); - } + //check for write permissions + if(is_writable(OC::$SERVERROOT.'/.htaccess')) { + file_put_contents(OC::$SERVERROOT.'/.htaccess', $htaccess); + return OC_Helper::computerFileSize($size); + } else { OC_Log::write('files','Can\'t write upload limit to '.OC::$SERVERROOT.'/.htaccess. Please check the file permissions',OC_Log::WARN); } + return false; } diff --git a/lib/filestorage.php b/lib/filestorage.php index 1a33d304630..5bfd09253d5 100644 --- a/lib/filestorage.php +++ b/lib/filestorage.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,10 +21,10 @@ */ /** - * Privde a common interface to all different storage options + * Provde a common interface to all different storage options */ abstract class OC_Filestorage{ - public function __construct($parameters){} + abstract public function __construct($parameters); abstract public function mkdir($path); abstract public function rmdir($path); abstract public function opendir($path); @@ -33,8 +33,11 @@ abstract class OC_Filestorage{ abstract public function stat($path); abstract public function filetype($path); abstract public function filesize($path); - abstract public function is_readable($path); - abstract public function is_writable($path); + abstract public function isCreatable($path); + abstract public function isReadable($path); + abstract public function isUpdatable($path); + abstract public function isDeletable($path); + abstract public function isSharable($path); abstract public function file_exists($path); abstract public function filectime($path); abstract public function filemtime($path); @@ -50,4 +53,14 @@ abstract class OC_Filestorage{ abstract public function search($query); abstract public function touch($path, $mtime=null); abstract public function getLocalFile($path);// get a path to a local version of the file, whether the original file is local or remote + abstract public function getLocalFolder($path);// get a path to a local version of the folder, whether the original file is local or remote + /** + * check if a file or folder has been updated since $time + * @param int $time + * @return bool + * + * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. + * returning true for other changes in the folder is optional + */ + abstract public function hasUpdated($path,$time); } diff --git a/lib/filestorage/common.php b/lib/filestorage/common.php index 0d3e2c25fd6..c829be62f74 100644 --- a/lib/filestorage/common.php +++ b/lib/filestorage/common.php @@ -20,6 +20,18 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ +/** + * Storage backend class for providing common filesystem operation methods + * which are not storage-backend specific. + * + * OC_Filestorage_Common is never used directly; it is extended by all other + * storage backends, where its methods may be overridden, and additional + * (backend-specific) methods are defined. + * + * Some OC_Filestorage_Common methods call functions which are first defined + * in classes which extend it, e.g. $this->stat() . + */ + abstract class OC_Filestorage_Common extends OC_Filestorage { public function __construct($parameters){} @@ -42,8 +54,17 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { return $stat['size']; } } -// abstract public function is_readable($path); -// abstract public function is_writable($path); + public function isCreatable($path) { + return $this->isUpdatable($path); + } +// abstract public function isReadable($path); +// abstract public function isUpdatable($path); + public function isDeletable($path) { + return $this->isUpdatable($path); + } + public function isSharable($path) { + return $this->isReadable($path); + } // abstract public function file_exists($path); public function filectime($path) { $stat = $this->stat($path); @@ -87,6 +108,79 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { return $count>0; } // abstract public function fopen($path,$mode); + + /** + * @brief Deletes all files and folders recursively within a directory + * @param $directory The directory whose contents will be deleted + * @param $empty Flag indicating whether directory will be emptied + * @returns true/false + * + * @note By default the directory specified by $directory will be + * deleted together with its contents. To avoid this set $empty to true + */ + public function deleteAll( $directory, $empty = false ) { + + // strip leading slash + if( substr( $directory, 0, 1 ) == "/" ) { + + $directory = substr( $directory, 1 ); + + } + + // strip trailing slash + if( substr( $directory, -1) == "/" ) { + + $directory = substr( $directory, 0, -1 ); + + } + + if ( !$this->file_exists( \OCP\USER::getUser() . '/' . $directory ) || !$this->is_dir( \OCP\USER::getUser() . '/' . $directory ) ) { + + return false; + + } elseif( !$this->is_readable( \OCP\USER::getUser() . '/' . $directory ) ) { + + return false; + + } else { + + $directoryHandle = $this->opendir( \OCP\USER::getUser() . '/' . $directory ); + + while ( $contents = readdir( $directoryHandle ) ) { + + if ( $contents != '.' && $contents != '..') { + + $path = $directory . "/" . $contents; + + if ( $this->is_dir( $path ) ) { + + deleteAll( $path ); + + } else { + + $this->unlink( \OCP\USER::getUser() .'/' . $path ); // TODO: make unlink use same system path as is_dir + + } + } + + } + + //$this->closedir( $directoryHandle ); // TODO: implement closedir in OC_FSV + + if ( $empty == false ) { + + if ( !$this->rmdir( $directory ) ) { + + return false; + + } + + } + + return true; + } + + } public function getMimeType($path){ if(!$this->file_exists($path)){ return false; @@ -135,9 +229,29 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { } $tmpFile=OC_Helper::tmpFile($extension); $target=fopen($tmpFile,'w'); - $count=OC_Helper::streamCopy($source,$target); + OC_Helper::streamCopy($source,$target); return $tmpFile; } + public function getLocalFolder($path){ + $baseDir=OC_Helper::tmpFolder(); + $this->addLocalFolder($path,$baseDir); + return $baseDir; + } + private function addLocalFolder($path,$target){ + if($dh=$this->opendir($path)){ + while($file=readdir($dh)){ + if($file!=='.' and $file!=='..'){ + if($this->is_dir($path.'/'.$file)){ + mkdir($target.'/'.$file); + $this->addLocalFolder($path.'/'.$file,$target.'/'.$file); + }else{ + $tmp=$this->toTmpFile($path.'/'.$file); + rename($tmp,$target.'/'.$file); + } + } + } + } + } // abstract public function touch($path, $mtime=null); protected function searchInDir($query,$dir=''){ @@ -156,4 +270,13 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { } return $files; } + + /** + * check if a file or folder has been updated since $time + * @param int $time + * @return bool + */ + public function hasUpdated($path,$time){ + return $this->filemtime($path)>$time; + } } diff --git a/lib/filestorage/commontest.php b/lib/filestorage/commontest.php index 1b01ff856a3..b5126a407b3 100644 --- a/lib/filestorage/commontest.php +++ b/lib/filestorage/commontest.php @@ -51,11 +51,11 @@ class OC_Filestorage_CommonTest extends OC_Filestorage_Common{ public function filetype($path){ return $this->storage->filetype($path); } - public function is_readable($path){ - return $this->storage->is_readable($path); + public function isReadable($path){ + return $this->storage->isReadable($path); } - public function is_writable($path){ - return $this->storage->is_writable($path); + public function isUpdatable($path){ + return $this->storage->isUpdatable($path); } public function file_exists($path){ return $this->storage->file_exists($path); diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index 1f3fd2e9249..22d17469df3 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -2,9 +2,8 @@ /** * for local filestore, we only have to map the paths */ -class OC_Filestorage_Local extends OC_Filestorage{ +class OC_Filestorage_Local extends OC_Filestorage_Common{ protected $datadir; - private static $mimetypes=null; public function __construct($arguments){ $this->datadir=$arguments['datadir']; if(substr($this->datadir,-1)!=='/'){ @@ -41,15 +40,15 @@ class OC_Filestorage_Local extends OC_Filestorage{ } public function filesize($path){ if($this->is_dir($path)){ - return $this->getFolderSize($path); + return 0; }else{ return filesize($this->datadir.$path); } } - public function is_readable($path){ + public function isReadable($path){ return is_readable($this->datadir.$path); } - public function is_writable($path){ + public function isUpdatable($path){ return is_writable($this->datadir.$path); } public function file_exists($path){ @@ -86,7 +85,7 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $this->delTree($path); } public function rename($path1,$path2){ - if (!$this->is_writable($path1)) { + if (!$this->isUpdatable($path1)) { OC_Log::write('core','unable to rename, file is not writable : '.$path1,OC_Log::ERROR); return false; } @@ -129,7 +128,7 @@ class OC_Filestorage_Local extends OC_Filestorage{ } public function getMimeType($path){ - if($this->is_readable($path)){ + if($this->isReadable($path)){ return OC_Helper::getMimeType($this->datadir.$path); }else{ return false; @@ -157,7 +156,7 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $return; } - public function hash($type,$path,$raw = false){ + public function hash($path,$type,$raw=false){ return hash_file($type,$this->datadir.$path,$raw); } @@ -169,10 +168,13 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $this->searchInDir($query); } public function getLocalFile($path){ - return $this->datadir.$path; + return $this->datadir.$path; + } + public function getLocalFolder($path){ + return $this->datadir.$path; } - private function searchInDir($query,$dir=''){ + protected function searchInDir($query,$dir=''){ $files=array(); foreach (scandir($this->datadir.$dir) as $item) { if ($item == '.' || $item == '..') continue; @@ -187,11 +189,11 @@ class OC_Filestorage_Local extends OC_Filestorage{ } /** - * @brief get the size of folder and it's content - * @param string $path file path - * @return int size of folder and it's content + * check if a file or folder has been updated since $time + * @param int $time + * @return bool */ - public function getFolderSize($path){ - return 0;//depricated, use OC_FileCach instead + public function hasUpdated($path,$time){ + return $this->filemtime($path)>$time; } } diff --git a/lib/filesystem.php b/lib/filesystem.php index 2a0c1cea93e..0a8e6982151 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -25,7 +25,7 @@ /** * Class for abstraction of filesystem functions * This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object - * this class should also handle all the file premission related stuff + * this class should also handle all the file permission related stuff * * Hooks provided: * read(path) @@ -46,105 +46,106 @@ class OC_Filesystem{ static private $storages=array(); static private $mounts=array(); - static private $storageTypes=array(); public static $loaded=false; - private $fakeRoot=''; + /** + * @var OC_Filestorage $defaultInstance + */ static private $defaultInstance; - /** - * classname which used for hooks handling - * used as signalclass in OC_Hooks::emit() - */ - const CLASSNAME = 'OC_Filesystem'; - - /** - * signalname emited before file renaming - * @param oldpath - * @param newpath - */ - const signal_rename = 'rename'; - - /** - * signal emited after file renaming - * @param oldpath - * @param newpath - */ - const signal_post_rename = 'post_rename'; - - /** - * signal emited before file/dir creation - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_create = 'create'; - - /** - * signal emited after file/dir creation - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_post_create = 'post_create'; - - /** - * signal emits before file/dir copy - * @param oldpath - * @param newpath - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_copy = 'copy'; - - /** - * signal emits after file/dir copy - * @param oldpath - * @param newpath - */ - const signal_post_copy = 'post_copy'; - - /** - * signal emits before file/dir save - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_write = 'write'; - - /** - * signal emits after file/dir save - * @param path - */ - const signal_post_write = 'post_write'; - - /** - * signal emits when reading file/dir - * @param path - */ - const signal_read = 'read'; - - /** - * signal emits when removing file/dir - * @param path - */ - const signal_delete = 'delete'; - - /** - * parameters definitions for signals - */ - const signal_param_path = 'path'; - const signal_param_oldpath = 'oldpath'; - const signal_param_newpath = 'newpath'; - - /** - * run - changing this flag to false in hook handler will cancel event - */ - const signal_param_run = 'run'; - - /** - * get the mountpoint of the storage object for a path - ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account - * - * @param string path - * @return string - */ + /** + * classname which used for hooks handling + * used as signalclass in OC_Hooks::emit() + */ + const CLASSNAME = 'OC_Filesystem'; + + /** + * signalname emited before file renaming + * @param oldpath + * @param newpath + */ + const signal_rename = 'rename'; + + /** + * signal emited after file renaming + * @param oldpath + * @param newpath + */ + const signal_post_rename = 'post_rename'; + + /** + * signal emited before file/dir creation + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_create = 'create'; + + /** + * signal emited after file/dir creation + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_post_create = 'post_create'; + + /** + * signal emits before file/dir copy + * @param oldpath + * @param newpath + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_copy = 'copy'; + + /** + * signal emits after file/dir copy + * @param oldpath + * @param newpath + */ + const signal_post_copy = 'post_copy'; + + /** + * signal emits before file/dir save + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_write = 'write'; + + /** + * signal emits after file/dir save + * @param path + */ + const signal_post_write = 'post_write'; + + /** + * signal emits when reading file/dir + * @param path + */ + const signal_read = 'read'; + + /** + * signal emits when removing file/dir + * @param path + */ + const signal_delete = 'delete'; + + /** + * parameters definitions for signals + */ + const signal_param_path = 'path'; + const signal_param_oldpath = 'oldpath'; + const signal_param_newpath = 'newpath'; + + /** + * run - changing this flag to false in hook handler will cancel event + */ + const signal_param_run = 'run'; + + /** + * get the mountpoint of the storage object for a path + ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account + * + * @param string path + * @return string + */ static public function getMountPoint($path){ OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path)); if(!$path){ @@ -153,8 +154,10 @@ class OC_Filesystem{ if($path[0]!=='/'){ $path='/'.$path; } + $path=str_replace('//', '/',$path); $foundMountPoint=''; - foreach(OC_Filesystem::$mounts as $mountpoint=>$storage){ + $mountPoints=array_keys(OC_Filesystem::$mounts); + foreach($mountPoints as $mountpoint){ if($mountpoint==$path){ return $mountpoint; } @@ -259,10 +262,7 @@ class OC_Filesystem{ * tear down the filesystem, removing all storage providers */ static public function tearDown(){ - foreach(self::$storages as $mountpoint=>$storage){ - unset(self::$storages[$mountpoint]); - } - $fakeRoot=''; + self::$storages=array(); } /** @@ -273,7 +273,12 @@ class OC_Filesystem{ */ static private function createStorage($class,$arguments){ if(class_exists($class)){ - return new $class($arguments); + try { + return new $class($arguments); + } catch (Exception $exception) { + OC_Log::write('core', $exception->getMessage(), OC_Log::ERROR); + return false; + } }else{ OC_Log::write('core','storage backend '.$class.' not found',OC_Log::ERROR); return false; @@ -281,17 +286,19 @@ class OC_Filesystem{ } /** - * change the root to a fake toor + * change the root to a fake root * @param string fakeRoot * @return bool */ static public function chroot($fakeRoot){ - return self::$defaultInstance->chroot($path); + return self::$defaultInstance->chroot($fakeRoot); } /** - * get the fake root + * @brief get the relative path of the root data directory for the current user * @return string + * + * Returns path like /admin/files */ static public function getRoot(){ return self::$defaultInstance->getRoot(); @@ -319,17 +326,6 @@ class OC_Filesystem{ } self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments); } - - /** - * create all storage backends mounted in the filesystem - */ - static private function mountAll(){ - foreach(self::$mounts as $mountPoint=>$mount){ - if(!isset(self::$storages[$mountPoint])){ - self::$storages[$mountPoint]=self::createStorage($mount['type'],$mount['arguments']); - } - } - } /** * return the path to a local version of the file @@ -340,6 +336,27 @@ class OC_Filesystem{ static public function getLocalFile($path){ return self::$defaultInstance->getLocalFile($path); } + /** + * @param string path + * @return string + */ + static public function getLocalFolder($path){ + return self::$defaultInstance->getLocalFolder($path); + } + + /** + * return path to file which reflects one visible in browser + * @param string path + * @return string + */ + static public function getLocalPath($path) { + $datadir = \OCP\Config::getSystemValue('datadirectory').'/'.\OC_User::getUser().'/files'; + $newpath = $path; + if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { + $newpath = substr($path, strlen($datadir)); + } + return $newpath; + } /** * return path to file which reflects one visible in browser @@ -402,6 +419,9 @@ class OC_Filesystem{ static public function opendir($path){ return self::$defaultInstance->opendir($path); } + static public function readdir($path){ + return self::$defaultInstance->readdir($path); + } static public function is_dir($path){ return self::$defaultInstance->is_dir($path); } @@ -420,12 +440,33 @@ class OC_Filesystem{ static public function readfile($path){ return self::$defaultInstance->readfile($path); } + /** + * @deprecated Replaced by isReadable() as part of CRUDS + */ static public function is_readable($path){ return self::$defaultInstance->is_readable($path); } + /** + * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS + */ static public function is_writable($path){ return self::$defaultInstance->is_writable($path); } + static public function isCreatable($path) { + return self::$defaultInstance->isCreatable($path); + } + static public function isReadable($path) { + return self::$defaultInstance->isReadable($path); + } + static public function isUpdatable($path) { + return self::$defaultInstance->isUpdatable($path); + } + static public function isDeletable($path) { + return self::$defaultInstance->isDeletable($path); + } + static public function isSharable($path) { + return self::$defaultInstance->isSharable($path); + } static public function file_exists($path){ return self::$defaultInstance->file_exists($path); } @@ -477,6 +518,59 @@ class OC_Filesystem{ static public function search($query){ return OC_FileCache::search($query); } + + /** + * check if a file or folder has been updated since $time + * @param int $time + * @return bool + */ + static public function hasUpdated($path,$time){ + return self::$defaultInstance->hasUpdated($path,$time); + } + + static public function removeETagHook($params) { + if (isset($params['path'])) { + $path=$params['path']; + } else { + $path=$params['oldpath']; + } + OC_Connector_Sabre_Node::removeETagPropertyForPath($path); + } + + /** + * normalize a path + * @param string path + * @param bool $stripTrailingSlash + * @return string + */ + public static function normalizePath($path,$stripTrailingSlash=true){ + if($path==''){ + return '/'; + } + //no windows style slashes + $path=str_replace('\\','/',$path); + //add leading slash + if($path[0]!=='/'){ + $path='/'.$path; + } + //remove trainling slash + if($stripTrailingSlash and strlen($path)>1 and substr($path,-1,1)==='/'){ + $path=substr($path,0,-1); + } + //remove duplicate slashes + while(strpos($path,'//')!==false){ + $path=str_replace('//','/',$path); + } + //normalize unicode if possible + if(class_exists('Normalizer')){ + $path=Normalizer::normalize($path); + } + return $path; + } } +OC_Hook::connect('OC_Filesystem','post_write', 'OC_Filesystem','removeETagHook'); +OC_Hook::connect('OC_Filesystem','post_delete','OC_Filesystem','removeETagHook'); +OC_Hook::connect('OC_Filesystem','post_rename','OC_Filesystem','removeETagHook'); +OC_Util::setupFS(); require_once('filecache.php'); diff --git a/lib/filesystemview.php b/lib/filesystemview.php index ed52ba1be46..a888e5340ea 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -1,36 +1,53 @@ <?php /** -* ownCloud -* -* @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org -* -* This library is free software; you can redistribute it and/or -* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE -* License as published by the Free Software Foundation; either -* version 3 of the License, or any later version. -* -* This library is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU AFFERO GENERAL PUBLIC LICENSE for more details. -* -* You should have received a copy of the GNU Affero General Public -* License along with this library. If not, see <http://www.gnu.org/licenses/>. -* -*/ + * ownCloud + * + * @author Frank Karlitschek + * @copyright 2012 Frank Karlitschek frank@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + + +/** + * Class to provide access to ownCloud filesystem via a "view", and methods for + * working with files within that view (e.g. read, write, delete, etc.). Each + * view is restricted to a set of directories via a virtual root. The default view + * uses the currently logged in user's data directory as root (parts of + * OC_Filesystem are merely a wrapper for OC_FilesystemView). + * + * Apps that need to access files outside of the user data folders (to modify files + * belonging to a user other than the one currently logged in, for example) should + * use this class directly rather than using OC_Filesystem, or making use of PHP's + * built-in file manipulation functions. This will ensure all hooks and proxies + * are triggered correctly. + * + * Filesystem functions are not called directly; they are passed to the correct + * OC_Filestorage object + */ class OC_FilesystemView { private $fakeRoot=''; private $internal_path_cache=array(); private $storage_cache=array(); - public function __construct($root){ + public function __construct($root) { $this->fakeRoot=$root; } - public function getAbsolutePath($path){ + public function getAbsolutePath($path) { if(!$path){ $path='/'; } @@ -39,16 +56,15 @@ class OC_FilesystemView { } return $this->fakeRoot.$path; } - - + /** * change the root to a fake toor * @param string fakeRoot * @return bool */ - public function chroot($fakeRoot){ + public function chroot($fakeRoot) { if(!$fakeRoot==''){ - if($fakeRoot[0]!=='/'){ + if($fakeRoot[0]!=='/') { $fakeRoot='/'.$fakeRoot; } } @@ -59,7 +75,7 @@ class OC_FilesystemView { * get the fake root * @return string */ - public function getRoot(){ + public function getRoot() { return $this->fakeRoot; } @@ -68,7 +84,7 @@ class OC_FilesystemView { * @param string path * @return bool */ - public function getInternalPath($path){ + public function getInternalPath($path) { if (!isset($this->internal_path_cache[$path])) { $this->internal_path_cache[$path] = OC_Filesystem::getInternalPath($this->getAbsolutePath($path)); } @@ -80,23 +96,28 @@ class OC_FilesystemView { * @param string path * @return string */ - public function getRelativePath($path){ - if($this->fakeRoot==''){ + public function getRelativePath($path) { + if($this->fakeRoot=='') { return $path; } - if(strpos($path,$this->fakeRoot)!==0){ + if(strpos($path, $this->fakeRoot)!==0) { return null; }else{ - return substr($path,strlen($this->fakeRoot)); + $path=substr($path, strlen($this->fakeRoot)); + if(strlen($path)===0){ + return '/'; + }else{ + return $path; + } } } - + /** * get the storage object for a path * @param string path * @return OC_Filestorage */ - public function getStorage($path){ + public function getStorage($path) { if (!isset($this->storage_cache[$path])) { $this->storage_cache[$path] = OC_Filesystem::getStorage($this->getAbsolutePath($path)); } @@ -110,7 +131,7 @@ class OC_FilesystemView { * @param string path * @return string */ - public function getMountPoint($path){ + public function getMountPoint($path) { return OC_Filesystem::getMountPoint($this->getAbsolutePath($path)); } @@ -120,49 +141,65 @@ class OC_FilesystemView { * @param string path * @return string */ - public function getLocalFile($path){ - $parent=substr($path,0,strrpos($path,'/')); - if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)){ + public function getLocalFile($path) { + $parent=substr($path, 0, strrpos($path,'/')); + if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { return $storage->getLocalFile($this->getInternalPath($path)); } } + /** + * @param string path + * @return string + */ + public function getLocalFolder($path) { + $parent=substr($path, 0, strrpos($path,'/')); + if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { + return $storage->getLocalFolder($this->getInternalPath($path)); + } + } /** - * following functions are equivilent to their php buildin equivilents for arguments/return values. + * the following functions operate with arguments and return values identical + * to those of their PHP built-in equivalents. Mostly they are merely wrappers + * for OC_Filestorage via basicOperation(). */ - public function mkdir($path){ - return $this->basicOperation('mkdir',$path,array('create','write')); + public function mkdir($path) { + return $this->basicOperation('mkdir', $path, array('create', 'write')); + } + public function rmdir($path) { + return $this->basicOperation('rmdir', $path, array('delete')); } - public function rmdir($path){ - return $this->basicOperation('rmdir',$path,array('delete')); + public function opendir($path) { + return $this->basicOperation('opendir', $path, array('read')); } - public function opendir($path){ - return $this->basicOperation('opendir',$path,array('read')); + public function readdir($handle) { + $fsLocal= new OC_Filestorage_Local( array( 'datadir' => '/' ) ); + return $fsLocal->readdir( $handle ); } - public function is_dir($path){ + public function is_dir($path) { if($path=='/'){ return true; } - return $this->basicOperation('is_dir',$path); + return $this->basicOperation('is_dir', $path); } - public function is_file($path){ + public function is_file($path) { if($path=='/'){ return false; } - return $this->basicOperation('is_file',$path); + return $this->basicOperation('is_file', $path); } - public function stat($path){ - return $this->basicOperation('stat',$path); + public function stat($path) { + return $this->basicOperation('stat', $path); } - public function filetype($path){ - return $this->basicOperation('filetype',$path); + public function filetype($path) { + return $this->basicOperation('filetype', $path); } - public function filesize($path){ - return $this->basicOperation('filesize',$path); + public function filesize($path) { + return $this->basicOperation('filesize', $path); } - public function readfile($path){ + public function readfile($path) { @ob_end_clean(); - $handle=$this->fopen($path,'rb'); + $handle=$this->fopen($path, 'rb'); if ($handle) { $chunkSize = 8192;// 8 MB chunks while (!feof($handle)) { @@ -174,33 +211,54 @@ class OC_FilesystemView { } return false; } + /** + * @deprecated Replaced by isReadable() as part of CRUDS + */ public function is_readable($path){ - return $this->basicOperation('is_readable',$path); + return $this->basicOperation('isReadable',$path); } + /** + * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS + */ public function is_writable($path){ - return $this->basicOperation('is_writable',$path); + return $this->basicOperation('isUpdatable',$path); + } + public function isCreatable($path) { + return $this->basicOperation('isCreatable', $path); + } + public function isReadable($path) { + return $this->basicOperation('isReadable', $path); } - public function file_exists($path){ + public function isUpdatable($path) { + return $this->basicOperation('isUpdatable', $path); + } + public function isDeletable($path) { + return $this->basicOperation('isDeletable', $path); + } + public function isSharable($path) { + return $this->basicOperation('isSharable', $path); + } + public function file_exists($path) { if($path=='/'){ return true; } - return $this->basicOperation('file_exists',$path); + return $this->basicOperation('file_exists', $path); } - public function filectime($path){ - return $this->basicOperation('filectime',$path); + public function filectime($path) { + return $this->basicOperation('filectime', $path); } - public function filemtime($path){ - return $this->basicOperation('filemtime',$path); + public function filemtime($path) { + return $this->basicOperation('filemtime', $path); } - public function touch($path, $mtime=null){ + public function touch($path, $mtime=null) { return $this->basicOperation('touch', $path, array('write'), $mtime); } - public function file_get_contents($path){ - return $this->basicOperation('file_get_contents',$path,array('read')); + public function file_get_contents($path) { + return $this->basicOperation('file_get_contents', $path, array('read')); } public function file_put_contents($path, $data) { if(is_resource($data)) {//not having to deal with streams in file_put_contents makes life easier - $absolutePath = $this->getAbsolutePath($path); + $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) && OC_Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); $exists = $this->file_exists($path); @@ -250,85 +308,145 @@ class OC_FilesystemView { } } }else{ - return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + return $this->basicOperation('file_put_contents', $path, array('create', 'write'), $data); } } - public function unlink($path){ - return $this->basicOperation('unlink',$path,array('delete')); - } - public function rename($path1,$path2){ - $absolutePath1=$this->getAbsolutePath($path1); - $absolutePath2=$this->getAbsolutePath($path2); - if(OC_FileProxy::runPreProxies('rename',$absolutePath1,$absolutePath2) and OC_Filesystem::isValidPath($path2)){ - $path1=$this->getRelativePath($absolutePath1); - $path2=$this->getRelativePath($absolutePath2); - if($path1==null or $path2==null){ + public function unlink($path) { + return $this->basicOperation('unlink', $path, array('delete')); + } + public function deleteAll( $directory, $empty = false ) { + return $this->basicOperation( 'deleteAll', $directory, array('delete'), $empty ); + } + public function rename($path1, $path2) { + $postFix1=(substr($path1,-1,1)==='/')?'/':''; + $postFix2=(substr($path2,-1,1)==='/')?'/':''; + $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); + $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); + if(OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + $path1 = $this->getRelativePath($absolutePath1); + $path2 = $this->getRelativePath($absolutePath2); + + if($path1 == null or $path2 == null) { return false; } $run=true; - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run)); - if($run){ - $mp1=$this->getMountPoint($path1); - $mp2=$this->getMountPoint($path2); - if($mp1==$mp2){ - if($storage=$this->getStorage($path1)){ - $result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath => $path2, + OC_Filesystem::signal_param_run => &$run + ) + ); + if($run) { + $mp1 = $this->getMountPoint($path1.$postFix1); + $mp2 = $this->getMountPoint($path2.$postFix2); + if($mp1 == $mp2) { + if($storage = $this->getStorage($path1)) { + $result = $storage->rename($this->getInternalPath($path1.$postFix1), $this->getInternalPath($path2.$postFix2)); } - }else{ - $source=$this->fopen($path1,'r'); - $target=$this->fopen($path2,'w'); - $count=OC_Helper::streamCopy($data,$target); - $storage1=$this->getStorage($path1); - $storage1->unlink($this->getInternalPath($path1)); - $result=$count>0; - } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2)); + } else { + $source = $this->fopen($path1.$postFix1, 'r'); + $target = $this->fopen($path2.$postFix2, 'w'); + $count = OC_Helper::streamCopy($source, $target); + $storage1 = $this->getStorage($path1); + $storage1->unlink($this->getInternalPath($path1.$postFix1)); + $result = $count>0; + } + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_rename, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath => $path2 + ) + ); return $result; } } } - public function copy($path1,$path2){ - $absolutePath1=$this->getAbsolutePath($path1); - $absolutePath2=$this->getAbsolutePath($path2); - if(OC_FileProxy::runPreProxies('copy',$absolutePath1,$absolutePath2) and OC_Filesystem::isValidPath($path2)){ - $path1=$this->getRelativePath($absolutePath1); - $path2=$this->getRelativePath($absolutePath2); - if($path1==null or $path2==null){ + public function copy($path1, $path2) { + $postFix1=(substr($path1,-1,1)==='/')?'/':''; + $postFix2=(substr($path2,-1,1)==='/')?'/':''; + $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); + $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); + if(OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + $path1 = $this->getRelativePath($absolutePath1); + $path2 = $this->getRelativePath($absolutePath2); + + if($path1 == null or $path2 == null) { return false; } $run=true; - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_copy, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath=>$path2, + OC_Filesystem::signal_param_run => &$run + ) + ); $exists=$this->file_exists($path2); - if($run and !$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); + if($run and !$exists) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_create, + array( + OC_Filesystem::signal_param_path => $path2, + OC_Filesystem::signal_param_run => &$run + ) + ); } - if($run){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); + if($run) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_write, + array( + OC_Filesystem::signal_param_path => $path2, + OC_Filesystem::signal_param_run => &$run + ) + ); } - if($run){ - $mp1=$this->getMountPoint($path1); - $mp2=$this->getMountPoint($path2); - if($mp1==$mp2){ - if($storage=$this->getStorage($path1)){ - $result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2)); + if($run) { + $mp1=$this->getMountPoint($path1.$postFix1); + $mp2=$this->getMountPoint($path2.$postFix2); + if($mp1 == $mp2){ + if($storage = $this->getStorage($path1.$postFix1)) { + $result=$storage->copy($this->getInternalPath($path1.$postFix1), $this->getInternalPath($path2.$postFix2)); } - }else{ - $source=$this->fopen($path1,'r'); - $target=$this->fopen($path2,'w'); - $count=OC_Helper::streamCopy($data,$target); + } else { + $source = $this->fopen($path1.$postFix1, 'r'); + $target = $this->fopen($path2.$postFix2, 'w'); + $result = OC_Helper::streamCopy($source, $target); } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2)); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_copy, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath=>$path2 + ) + ); + if(!$exists) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_create, + array(OC_Filesystem::signal_param_path => $path2) + ); } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_write, + array( OC_Filesystem::signal_param_path => $path2) + ); return $result; } } } - public function fopen($path,$mode){ + public function fopen($path, $mode) { $hooks=array(); - switch($mode){ + switch($mode) { case 'r': case 'rb': $hooks[]='read'; @@ -356,45 +474,46 @@ class OC_FilesystemView { OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR); } - return $this->basicOperation('fopen',$path,$hooks,$mode); + return $this->basicOperation('fopen', $path, $hooks, $mode); } - public function toTmpFile($path){ - if(OC_Filesystem::isValidPath($path)){ - $source=$this->fopen($path,'r'); - if($source){ + public function toTmpFile($path) { + if(OC_Filesystem::isValidPath($path)) { + $source = $this->fopen($path, 'r'); + if($source) { $extension=''; - $extOffset=strpos($path,'.'); + $extOffset=strpos($path, '.'); if($extOffset !== false) { - $extension=substr($path,strrpos($path,'.')); + $extension=substr($path, strrpos($path,'.')); } - $tmpFile=OC_Helper::tmpFile($extension); - file_put_contents($tmpFile,$source); + $tmpFile = OC_Helper::tmpFile($extension); + file_put_contents($tmpFile, $source); return $tmpFile; } } } - public function fromTmpFile($tmpFile,$path){ - if(OC_Filesystem::isValidPath($path)){ - if(!$tmpFile){ + public function fromTmpFile($tmpFile, $path) { + if(OC_Filesystem::isValidPath($path)) { + if(!$tmpFile) { debug_print_backtrace(); } - $source=fopen($tmpFile,'r'); - if($source){ - $this->file_put_contents($path,$source); + $source=fopen($tmpFile, 'r'); + if($source) { + $this->file_put_contents($path, $source); unlink($tmpFile); return true; - }else{ + } else { } - }else{ + } else { return false; } } - public function getMimeType($path){ - return $this->basicOperation('getMimeType',$path); + public function getMimeType($path) { + return $this->basicOperation('getMimeType', $path); } public function hash($type, $path, $raw = false) { - $absolutePath = $this->getAbsolutePath($path); + $postFix=(substr($path,-1,1)==='/')?'/':''; + $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); if (OC_FileProxy::runPreProxies('hash', $absolutePath) && OC_Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { @@ -407,8 +526,8 @@ class OC_FilesystemView { array( OC_Filesystem::signal_param_path => $path) ); } - if ($storage = $this->getStorage($path)) { - $result = $storage->hash($type, $this->getInternalPath($path), $raw); + if ($storage = $this->getStorage($path.$postFix)) { + $result = $storage->hash($type, $this->getInternalPath($path.$postFix), $raw); $result = OC_FileProxy::runPostProxies('hash', $absolutePath, $result); return $result; } @@ -416,50 +535,42 @@ class OC_FilesystemView { return null; } - public function free_space($path='/'){ - return $this->basicOperation('free_space',$path); + public function free_space($path='/') { + return $this->basicOperation('free_space', $path); } /** - * abstraction for running most basic operations + * @brief abstraction layer for basic filesystem functions: wrapper for OC_Filestorage * @param string $operation * @param string #path * @param array (optional) hooks * @param mixed (optional) $extraParam * @return mixed + * + * This method takes requests for basic filesystem functions (e.g. reading & writing + * files), processes hooks and proxies, sanitises paths, and finally passes them on to + * OC_Filestorage for delegation to a storage backend for execution */ - private function basicOperation($operation,$path,$hooks=array(),$extraParam=null){ - $absolutePath=$this->getAbsolutePath($path); - if(OC_FileProxy::runPreProxies($operation,$absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)){ - $path=$this->getRelativePath($absolutePath); - if($path==null){ + private function basicOperation($operation, $path, $hooks=array(), $extraParam=null) { + $postFix=(substr($path,-1,1)==='/')?'/':''; + $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); + if(OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)) { + $path = $this->getRelativePath($absolutePath); + if($path == null) { return false; } - $internalPath=$this->getInternalPath($path); - $run=true; - if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - }else{ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); - } + $internalPath = $this->getInternalPath($path.$postFix); + $run=$this->runHooks($hooks,$path); + if($run and $storage = $this->getStorage($path.$postFix)) { + if(!is_null($extraParam)) { + $result = $storage->$operation($internalPath, $extraParam); + } else { + $result = $storage->$operation($internalPath); } - } - if($run and $storage=$this->getStorage($path)){ - if(!is_null($extraParam)){ - $result=$storage->$operation($internalPath,$extraParam); - }else{ - $result=$storage->$operation($internalPath); - } - $result=OC_FileProxy::runPostProxies($operation,$this->getAbsolutePath($path),$result); - if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ - if($operation!='fopen'){//no post hooks for fopen, the file stream is still open - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); - } - } + $result = OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { + if($operation!='fopen') {//no post hooks for fopen, the file stream is still open + $this->runHooks($hooks,$path,true); } } return $result; @@ -467,4 +578,41 @@ class OC_FilesystemView { } return null; } + + private function runHooks($hooks,$path,$post=false){ + $prefix=($post)?'post_':''; + $run=true; + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { + foreach($hooks as $hook) { + if($hook!='read') { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + $prefix.$hook, + array( + OC_Filesystem::signal_param_run => &$run, + OC_Filesystem::signal_param_path => $path + ) + ); + } elseif(!$post) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + $prefix.$hook, + array( + OC_Filesystem::signal_param_path => $path + ) + ); + } + } + } + return $run; + } + + /** + * check if a file or folder has been updated since $time + * @param int $time + * @return bool + */ + public function hasUpdated($path, $time) { + return $this->basicOperation('hasUpdated', $path, array(), $time); + } } diff --git a/lib/group.php b/lib/group.php index 91772d510ff..72cf5dc89af 100644 --- a/lib/group.php +++ b/lib/group.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -43,7 +43,7 @@ class OC_Group { * @returns true/false */ public static function useBackend( $backend ){ - if($backend instanceof OC_Group_Backend){ + if($backend instanceof OC_Group_Interface){ self::$_usedBackends[]=$backend; } } @@ -84,7 +84,7 @@ class OC_Group { OC_Hook::emit( "OC_Group", "pre_createGroup", array( "run" => &$run, "gid" => $gid )); if($run){ - //create the user in the first backend that supports creating users + //create the group in the first backend that supports creating groups foreach(self::$_usedBackends as $backend){ if(!$backend->implementsActions(OC_GROUP_BACKEND_CREATE_GROUP)) continue; @@ -141,9 +141,6 @@ class OC_Group { */ public static function inGroup( $uid, $gid ){ foreach(self::$_usedBackends as $backend){ - if(!$backend->implementsActions(OC_GROUP_BACKEND_IN_GROUP)) - continue; - if($backend->inGroup($uid,$gid)){ return true; } @@ -171,7 +168,7 @@ class OC_Group { if($run){ $succes=false; - + //add the user to the all backends that have the group foreach(self::$_usedBackends as $backend){ if(!$backend->implementsActions(OC_GROUP_BACKEND_ADD_TO_GROUP)) @@ -228,9 +225,6 @@ class OC_Group { public static function getUserGroups( $uid ){ $groups=array(); foreach(self::$_usedBackends as $backend){ - if(!$backend->implementsActions(OC_GROUP_BACKEND_GET_USER_GROUPS)) - continue; - $groups=array_merge($backend->getUserGroups($uid),$groups); } asort($groups); @@ -243,18 +237,15 @@ class OC_Group { * * Returns a list with all groups */ - public static function getGroups(){ - $groups=array(); - foreach(self::$_usedBackends as $backend){ - if(!$backend->implementsActions(OC_GROUP_BACKEND_GET_GROUPS)) - continue; - - $groups=array_merge($backend->getGroups(),$groups); + public static function getGroups($search = '', $limit = -1, $offset = 0) { + $groups = array(); + foreach (self::$_usedBackends as $backend) { + $groups = array_merge($backend->getGroups($search, $limit, $offset), $groups); } asort($groups); return $groups; } - + /** * check if a group exists * @param string $gid @@ -268,18 +259,29 @@ class OC_Group { } return false; } - + /** * @brief get a list of all users in a group * @returns array with user ids */ - public static function usersInGroup($gid){ + public static function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { $users=array(); foreach(self::$_usedBackends as $backend){ - if(!$backend->implementsActions(OC_GROUP_BACKEND_GET_USERS)) - continue; - - $users=array_merge($backend->usersInGroup($gid),$users); + $users = array_merge($backend->usersInGroup($gid, $search, $limit, $offset), $users); + } + return $users; + } + + /** + * @brief get a list of all users in several groups + * @param array $gids + * @returns array with user ids + */ + public static function usersInGroups($gids, $search = '', $limit = -1, $offset = 0) { + $users = array(); + foreach ($gids as $gid) { + // TODO Need to apply limits to groups as total + $users = array_merge(array_diff(self::usersInGroup($gid, $search, $limit, $offset), $users), $users); } return $users; } diff --git a/lib/group/backend.php b/lib/group/backend.php index af6c53c8035..5969986c652 100644 --- a/lib/group/backend.php +++ b/lib/group/backend.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -31,28 +31,20 @@ define('OC_GROUP_BACKEND_NOT_IMPLEMENTED', -501); */ define('OC_GROUP_BACKEND_CREATE_GROUP', 0x00000001); define('OC_GROUP_BACKEND_DELETE_GROUP', 0x00000010); -define('OC_GROUP_BACKEND_IN_GROUP', 0x00000100); -define('OC_GROUP_BACKEND_ADD_TO_GROUP', 0x00001000); -define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00010000); -define('OC_GROUP_BACKEND_GET_USER_GROUPS', 0x00100000); -define('OC_GROUP_BACKEND_GET_USERS', 0x01000000); -define('OC_GROUP_BACKEND_GET_GROUPS', 0x10000000); +define('OC_GROUP_BACKEND_ADD_TO_GROUP', 0x00000100); +define('OC_GROUP_BACKEND_REMOVE_FROM_GOUP', 0x00001000); /** * Abstract base class for user management */ -abstract class OC_Group_Backend { +abstract class OC_Group_Backend implements OC_Group_Interface { protected $possibleActions = array( OC_GROUP_BACKEND_CREATE_GROUP => 'createGroup', OC_GROUP_BACKEND_DELETE_GROUP => 'deleteGroup', - OC_GROUP_BACKEND_IN_GROUP => 'inGroup', OC_GROUP_BACKEND_ADD_TO_GROUP => 'addToGroup', OC_GROUP_BACKEND_REMOVE_FROM_GOUP => 'removeFromGroup', - OC_GROUP_BACKEND_GET_USER_GROUPS => 'getUserGroups', - OC_GROUP_BACKEND_GET_USERS => 'usersInGroup', - OC_GROUP_BACKEND_GET_GROUPS => 'getGroups' ); - + /** * @brief Get all supported actions * @returns bitwise-or'ed actions @@ -70,7 +62,7 @@ abstract class OC_Group_Backend { return $actions; } - + /** * @brief Check if backend implements actions * @param $actions bitwise-or'ed actions @@ -84,14 +76,55 @@ abstract class OC_Group_Backend { } /** + * @brief is user in group? + * @param $uid uid of the user + * @param $gid gid of the group + * @returns true/false + * + * Checks whether the user is member of a group or not. + */ + public function inGroup($uid, $gid){ + return in_array($gid, $this->getUserGroups($uid)); + } + + /** + * @brief Get all groups a user belongs to + * @param $uid Name of the user + * @returns array with group names + * + * This function fetches all groups a user belongs to. It does not check + * if the user exists at all. + */ + public function getUserGroups($uid){ + return array(); + } + + /** + * @brief get a list of all groups + * @returns array with group names + * + * Returns a list with all groups + */ + + public function getGroups($search = '', $limit = -1, $offset = 0) { + return array(); + } + + /** * check if a group exists * @param string $gid * @return bool */ public function groupExists($gid){ - if(!$this->implementsActions(OC_GROUP_BACKEND_GET_GROUPS)){ - return false; - } - return in_array($gid, $this->getGroups()); + return in_array($gid, $this->getGroups($gid, 1)); } + + /** + * @brief get a list of all users in a group + * @returns array with user ids + */ + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { + return array(); + } + } diff --git a/lib/group/database.php b/lib/group/database.php index 5e52432c492..52608b2db73 100644 --- a/lib/group/database.php +++ b/lib/group/database.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -41,7 +41,6 @@ * Class for group management in a SQL Database (e.g. MySQL, SQLite) */ class OC_Group_Database extends OC_Group_Backend { - static private $userGroupCache=array(); /** * @brief Try to create a new group @@ -51,10 +50,10 @@ class OC_Group_Database extends OC_Group_Backend { * Trys to create a new group. If the group name already exists, false will * be returned. */ - public static function createGroup( $gid ){ + public function createGroup( $gid ){ // Check for existence - $query = OC_DB::prepare( 'SELECT `gid` FROM `*PREFIX*groups` WHERE `gid` = ?' ); - $result = $query->execute( array( $gid )); + $stmt = OC_DB::prepare( "SELECT `gid` FROM `*PREFIX*groups` WHERE `gid` = ?" ); + $result = $stmt->execute( array( $gid )); if( $result->fetchRow() ){ // Can not add an existing group @@ -62,8 +61,8 @@ class OC_Group_Database extends OC_Group_Backend { } else{ // Add group and exit - $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*groups` ( `gid` ) VALUES( ? )' ); - $result = $query->execute( array( $gid )); + $stmt = OC_DB::prepare( "INSERT INTO `*PREFIX*groups` ( `gid` ) VALUES( ? )" ); + $result = $stmt->execute( array( $gid )); return $result ? true : false; } @@ -76,14 +75,14 @@ class OC_Group_Database extends OC_Group_Backend { * * Deletes a group and removes it from the group_user-table */ - public static function deleteGroup( $gid ){ + public function deleteGroup( $gid ){ // Delete the group - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*groups` WHERE `gid` = ?' ); - $result = $query->execute( array( $gid )); + $stmt = OC_DB::prepare( "DELETE FROM `*PREFIX*groups` WHERE `gid` = ?" ); + $result = $stmt->execute( array( $gid )); // Delete the group-user relation - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*group_user` WHERE `gid` = ?' ); - $result = $query->execute( array( $gid )); + $stmt = OC_DB::prepare( "DELETE FROM `*PREFIX*group_user` WHERE `gid` = ?" ); + $result = $stmt->execute( array( $gid )); return true; } @@ -96,10 +95,10 @@ class OC_Group_Database extends OC_Group_Backend { * * Checks whether the user is member of a group or not. */ - public static function inGroup( $uid, $gid ){ + public function inGroup( $uid, $gid ){ // check - $query = OC_DB::prepare( 'SELECT `uid` FROM `*PREFIX*group_user` WHERE `gid` = ? AND `uid` = ?' ); - $result = $query->execute( array( $gid, $uid )); + $stmt = OC_DB::prepare( "SELECT `uid` FROM `*PREFIX*group_user` WHERE `gid` = ? AND `uid` = ?" ); + $result = $stmt->execute( array( $gid, $uid )); return $result->fetchRow() ? true : false; } @@ -112,11 +111,11 @@ class OC_Group_Database extends OC_Group_Backend { * * Adds a user to a group. */ - public static function addToGroup( $uid, $gid ){ + public function addToGroup( $uid, $gid ){ // No duplicate entries! - if( !self::inGroup( $uid, $gid )){ - $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*group_user` ( `uid`, `gid` ) VALUES( ?, ? )' ); - $result = $query->execute( array( $uid, $gid )); + if( !$this->inGroup( $uid, $gid )){ + $stmt = OC_DB::prepare( "INSERT INTO `*PREFIX*group_user` ( `uid`, `gid` ) VALUES( ?, ? )" ); + $stmt->execute( array( $uid, $gid )); return true; }else{ return false; @@ -131,9 +130,9 @@ class OC_Group_Database extends OC_Group_Backend { * * removes the user from a group. */ - public static function removeFromGroup( $uid, $gid ){ - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*group_user` WHERE `uid` = ? AND `gid` = ?' ); - $result = $query->execute( array( $uid, $gid )); + public function removeFromGroup( $uid, $gid ){ + $stmt = OC_DB::prepare( "DELETE FROM `*PREFIX*group_user` WHERE `uid` = ? AND `gid` = ?" ); + $stmt->execute( array( $uid, $gid )); return true; } @@ -146,10 +145,10 @@ class OC_Group_Database extends OC_Group_Backend { * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ - public static function getUserGroups( $uid ){ + public function getUserGroups( $uid ){ // No magic! - $query = OC_DB::prepare( 'SELECT `gid` FROM `*PREFIX*group_user` WHERE `uid` = ?' ); - $result = $query->execute( array( $uid )); + $stmt = OC_DB::prepare( "SELECT `gid` FROM `*PREFIX*group_user` WHERE `uid` = ?" ); + $result = $stmt->execute( array( $uid )); $groups = array(); while( $row = $result->fetchRow()){ @@ -165,28 +164,40 @@ class OC_Group_Database extends OC_Group_Backend { * * Returns a list with all groups */ - public static function getGroups(){ - $query = OC_DB::prepare( 'SELECT `gid` FROM `*PREFIX*groups`' ); - $result = $query->execute(); - + public function getGroups($search = '', $limit = null, $offset = null) { + $stmt = OC_DB::prepare('SELECT `gid` FROM `*PREFIX*groups` WHERE `gid` LIKE ?', $limit, $offset); + $result = $stmt->execute(array($search.'%')); $groups = array(); - while( $row = $result->fetchRow()){ - $groups[] = $row["gid"]; + while ($row = $result->fetchRow()) { + $groups[] = $row['gid']; } - return $groups; } - + + /** + * check if a group exists + * @param string $gid + * @return bool + */ + public function groupExists($gid) { + $query = OC_DB::prepare('SELECT `gid` FROM `*PREFIX*groups` WHERE `gid` = ?'); + $result = $query->execute(array($gid))->fetchOne(); + if ($result) { + return true; + } + return false; + } + /** * @brief get a list of all users in a group * @returns array with user ids */ - public static function usersInGroup($gid){ - $query=OC_DB::prepare('SELECT `uid` FROM `*PREFIX*group_user` WHERE `gid`=?'); - $users=array(); - $result=$query->execute(array($gid)); - while($row=$result->fetchRow()){ - $users[]=$row['uid']; + public function usersInGroup($gid, $search = '', $limit = null, $offset = null) { + $stmt = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*group_user` WHERE `gid` = ? AND `uid` LIKE ?', $limit, $offset); + $result = $stmt->execute(array($gid, $search.'%')); + $users = array(); + while ($row = $result->fetchRow()) { + $users[] = $row['uid']; } return $users; } diff --git a/lib/group/dummy.php b/lib/group/dummy.php index 5220237ecbf..51eca28f3f4 100644 --- a/lib/group/dummy.php +++ b/lib/group/dummy.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -126,7 +126,8 @@ class OC_Group_Dummy extends OC_Group_Backend { */ public function getUserGroups($uid){ $groups=array(); - foreach($this->groups as $group=>$user){ + $allGroups=array_keys($this->groups); + foreach($allGroups as $group){ if($this->inGroup($uid,$group)){ $groups[]=$group; } @@ -140,7 +141,7 @@ class OC_Group_Dummy extends OC_Group_Backend { * * Returns a list with all groups */ - public function getGroups(){ + public function getGroups($search = '', $limit = -1, $offset = 0) { return array_keys($this->groups); } @@ -148,7 +149,7 @@ class OC_Group_Dummy extends OC_Group_Backend { * @brief get a list of all users in a group * @returns array with user ids */ - public function usersInGroup($gid){ + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { if(isset($this->groups[$gid])){ return $this->groups[$gid]; }else{ diff --git a/lib/group/example.php b/lib/group/example.php index a88159f91be..76d12629763 100644 --- a/lib/group/example.php +++ b/lib/group/example.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -34,7 +34,7 @@ abstract class OC_Group_Example { * Trys to create a new group. If the group name already exists, false will * be returned. */ - public static function createGroup($gid){} + abstract public static function createGroup($gid); /** * @brief delete a group @@ -43,7 +43,7 @@ abstract class OC_Group_Example { * * Deletes a group and removes it from the group_user-table */ - public static function deleteGroup($gid){} + abstract public static function deleteGroup($gid); /** * @brief is user in group? @@ -53,7 +53,7 @@ abstract class OC_Group_Example { * * Checks whether the user is member of a group or not. */ - public static function inGroup($uid, $gid){} + abstract public static function inGroup($uid, $gid); /** * @brief Add a user to a group @@ -63,7 +63,7 @@ abstract class OC_Group_Example { * * Adds a user to a group. */ - public static function addToGroup($uid, $gid){} + abstract public static function addToGroup($uid, $gid); /** * @brief Removes a user from a group @@ -73,7 +73,7 @@ abstract class OC_Group_Example { * * removes the user from a group. */ - public static function removeFromGroup($uid,$gid){} + abstract public static function removeFromGroup($uid,$gid); /** * @brief Get all groups a user belongs to @@ -83,7 +83,7 @@ abstract class OC_Group_Example { * This function fetches all groups a user belongs to. It does not check * if the user exists at all. */ - public static function getUserGroups($uid){} + abstract public static function getUserGroups($uid); /** * @brief get a list of all groups @@ -91,12 +91,19 @@ abstract class OC_Group_Example { * * Returns a list with all groups */ - public static function getGroups(){} + abstract public static function getGroups($search = '', $limit = -1, $offset = 0); + + /** + * check if a group exists + * @param string $gid + * @return bool + */ + abstract public function groupExists($gid); /** * @brief get a list of all users in a group * @returns array with user ids */ - public static function usersInGroup($gid){} + abstract public static function usersInGroup($gid, $search = '', $limit = -1, $offset = 0); } diff --git a/lib/group/interface.php b/lib/group/interface.php new file mode 100644 index 00000000000..12cc07a5374 --- /dev/null +++ b/lib/group/interface.php @@ -0,0 +1,76 @@ +<?php + +/** + * ownCloud - group interface + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +interface OC_Group_Interface { + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_GROUP_BACKEND_CREATE_GROUP etc. + */ + public function implementsActions($actions); + + /** + * @brief is user in group? + * @param $uid uid of the user + * @param $gid gid of the group + * @returns true/false + * + * Checks whether the user is member of a group or not. + */ + public function inGroup($uid, $gid); + + /** + * @brief Get all groups a user belongs to + * @param $uid Name of the user + * @returns array with group names + * + * This function fetches all groups a user belongs to. It does not check + * if the user exists at all. + */ + public function getUserGroups($uid); + + /** + * @brief get a list of all groups + * @returns array with group names + * + * Returns a list with all groups + */ + public function getGroups($search = '', $limit = -1, $offset = 0); + + /** + * check if a group exists + * @param string $gid + * @return bool + */ + public function groupExists($gid); + + /** + * @brief get a list of all users in a group + * @returns array with user ids + */ + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0); + +}
\ No newline at end of file diff --git a/lib/helper.php b/lib/helper.php index 080df8613ec..8c362747a27 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -4,7 +4,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -38,21 +38,18 @@ class OC_Helper { */ public static function linkTo( $app, $file ){ if( $app != '' ){ - $app .= '/'; + $app_path = OC_App::getAppPath($app); // Check if the app is in the app folder - if( file_exists( OC::$APPSROOT . '/apps/'. $app.$file )){ - if(substr($file, -3) == 'php' || substr($file, -3) == 'css'){ - if(substr($app, -1, 1) == '/'){ - $app = substr($app, 0, strlen($app) - 1); - } + if( $app_path && file_exists( $app_path.'/'.$file )){ + if(substr($file, -3) == 'php' || substr($file, -3) == 'css'){ $urlLinkTo = OC::$WEBROOT . '/?app=' . $app; $urlLinkTo .= ($file!='index.php')?'&getfile=' . urlencode($file):''; }else{ - $urlLinkTo = OC::$APPSWEBROOT . '/apps/' . $app . $file; + $urlLinkTo = OC_App::getAppWebPath($app) . '/' . $file; } } else{ - $urlLinkTo = OC::$WEBROOT . '/' . $app . $file; + $urlLinkTo = OC::$WEBROOT . '/' . $app . '/' . $file; } } else{ @@ -68,48 +65,6 @@ class OC_Helper { } /** - * @brief Returns the server host - * @returns the server host - * - * Returns the server host, even if the website uses one or more - * reverse proxies - */ - public static function serverHost() { - if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { - if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) { - $host = trim(array_pop(explode(",", $_SERVER['HTTP_X_FORWARDED_HOST']))); - } - else{ - $host=$_SERVER['HTTP_X_FORWARDED_HOST']; - } - } - else{ - $host = $_SERVER['HTTP_HOST']; - } - return $host; - } - - /** - * @brief Returns the server protocol - * @returns the server protocol - * - * Returns the server protocol. It respects reverse proxy servers and load balancers - */ - public static function serverProtocol() { - if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { - $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); - }else{ - if(isset($_SERVER['HTTPS']) and !empty($_SERVER['HTTPS']) and ($_SERVER['HTTPS']!='off')) { - $proto = 'https'; - }else{ - $proto = 'http'; - } - } - return($proto); - } - - - /** * @brief Creates an absolute url * @param $app app * @param $file file @@ -119,8 +74,19 @@ class OC_Helper { */ public static function linkToAbsolute( $app, $file ) { $urlLinkTo = self::linkTo( $app, $file ); - $urlLinkTo = OC_Helper::serverProtocol(). '://' . self::serverHost() . $urlLinkTo; - return $urlLinkTo; + return self::makeURLAbsolute($urlLinkTo); + } + + /** + * @brief Makes an $url absolute + * @param $url the url + * @returns the absolute url + * + * Returns a absolute url to the given app and file. + */ + public static function makeURLAbsolute( $url ) + { + return OC_Request::serverProtocol(). '://' . OC_Request::serverHost() . $url; } /** @@ -130,8 +96,8 @@ class OC_Helper { * * Returns a absolute url to the given service. */ - public static function linkToRemote( $service ) { - return self::linkToAbsolute( '', 'remote.php') . '/' . $service . '/'; + public static function linkToRemote( $service, $add_slash = true ) { + return self::linkToAbsolute( '', 'remote.php') . '/' . $service . (($add_slash && $service[strlen($service)-1]!='/')?'/':''); } /** @@ -142,15 +108,15 @@ class OC_Helper { * * Returns the path to the image. */ - public static function imagePath( $app, $image ){ + public static function imagePath( $app, $image ){ // Read the selected theme from the config file $theme=OC_Config::getValue( "theme" ); // Check if the app is in the app folder if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )){ return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image"; - }elseif( file_exists( OC::$APPSROOT."/apps/$app/img/$image" )){ - return OC::$APPSWEBROOT."/apps/$app/img/$image"; + }elseif( file_exists(OC_App::getAppPath($app)."/img/$image" )){ + return OC_App::getAppWebPath($app)."/img/$image"; }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )){ return OC::$WEBROOT."/themes/$theme/$app/img/$image"; }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )){ @@ -174,10 +140,8 @@ class OC_Helper { */ public static function mimetypeIcon( $mimetype ){ $alias=array('application/xml'=>'code/xml'); -// echo $mimetype; if(isset($alias[$mimetype])){ $mimetype=$alias[$mimetype]; -// echo $mimetype; } // Replace slash with a minus $mimetype = str_replace( "/", "-", $mimetype ); @@ -344,18 +308,24 @@ class OC_Helper { */ static function getMimeType($path){ $isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://'); - $mimeType='application/octet-stream'; - if ($mimeType=='application/octet-stream') { - self::$mimetypes = include('mimetypes.fixlist.php'); - $extension=strtolower(strrchr(basename($path), ".")); - $extension=substr($extension,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream'; - } if (@is_dir($path)) { // directories are easy return "httpd/unix-directory"; } + + if(strpos($path,'.')){ + //try to guess the type by the file extension + if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ + self::$mimetypes=include('mimetypes.list.php'); + } + $extension=strtolower(strrchr(basename($path), ".")); + $extension=substr($extension,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream'; + }else{ + $mimeType='application/octet-stream'; + } + if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ $info = @strtolower(finfo_file($finfo,$path)); if($info){ @@ -378,21 +348,12 @@ class OC_Helper { //trim the character set from the end of the response $mimeType=substr($reply,0,strrpos($reply,' ')); - //trim ; + //trim ; if (strpos($mimeType, ';') !== false) { $mimeType = strstr($mimeType, ';', true); } } - if ($mimeType=='application/octet-stream') { - // Fallback solution: (try to guess the type by the file extension - if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ - self::$mimetypes=include('mimetypes.list.php'); - } - $extension=strtolower(strrchr(basename($path), ".")); - $extension=substr($extension,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream'; - } return $mimeType; } @@ -556,40 +517,40 @@ class OC_Helper { } } - /** - * Adds a suffix to the name in case the file exists - * - * @param $path - * @param $filename - * @return string - */ - public static function buildNotExistingFileName($path, $filename){ - if($path==='/'){ - $path=''; - } - if ($pos = strrpos($filename, '.')) { - $name = substr($filename, 0, $pos); - $ext = substr($filename, $pos); - } else { - $name = $filename; - } - - $newpath = $path . '/' . $filename; - $newname = $filename; - $counter = 2; - while (OC_Filesystem::file_exists($newpath)) { - $newname = $name . ' (' . $counter . ')' . $ext; - $newpath = $path . '/' . $newname; - $counter++; - } - - return $newpath; - } - + /** + * Adds a suffix to the name in case the file exists + * + * @param $path + * @param $filename + * @return string + */ + public static function buildNotExistingFileName($path, $filename){ + if($path==='/'){ + $path=''; + } + if ($pos = strrpos($filename, '.')) { + $name = substr($filename, 0, $pos); + $ext = substr($filename, $pos); + } else { + $name = $filename; + } + + $newpath = $path . '/' . $filename; + $newname = $filename; + $counter = 2; + while (OC_Filesystem::file_exists($newpath)) { + $newname = $name . ' (' . $counter . ')' . $ext; + $newpath = $path . '/' . $newname; + $counter++; + } + + return $newpath; + } + /* * checks if $sub is a subdirectory of $parent - * - * @param $sub + * + * @param $sub * @param $parent * @return bool */ @@ -619,4 +580,94 @@ class OC_Helper { exit;*/ return false; } + + /** + * @brief Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. + * + * @param $input The array to work on + * @param $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) + * @param $encoding The encoding parameter is the character encoding. Defaults to UTF-8 + * @return array + * + * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. + * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715 + * + */ + public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8'){ + $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER; + $ret = array(); + foreach ($input as $k => $v) { + $ret[mb_convert_case($k, $case, $encoding)] = $v; + } + return $ret; + } + + /** + * @brief replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. + * + * @param $input The input string. .Opposite to the PHP build-in function does not accept an array. + * @param $replacement The replacement string. + * @param $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string. + * @param $length Length of the part to be replaced + * @param $encoding The encoding parameter is the character encoding. Defaults to UTF-8 + * @return string + * + */ + public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { + $start = intval($start); + $length = intval($length); + $string = mb_substr($string, 0, $start, $encoding) . + $replacement . + mb_substr($string, $start+$length, mb_strlen($string, 'UTF-8')-$start, $encoding); + + return $string; + } + + /** + * @brief Replace all occurrences of the search string with the replacement string + * + * @param $search The value being searched for, otherwise known as the needle. String. + * @param $replace The replacement string. + * @param $subject The string or array being searched and replaced on, otherwise known as the haystack. + * @param $encoding The encoding parameter is the character encoding. Defaults to UTF-8 + * @param $count If passed, this will be set to the number of replacements performed. + * @return string + * + */ + public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { + $offset = -1; + $length = mb_strlen($search, $encoding); + while(($i = mb_strrpos($subject, $search, $offset, $encoding))) { + $subject = OC_Helper::mb_substr_replace($subject, $replace, $i, $length); + $offset = $i - mb_strlen($subject, $encoding) - 1; + $count++; + } + return $subject; + } + + /** + * @brief performs a search in a nested array + * @param haystack the array to be searched + * @param needle the search string + * @param $index optional, only search this key name + * @return the key of the matching field, otherwise false + * + * performs a search in a nested array + * + * taken from http://www.php.net/manual/en/function.array-search.php#97645 + */ + public static function recursiveArraySearch($haystack, $needle, $index = null) { + $aIt = new RecursiveArrayIterator($haystack); + $it = new RecursiveIteratorIterator($aIt); + + while($it->valid()) { + if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { + return $aIt->key(); + } + + $it->next(); + } + + return false; + } } diff --git a/lib/image.php b/lib/image.php index 4d1e7aa4bc7..90c64320a7c 100644 --- a/lib/image.php +++ b/lib/image.php @@ -23,12 +23,12 @@ //From user comments at http://dk2.php.net/manual/en/function.exif-imagetype.php if ( ! function_exists( 'exif_imagetype' ) ) { - function exif_imagetype ( $filename ) { - if ( ( list($width, $height, $type, $attr) = getimagesize( $filename ) ) !== false ) { - return $type; - } - return false; - } + function exif_imagetype ( $filename ) { + if ( ( $info = getimagesize( $filename ) ) !== false ) { + return $info[2]; + } + return false; + } } function ellipsis($str, $maxlen) { @@ -66,7 +66,6 @@ class OC_Image { public function __construct($imageref = null) { //OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG); if(!extension_loaded('gd') || !function_exists('gd_info')) { - //if(!function_exists('imagecreatefromjpeg')) { OC_Log::write('core',__METHOD__.'(): GD module not installed', OC_Log::ERROR); return false; } @@ -108,6 +107,56 @@ class OC_Image { } /** + * @brief Returns the width when the image orientation is top-left. + * @returns int + */ + public function widthTopLeft() { + $o = $this->getOrientation(); + OC_Log::write('core','OC_Image->widthTopLeft() Orientation: '.$o, OC_Log::DEBUG); + switch($o) { + case -1: + case 1: + case 2: // Not tested + case 3: + case 4: // Not tested + return $this->width(); + break; + case 5: // Not tested + case 6: + case 7: // Not tested + case 8: + return $this->height(); + break; + } + return $this->width(); + } + + /** + * @brief Returns the height when the image orientation is top-left. + * @returns int + */ + public function heightTopLeft() { + $o = $this->getOrientation(); + OC_Log::write('core','OC_Image->heightTopLeft() Orientation: '.$o, OC_Log::DEBUG); + switch($o) { + case -1: + case 1: + case 2: // Not tested + case 3: + case 4: // Not tested + return $this->height(); + break; + case 5: // Not tested + case 6: + case 7: // Not tested + case 8: + return $this->width(); + break; + } + return $this->height(); + } + + /** * @brief Outputs the image. * @returns bool */ @@ -189,47 +238,66 @@ class OC_Image { } /** - * @returns Returns a base64 encoded string suitable for embedding in a VCard. + * @returns Returns the raw image data. */ - function __toString() { + function data() { ob_start(); $res = imagepng($this->resource); if (!$res) { - OC_Log::write('core','OC_Image->__toString. Error writing image',OC_Log::ERROR); + OC_Log::write('core','OC_Image->data. Error getting image data.',OC_Log::ERROR); } - return base64_encode(ob_get_clean()); + return ob_get_clean(); + } + + /** + * @returns Returns a base64 encoded string suitable for embedding in a VCard. + */ + function __toString() { + return base64_encode($this->data()); } /** * (I'm open for suggestions on better method name ;) - * @brief Fixes orientation based on EXIF data. - * @returns bool. + * @brief Get the orientation based on EXIF data. + * @returns The orientation or -1 if no EXIF data is available. */ - public function fixOrientation() { + public function getOrientation() { if(!is_callable('exif_read_data')){ OC_Log::write('core','OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG); - return false; + return -1; } if(!$this->valid()) { OC_Log::write('core','OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG); - return false; + return -1; } if(is_null($this->filepath) || !is_readable($this->filepath)) { OC_Log::write('core','OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG); - return false; + return -1; } $exif = @exif_read_data($this->filepath, 'IFD0'); if(!$exif) { - return false; + return -1; } if(!isset($exif['Orientation'])) { - return true; // Nothing to fix + return -1; } - $o = $exif['Orientation']; + return $exif['Orientation']; + } + + /** + * (I'm open for suggestions on better method name ;) + * @brief Fixes orientation based on EXIF data. + * @returns bool. + */ + public function fixOrientation() { + $o = $this->getOrientation(); OC_Log::write('core','OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG); $rotate = 0; $flip = false; switch($o) { + case -1: + return false; //Nothing to fix + break; case 1: $rotate = 0; $flip = false; @@ -295,7 +363,7 @@ class OC_Image { public function load($imageref) { if(is_resource($imageref)) { if(get_resource_type($imageref) == 'gd') { - $this->resource = $res; + $this->resource = $imageref; return $this->resource; } elseif(in_array(get_resource_type($imageref), array('file','stream'))) { return $this->loadFromFileHandle($imageref); @@ -581,9 +649,6 @@ class OC_Image { OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR); return false; } - $width_orig=imageSX($this->resource); - $height_orig=imageSY($this->resource); - //OC_Log::write('core',__METHOD__.'(): Original size: '.$width_orig.'x'.$height_orig, OC_Log::DEBUG); $process = imagecreatetruecolor($w, $h); if ($process == false) { OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR); diff --git a/lib/installer.php b/lib/installer.php index c5ca0883d76..b8a3226aa0b 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -3,7 +3,7 @@ * ownCloud * * @author Robin Appelman - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -60,7 +60,7 @@ class OC_Installer{ OC_Log::write('core','No source specified when installing app',OC_Log::ERROR); return false; } - + //download the file if necesary if($data['source']=='http'){ $path=OC_Helper::tmpFile(); @@ -76,7 +76,7 @@ class OC_Installer{ } $path=$data['path']; } - + //detect the archive type $mime=OC_Helper::getMimeType($path); if($mime=='application/zip'){ @@ -89,7 +89,7 @@ class OC_Installer{ OC_Log::write('core','Archives of type '.$mime.' are not supported',OC_Log::ERROR); return false; } - + //extract the archive in a temporary folder $extractDir=OC_Helper::tmpFolder(); OC_Helper::rmdirr($extractDir); @@ -104,7 +104,7 @@ class OC_Installer{ } return false; } - + //load the info.xml file of the app if(!is_file($extractDir.'/appinfo/info.xml')){ //try to find it in a subdir @@ -126,21 +126,19 @@ class OC_Installer{ return false; } $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml',true); - $basedir=OC::$APPSROOT.'/apps/'.$info['id']; - - // check the code for not allowed calls - if(!OC_Installer::checkCode($info['id'],$extractDir)){ + // check the code for not allowed calls + if(!OC_Installer::checkCode($info['id'],$extractDir)){ OC_Log::write('core','App can\'t be installed because of not allowed code in the App',OC_Log::ERROR); OC_Helper::rmdirr($extractDir); - return false; + return false; } - // check if the app is compatible with this version of ownCloud - $version=OC_Util::getVersion(); - if(!isset($info['require']) or ($version[0]>$info['require'])){ + // check if the app is compatible with this version of ownCloud + $version=OC_Util::getVersion(); + if(!isset($info['require']) or ($version[0]>$info['require'])){ OC_Log::write('core','App can\'t be installed because it is not compatible with this version of ownCloud',OC_Log::ERROR); OC_Helper::rmdirr($extractDir); - return false; + return false; } //check if an app with the same id is already installed @@ -153,6 +151,7 @@ class OC_Installer{ return false; } + $basedir=OC_App::getInstallPath().'/'.$info['id']; //check if the destination directory already exists if(is_dir($basedir)){ OC_Log::write('core','App directory already exists',OC_Log::WARN); @@ -162,11 +161,11 @@ class OC_Installer{ } return false; } - + if(isset($data['pretent']) and $data['pretent']==true){ return false; } - + //copy the app to the correct place if(@!mkdir($basedir)){ OC_Log::write('core','Can\'t create app folder. Please fix permissions. ('.$basedir.')',OC_Log::ERROR); @@ -177,34 +176,34 @@ class OC_Installer{ return false; } OC_Helper::copyr($extractDir,$basedir); - + //remove temporary files OC_Helper::rmdirr($extractDir); - + //install the database if(is_file($basedir.'/appinfo/database.xml')){ OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml'); } - + //run appinfo/install.php if((!isset($data['noinstall']) or $data['noinstall']==false) and file_exists($basedir.'/appinfo/install.php')){ include($basedir.'/appinfo/install.php'); } - + //set the installed version OC_Appconfig::setValue($info['id'],'installed_version',OC_App::getAppVersion($info['id'])); OC_Appconfig::setValue($info['id'],'enabled','no'); //set remote/public handelers foreach($info['remote'] as $name=>$path){ - OCP\CONFIG::setAppValue('core', 'remote_'.$name, '/apps/'.$info['id'].'/'.$path); + OCP\CONFIG::setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); } foreach($info['public'] as $name=>$path){ - OCP\CONFIG::setAppValue('core', 'public_'.$name, '/apps/'.$info['id'].'/'.$path); + OCP\CONFIG::setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); } OC_App::setAppTypes($info['id']); - + return $info['id']; } @@ -287,22 +286,24 @@ class OC_Installer{ * This function installs all apps found in the 'apps' directory that should be enabled by default; */ public static function installShippedApps(){ - if($dir = opendir( OC::$APPSROOT."/apps" )){ - while( false !== ( $filename = readdir( $dir ))){ - if( substr( $filename, 0, 1 ) != '.' and is_dir(OC::$APPSROOT."/apps/$filename") ){ - if( file_exists( OC::$APPSROOT."/apps/$filename/appinfo/app.php" )){ - if(!OC_Installer::isInstalled($filename)){ - $info=OC_App::getAppInfo($filename); - $enabled = isset($info['default_enable']); - if( $enabled ){ - OC_Installer::installShippedApp($filename); - OC_Appconfig::setValue($filename,'enabled','yes'); + foreach(OC::$APPSROOTS as $app_dir) { + if($dir = opendir( $app_dir['path'] )){ + while( false !== ( $filename = readdir( $dir ))){ + if( substr( $filename, 0, 1 ) != '.' and is_dir($app_dir['path']."/$filename") ){ + if( file_exists( $app_dir['path']."/$filename/appinfo/app.php" )){ + if(!OC_Installer::isInstalled($filename)){ + $info=OC_App::getAppInfo($filename); + $enabled = isset($info['default_enable']); + if( $enabled ){ + OC_Installer::installShippedApp($filename); + OC_Appconfig::setValue($filename,'enabled','yes'); + } } } } } + closedir( $dir ); } - closedir( $dir ); } } @@ -313,37 +314,37 @@ class OC_Installer{ */ public static function installShippedApp($app){ //install the database - if(is_file(OC::$APPSROOT."/apps/$app/appinfo/database.xml")){ - OC_DB::createDbFromStructure(OC::$APPSROOT."/apps/$app/appinfo/database.xml"); + if(is_file(OC_App::getAppPath($app)."/appinfo/database.xml")){ + OC_DB::createDbFromStructure(OC_App::getAppPath($app)."/appinfo/database.xml"); } //run appinfo/install.php - if(is_file(OC::$APPSROOT."/apps/$app/appinfo/install.php")){ - include(OC::$APPSROOT."/apps/$app/appinfo/install.php"); + if(is_file(OC_App::getAppPath($app)."/appinfo/install.php")){ + include(OC_App::getAppPath($app)."/appinfo/install.php"); } $info=OC_App::getAppInfo($app); OC_Appconfig::setValue($app,'installed_version',OC_App::getAppVersion($app)); - + //set remote/public handelers foreach($info['remote'] as $name=>$path){ - OCP\CONFIG::setAppValue('core', 'remote_'.$name, '/apps/'.$app.'/'.$path); + OCP\CONFIG::setAppValue('core', 'remote_'.$name, $app.'/'.$path); } foreach($info['public'] as $name=>$path){ - OCP\CONFIG::setAppValue('core', 'public_'.$name, '/apps/'.$app.'/'.$path); + OCP\CONFIG::setAppValue('core', 'public_'.$name, $app.'/'.$path); } - + OC_App::setAppTypes($info['id']); - - return $info; + + return $info['id']; } - /** - * check the code of an app with some static code checks - * @param string $folder the folder of the app to check - * @returns true for app is o.k. and false for app is not o.k. - */ - public static function checkCode($appname,$folder){ + /** + * check the code of an app with some static code checks + * @param string $folder the folder of the app to check + * @returns true for app is o.k. and false for app is not o.k. + */ + public static function checkCode($appname,$folder){ $blacklist=array( 'exec(', @@ -354,7 +355,7 @@ class OC_Installer{ ); // is the code checker enabled? - if(OC_Config::getValue('appcodechecker', false)){ + if(OC_Config::getValue('appcodechecker', false)){ // check if grep is installed $grep = exec('which grep'); @@ -374,11 +375,9 @@ class OC_Installer{ } } return true; - + }else{ - return true; + return true; } - } - - + } } diff --git a/lib/json.php b/lib/json.php index 78fbf564366..3d9d5c96fa3 100644 --- a/lib/json.php +++ b/lib/json.php @@ -64,6 +64,18 @@ class OC_JSON{ exit(); } } + + /** + * Check if the user is a subadmin, send json error msg if not + */ + public static function checkSubAdminUser(){ + self::checkLoggedIn(); + if(!OC_Group::inGroup(OC_User::getUser(),'admin') && !OC_SubAdmin::isSubAdmin(OC_User::getUser())){ + $l = OC_L10N::get('core'); + self::error(array( 'data' => array( 'message' => $l->t('Authentication error') ))); + exit(); + } + } /** * Send json error msg @@ -82,14 +94,24 @@ class OC_JSON{ } /** + * Convert OC_L10N_String to string, for use in json encodings + */ + protected static function to_string(&$value){ + if ($value instanceof OC_L10N_String) { + $value = (string)$value; + } + } + + /** * Encode and print $data in json format */ public static function encodedPrint($data,$setContentType=true){ - // Disable mimesniffing, don't move this to setContentTypeHeader! - header( 'X-Content-Type-Options: nosniff' ); - if($setContentType){ - self::setContentTypeHeader(); - } - echo json_encode($data); + // Disable mimesniffing, don't move this to setContentTypeHeader! + header( 'X-Content-Type-Options: nosniff' ); + if($setContentType){ + self::setContentTypeHeader(); + } + array_walk_recursive($data, array('OC_JSON', 'to_string')); + echo json_encode($data); } } diff --git a/lib/l10n.php b/lib/l10n.php index d47637d23de..e7f5ffea0e4 100644 --- a/lib/l10n.php +++ b/lib/l10n.php @@ -3,7 +3,7 @@ * ownCloud * * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -40,6 +40,16 @@ class OC_L10N{ protected static $language = ''; /** + * App of this object + */ + protected $app; + + /** + * Language of this object + */ + protected $lang; + + /** * Translations */ private $translations = array(); @@ -77,6 +87,17 @@ class OC_L10N{ * language. */ public function __construct($app, $lang = null){ + $this->app = $app; + $this->lang = $lang; + } + + protected function init(){ + if ($this->app === true) { + return; + } + $app = $this->app; + $lang = $this->lang; + $this->app = true; // Find the right language if(is_null($lang)){ $lang = self::findLanguage($app); @@ -92,13 +113,13 @@ class OC_L10N{ $i18ndir = self::findI18nDir($app); // Localization is in /l10n, Texts are in $i18ndir // (Just no need to define date/time format etc. twice) - if((OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$APPSROOT."/apps") || OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/core/l10n/') || OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/settings')) && file_exists($i18ndir.$lang.'.php')) { + if((OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC_App::getAppPath($app).'/l10n/') || OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/core/l10n/') || OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/settings')) && file_exists($i18ndir.$lang.'.php')) { // Include the file, save the data from $CONFIG - include($i18ndir.$lang.'.php'); + include(strip_tags($i18ndir).strip_tags($lang).'.php'); if(isset($TRANSLATIONS) && is_array($TRANSLATIONS)){ $this->translations = $TRANSLATIONS; } - } + } if(file_exists(OC::$SERVERROOT.'/core/l10n/l10n-'.$lang.'.php')){ // Include the file, save the data from $CONFIG @@ -123,10 +144,7 @@ class OC_L10N{ * returned. */ public function t($text, $parameters = array()){ - if(array_key_exists($text, $this->translations)){ - return vsprintf($this->translations[$text], $parameters); - } - return vsprintf($text, $parameters); + return new OC_L10N_String($this, $text, $parameters); } /** @@ -136,11 +154,18 @@ class OC_L10N{ * * Returns the translation. If no translation is found, $textArray will be * returned. + * + * + * @deprecated deprecated since ownCloud version 5.0 + * This method will probably be removed with ownCloud 6.0 + * + * */ public function tA($textArray){ + OC_Log::write('core', 'DEPRECATED: the method tA is deprecated and will be removed soon.',OC_Log::WARN); $result = array(); foreach($textArray as $key => $text){ - $result[$key] = $this->t($text); + $result[$key] = (string)$this->t($text); } return $result; } @@ -152,6 +177,7 @@ class OC_L10N{ * Returns an associative array with all translations */ public function getTranslations(){ + $this->init(); return $this->translations; } @@ -178,6 +204,7 @@ class OC_L10N{ * - params: timestamp (int/string) */ public function l($type, $data){ + $this->init(); switch($type){ // If you add something don't forget to add it to $localizations // at the top of the page @@ -224,23 +251,29 @@ class OC_L10N{ return self::$language; } - $available = array(); - if(is_array($app)){ - $available = $app; - } - else{ - $available=self::findAvailableLanguages($app); - } if(OC_User::getUser() && OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang')){ $lang = OC_Preferences::getValue(OC_User::getUser(), 'core', 'lang'); self::$language = $lang; - if(array_search($lang, $available) !== false){ + if(is_array($app)){ + $available = $app; + $lang_exists = array_search($lang, $available) !== false; + } + else { + $lang_exists = self::languageExists($app, $lang); + } + if($lang_exists){ return $lang; } } if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])){ $accepted_languages = preg_split('/,\s*/', $_SERVER['HTTP_ACCEPT_LANGUAGE']); + if(is_array($app)){ + $available = $app; + } + else{ + $available = self::findAvailableLanguages($app); + } foreach($accepted_languages as $i){ $temp = explode(';', $i); if(array_search($temp[0], $available) !== false){ @@ -263,8 +296,8 @@ class OC_L10N{ $i18ndir = OC::$SERVERROOT.'/core/l10n/'; if($app != ''){ // Check if the app is in the app folder - if(file_exists(OC::$APPSROOT.'/apps/'.$app.'/l10n/')){ - $i18ndir = OC::$APPSROOT.'/apps/'.$app.'/l10n/'; + if(file_exists(OC_App::getAppPath($app).'/l10n/')){ + $i18ndir = OC_App::getAppPath($app).'/l10n/'; } else{ $i18ndir = OC::$SERVERROOT.'/'.$app.'/l10n/'; @@ -292,4 +325,15 @@ class OC_L10N{ } return $available; } + + public static function languageExists($app, $lang){ + if ($lang == 'en'){//english is always available + return true; + } + $dir = self::findI18nDir($app); + if(is_dir($dir)){ + return file_exists($dir.'/'.$lang.'.php'); + } + return false; + } } diff --git a/lib/l10n/ca.php b/lib/l10n/ca.php new file mode 100644 index 00000000000..8e4c30caec9 --- /dev/null +++ b/lib/l10n/ca.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Ajuda", +"Personal" => "Personal", +"Settings" => "Configuració", +"Users" => "Usuaris", +"Apps" => "Aplicacions", +"Admin" => "Administració", +"ZIP download is turned off." => "La baixada en ZIP està desactivada.", +"Files need to be downloaded one by one." => "Els fitxers s'han de baixar d'un en un.", +"Back to Files" => "Torna a Fitxers", +"Selected files too large to generate zip file." => "Els fitxers seleccionats son massa grans per generar un fitxer zip.", +"Application is not enabled" => "L'aplicació no està habilitada", +"Authentication error" => "Error d'autenticació", +"Token expired. Please reload page." => "El testimoni ha expirat. Torneu a carregar la pàgina.", +"seconds ago" => "segons enrere", +"1 minute ago" => "fa 1 minut", +"%d minutes ago" => "fa %d minuts", +"today" => "avui", +"yesterday" => "ahir", +"%d days ago" => "fa %d dies", +"last month" => "el mes passat", +"months ago" => "mesos enrere", +"last year" => "l'any passat", +"years ago" => "fa anys" +); diff --git a/lib/l10n/cs_CZ.php b/lib/l10n/cs_CZ.php new file mode 100644 index 00000000000..933dbe541f5 --- /dev/null +++ b/lib/l10n/cs_CZ.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Nápověda", +"Personal" => "Osobní", +"Settings" => "Nastavení", +"Users" => "Uživatelé", +"Apps" => "Aplikace", +"Admin" => "Admin", +"ZIP download is turned off." => "Stahování ZIPu je vypnuto.", +"Files need to be downloaded one by one." => "Soubory je nutno stahovat samostatně.", +"Back to Files" => "Zpět k souborům", +"Selected files too large to generate zip file." => "Vybarné soubory jsou pro vytvoření zipu příliš velké.", +"Application is not enabled" => "Aplikace není povolena", +"Authentication error" => "Chyba autorizace", +"Token expired. Please reload page." => "Realce expirovala. Obnovte prosím stranu.", +"seconds ago" => "před vteřinami", +"1 minute ago" => "před 1 minutou", +"%d minutes ago" => "před %d minutami", +"today" => "dnes", +"yesterday" => "včera", +"%d days ago" => "před %d dny", +"last month" => "minulý měsíc", +"months ago" => "před měsíci", +"last year" => "loni", +"years ago" => "před lety" +); diff --git a/lib/l10n/de.php b/lib/l10n/de.php new file mode 100644 index 00000000000..e77ec97df75 --- /dev/null +++ b/lib/l10n/de.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Hilfe", +"Personal" => "Persönlich", +"Settings" => "Einstellungen", +"Users" => "Benutzer", +"Apps" => "Apps", +"Admin" => "Administrator", +"ZIP download is turned off." => "Der ZIP-Download ist deaktiviert.", +"Files need to be downloaded one by one." => "Die Dateien müssen einzeln heruntergeladen werden.", +"Back to Files" => "Zurück zu \"Dateien\"", +"Selected files too large to generate zip file." => "Die gewählten Dateien sind zu groß, um eine ZIP-Datei zu erstellen.", +"Application is not enabled" => "Die Anwendung ist nicht aktiviert", +"Authentication error" => "Authentifizierungs-Fehler", +"Token expired. Please reload page." => "Token abgelaufen. Bitte laden Sie die Seite neu.", +"seconds ago" => "Vor wenigen Sekunden", +"1 minute ago" => "Vor einer Minute", +"%d minutes ago" => "Vor %d Minuten", +"today" => "Heute", +"yesterday" => "Gestern", +"%d days ago" => "Vor %d Tagen", +"last month" => "Letzten Monat", +"months ago" => "Vor Monaten", +"last year" => "Letztes Jahr", +"years ago" => "Vor Jahren" +); diff --git a/lib/l10n/el.php b/lib/l10n/el.php new file mode 100644 index 00000000000..d9f272258e1 --- /dev/null +++ b/lib/l10n/el.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Βοήθεια", +"Personal" => "Προσωπικά", +"Settings" => "Ρυθμίσεις", +"Users" => "Χρήστες", +"Apps" => "Εφαρμογές", +"Admin" => "Διαχειριστής", +"ZIP download is turned off." => "Η λήψη ZIP απενεργοποιήθηκε.", +"Files need to be downloaded one by one." => "Τα αρχεία πρέπει να ληφθούν ένα-ένα.", +"Back to Files" => "Πίσω στα Αρχεία", +"Selected files too large to generate zip file." => "Τα επιλεγμένα αρχεία είναι μεγάλα ώστε να δημιουργηθεί αρχείο zip.", +"Application is not enabled" => "Δεν ενεργοποιήθηκε η εφαρμογή", +"Authentication error" => "Σφάλμα πιστοποίησης", +"Token expired. Please reload page." => "Το αναγνωριστικό έληξε. Παρακαλώ επανα-φορτώστε την σελίδα.", +"seconds ago" => "δευτερόλεπτα πριν", +"1 minute ago" => "1 λεπτό πριν", +"%d minutes ago" => "%d λεπτά πριν", +"today" => "σήμερα", +"yesterday" => "χθές", +"%d days ago" => "%d ημέρες πριν", +"last month" => "τον προηγούμενο μήνα", +"months ago" => "μήνες πριν", +"last year" => "τον προηγούμενο χρόνο", +"years ago" => "χρόνια πριν" +); diff --git a/lib/l10n/eo.php b/lib/l10n/eo.php new file mode 100644 index 00000000000..96bcb06a8d7 --- /dev/null +++ b/lib/l10n/eo.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Help" => "Helpo", +"Personal" => "Persona", +"Settings" => "Agordo", +"Users" => "Uzantoj", +"Apps" => "Aplikaĵoj", +"ZIP download is turned off." => "ZIP-elŝuto estas malkapabligita.", +"Files need to be downloaded one by one." => "Dosieroj devas elŝutiĝi unuope.", +"Back to Files" => "Reen al la dosieroj", +"Selected files too large to generate zip file." => "La elektitaj dosieroj tro grandas por genero de ZIP-dosiero.", +"Application is not enabled" => "La aplikaĵo ne estas kapabligita", +"Authentication error" => "Aŭtentiga eraro", +"Token expired. Please reload page." => "Ĵetono eksvalidiĝis. Bonvolu reŝargi la paĝon.", +"seconds ago" => "sekundojn antaŭe", +"1 minute ago" => "antaŭ 1 minuto", +"%d minutes ago" => "antaŭ %d minutoj", +"today" => "hodiaŭ", +"yesterday" => "hieraŭ", +"%d days ago" => "antaŭ %d tagoj", +"last month" => "lasta monato", +"months ago" => "monatojn antaŭe", +"last year" => "lasta jaro", +"years ago" => "jarojn antaŭe" +); diff --git a/lib/l10n/es.php b/lib/l10n/es.php new file mode 100644 index 00000000000..174fe0720fd --- /dev/null +++ b/lib/l10n/es.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Ayuda", +"Personal" => "Personal", +"Settings" => "Ajustes", +"Users" => "Usuarios", +"Apps" => "Aplicaciones", +"Admin" => "Administración", +"ZIP download is turned off." => "La descarga en ZIP está desactivada.", +"Files need to be downloaded one by one." => "Los archivos deben ser descargados uno por uno.", +"Back to Files" => "Volver a Archivos", +"Selected files too large to generate zip file." => "Los archivos seleccionados son demasiado grandes para generar el archivo zip.", +"Application is not enabled" => "La aplicación no está habilitada", +"Authentication error" => "Error de autenticación", +"Token expired. Please reload page." => "Token expirado. Por favor, recarga la página.", +"seconds ago" => "hace segundos", +"1 minute ago" => "hace 1 minuto", +"%d minutes ago" => "hace %d minutos", +"today" => "hoy", +"yesterday" => "ayer", +"%d days ago" => "hace %d días", +"last month" => "este mes", +"months ago" => "hace meses", +"last year" => "este año", +"years ago" => "hace años" +); diff --git a/lib/l10n/et_EE.php b/lib/l10n/et_EE.php new file mode 100644 index 00000000000..d8da90a3cbf --- /dev/null +++ b/lib/l10n/et_EE.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Abiinfo", +"Personal" => "Isiklik", +"Settings" => "Seaded", +"Users" => "Kasutajad", +"Apps" => "Rakendused", +"Admin" => "Admin", +"ZIP download is turned off." => "ZIP-ina allalaadimine on välja lülitatud.", +"Files need to be downloaded one by one." => "Failid tuleb alla laadida ükshaaval.", +"Back to Files" => "Tagasi failide juurde", +"Selected files too large to generate zip file." => "Valitud failid on ZIP-faili loomiseks liiga suured.", +"Application is not enabled" => "Rakendus pole sisse lülitatud", +"Authentication error" => "Autentimise viga", +"Token expired. Please reload page." => "Kontrollkood aegus. Paelun lae leht uuesti.", +"seconds ago" => "sekundit tagasi", +"1 minute ago" => "1 minut tagasi", +"%d minutes ago" => "%d minutit tagasi", +"today" => "täna", +"yesterday" => "eile", +"%d days ago" => "%d päeva tagasi", +"last month" => "eelmisel kuul", +"months ago" => "kuud tagasi", +"last year" => "eelmisel aastal", +"years ago" => "aastat tagasi" +); diff --git a/lib/l10n/fa.php b/lib/l10n/fa.php new file mode 100644 index 00000000000..3579329820f --- /dev/null +++ b/lib/l10n/fa.php @@ -0,0 +1,16 @@ +<?php $TRANSLATIONS = array( +"Help" => "راهنما", +"Personal" => "شخصی", +"Settings" => "تنظیمات", +"Users" => "کاربران", +"Admin" => "مدیر", +"seconds ago" => "ثانیهها پیش", +"1 minute ago" => "1 دقیقه پیش", +"%d minutes ago" => "%d دقیقه پیش", +"today" => "امروز", +"yesterday" => "دیروز", +"last month" => "ماه قبل", +"months ago" => "ماههای قبل", +"last year" => "سال قبل", +"years ago" => "سالهای قبل" +); diff --git a/lib/l10n/fi_FI.php b/lib/l10n/fi_FI.php new file mode 100644 index 00000000000..dda2c760373 --- /dev/null +++ b/lib/l10n/fi_FI.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Ohje", +"Personal" => "Henkilökohtainen", +"Settings" => "Asetukset", +"Users" => "Käyttäjät", +"Apps" => "Sovellukset", +"Admin" => "Ylläpitäjä", +"ZIP download is turned off." => "ZIP-lataus on poistettu käytöstä.", +"Files need to be downloaded one by one." => "Tiedostot on ladattava yksittäin.", +"Back to Files" => "Takaisin tiedostoihin", +"Selected files too large to generate zip file." => "Valitut tiedostot ovat liian suurikokoisia mahtuakseen zip-tiedostoon.", +"Application is not enabled" => "Sovellusta ei ole otettu käyttöön", +"Authentication error" => "Todennusvirhe", +"Token expired. Please reload page." => "Valtuutus vanheni. Lataa sivu uudelleen.", +"seconds ago" => "sekuntia sitten", +"1 minute ago" => "1 minuutti sitten", +"%d minutes ago" => "%d minuuttia sitten", +"today" => "tänään", +"yesterday" => "eilen", +"%d days ago" => "%d päivää sitten", +"last month" => "viime kuussa", +"months ago" => "kuukautta sitten", +"last year" => "viime vuonna", +"years ago" => "vuotta sitten" +); diff --git a/lib/l10n/fr.php b/lib/l10n/fr.php new file mode 100644 index 00000000000..c674b79b959 --- /dev/null +++ b/lib/l10n/fr.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Aide", +"Personal" => "Personnel", +"Settings" => "Paramètres", +"Users" => "Utilisateurs", +"Apps" => "Applications", +"Admin" => "Administration", +"ZIP download is turned off." => "Téléchargement ZIP désactivé.", +"Files need to be downloaded one by one." => "Les fichiers nécessitent d'être téléchargés un par un.", +"Back to Files" => "Retour aux Fichiers", +"Selected files too large to generate zip file." => "Les fichiers sélectionnés sont trop volumineux pour être compressés.", +"Application is not enabled" => "L'application n'est pas activée", +"Authentication error" => "Erreur d'authentification", +"Token expired. Please reload page." => "La session a expiré. Veuillez recharger la page.", +"seconds ago" => "à l'instant", +"1 minute ago" => "il y a 1 minute", +"%d minutes ago" => "il y a %d minutes", +"today" => "aujourd'hui", +"yesterday" => "hier", +"%d days ago" => "il y a %d jours", +"last month" => "le mois dernier", +"months ago" => "il y a plusieurs mois", +"last year" => "l'année dernière", +"years ago" => "il y a plusieurs années" +); diff --git a/lib/l10n/it.php b/lib/l10n/it.php new file mode 100644 index 00000000000..2c88818dc6c --- /dev/null +++ b/lib/l10n/it.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Aiuto", +"Personal" => "Personale", +"Settings" => "Impostazioni", +"Users" => "Utenti", +"Apps" => "Applicazioni", +"Admin" => "Admin", +"ZIP download is turned off." => "Lo scaricamento in formato ZIP è stato disabilitato.", +"Files need to be downloaded one by one." => "I file devono essere scaricati uno alla volta.", +"Back to Files" => "Torna ai file", +"Selected files too large to generate zip file." => "I file selezionati sono troppo grandi per generare un file zip.", +"Application is not enabled" => "L'applicazione non è abilitata", +"Authentication error" => "Errore di autenticazione", +"Token expired. Please reload page." => "Token scaduto. Ricarica la pagina.", +"seconds ago" => "secondi fa", +"1 minute ago" => "1 minuto fa", +"%d minutes ago" => "%d minuti fa", +"today" => "oggi", +"yesterday" => "ieri", +"%d days ago" => "%d giorni fa", +"last month" => "il mese scorso", +"months ago" => "mesi fa", +"last year" => "l'anno scorso", +"years ago" => "anni fa" +); diff --git a/lib/l10n/lt_LT.php b/lib/l10n/lt_LT.php new file mode 100644 index 00000000000..c6702a62287 --- /dev/null +++ b/lib/l10n/lt_LT.php @@ -0,0 +1,21 @@ +<?php $TRANSLATIONS = array( +"Help" => "Pagalba", +"Personal" => "Asmeniniai", +"Settings" => "Nustatymai", +"Users" => "Vartotojai", +"Apps" => "Programos", +"Admin" => "Administravimas", +"ZIP download is turned off." => "ZIP atsisiuntimo galimybė yra išjungta.", +"Files need to be downloaded one by one." => "Failai turi būti parsiunčiami vienas po kito.", +"Back to Files" => "Atgal į Failus", +"Selected files too large to generate zip file." => "Pasirinkti failai per dideli archyvavimui į ZIP.", +"Application is not enabled" => "Programa neįjungta", +"Authentication error" => "Autentikacijos klaida", +"1 minute ago" => "prieš 1 minutę", +"%d minutes ago" => "prieš %d minučių", +"today" => "šiandien", +"yesterday" => "vakar", +"%d days ago" => "prieš %d dienų", +"last month" => "praėjusį mėnesį", +"last year" => "pereitais metais" +); diff --git a/lib/l10n/nb_NO.php b/lib/l10n/nb_NO.php new file mode 100644 index 00000000000..af9503b7bf4 --- /dev/null +++ b/lib/l10n/nb_NO.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Help" => "Hjelp", +"Personal" => "Personlig", +"Settings" => "Innstillinger", +"Users" => "Brukere", +"Apps" => "Apper", +"Admin" => "Admin", +"ZIP download is turned off." => "ZIP-nedlasting av avslått", +"Files need to be downloaded one by one." => "Filene må lastes ned en om gangen", +"Back to Files" => "Tilbake til filer", +"Selected files too large to generate zip file." => "De valgte filene er for store til å kunne generere ZIP-fil", +"Application is not enabled" => "Applikasjon er ikke påslått", +"Authentication error" => "Autentiseringsfeil", +"seconds ago" => "sekunder siden", +"1 minute ago" => "1 minuitt siden", +"%d minutes ago" => "%d minutter siden", +"today" => "i dag", +"yesterday" => "i går", +"%d days ago" => "%d dager siden", +"last month" => "forrige måned", +"months ago" => "måneder siden", +"last year" => "i fjor", +"years ago" => "år siden" +); diff --git a/lib/l10n/sl.php b/lib/l10n/sl.php new file mode 100644 index 00000000000..fad16417253 --- /dev/null +++ b/lib/l10n/sl.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Pomoč", +"Personal" => "Osebno", +"Settings" => "Nastavitve", +"Users" => "Uporabniki", +"Apps" => "Aplikacije", +"Admin" => "Skrbnik", +"ZIP download is turned off." => "ZIP prenos je onemogočen.", +"Files need to be downloaded one by one." => "Datoteke morajo biti prenešene posamezno.", +"Back to Files" => "Nazaj na datoteke", +"Selected files too large to generate zip file." => "Izbrane datoteke so prevelike, da bi lahko ustvarili zip datoteko.", +"Application is not enabled" => "Aplikacija ni omogočena", +"Authentication error" => "Napaka overitve", +"Token expired. Please reload page." => "Žeton je potekel. Prosimo, če spletno stran znova naložite.", +"seconds ago" => "sekund nazaj", +"1 minute ago" => "pred minuto", +"%d minutes ago" => "pred %d minutami", +"today" => "danes", +"yesterday" => "včeraj", +"%d days ago" => "pred %d dnevi", +"last month" => "prejšnji mesec", +"months ago" => "mesecev nazaj", +"last year" => "lani", +"years ago" => "let nazaj" +); diff --git a/lib/l10n/string.php b/lib/l10n/string.php new file mode 100644 index 00000000000..4769790a16d --- /dev/null +++ b/lib/l10n/string.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_L10N_String{ + protected $l10n; + public function __construct($l10n, $text, $parameters){ + $this->l10n = $l10n; + $this->text = $text; + $this->parameters = $parameters; + + } + + public function __toString(){ + $translations = $this->l10n->getTranslations(); + if(array_key_exists($this->text, $translations)){ + return vsprintf($translations[$this->text], $this->parameters); + } + return vsprintf($this->text, $this->parameters); + } +} diff --git a/lib/l10n/sv.php b/lib/l10n/sv.php new file mode 100644 index 00000000000..cff3a63c442 --- /dev/null +++ b/lib/l10n/sv.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "Hjälp", +"Personal" => "Personligt", +"Settings" => "Inställningar", +"Users" => "Användare", +"Apps" => "Program", +"Admin" => "Admin", +"ZIP download is turned off." => "Nerladdning av ZIP är avstängd.", +"Files need to be downloaded one by one." => "Filer laddas ner en åt gången.", +"Back to Files" => "Tillbaka till Filer", +"Selected files too large to generate zip file." => "Valda filer är för stora för att skapa zip-fil.", +"Application is not enabled" => "Applikationen är inte aktiverad", +"Authentication error" => "Fel vid autentisering", +"Token expired. Please reload page." => "Ogiltig token. Ladda om sidan.", +"seconds ago" => "sekunder sedan", +"1 minute ago" => "1 minut sedan", +"%d minutes ago" => "%d minuter sedan", +"today" => "idag", +"yesterday" => "igår", +"%d days ago" => "%d dagar sedan", +"last month" => "förra månaden", +"months ago" => "månader sedan", +"last year" => "förra året", +"years ago" => "år sedan" +); diff --git a/lib/l10n/th_TH.php b/lib/l10n/th_TH.php new file mode 100644 index 00000000000..cb2610fa204 --- /dev/null +++ b/lib/l10n/th_TH.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "ช่วยเหลือ", +"Personal" => "ส่วนตัว", +"Settings" => "ตั้งค่า", +"Users" => "ผู้ใช้งาน", +"Apps" => "แอปฯ", +"Admin" => "ผู้ดูแล", +"ZIP download is turned off." => "คุณสมบัติการดาวน์โหลด zip ถูกปิดการใช้งานไว้", +"Files need to be downloaded one by one." => "ไฟล์สามารถดาวน์โหลดได้ทีละครั้งเท่านั้น", +"Back to Files" => "กลับไปที่ไฟล์", +"Selected files too large to generate zip file." => "ไฟล์ที่เลือกมีขนาดใหญ่เกินกว่าที่จะสร้างเป็นไฟล์ zip", +"Application is not enabled" => "แอพพลิเคชั่นดังกล่าวยังไม่ได้เปิดใช้งาน", +"Authentication error" => "เกิดข้อผิดพลาดในสิทธิ์การเข้าใช้งาน", +"Token expired. Please reload page." => "รหัสยืนยันความถูกต้องหมดอายุแล้ว กรุณาโหลดหน้าเว็บใหม่อีกครั้ง", +"seconds ago" => "วินาทีที่ผ่านมา", +"1 minute ago" => "1 นาทีมาแล้ว", +"%d minutes ago" => "%d นาทีที่ผ่านมา", +"today" => "วันนี้", +"yesterday" => "เมื่อวานนี้", +"%d days ago" => "%d วันที่ผ่านมา", +"last month" => "เดือนที่แล้ว", +"months ago" => "เดือนมาแล้ว", +"last year" => "ปีที่แล้ว", +"years ago" => "ปีที่ผ่านมา" +); diff --git a/lib/l10n/uk.php b/lib/l10n/uk.php new file mode 100644 index 00000000000..18f6a4a623d --- /dev/null +++ b/lib/l10n/uk.php @@ -0,0 +1,24 @@ +<?php $TRANSLATIONS = array( +"Help" => "Допомога", +"Personal" => "Особисте", +"Settings" => "Налаштування", +"Users" => "Користувачі", +"Apps" => "Додатки", +"Admin" => "Адмін", +"ZIP download is turned off." => "ZIP завантаження вимкнено.", +"Files need to be downloaded one by one." => "Файли повинні бути завантаженні послідовно.", +"Back to Files" => "Повернутися до файлів", +"Selected files too large to generate zip file." => "Вибрані фали завеликі для генерування zip файлу.", +"Application is not enabled" => "Додаток не увімкнений", +"Authentication error" => "Помилка автентифікації", +"seconds ago" => "секунди тому", +"1 minute ago" => "1 хвилину тому", +"%d minutes ago" => "%d хвилин тому", +"today" => "сьогодні", +"yesterday" => "вчора", +"%d days ago" => "%d днів тому", +"last month" => "минулого місяця", +"months ago" => "місяці тому", +"last year" => "минулого року", +"years ago" => "роки тому" +); diff --git a/lib/l10n/zh_CN.php b/lib/l10n/zh_CN.php new file mode 100644 index 00000000000..2d05ad3567e --- /dev/null +++ b/lib/l10n/zh_CN.php @@ -0,0 +1,25 @@ +<?php $TRANSLATIONS = array( +"Help" => "帮助", +"Personal" => "个人", +"Settings" => "设置", +"Users" => "用户", +"Apps" => "应用", +"Admin" => "管理", +"ZIP download is turned off." => "ZIP 下载已经关闭", +"Files need to be downloaded one by one." => "需要逐一下载文件", +"Back to Files" => "回到文件", +"Selected files too large to generate zip file." => "选择的文件太大,无法生成 zip 文件。", +"Application is not enabled" => "不需要程序", +"Authentication error" => "认证错误", +"Token expired. Please reload page." => "Token 过期,请刷新页面。", +"seconds ago" => "几秒前", +"1 minute ago" => "1分钟前", +"%d minutes ago" => "%d 分钟前", +"today" => "今天", +"yesterday" => "昨天", +"%d days ago" => "%d 天前", +"last month" => "上月", +"months ago" => "几月前", +"last year" => "上年", +"years ago" => "几年前" +); diff --git a/lib/mail.php b/lib/mail.php index 0ecee0f01c8..0ac9a97c1bf 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -91,19 +91,8 @@ class OC_Mail { /** - * sending a mail based on a template + * return the footer for a mail * - * @param texttemplate $texttemplate - * @param htmltemplate $htmltemplate - * @param data $data - * @param To $toaddress - * @param ToName $toname - * @param Subject $subject - * @param From $fromaddress - * @param FromName $fromname - * @param ccaddress $ccaddress - * @param ccname $ccname - * @param bcc $bcc */ public static function getfooter() { diff --git a/lib/migrate.php b/lib/migrate.php index 06f88b37794..917d77eaca0 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -64,7 +64,7 @@ class OC_Migrate{ $apps = OC_App::getAllApps(); foreach($apps as $app){ - $path = OC::$SERVERROOT . '/apps/' . $app . '/appinfo/migrate.php'; + $path = OC_App::getAppPath($app) . '/appinfo/migrate.php'; if( file_exists( $path ) ){ include( $path ); } @@ -91,7 +91,7 @@ class OC_Migrate{ if( self::$exporttype == 'user' ){ // Check user exists if( !is_null($uid) ){ - $db = new OC_User_Database; + $db = new OC_User_Database; if( !$db->userExists( $uid ) ){ OC_Log::write('migration', 'User: '.$uid.' is not in the database and so cannot be exported.', OC_Log::ERROR); return json_encode( array( 'success' => false ) ); @@ -278,7 +278,7 @@ class OC_Migrate{ return json_encode( array( 'success' => false ) ); } // Done - return json_encode( 'success' => true ); + return json_encode( array( 'success' => true ) ); */ break; } @@ -398,7 +398,7 @@ class OC_Migrate{ if( OC_App::isEnabled( $provider->getID() ) ){ $success = true; // Does this app use the database? - if( file_exists( OC::$SERVERROOT.'/apps/'.$provider->getID().'/appinfo/database.xml' ) ){ + if( file_exists( OC_App::getAppPath($provider->getID()).'/appinfo/database.xml' ) ){ // Create some app tables $tables = self::createAppTables( $provider->getID() ); if( is_array( $tables ) ){ @@ -443,21 +443,10 @@ class OC_Migrate{ 'ocversion' => OC_Util::getVersion(), 'exporttime' => time(), 'exportedby' => OC_User::getUser(), - 'exporttype' => self::$exporttype + 'exporttype' => self::$exporttype, + 'exporteduser' => self::$uid ); - // Add hash if user export - if( self::$exporttype == 'user' ){ - $query = OC_DB::prepare( "SELECT `password` FROM `*PREFIX*users` WHERE `uid` = ?" ); - $result = $query->execute( array( self::$uid ) ); - $row = $result->fetchRow(); - $hash = $row ? $row['password'] : false; - if( !$hash ){ - OC_Log::write( 'migration', 'Failed to get the users password hash', OC_log::ERROR); - return false; - } - $info['hash'] = $hash; - $info['exporteduser'] = self::$uid; - } + if( !is_array( $array ) ){ OC_Log::write( 'migration', 'Supplied $array was not an array in getExportInfo()', OC_Log::ERROR ); } @@ -539,7 +528,7 @@ class OC_Migrate{ } // There is a database.xml file - $content = file_get_contents( OC::$SERVERROOT . '/apps/' . $appid . '/appinfo/database.xml' ); + $content = file_get_contents(OC_App::getAppPath($appid) . '/appinfo/database.xml' ); $file2 = 'static://db_scheme'; // TODO get the relative path to migration.db from the data dir @@ -608,7 +597,7 @@ class OC_Migrate{ static public function getApps(){ $allapps = OC_App::getAllApps(); foreach($allapps as $app){ - $path = OC::$SERVERROOT . '/apps/' . $app . '/lib/migrate.php'; + $path = self::getAppPath($app) . '/lib/migrate.php'; if( file_exists( $path ) ){ $supportsmigration[] = $app; } diff --git a/lib/mimetypes.fixlist.php b/lib/mimetypes.fixlist.php deleted file mode 100644 index 13e3f16b369..00000000000 --- a/lib/mimetypes.fixlist.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -return array( - 'ics'=>'text/calendar', - 'ical'=>'text/calendar', - 'js'=>'application/javascript', - 'odt'=>'application/vnd.oasis.opendocument.text', - 'ods'=>'application/vnd.oasis.opendocument.spreadsheet', - 'odg'=>'application/vnd.oasis.opendocument.graphics', - 'odp'=>'application/vnd.oasis.opendocument.presentation', - 'pl'=>'text/x-script.perl', - 'py'=>'text/x-script.phyton', - 'vcf' => 'text/vcard', - 'vcard' => 'text/vcard', - 'doc'=>'application/msword', - 'docx'=>'application/msword', - 'xls'=>'application/msexcel', - 'xlsx'=>'application/msexcel', - 'ppt'=>'application/mspowerpoint', - 'pptx'=>'application/mspowerpoint', - 'sgf' => 'application/sgf', - 'cdr' => 'application/coreldraw' -); diff --git a/lib/mimetypes.list.php b/lib/mimetypes.list.php index ccf47999b1c..8386bcb93f3 100644 --- a/lib/mimetypes.list.php +++ b/lib/mimetypes.list.php @@ -55,6 +55,9 @@ return array( 'ods'=>'application/vnd.oasis.opendocument.spreadsheet', 'odg'=>'application/vnd.oasis.opendocument.graphics', 'odp'=>'application/vnd.oasis.opendocument.presentation', + 'pages'=>'application/x-iwork-pages-sffpages', + 'numbers'=>'application/x-iwork-numbers-sffnumbers', + 'keynote'=>'application/x-iwork-keynote-sffkey', 'kra'=>'application/x-krita', 'mp3'=>'audio/mpeg', 'doc'=>'application/msword', @@ -78,5 +81,17 @@ return array( 'mpeg'=>'video/mpeg', 'mov'=>'video/quicktime', 'webm'=>'video/webm', - 'wmv'=>'video/x-ms-asf' + 'wmv'=>'video/x-ms-asf', + 'py'=>'text/x-script.phyton', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'doc'=>'application/msword', + 'docx'=>'application/msword', + 'xls'=>'application/msexcel', + 'xlsx'=>'application/msexcel', + 'ppt'=>'application/mspowerpoint', + 'pptx'=>'application/mspowerpoint', + 'sgf' => 'application/sgf', + 'cdr' => 'application/coreldraw', + 'impress' => 'text/impress', ); diff --git a/lib/minimizer.php b/lib/minimizer.php new file mode 100644 index 00000000000..3dc89e331a6 --- /dev/null +++ b/lib/minimizer.php @@ -0,0 +1,58 @@ +<?php + +abstract class OC_Minimizer { + public function getLastModified($files) { + $last_modified = 0; + foreach($files as $file_info) { + $file = $file_info[0] . '/' . $file_info[2]; + $filemtime = filemtime($file); + if ($filemtime > $last_modified) { + $last_modified = $filemtime; + } + } + return $last_modified; + } + + abstract public function minimizeFiles($files); + + public function output($files, $cache_key) { + header('Content-Type: '.$this->contentType); + OC_Response::enableCaching(); + $last_modified = $this->getLastModified($files); + OC_Response::setLastModifiedHeader($last_modified); + + $gzout = false; + $cache = OC_Cache::getGlobalCache(); + if (!OC_Request::isNoCache() && (!defined('DEBUG') || !DEBUG)){ + $gzout = $cache->get($cache_key.'.gz'); + if ($gzout) { + OC_Response::setETagHeader(md5($gzout)); + } + } + + if (!$gzout) { + $out = $this->minimizeFiles($files); + $gzout = gzencode($out); + OC_Response::setETagHeader(md5($gzout)); + $cache->set($cache_key.'.gz', $gzout); + } + if ($encoding = OC_Request::acceptGZip()) { + header('Content-Encoding: '.$encoding); + $out = $gzout; + } else { + $out = gzdecode($gzout); + } + header('Content-Length: '.strlen($out)); + echo $out; + } +} + +if (!function_exists('gzdecode')) { + function gzdecode($data,$maxlength=null,&$filename='',&$error='') + { + if (strcmp(substr($data,0,9),"\x1f\x8b\x8\0\0\0\0\0\0")) { + return null; // Not the GZIP format we expect (See RFC 1952) + } + return gzinflate(substr($data,10,-8)); + } +} diff --git a/lib/minimizer/css.php b/lib/minimizer/css.php new file mode 100644 index 00000000000..110935ea3b7 --- /dev/null +++ b/lib/minimizer/css.php @@ -0,0 +1,38 @@ +<?php + +require_once('mediawiki/CSSMin.php'); + +class OC_Minimizer_CSS extends OC_Minimizer +{ + protected $contentType = 'text/css'; + + public function minimizeFiles($files) { + $css_out = ''; + $webroot = (string) OC::$WEBROOT; + foreach($files as $file_info) { + $file = $file_info[0] . '/' . $file_info[2]; + $css_out .= '/* ' . $file . ' */' . "\n"; + $css = file_get_contents($file); + + $in_root = false; + foreach(OC::$APPSROOTS as $app_root) { + if(strpos($file, $app_root['path']) === 0) { + $in_root = rtrim($webroot.$app_root['url'], '/'); + break; + } + } + if ($in_root !== false) { + $css = str_replace('%appswebroot%', $in_root, $css); + $css = str_replace('%webroot%', $webroot, $css); + } + $remote = $file_info[1]; + $remote .= '/'; + $remote .= dirname($file_info[2]); + $css_out .= CSSMin::remap($css, dirname($file), $remote, true); + } + if (!defined('DEBUG') || !DEBUG){ + $css_out = CSSMin::minify($css_out); + } + return $css_out; + } +} diff --git a/lib/minimizer/js.php b/lib/minimizer/js.php new file mode 100644 index 00000000000..0f5cb7e5577 --- /dev/null +++ b/lib/minimizer/js.php @@ -0,0 +1,21 @@ +<?php + +require_once('mediawiki/JavaScriptMinifier.php'); + +class OC_Minimizer_JS extends OC_Minimizer +{ + protected $contentType = 'application/javascript'; + + public function minimizeFiles($files) { + $js_out = ''; + foreach($files as $file_info) { + $file = $file_info[0] . '/' . $file_info[2]; + $js_out .= '/* ' . $file . ' */' . "\n"; + $js_out .= file_get_contents($file); + } + if (!defined('DEBUG') || !DEBUG){ + $js_out = JavaScriptMinifier::minify($js_out); + } + return $js_out; + } +} diff --git a/lib/ocs.php b/lib/ocs.php index aba29275a92..3157aae99e6 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -4,7 +4,9 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @author Michael Gapczynski +* @copyright 2012 Frank Karlitschek frank@owncloud.org +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -29,429 +31,453 @@ */ class OC_OCS { - /** - * reads input date from get/post/cookies and converts the date to a special data-type - * - * @param variable $key - * @param variable-type $type - * @param priority $getpriority - * @param default $default - * @return data - */ - public static function readData($key,$type='raw',$getpriority=false,$default='') { - if($getpriority) { - if(isset($_GET[$key])) { - $data=$_GET[$key]; - } elseif(isset($_POST[$key])) { - $data=$_POST[$key]; - } else { - if($default=='') { - if(($type=='int') or ($type=='float')) $data=0; else $data=''; - } else { - $data=$default; - } - } - } else { - if(isset($_POST[$key])) { - $data=$_POST[$key]; - } elseif(isset($_GET[$key])) { - $data=$_GET[$key]; - } elseif(isset($_COOKIE[$key])) { - $data=$_COOKIE[$key]; - } else { - if($default=='') { - if(($type=='int') or ($type=='float')) $data=0; else $data=''; - } else { - $data=$default; - } - } - } - - if($type=='raw') return($data); - elseif($type=='text') return(addslashes(strip_tags($data))); - elseif($type=='int') { $data = (int) $data; return($data); } - elseif($type=='float') { $data = (float) $data; return($data); } - elseif($type=='array') { $data = $data; return($data); } - } - - - /** - main function to handle the REST request - **/ - public static function handle() { - - // overwrite the 404 error page returncode - header("HTTP/1.0 200 OK"); - - - if($_SERVER['REQUEST_METHOD'] == 'GET') { - $method='get'; - }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { - $method='put'; - parse_str(file_get_contents("php://input"),$put_vars); - }elseif($_SERVER['REQUEST_METHOD'] == 'POST') { - $method='post'; - }else{ - echo('internal server error: method not supported'); - exit(); - } - - // preprocess url - $url=$_SERVER['REQUEST_URI']; - if(substr($url,(strlen($url)-1))<>'/') $url.='/'; - $ex=explode('/',$url); - $paracount=count($ex); - - // eventhandler - // CONFIG - // apiconfig - GET - CONFIG - if(($method=='get') and (strtolower($ex[$paracount-3])=='v1.php') and (strtolower($ex[$paracount-2])=='config')){ - $format=OC_OCS::readdata('format','text'); - OC_OCS::apiconfig($format); - - // PERSON - // personcheck - POST - PERSON/CHECK - }elseif(($method=='post') and (strtolower($ex[$paracount-4])=='v1.php') and (strtolower($ex[$paracount-3])=='person') and (strtolower($ex[$paracount-2])=='check')){ - $format=OC_OCS::readdata('format','text'); - $login=OC_OCS::readdata('login','text'); - $passwd=OC_OCS::readdata('password','text'); - OC_OCS::personcheck($format,$login,$passwd); - - // ACTIVITY - // activityget - GET ACTIVITY page,pagesize als urlparameter - }elseif(($method=='get') and (strtolower($ex[$paracount-3])=='v1.php')and (strtolower($ex[$paracount-2])=='activity')){ - $format=OC_OCS::readdata('format','text'); - $page=OC_OCS::readdata('page','int'); - $pagesize=OC_OCS::readdata('pagesize','int'); - if($pagesize<1 or $pagesize>100) $pagesize=10; - OC_OCS::activityget($format,$page,$pagesize); - - // activityput - POST ACTIVITY - }elseif(($method=='post') and (strtolower($ex[$paracount-3])=='v1.php')and (strtolower($ex[$paracount-2])=='activity')){ - $format=OC_OCS::readdata('format','text'); - $message=OC_OCS::readdata('message','text'); - OC_OCS::activityput($format,$message); - - // PRIVATEDATA - // get - GET DATA - }elseif(($method=='get') and (strtolower($ex[$paracount-4])=='v1.php')and (strtolower($ex[$paracount-2])=='getattribute')){ - $format=OC_OCS::readdata('format','text'); - OC_OCS::privateDataGet($format); - - }elseif(($method=='get') and (strtolower($ex[$paracount-5])=='v1.php')and (strtolower($ex[$paracount-3])=='getattribute')){ - $format=OC_OCS::readdata('format','text'); - $app=$ex[$paracount-2]; - OC_OCS::privateDataGet($format, $app); - }elseif(($method=='get') and (strtolower($ex[$paracount-6])=='v1.php')and (strtolower($ex[$paracount-4])=='getattribute')){ - $format=OC_OCS::readdata('format','text'); - $key=$ex[$paracount-2]; - $app=$ex[$paracount-3]; - OC_OCS::privateDataGet($format, $app,$key); - - // set - POST DATA - }elseif(($method=='post') and (strtolower($ex[$paracount-6])=='v1.php')and (strtolower($ex[$paracount-4])=='setattribute')){ - $format=OC_OCS::readdata('format','text'); - $key=$ex[$paracount-2]; - $app=$ex[$paracount-3]; - $value=OC_OCS::readdata('value','text'); - OC_OCS::privatedataset($format, $app, $key, $value); - // delete - POST DATA - }elseif(($method=='post') and (strtolower($ex[$paracount-6])=='v1.php')and (strtolower($ex[$paracount-4])=='deleteattribute')){ - $format=OC_OCS::readdata('format','text'); - $key=$ex[$paracount-2]; - $app=$ex[$paracount-3]; - OC_OCS::privatedatadelete($format, $app, $key); - - }else{ - $format=OC_OCS::readdata('format','text'); - $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; - $txt.=OC_OCS::getdebugoutput(); - echo(OC_OCS::generatexml($format,'failed',999,$txt)); - } - exit(); - } - - /** - * generated some debug information to make it easier to find faild API calls - * @return debug data string - */ - private static function getDebugOutput() { - $txt=''; - $txt.="debug output:\n"; - if(isset($_SERVER['REQUEST_METHOD'])) $txt.='http request method: '.$_SERVER['REQUEST_METHOD']."\n"; - if(isset($_SERVER['REQUEST_URI'])) $txt.='http request uri: '.$_SERVER['REQUEST_URI']."\n"; - if(isset($_GET)) foreach($_GET as $key=>$value) $txt.='get parameter: '.$key.'->'.$value."\n"; - if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; - return($txt); - } - - /** - * checks if the user is authenticated - * checks the IP whitlist, apikeys and login/password combination - * if $forceuser is true and the authentication failed it returns an 401 http response. - * if $forceuser is false and authentification fails it returns an empty username string - * @param bool $forceuser - * @return username string - */ - private static function checkPassword($forceuser=true) { - //valid user account ? - if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser=''; - if(isset($_SERVER['PHP_AUTH_PW'])) $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw=''; - - if(empty($authuser)) { - if($forceuser){ - header('WWW-Authenticate: Basic realm="your valid user account or api key"'); - header('HTTP/1.0 401 Unauthorized'); - exit; - }else{ - $identifieduser=''; - } - }else{ - if(!OC_User::login($authuser,$authpw)){ - if($forceuser){ - header('WWW-Authenticate: Basic realm="your valid user account or api key"'); - header('HTTP/1.0 401 Unauthorized'); - exit; - }else{ - $identifieduser=''; - } - }else{ - $identifieduser=$authuser; - } - } - - return($identifieduser); - } - - - /** - * generates the xml or json response for the API call from an multidimenional data array. - * @param string $format - * @param string $status - * @param string $statuscode - * @param string $message - * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension - * @param int $itemscount - * @param int $itemsperpage - * @return string xml/json - */ - private static function generateXml($format,$status,$statuscode,$message,$data=array(),$tag='',$tagattribute='',$dimension=-1,$itemscount='',$itemsperpage='') { - if($format=='json') { - - $json=array(); - $json['status']=$status; - $json['statuscode']=$statuscode; - $json['message']=$message; - $json['totalitems']=$itemscount; - $json['itemsperpage']=$itemsperpage; - $json['data']=$data; - return(json_encode($json)); - - - }else{ - $txt=''; - $writer = xmlwriter_open_memory(); - xmlwriter_set_indent( $writer, 2 ); - xmlwriter_start_document($writer ); - xmlwriter_start_element($writer,'ocs'); - xmlwriter_start_element($writer,'meta'); - xmlwriter_write_element($writer,'status',$status); - xmlwriter_write_element($writer,'statuscode',$statuscode); - xmlwriter_write_element($writer,'message',$message); - if($itemscount<>'') xmlwriter_write_element($writer,'totalitems',$itemscount); - if(!empty($itemsperpage)) xmlwriter_write_element($writer,'itemsperpage',$itemsperpage); - xmlwriter_end_element($writer); - if($dimension=='0') { - // 0 dimensions - xmlwriter_write_element($writer,'data',$data); - - }elseif($dimension=='1') { - xmlwriter_start_element($writer,'data'); - foreach($data as $key=>$entry) { - xmlwriter_write_element($writer,$key,$entry); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='2') { - xmlwriter_start_element($writer,'data'); - foreach($data as $entry) { - xmlwriter_start_element($writer,$tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer,'details',$tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)){ - foreach($value as $k=>$v) { - xmlwriter_write_element($writer,$k,$v); - } - } else { - xmlwriter_write_element($writer,$key,$value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='3') { - xmlwriter_start_element($writer,'data'); - foreach($data as $entrykey=>$entry) { - xmlwriter_start_element($writer,$tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer,'details',$tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)){ - xmlwriter_start_element($writer,$entrykey); - foreach($value as $k=>$v) { - xmlwriter_write_element($writer,$k,$v); - } - xmlwriter_end_element($writer); - } else { - xmlwriter_write_element($writer,$key,$value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - }elseif($dimension=='dynamic') { - xmlwriter_start_element($writer,'data'); - OC_OCS::toxml($writer,$data,'comment'); - xmlwriter_end_element($writer); - } - - xmlwriter_end_element($writer); - - xmlwriter_end_document( $writer ); - $txt.=xmlwriter_output_memory( $writer ); - unset($writer); - return($txt); - } - } - - public static function toXml($writer,$data,$node) { - foreach($data as $key => $value) { - if (is_numeric($key)) { - $key = $node; - } - if (is_array($value)){ - xmlwriter_start_element($writer,$key); - OC_OCS::toxml($writer,$value,$node); - xmlwriter_end_element($writer); - }else{ - xmlwriter_write_element($writer,$key,$value); - } - - } - } - - - - - /** - * return the config data of this server - * @param string $format - * @return string xml/json - */ - private static function apiConfig($format) { - $user=OC_OCS::checkpassword(false); - $url=substr(OC_Helper::serverHost().$_SERVER['SCRIPT_NAME'],0,-11).''; - - $xml['version']='1.5'; - $xml['website']='ownCloud'; - $xml['host']=OC_Helper::serverHost(); - $xml['contact']=''; - $xml['ssl']='false'; - echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'config','',1)); - } - - - /** - * check if the provided login/apikey/password is valid - * @param string $format - * @param string $login - * @param string $passwd - * @return string xml/json - */ - private static function personCheck($format,$login,$passwd) { - if($login<>''){ - if(OC_User::login($login,$passwd)){ - $xml['person']['personid']=$login; - echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'person','check',2)); - }else{ - echo(OC_OCS::generatexml($format,'failed',102,'login not valid')); - } - }else{ - echo(OC_OCS::generatexml($format,'failed',101,'please specify all mandatory fields')); - } - } - - - - // ACTIVITY API ############################################# - - /** - * get my activities - * @param string $format - * @param string $page - * @param string $pagesize - * @return string xml/json - */ - private static function activityGet($format,$page,$pagesize) { - $user=OC_OCS::checkpassword(); - - //TODO - - $txt=OC_OCS::generatexml($format,'ok',100,'',$xml,'activity','full',2,$totalcount,$pagesize); - echo($txt); - } - - /** - * submit a activity - * @param string $format - * @param string $message - * @return string xml/json - */ - private static function activityPut($format,$message) { - // not implemented in ownCloud - $user=OC_OCS::checkpassword(); - echo(OC_OCS::generatexml($format,'ok',100,'')); - } - - // PRIVATEDATA API ############################################# - - /** - * get private data and create the xml for ocs - * @param string $format - * @param string $app - * @param string $key - * @return string xml/json - */ - private static function privateDataGet($format,$app="",$key="") { - $user=OC_OCS::checkpassword(); - $result=OC_OCS::getData($user,$app,$key); - $xml=array(); - foreach($result as $i=>$log) { - $xml[$i]['key']=$log['key']; - $xml[$i]['app']=$log['app']; - $xml[$i]['value']=$log['value']; - } - - - $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'privatedata', 'full', 2, count($xml), 0);//TODO: replace 'privatedata' with 'attribute' once a new libattice has been released that works with it - echo($txt); - } - - /** - * set private data referenced by $key to $value and generate the xml for ocs - * @param string $format - * @param string $app - * @param string $key - * @param string $value - * @return string xml/json - */ + /** + * reads input date from get/post/cookies and converts the date to a special data-type + * + * @param string HTTP method to read the key from + * @param string Parameter to read + * @param string Variable type to format data + * @param mixed Default value to return if the key is not found + * @return mixed Data or if the key is not found and no default is set it will exit with a 400 Bad request + */ + public static function readData($method, $key, $type = 'raw', $default = null) { + if ($method == 'get') { + if (isset($_GET[$key])) { + $data = $_GET[$key]; + } else if (isset($default)) { + return $default; + } else { + $data = false; + } + } else if ($method == 'post') { + if (isset($_POST[$key])) { + $data = $_POST[$key]; + } else if (isset($default)) { + return $default; + } else { + $data = false; + } + } + if ($data === false) { + echo self::generateXml('', 'fail', 400, 'Bad request. Please provide a valid '.$key); + exit(); + } else { + // NOTE: Is the raw type necessary? It might be a little risky without sanitization + if ($type == 'raw') return $data; + elseif ($type == 'text') return OC_Util::sanitizeHTML($data); + elseif ($type == 'int') return (int) $data; + elseif ($type == 'float') return (float) $data; + elseif ($type == 'array') return OC_Util::sanitizeHTML($data); + else return OC_Util::sanitizeHTML($data); + } + } + + /** + main function to handle the REST request + **/ + public static function handle() { + // overwrite the 404 error page returncode + header("HTTP/1.0 200 OK"); + + + if($_SERVER['REQUEST_METHOD'] == 'GET') { + $method='get'; + }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { + $method='put'; + parse_str(file_get_contents("php://input"),$put_vars); + }elseif($_SERVER['REQUEST_METHOD'] == 'POST') { + $method='post'; + }else{ + echo('internal server error: method not supported'); + exit(); + } + + // preprocess url + $url = strtolower($_SERVER['REQUEST_URI']); + if(substr($url,(strlen($url)-1))<>'/') $url.='/'; + $ex=explode('/',$url); + $paracount=count($ex); + $format = self::readData($method, 'format', 'text', ''); + + // eventhandler + // CONFIG + // apiconfig - GET - CONFIG + if(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'config')){ + OC_OCS::apiconfig($format); + + // PERSON + // personcheck - POST - PERSON/CHECK + }elseif(($method=='post') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-3]=='person') and ($ex[$paracount-2] == 'check')){ + $login = self::readData($method, 'login', 'text'); + $passwd = self::readData($method, 'password', 'text'); + OC_OCS::personcheck($format,$login,$passwd); + + // ACTIVITY + // activityget - GET ACTIVITY page,pagesize als urlparameter + }elseif(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')){ + $page = self::readData($method, 'page', 'int', 0); + $pagesize = self::readData($method, 'pagesize','int', 10); + if($pagesize<1 or $pagesize>100) $pagesize=10; + OC_OCS::activityget($format,$page,$pagesize); + + // activityput - POST ACTIVITY + }elseif(($method=='post') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'activity')){ + $message = self::readData($method, 'message', 'text'); + OC_OCS::activityput($format,$message); + + + // PRIVATEDATA + // get - GET DATA + }elseif(($method=='get') and ($ex[$paracount-4] == 'v1.php') and ($ex[$paracount-2] == 'getattribute')){ + OC_OCS::privateDataGet($format); + + }elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-3] == 'getattribute')){ + $app=$ex[$paracount-2]; + OC_OCS::privateDataGet($format, $app); + }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'getattribute')){ + + $key=$ex[$paracount-2]; + $app=$ex[$paracount-3]; + OC_OCS::privateDataGet($format, $app,$key); + + // set - POST DATA + }elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-4] == 'setattribute')){ + $key=$ex[$paracount-2]; + $app=$ex[$paracount-3]; + $value = self::readData($method, 'value', 'text'); + OC_OCS::privatedataset($format, $app, $key, $value); + // delete - POST DATA + }elseif(($method=='post') and ($ex[$paracount-6] =='v1.php') and ($ex[$paracount-4] == 'deleteattribute')){ + $key=$ex[$paracount-2]; + $app=$ex[$paracount-3]; + OC_OCS::privatedatadelete($format, $app, $key); + + // CLOUD + // systemWebApps + }elseif(($method=='get') and ($ex[$paracount-5] == 'v1.php') and ($ex[$paracount-4]=='cloud') and ($ex[$paracount-3] == 'system') and ($ex[$paracount-2] == 'webapps')){ + OC_OCS::systemwebapps($format); + + // quotaget + }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'quota')){ + $user=$ex[$paracount-3]; + OC_OCS::quotaget($format,$user); + + // quotaset + }elseif(($method=='post') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'quota')){ + $user=$ex[$paracount-3]; + $quota = self::readData('post', 'quota', 'int'); + OC_OCS::quotaset($format,$user,$quota); + + // keygetpublic + }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'publickey')){ + $user=$ex[$paracount-3]; + OC_OCS::publicKeyGet($format,$user); + + // keygetprivate + }elseif(($method=='get') and ($ex[$paracount-6] == 'v1.php') and ($ex[$paracount-5]=='cloud') and ($ex[$paracount-4] == 'user') and ($ex[$paracount-2] == 'privatekey')){ + $user=$ex[$paracount-3]; + OC_OCS::privateKeyGet($format,$user); + + +// add more calls here +// please document all the call in the draft spec +// http://www.freedesktop.org/wiki/Specifications/open-collaboration-services-1.7#CLOUD + +// TODO: +// users +// groups +// bookmarks +// sharing +// versioning +// news (rss) + + + + }else{ + $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; + $txt.=OC_OCS::getdebugoutput(); + echo(OC_OCS::generatexml($format,'failed',999,$txt)); + } + exit(); + } + + /** + * generated some debug information to make it easier to find faild API calls + * @return debug data string + */ + private static function getDebugOutput() { + $txt=''; + $txt.="debug output:\n"; + if(isset($_SERVER['REQUEST_METHOD'])) $txt.='http request method: '.$_SERVER['REQUEST_METHOD']."\n"; + if(isset($_SERVER['REQUEST_URI'])) $txt.='http request uri: '.$_SERVER['REQUEST_URI']."\n"; + if(isset($_GET)) foreach($_GET as $key=>$value) $txt.='get parameter: '.$key.'->'.$value."\n"; + if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; + return($txt); + } + + /** + * checks if the user is authenticated + * checks the IP whitlist, apikeys and login/password combination + * if $forceuser is true and the authentication failed it returns an 401 http response. + * if $forceuser is false and authentification fails it returns an empty username string + * @param bool $forceuser + * @return username string + */ + private static function checkPassword($forceuser=true) { + //valid user account ? + if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser=''; + if(isset($_SERVER['PHP_AUTH_PW'])) $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw=''; + + if(empty($authuser)) { + if($forceuser){ + header('WWW-Authenticate: Basic realm="your valid user account or api key"'); + header('HTTP/1.0 401 Unauthorized'); + exit; + }else{ + $identifieduser=''; + } + }else{ + if(!OC_User::login($authuser,$authpw)){ + if($forceuser){ + header('WWW-Authenticate: Basic realm="your valid user account or api key"'); + header('HTTP/1.0 401 Unauthorized'); + exit; + }else{ + $identifieduser=''; + } + }else{ + $identifieduser=$authuser; + } + } + + return($identifieduser); + } + + + /** + * generates the xml or json response for the API call from an multidimenional data array. + * @param string $format + * @param string $status + * @param string $statuscode + * @param string $message + * @param array $data + * @param string $tag + * @param string $tagattribute + * @param int $dimension + * @param int $itemscount + * @param int $itemsperpage + * @return string xml/json + */ + private static function generateXml($format,$status,$statuscode,$message,$data=array(),$tag='',$tagattribute='',$dimension=-1,$itemscount='',$itemsperpage='') { + if($format=='json') { + $json=array(); + $json['status']=$status; + $json['statuscode']=$statuscode; + $json['message']=$message; + $json['totalitems']=$itemscount; + $json['itemsperpage']=$itemsperpage; + $json['data']=$data; + return(json_encode($json)); + }else{ + $txt=''; + $writer = xmlwriter_open_memory(); + xmlwriter_set_indent( $writer, 2 ); + xmlwriter_start_document($writer ); + xmlwriter_start_element($writer,'ocs'); + xmlwriter_start_element($writer,'meta'); + xmlwriter_write_element($writer,'status',$status); + xmlwriter_write_element($writer,'statuscode',$statuscode); + xmlwriter_write_element($writer,'message',$message); + if($itemscount<>'') xmlwriter_write_element($writer,'totalitems',$itemscount); + if(!empty($itemsperpage)) xmlwriter_write_element($writer,'itemsperpage',$itemsperpage); + xmlwriter_end_element($writer); + if($dimension=='0') { + // 0 dimensions + xmlwriter_write_element($writer,'data',$data); + + }elseif($dimension=='1') { + xmlwriter_start_element($writer,'data'); + foreach($data as $key=>$entry) { + xmlwriter_write_element($writer,$key,$entry); + } + xmlwriter_end_element($writer); + + }elseif($dimension=='2') { + xmlwriter_start_element($writer,'data'); + foreach($data as $entry) { + xmlwriter_start_element($writer,$tag); + if(!empty($tagattribute)) { + xmlwriter_write_attribute($writer,'details',$tagattribute); + } + foreach($entry as $key=>$value) { + if(is_array($value)){ + foreach($value as $k=>$v) { + xmlwriter_write_element($writer,$k,$v); + } + } else { + xmlwriter_write_element($writer,$key,$value); + } + } + xmlwriter_end_element($writer); + } + xmlwriter_end_element($writer); + + }elseif($dimension=='3') { + xmlwriter_start_element($writer,'data'); + foreach($data as $entrykey=>$entry) { + xmlwriter_start_element($writer,$tag); + if(!empty($tagattribute)) { + xmlwriter_write_attribute($writer,'details',$tagattribute); + } + foreach($entry as $key=>$value) { + if(is_array($value)){ + xmlwriter_start_element($writer,$entrykey); + foreach($value as $k=>$v) { + xmlwriter_write_element($writer,$k,$v); + } + xmlwriter_end_element($writer); + } else { + xmlwriter_write_element($writer,$key,$value); + } + } + xmlwriter_end_element($writer); + } + xmlwriter_end_element($writer); + }elseif($dimension=='dynamic') { + xmlwriter_start_element($writer,'data'); + OC_OCS::toxml($writer,$data,'comment'); + xmlwriter_end_element($writer); + } + + xmlwriter_end_element($writer); + + xmlwriter_end_document( $writer ); + $txt.=xmlwriter_output_memory( $writer ); + unset($writer); + return($txt); + } + } + + public static function toXml($writer,$data,$node) { + foreach($data as $key => $value) { + if (is_numeric($key)) { + $key = $node; + } + if (is_array($value)){ + xmlwriter_start_element($writer,$key); + OC_OCS::toxml($writer,$value,$node); + xmlwriter_end_element($writer); + }else{ + xmlwriter_write_element($writer,$key,$value); + } + } + } + + + + + /** + * return the config data of this server + * @param string $format + * @return string xml/json + */ + private static function apiConfig($format) { + $user=OC_OCS::checkpassword(false); + $url=substr(OCP\Util::getServerHost().$_SERVER['SCRIPT_NAME'],0,-11).''; + + $xml['version']='1.7'; + $xml['website']='ownCloud'; + $xml['host']=OCP\Util::getServerHost(); + $xml['contact']=''; + $xml['ssl']='false'; + echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'config','',1)); + } + + + /** + * check if the provided login/apikey/password is valid + * @param string $format + * @param string $login + * @param string $passwd + * @return string xml/json + */ + private static function personCheck($format,$login,$passwd) { + if($login<>''){ + if(OC_User::login($login,$passwd)){ + $xml['person']['personid']=$login; + echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'person','check',2)); + }else{ + echo(OC_OCS::generatexml($format,'failed',102,'login not valid')); + } + }else{ + echo(OC_OCS::generatexml($format,'failed',101,'please specify all mandatory fields')); + } + } + + + + // ACTIVITY API ############################################# + + /** + * get my activities + * @param string $format + * @param string $page + * @param string $pagesize + * @return string xml/json + */ + private static function activityGet($format,$page,$pagesize) { + $user=OC_OCS::checkpassword(); + + //TODO + + $txt=OC_OCS::generatexml($format,'ok',100,'',$xml,'activity','full',2,$totalcount,$pagesize); + echo($txt); + } + + /** + * submit a activity + * @param string $format + * @param string $message + * @return string xml/json + */ + private static function activityPut($format,$message) { + // not implemented in ownCloud + $user=OC_OCS::checkpassword(); + echo(OC_OCS::generatexml($format,'ok',100,'')); + } + + // PRIVATEDATA API ############################################# + + /** + * get private data and create the xml for ocs + * @param string $format + * @param string $app + * @param string $key + * @return string xml/json + */ + private static function privateDataGet($format,$app="",$key="") { + $user=OC_OCS::checkpassword(); + $result=OC_OCS::getData($user,$app,$key); + $xml=array(); + foreach($result as $i=>$log) { + $xml[$i]['key']=$log['key']; + $xml[$i]['app']=$log['app']; + $xml[$i]['value']=$log['value']; + } + + + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'privatedata', 'full', 2, count($xml), 0);//TODO: replace 'privatedata' with 'attribute' once a new libattice has been released that works with it + echo($txt); + } + + /** + * set private data referenced by $key to $value and generate the xml for ocs + * @param string $format + * @param string $app + * @param string $key + * @param string $value + * @return string xml/json + */ private static function privateDataSet($format, $app, $key, $value) { $user=OC_OCS::checkpassword(); if(OC_OCS::setData($user,$app,$key,$value)){ @@ -529,4 +555,134 @@ class OC_OCS { public static function deleteData($user, $app, $key) { return OC_Preferences::deleteKey($user,$app,$key); } + + + // CLOUD API ############################################# + + /** + * get a list of installed web apps + * @param string $format + * @return string xml/json + */ + private static function systemWebApps($format) { + $login=OC_OCS::checkpassword(); + $apps=OC_App::getEnabledApps(); + $values=array(); + foreach($apps as $app) { + $info=OC_App::getAppInfo($app); + if(isset($info['standalone'])) { + $newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>''); + $values[]=$newvalue; + } + + } + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $values, 'cloud', '', 2, 0, 0); + echo($txt); + + } + + + /** + * get the quota of a user + * @param string $format + * @param string $user + * @return string xml/json + */ + private static function quotaGet($format,$user) { + $login=OC_OCS::checkpassword(); + if(OC_Group::inGroup($login, 'admin') or ($login==$user)) { + + if(OC_User::userExists($user)){ + // calculate the disc space + $user_dir = '/'.$user.'/files'; + OC_Filesystem::init($user_dir); + $rootInfo=OC_FileCache::get(''); + $sharedInfo=OC_FileCache::get('/Shared'); + $used=$rootInfo['size']-$sharedInfo['size']; + $free=OC_Filesystem::free_space(); + $total=$free+$used; + if($total==0) $total=1; // prevent division by zero + $relative=round(($used/$total)*10000)/100; + + $xml=array(); + $xml['quota']=$total; + $xml['free']=$free; + $xml['used']=$used; + $xml['relative']=$relative; + + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', '', 1, 0, 0); + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + }else{ + echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); + } + } + + /** + * set the quota of a user + * @param string $format + * @param string $user + * @param string $quota + * @return string xml/json + */ + private static function quotaSet($format,$user,$quota) { + $login=OC_OCS::checkpassword(); + if(OC_Group::inGroup($login, 'admin')) { + + // todo + // not yet implemented + // add logic here + error_log('OCS call: user:'.$user.' quota:'.$quota); + + $xml=array(); + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', '', 1, 0, 0); + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); + } + } + + /** + * get the public key of a user + * @param string $format + * @param string $user + * @return string xml/json + */ + private static function publicKeyGet($format,$user) { + $login=OC_OCS::checkpassword(); + + if(OC_User::userExists($user)){ + // calculate the disc space + $txt='this is the public key of '.$user; + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + } + + /** + * get the private key of a user + * @param string $format + * @param string $user + * @return string xml/json + */ + private static function privateKeyGet($format,$user) { + $login=OC_OCS::checkpassword(); + if(OC_Group::inGroup($login, 'admin') or ($login==$user)) { + + if(OC_User::userExists($user)){ + // calculate the disc space + $txt='this is the private key of '.$user; + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + }else{ + echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); + } + } + + } diff --git a/lib/ocsclient.php b/lib/ocsclient.php index d0d27263bac..ae35470cff6 100644 --- a/lib/ocsclient.php +++ b/lib/ocsclient.php @@ -4,7 +4,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -35,12 +35,7 @@ class OC_OCSClient{ * This function returns the url of the OCS AppStore server. It´s possible to set it in the config file or it will fallback to the default */ private static function getAppStoreURL(){ - $configurl=OC_Config::getValue('appstoreurl', ''); - if($configurl<>'') { - $url=$configurl; - }else{ - $url='http://api.apps.owncloud.com/v1'; - } + $url = OC_Config::getValue('appstoreurl', 'http://api.apps.owncloud.com/v1'); return($url); } @@ -50,12 +45,7 @@ class OC_OCSClient{ * This function returns the url of the OCS knowledge base server. It´s possible to set it in the config file or it will fallback to the default */ private static function getKBURL(){ - $configurl=OC_Config::getValue('knowledgebaseurl', ''); - if($configurl<>'') { - $url=$configurl; - }else{ - $url='http://api.apps.owncloud.com/v1'; - } + $url = OC_Config::getValue('knowledgebaseurl', 'http://api.apps.owncloud.com/v1'); return($url); } @@ -67,6 +57,9 @@ class OC_OCSClient{ * This function returns a list of all the application categories on the OCS server */ public static function getCategories(){ + if(OC_Config::getValue('appstoreenabled', true)==false){ + return NULL; + } $url=OC_OCSClient::getAppStoreURL().'/content/categories'; $xml=@file_get_contents($url); @@ -78,7 +71,7 @@ class OC_OCSClient{ $tmp=$data->data; $cats=array(); - foreach($tmp->category as $key=>$value) { + foreach($tmp->category as $value) { $id= (int) $value->id; $name= (string) $value->name; @@ -140,6 +133,9 @@ class OC_OCSClient{ * This function returns an applications from the OCS server */ public static function getApplication($id){ + if(OC_Config::getValue('appstoreenabled', true)==false){ + return NULL; + } $url=OC_OCSClient::getAppStoreURL().'/content/data/'.urlencode($id); $xml=@file_get_contents($url); @@ -167,31 +163,34 @@ class OC_OCSClient{ return $app; } - /** - * @brief Get the download url for an application from the OCS server - * @returns array with application data - * - * This function returns an download url for an applications from the OCS server - */ - public static function getApplicationDownload($id,$item){ - $url=OC_OCSClient::getAppStoreURL().'/content/download/'.urlencode($id).'/'.urlencode($item); - - $xml=@file_get_contents($url); - if($xml==FALSE){ - OC_Log::write('core','Unable to parse OCS content',OC_Log::FATAL); - return NULL; - } - $data=simplexml_load_string($xml); - - $tmp=$data->data->content; - $app=array(); - if(isset($tmp->downloadlink)) { - $app['downloadlink']=$tmp->downloadlink; + /** + * @brief Get the download url for an application from the OCS server + * @returns array with application data + * + * This function returns an download url for an applications from the OCS server + */ + public static function getApplicationDownload($id,$item){ + if(OC_Config::getValue('appstoreenabled', true)==false){ + return NULL; + } + $url=OC_OCSClient::getAppStoreURL().'/content/download/'.urlencode($id).'/'.urlencode($item); + + $xml=@file_get_contents($url); + if($xml==FALSE){ + OC_Log::write('core','Unable to parse OCS content',OC_Log::FATAL); + return NULL; + } + $data=simplexml_load_string($xml); + + $tmp=$data->data->content; + $app=array(); + if(isset($tmp->downloadlink)) { + $app['downloadlink']=$tmp->downloadlink; }else{ $app['downloadlink']=''; } - return $app; - } + return $app; + } /** diff --git a/lib/preferences.php b/lib/preferences.php index 18627d7d2ea..b6c4c3a163f 100644 --- a/lib/preferences.php +++ b/lib/preferences.php @@ -4,7 +4,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -150,6 +150,7 @@ class OC_Preferences{ $query = OC_DB::prepare( 'UPDATE `*PREFIX*preferences` SET `configvalue` = ? WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?' ); $query->execute( array( $value, $user, $app, $key )); } + return true; } /** diff --git a/lib/public/app.php b/lib/public/app.php index 9cadabeca74..e74f1550740 100644 --- a/lib/public/app.php +++ b/lib/public/app.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -46,10 +46,9 @@ class App { * */ public static function register( $data ){ - return \OC_App::register( $data ); + return true; // don't do anything } - /** * @brief adds an entry to the navigation * @param $data array containing the data @@ -152,6 +151,3 @@ class App { } - - -?> diff --git a/lib/public/backgroundjob.php b/lib/public/backgroundjob.php new file mode 100644 index 00000000000..834bebb5c3c --- /dev/null +++ b/lib/public/backgroundjob.php @@ -0,0 +1,117 @@ +<?php +/** +* ownCloud +* +* @author Jakob Sack +* @copyright 2012 Jakob Sack owncloud@jakobsack.de +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * Public interface of ownCloud forbackground jobs. + */ + +// use OCP namespace for all classes that are considered public. +// This means that they should be used by apps instead of the internal ownCloud classes +namespace OCP; + +/** + * This class provides functions to manage backgroundjobs in ownCloud + * + * There are two kind of background jobs in ownCloud: regular tasks and + * queued tasks. + * + * Regular tasks have to be registered in appinfo.php and + * will run on a regular base. Fetching news could be a task that should run + * frequently. + * + * Queued tasks have to be registered each time you want to execute them. + * An example of the queued task would be the creation of the thumbnail. As + * soon as the user uploads a picture the gallery app registers the queued + * task "create thumbnail" and saves the path in the parameter instead of doing + * the work right away. This makes the app more responsive. As soon as the task + * is done it will be deleted from the list. + */ +class BackgroundJob { + /** + * @brief creates a regular task + * @param $klass class name + * @param $method method name + * @return true + */ + public static function addRegularTask( $klass, $method ){ + return \OC_BackgroundJob_RegularTask::register( $klass, $method ); + } + + /** + * @brief gets all regular tasks + * @return associative array + * + * key is string "$klass-$method", value is array( $klass, $method ) + */ + static public function allRegularTasks(){ + return \OC_BackgroundJob_RegularTask::all(); + } + + /** + * @brief Gets one queued task + * @param $id ID of the task + * @return associative array + */ + public static function findQueuedTask( $id ){ + return \OC_BackgroundJob_QueuedTask::find( $id ); + } + + /** + * @brief Gets all queued tasks + * @return array with associative arrays + */ + public static function allQueuedTasks(){ + return \OC_BackgroundJob_QueuedTask::all(); + } + + /** + * @brief Gets all queued tasks of a specific app + * @param $app app name + * @return array with associative arrays + */ + public static function queuedTaskWhereAppIs( $app ){ + return \OC_BackgroundJob_QueuedTask::whereAppIs( $app ); + } + + /** + * @brief queues a task + * @param $app app name + * @param $klass class name + * @param $method method name + * @param $parameters all useful data as text + * @return id of task + */ + public static function addQueuedTask( $app, $klass, $method, $parameters ){ + return \OC_BackgroundJob_QueuedTask::add( $app, $klass, $method, $parameters ); + } + + /** + * @brief deletes a queued task + * @param $id id of task + * @return true/false + * + * Deletes a report + */ + public static function deleteQueuedTask( $id ){ + return \OC_BackgroundJob_QueuedTask::delete( $id ); + } +} diff --git a/lib/public/config.php b/lib/public/config.php index 762fb6b1800..ab01902ffe6 100644 --- a/lib/public/config.php +++ b/lib/public/config.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -134,5 +134,3 @@ class Config { } - -?> diff --git a/lib/public/db.php b/lib/public/db.php index 39df58bf8cf..23c670cf442 100644 --- a/lib/public/db.php +++ b/lib/public/db.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -91,5 +91,3 @@ class DB { } - -?> diff --git a/lib/public/files.php b/lib/public/files.php index 24c0193e8a7..32b3f036744 100644 --- a/lib/public/files.php +++ b/lib/public/files.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -115,5 +115,3 @@ class Files { } - -?> diff --git a/lib/public/groupinterface.php b/lib/public/groupinterface.php new file mode 100644 index 00000000000..97833028118 --- /dev/null +++ b/lib/public/groupinterface.php @@ -0,0 +1,31 @@ +<?php +/** +* ownCloud +* +* @author Arthur Schiwon +* @copyright 2012 Arthur Schiwon blizzz@owncloud.org +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * Public interface of ownCloud for apps to use. + * Group Class. + * + */ + +namespace OCP; + +interface GroupInterface extends \OC_Group_Interface {}
\ No newline at end of file diff --git a/lib/public/json.php b/lib/public/json.php index 69a69925457..99df79173eb 100644 --- a/lib/public/json.php +++ b/lib/public/json.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -35,75 +35,140 @@ namespace OCP; */ class JSON { - /** * @brief Encode and print $data in JSON format * @param array $data The data to use * @param string $setContentType the optional content type + * @return string json formatted string. */ public static function encodedPrint( $data, $setContentType=true ){ return(\OC_JSON::encodedPrint( $data, $setContentType )); } - /** - * @brief Check if the user is logged in, send json error msg if not + * Check if the user is logged in, send json error msg if not. + * + * This method checks if a user is logged in. If not, a json error + * response will be return and the method will exit from execution + * of the script. + * The returned json will be in the format: + * + * {"status":"error","data":{"message":"Authentication error."}} + * + * Add this call to the start of all ajax method files that requires + * an authenticated user. + * + * @return string json formatted error string if not authenticated. */ public static function checkLoggedIn(){ return(\OC_JSON::checkLoggedIn()); } /** - * @brief Check an ajax get/post call if the request token is valid. - * @return json Error msg if not valid. - */ + * Check an ajax get/post call if the request token is valid. + * + * This method checks for a valid variable 'requesttoken' in $_GET, + * $_POST and $_SERVER. If a valid token is not found, a json error + * response will be return and the method will exit from execution + * of the script. + * The returned json will be in the format: + * + * {"status":"error","data":{"message":"Token expired. Please reload page."}} + * + * Add this call to the start of all ajax method files that creates, + * updates or deletes anything. + * In cases where you e.g. use an ajax call to load a dialog containing + * a submittable form, you will need to add the requesttoken first as a + * parameter to the ajax call, then assign it to the template and finally + * add a hidden input field also named 'requesttoken' containing the value. + * + * @return string json formatted error string if not valid. + */ public static function callCheck(){ return(\OC_JSON::callCheck()); } /** - * @brief Send json success msg + * Send json success msg + * + * Return a json success message with optional extra data. + * @see OCP\JSON::error() for the format to use. + * * @param array $data The data to use + * @return string json formatted string. */ public static function success( $data = array() ){ return(\OC_JSON::success( $data )); } - /** - * @brief Send json error msg + * Send json error msg + * + * Return a json error message with optional extra data for + * error message or app specific data. + * + * Example use: + * + * $id = [some value] + * OCP\JSON::error(array('data':array('message':'An error happened', 'id': $id))); + * + * Will return the json formatted string: + * + * {"status":"error","data":{"message":"An error happened", "id":[some value]}} + * * @param array $data The data to use + * @return string json formatted error string. */ public static function error( $data = array() ){ return(\OC_JSON::error( $data )); } - /** - * @brief set Content-Type header to jsonrequest - * @param array $type The contwnt type header - */ + * @brief set Content-Type header to jsonrequest + * @param array $type The contwnt type header + * @return string json formatted string. + */ public static function setContentTypeHeader( $type='application/json' ){ return(\OC_JSON::setContentTypeHeader( $type )); } - /** - * @brief Check if the App is enabled and send JSON error message instead - * @param string $app The app to check - */ + * Check if the App is enabled and send JSON error message instead + * + * This method checks if a specific app is enabled. If not, a json error + * response will be return and the method will exit from execution + * of the script. + * The returned json will be in the format: + * + * {"status":"error","data":{"message":"Application is not enabled."}} + * + * Add this call to the start of all ajax method files that requires + * a specific app to be enabled. + * + * @param string $app The app to check + * @return string json formatted string if not enabled. + */ public static function checkAppEnabled( $app ){ return(\OC_JSON::checkAppEnabled( $app )); } - /** - * @brief Check if the user is a admin, send json error msg if not + * Check if the user is a admin, send json error msg if not + * + * This method checks if the current user has admin rights. If not, a json error + * response will be return and the method will exit from execution + * of the script. + * The returned json will be in the format: + * + * {"status":"error","data":{"message":"Authentication error."}} + * + * Add this call to the start of all ajax method files that requires + * administrative rights. + * + * @return string json formatted string if not admin user. */ public static function checkAdminUser(){ return(\OC_JSON::checkAdminUser()); } } - -?> diff --git a/lib/public/response.php b/lib/public/response.php index c35c2654965..8dff3bcd354 100644 --- a/lib/public/response.php +++ b/lib/public/response.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -105,5 +105,3 @@ class Response { } - -?> diff --git a/lib/public/share.php b/lib/public/share.php new file mode 100644 index 00000000000..6221c75763f --- /dev/null +++ b/lib/public/share.php @@ -0,0 +1,1073 @@ +<?php +/** +* ownCloud +* +* @author Michael Gapczynski +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +*/ +namespace OCP; + +\OC_Hook::connect('OC_User', 'post_deleteUser', 'OCP\Share', 'post_deleteUser'); +\OC_Hook::connect('OC_User', 'post_addToGroup', 'OCP\Share', 'post_addToGroup'); +\OC_Hook::connect('OC_User', 'post_removeFromGroup', 'OCP\Share', 'post_removeFromGroup'); + +/** +* This class provides the ability for apps to share their content between users. +* Apps must create a backend class that implements OCP\Share_Backend and register it with this class. +*/ +class Share { + + const SHARE_TYPE_USER = 0; + const SHARE_TYPE_GROUP = 1; + const SHARE_TYPE_PRIVATE_LINK = 3; + const SHARE_TYPE_EMAIL = 4; + const SHARE_TYPE_CONTACT = 5; + const SHARE_TYPE_REMOTE = 6; + + /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask + * Construct permissions for share() and setPermissions with Or (|) e.g. Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE + * Check if permission is granted with And (&) e.g. Check if delete is granted: if ($permissions & PERMISSION_DELETE) + * Remove permissions with And (&) and Not (~) e.g. Remove the update permission: $permissions &= ~PERMISSION_UPDATE + * Apps are required to handle permissions on their own, this class only stores and manages the permissions of shares + */ + const PERMISSION_CREATE = 4; + const PERMISSION_READ = 1; + const PERMISSION_UPDATE = 2; + const PERMISSION_DELETE = 8; + const PERMISSION_SHARE = 16; + + const FORMAT_NONE = -1; + const FORMAT_STATUSES = -2; + const FORMAT_SOURCES = -3; + + private static $shareTypeUserAndGroups = -1; + private static $shareTypeGroupUserUnique = 2; + private static $backends = array(); + private static $backendTypes = array(); + + /** + * @brief Register a sharing backend class that implements OCP\Share_Backend for an item type + * @param string Item type + * @param string Backend class + * @param string (optional) Depends on item type + * @param array (optional) List of supported file extensions if this item type depends on files + * @return Returns true if backend is registered or false if error + */ + public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { + if (!isset(self::$backendTypes[$itemType])) { + self::$backendTypes[$itemType] = array('class' => $class, 'collectionOf' => $collectionOf, 'supportedFileExtensions' => $supportedFileExtensions); + if(count(self::$backendTypes) === 1) { + \OC_Util::addScript('core', 'share'); + \OC_Util::addStyle('core', 'share'); + } + return true; + } + \OC_Log::write('OCP\Share', 'Sharing backend '.$class.' not registered, '.self::$backendTypes[$itemType]['class'].' is already registered for '.$itemType, \OC_Log::WARN); + return false; + } + + /** + * @brief Get the items of item type shared with the current user + * @param string Item type + * @param int Format (optional) Format type must be defined by the backend + * @param int Number of items to return (optional) Returns all by default + * @return Return depends on format + */ + public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { + return self::getItems($itemType, null, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, $parameters, $limit, $includeCollections); + } + + /** + * @brief Get the item of item type shared with the current user + * @param string Item type + * @param string Item target + * @param int Format (optional) Format type must be defined by the backend + * @return Return depends on format + */ + public static function getItemSharedWith($itemType, $itemTarget, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemTarget, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, $parameters, 1, $includeCollections); + } + + /** + * @brief Get the item of item type shared with the current user by source + * @param string Item type + * @param string Item source + * @param int Format (optional) Format type must be defined by the backend + * @return Return depends on format + */ + public static function getItemSharedWithBySource($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, \OC_User::getUser(), null, $format, $parameters, 1, $includeCollections, true); + } + + /** + * @brief Get the shared items of item type owned by the current user + * @param string Item type + * @param int Format (optional) Format type must be defined by the backend + * @param int Number of items to return (optional) Returns all by default + * @return Return depends on format + */ + public static function getItemsShared($itemType, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { + return self::getItems($itemType, null, null, null, \OC_User::getUser(), $format, $parameters, $limit, $includeCollections); + } + + /** + * @brief Get the shared item of item type owned by the current user + * @param string Item type + * @param string Item source + * @param int Format (optional) Format type must be defined by the backend + * @return Return depends on format + */ + public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format, $parameters, -1, $includeCollections); + } + + /** + * @brief Share an item with a user, group, or via private link + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return bool Returns true on success or false on failure + */ + public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions) { + $uidOwner = \OC_User::getUser(); + // Verify share type and sharing conditions are met + if ($shareType === self::SHARE_TYPE_USER) { + if ($shareWith == $uidOwner) { + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is the item owner'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + if (!\OC_User::userExists($shareWith)) { + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' does not exist'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith)); + if (empty($inGroup)) { + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + // Check if the item source is already shared with the user, either from the same owner or a different user + if ($checkExists = self::getItems($itemType, $itemSource, self::$shareTypeUserAndGroups, $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { + // Only allow the same share to occur again if it is the same owner and is not a user share, this use case is for increasing permissions for a specific user + if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { + $message = 'Sharing '.$itemSource.' failed, because this item is already shared with '.$shareWith; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + } else if ($shareType === self::SHARE_TYPE_GROUP) { + if (!\OC_Group::groupExists($shareWith)) { + $message = 'Sharing '.$itemSource.' failed, because the group '.$shareWith.' does not exist'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + if (!\OC_Group::inGroup($uidOwner, $shareWith)) { + $message = 'Sharing '.$itemSource.' failed, because '.$uidOwner.' is not a member of the group '.$shareWith; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + // Check if the item source is already shared with the group, either from the same owner or a different user + // The check for each user in the group is done inside the put() function + if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_GROUP, $shareWith, null, self::FORMAT_NONE, null, 1, true, true)) { + // Only allow the same share to occur again if it is the same owner and is not a group share, this use case is for increasing permissions for a specific user + if ($checkExists['uid_owner'] != $uidOwner || $checkExists['share_type'] == $shareType) { + $message = 'Sharing '.$itemSource.' failed, because this item is already shared with '.$shareWith; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + // Convert share with into an array with the keys group and users + $group = $shareWith; + $shareWith = array(); + $shareWith['group'] = $group; + $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); + } else if ($shareType === self::SHARE_TYPE_PRIVATE_LINK) { + $shareWith = md5(uniqid($itemSource, true)); + return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions); + } else if ($shareType === self::SHARE_TYPE_CONTACT) { + if (!\OC_App::isEnabled('contacts')) { + $message = 'Sharing '.$itemSource.' failed, because the contacts app is not enabled'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + return false; + } + $vcard = \OC_Contacts_App::getContactVCard($shareWith); + if (!isset($vcard)) { + $message = 'Sharing '.$itemSource.' failed, because the contact does not exist'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + $details = \OC_Contacts_VCard::structureContact($vcard); + // TODO Add ownCloud user to contacts vcard + if (!isset($details['EMAIL'])) { + $message = 'Sharing '.$itemSource.' failed, because no email address is associated with the contact'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + return self::shareItem($itemType, $itemSource, self::SHARE_TYPE_EMAIL, $details['EMAIL'], $permissions); + } else { + // Future share types need to include their own conditions + $message = 'Share type '.$shareType.' is not valid for '.$itemSource; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + // If the item is a folder, scan through the folder looking for equivalent item types + if ($itemType == 'folder') { + $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); + if ($parentFolder && $files = \OC_Files::getDirectoryContent($itemSource)) { + for ($i = 0; $i < count($files); $i++) { + $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); + if ($files[$i]['mimetype'] == 'httpd/unix-directory' && $children = \OC_Files::getDirectoryContent($name, '/')) { + // Continue scanning into child folders + array_push($files, $children); + } else { + // Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted + self::put('file', $name, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder); + } + } + return true; + } + return false; + } else { + // Put the item into the database + return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions); + } + } + + /** + * @brief Unshare an item from a user, group, or delete a private link + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK + * @param string User or group the item is being shared with + * @return Returns true on success or false on failure + */ + public static function unshare($itemType, $itemSource, $shareType, $shareWith) { + if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1)) { + self::delete($item['id']); + return true; + } + return false; + } + + /** + * @brief Unshare an item shared with the current user + * @param string Item type + * @param string Item target + * @return Returns true on success or false on failure + * + * Unsharing from self is not allowed for items inside collections + * + */ + public static function unshareFromSelf($itemType, $itemTarget) { + if ($item = self::getItemSharedWith($itemType, $itemTarget)) { + if ((int)$item['share_type'] === self::SHARE_TYPE_GROUP) { + // TODO + } + // Delete + return self::delete($item['id'], true); + } + return false; + } + + /** + * @brief Set the permissions of an item for a specific user or group + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return Returns true on success or false on failure + */ + public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { + if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { + // Check if this item is a reshare and verify that the permissions granted don't exceed the parent shared item + if (isset($item['parent'])) { + $query = \OC_DB::prepare('SELECT permissions FROM *PREFIX*share WHERE id = ? LIMIT 1'); + $result = $query->execute(array($item['parent']))->fetchRow(); + if (~(int)$result['permissions'] & $permissions) { + $message = 'Setting permissions for '.$itemSource.' failed, because the permissions exceed permissions granted to '.\OC_User::getUser(); + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + $query = \OC_DB::prepare('UPDATE *PREFIX*share SET permissions = ? WHERE id = ?'); + $query->execute(array($permissions, $item['id'])); + // Check if permissions were removed + if ($item['permissions'] & ~$permissions) { + // If share permission is removed all reshares must be deleted + if (($item['permissions'] & self::PERMISSION_SHARE) && (~$permissions & self::PERMISSION_SHARE)) { + self::delete($item['id'], true); + } else { + $ids = array(); + $parents = array($item['id']); + while (!empty($parents)) { + $parents = "'".implode("','", $parents)."'"; + $query = \OC_DB::prepare('SELECT id, permissions FROM *PREFIX*share WHERE parent IN ('.$parents.')'); + $result = $query->execute(); + // Reset parents array, only go through loop again if items are found that need permissions removed + $parents = array(); + while ($item = $result->fetchRow()) { + // Check if permissions need to be removed + if ($item['permissions'] & ~$permissions) { + // Add to list of items that need permissions removed + $ids[] = $item['id']; + $parents[] = $item['id']; + } + } + } + // Remove the permissions for all reshares of this item + if (!empty($ids)) { + $ids = "'".implode("','", $ids)."'"; + $query = \OC_DB::prepare('UPDATE *PREFIX*share SET permissions = permissions & ? WHERE id IN ('.$ids.')'); + $query->execute(array($permissions)); + } + } + } + return true; + } + $message = 'Setting permissions for '.$itemSource.' failed, because the item was not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * @brief Get the backend class for the specified item type + * @param string Item type + * @return Sharing backend object + */ + private static function getBackend($itemType) { + if (isset(self::$backends[$itemType])) { + return self::$backends[$itemType]; + } else if (isset(self::$backendTypes[$itemType]['class'])) { + $class = self::$backendTypes[$itemType]['class']; + if (class_exists($class)) { + self::$backends[$itemType] = new $class; + if (!(self::$backends[$itemType] instanceof Share_Backend)) { + $message = 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + return self::$backends[$itemType]; + } else { + $message = 'Sharing backend '.$class.' not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + $message = 'Sharing backend for '.$itemType.' not found'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * @brief Get a list of collection item types for the specified item type + * @param string Item type + * @return array + */ + private static function getCollectionItemTypes($itemType) { + $collectionTypes = array($itemType); + foreach (self::$backendTypes as $type => $backend) { + if (in_array($backend['collectionOf'], $collectionTypes)) { + $collectionTypes[] = $type; + } + } + if (count($collectionTypes) > 1) { + unset($collectionTypes[0]); + return $collectionTypes; + } + return false; + } + + /** + * @brief Get shared items from the database + * @param string Item type + * @param string Item source or target (optional) + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_PRIVATE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique + * @param string User or group the item is being shared with + * @param string User that is the owner of shared items (optional) + * @param int Format to convert items to with formatItems() + * @param mixed Parameters to pass to formatItems() + * @param int Number of items to return, -1 to return all matches (optional) + * @param bool Include collection item types (optional) + * @return mixed + * + * See public functions getItem(s)... for parameter usage + * + */ + private static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false, $itemShareWithBySource = false) { + $backend = self::getBackend($itemType); + // Get filesystem root to add it to the file target and remove from the file source, match file_source with the file cache + if ($itemType == 'file' || $itemType == 'folder') { + $root = \OC_Filesystem::getRoot(); + $where = 'INNER JOIN *PREFIX*fscache ON file_source = *PREFIX*fscache.id '; + if (!isset($item)) { + $where .= 'WHERE file_target IS NOT NULL'; + } + $fileDependent = true; + $queryArgs = array(); + } else { + $fileDependent = false; + $root = ''; + if ($includeCollections && !isset($item) && $collectionTypes = self::getCollectionItemTypes($itemType)) { + // If includeCollections is true, find collections of this item type, e.g. a music album contains songs + $itemTypes = array_merge(array($itemType), $collectionTypes); + $placeholders = join(',', array_fill(0, count($itemTypes), '?')); + $where = "WHERE item_type IN ('".$placeholders."')"; + $queryArgs = $itemTypes; + } else { + $where = 'WHERE item_type = ?'; + $queryArgs = array($itemType); + } + } + if (isset($shareType) && isset($shareWith)) { + // Include all user and group items + if ($shareType == self::$shareTypeUserAndGroups) { + $where .= ' AND share_type IN (?,?,?)'; + $queryArgs[] = self::SHARE_TYPE_USER; + $queryArgs[] = self::SHARE_TYPE_GROUP; + $queryArgs[] = self::$shareTypeGroupUserUnique; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); + $where .= " AND share_with IN (".$placeholders.")"; + $queryArgs = array_merge($queryArgs, $userAndGroups); + // Don't include own group shares + $where .= ' AND uid_owner != ?'; + $queryArgs[] = $shareWith; + } else { + $where .= ' AND share_type = ? AND share_with = ?'; + $queryArgs[] = $shareType; + $queryArgs[] = $shareWith; + } + } + if (isset($uidOwner)) { + $where .= " AND uid_owner = ?"; + $queryArgs[] = $uidOwner; + if (!isset($shareType)) { + // Prevent unique user targets for group shares from being selected + $where .= " AND share_type != ?"; + $queryArgs[] = self::$shareTypeGroupUserUnique; + } + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_source'; + } else { + $column = 'item_source'; + } + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + } else { + $column = 'item_target'; + } + } + if (isset($item)) { + // If looking for own shared items, check item_source else check item_target + if (isset($uidOwner) || $itemShareWithBySource) { + // If item type is a file, file source needs to be checked in case the item was converted + if ($itemType == 'file' || $itemType == 'folder') { + $where .= " AND path = ?"; + $queryArgs[] = $root.$item; + } else { + $where .= " AND item_source = ?"; + $column = 'item_source'; + $queryArgs[] = $item; + } + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $where .= " AND file_target = ?"; + } else { + $where .= " AND item_target = ?"; + } + $queryArgs[] = $item; + } + if ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) { + // TODO Bart - this doesn't work with only one argument +// $placeholders = join(',', array_fill(0, count($collectionTypes), '?')); +// $where .= " OR item_type IN ('".$placeholders."')"; +// $queryArgs = array_merge($queryArgs, $collectionTypes); + } + } + if ($limit != -1 && !$includeCollections) { + if ($shareType == self::$shareTypeUserAndGroups) { + // Make sure the unique user target is returned if it exists, unique targets should follow the group share in the database + // If the limit is not 1, the filtering can be done later + $where .= ' ORDER BY *PREFIX*share.id DESC'; + } + // The limit must be at least 3, because filtering needs to be done + if ($limit < 3) { + $where .= ' LIMIT 3'; + } else { + $where .= ' LIMIT '.$limit; + } + } + // TODO Optimize selects + if ($format == self::FORMAT_STATUSES) { + if ($itemType == 'file' || $itemType == 'folder') { + $select = '*PREFIX*share.id, item_type, *PREFIX*share.parent, share_type, *PREFIX*fscache.path as file_source'; + } else { + $select = 'id, item_type, item_source, parent, share_type'; + } + } else { + if (isset($uidOwner)) { + if ($itemType == 'file' || $itemType == 'folder') { + $select = '*PREFIX*share.id, item_type, *PREFIX*fscache.path as file_source, *PREFIX*share.parent, share_type, share_with, permissions, stime'; + } else { + $select = 'id, item_type, item_source, parent, share_type, share_with, permissions, stime, file_source'; + } + } else { + if ($fileDependent) { + if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_FILE_APP || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) { + $select = '*PREFIX*share.id, item_type, *PREFIX*share.parent, share_type, share_with, permissions, file_target, *PREFIX*fscache.id, path as file_source, name, ctime, mtime, mimetype, size, encrypted, versioned, writable'; + } else { + $select = '*PREFIX*share.id, item_type, item_source, item_target, *PREFIX*share.parent, share_type, share_with, uid_owner, permissions, stime, path as file_source, file_target'; + } + } else { + $select = '*'; + } + } + } + $root = strlen($root); + $query = \OC_DB::prepare('SELECT '.$select.' FROM *PREFIX*share '.$where); + $result = $query->execute($queryArgs); + $items = array(); + $targets = array(); + while ($row = $result->fetchRow()) { + // Filter out duplicate group shares for users with unique targets + if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { + $row['share_type'] = self::SHARE_TYPE_GROUP; + $row['share_with'] = $items[$row['parent']]['share_with']; + // Remove the parent group share + unset($items[$row['parent']]); + } else if (!isset($uidOwner)) { + // Check if the same target already exists + if (isset($targets[$row[$column]])) { + // Check if the same owner shared with the user twice through a group and user share - this is allowed + $id = $targets[$row[$column]]; + if ($items[$id]['uid_owner'] == $row['uid_owner']) { + // Switch to group share type to ensure resharing conditions aren't bypassed + if ($items[$id]['share_type'] != self::SHARE_TYPE_GROUP) { + $items[$id]['share_type'] = self::SHARE_TYPE_GROUP; + $items[$id]['share_with'] = $row['share_with']; + } + // Switch ids if sharing permission is granted on only one share to ensure correct parent is used if resharing + if (~(int)$items[$id]['permissions'] & self::PERMISSION_SHARE && (int)$row['permissions'] & self::PERMISSION_SHARE) { + $items[$row['id']] = $items[$id]; + unset($items[$id]); + $id = $row['id']; + } + // Combine the permissions for the item + $items[$id]['permissions'] |= (int)$row['permissions']; + continue; + } + } else { + $targets[$row[$column]] = $row['id']; + } + } + // Remove root from file source paths if retrieving own shared items + if (isset($uidOwner) && isset($row['file_source'])) { + if (isset($row['parent'])) { + $row['file_source'] = '/Shared/'.basename($row['file_source']); + } else { + $row['file_source'] = substr($row['file_source'], $root); + } + } + $items[$row['id']] = $row; + } + if (!empty($items)) { + $collectionItems = array(); + foreach ($items as &$row) { + // Return only the item instead of a 2-dimensional array + if ($limit == 1 && $row['item_type'] == $itemType && $row[$column] == $item) { + if ($format == self::FORMAT_NONE) { + return $row; + } else { + break; + } + } + // Check if this is a collection of the requested item type + if ($includeCollections && $row['item_type'] != $itemType && $collectionBackend = self::getBackend($row['item_type']) && $collectionBackend instanceof Share_Backend_Collection) { + $row['collection'] = array('item_type' => $itemType, $column => $row[$column]); + // Fetch all of the children sources + $children = $collectionBackend->getChildren($row[$column]); + foreach ($children as $child) { + $childItem = $row; + $childItem['item_source'] = $child; +// $childItem['item_target'] = $child['target']; TODO + if (isset($item)) { + if ($childItem[$column] == $item) { + // Return only the item instead of a 2-dimensional array + if ($limit == 1 && $format == self::FORMAT_NONE) { + return $childItem; + } else { + // Unset the items array and break out of both loops + $items = array(); + $items[] = $childItem; + break 2; + } + } + } else { + $collectionItems[] = $childItem; + } + } + // Remove collection item + unset($items[$row['id']]); + } + } + if (!empty($collectionItems)) { + $items = array_merge($items, $collectionItems); + } + if ($format == self::FORMAT_NONE) { + return $items; + } else if ($format == self::FORMAT_STATUSES) { + $statuses = array(); + foreach ($items as $item) { + if ($item['share_type'] == self::SHARE_TYPE_PRIVATE_LINK) { + $statuses[$item[$column]] = true; + } else if (!isset($statuses[$item[$column]])) { + $statuses[$item[$column]] = false; + } + } + return $statuses; + } else { + return $backend->formatItems($items, $format, $parameters); + } + } else if ($limit == 1 || (isset($uidOwner) && isset($item))) { + return false; + } + return array(); + } + + /** + * @brief Put shared item into the database + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @param bool|array Parent folder target (optional) + * @return bool Returns true on success or false on failure + */ + private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder = null) { + // Check file extension for an equivalent item type to convert to + if ($itemType == 'file') { + $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1)); + foreach (self::$backends as $type => $backend) { + if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) { + $itemType = $type; + break; + } + } + // Exit if this is being called for a file inside a folder, and no equivalent item type is found + if (isset($parentFolder) && $itemType == 'file') { + return false; + } + } + $backend = self::getBackend($itemType); + // Check if this is a reshare + if ($checkReshare = self::getItemSharedWithBySource($itemType, $itemSource, self::FORMAT_NONE, null, true)) { + // Check if attempting to share back to owner + if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) { + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is the original sharer'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + // Check if attempting to share back to group TODO Check unique user target + } else if ($shareType == self::SHARE_TYPE_GROUP && $checkReshare['share_with'] == $shareWith['group']) { + $message = 'Sharing '.$itemSource.' failed, because the item was orignally shared with the group '.$shareWith['group']; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + // Check if attempting to share back a group share to a member of the same group + } else if (($checkReshare['share_type'] == self::SHARE_TYPE_GROUP || $checkReshare['share_type'] == self::$shareTypeGroupUserUnique) && $shareType == self::SHARE_TYPE_USER) { + if ($checkReshare['share_type'] == self::$shareTypeGroupUserUnique) { + $query = \OC_DB::prepare('SELECT share_with FROM *PREFIX*share WHERE id = ?'); + $group = $query->execute(array($checkReshare['parent']))->fetchOne(); + } else { + $group = $checkReshare['share_with']; + } + if (\OC_Group::inGroup($shareWith, $group)) { + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is a member of the original group share'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } + // Check if share permissions is granted + if ((int)$checkReshare['permissions'] & self::PERMISSION_SHARE) { + if (~(int)$checkReshare['permissions'] & $permissions) { + $message = 'Sharing '.$itemSource.' failed, because the permissions exceed permissions granted to '.$uidOwner; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } else { + // TODO Don't check if inside folder + $parent = $checkReshare['id']; + $itemSource = $checkReshare['item_source']; + // TODO Suggest item/file target + $suggestedItemTarget = $checkReshare['item_target']; + $fileSource = $checkReshare['file_source']; + $filePath = $checkReshare['file_target']; + } + } else { + $message = 'Sharing '.$itemSource.' failed, because resharing is not allowed'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } else { + $parent = null; + if (!$backend->isValidSource($itemSource, $uidOwner)) { + $message = 'Sharing '.$itemSource.' failed, because the sharing backend for '.$itemType.' could not find its source'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + $parent = null; + if ($backend instanceof Share_Backend_File_Dependent) { + // NOTE Apps should start using the file cache ids in their tables + $filePath = $backend->getFilePath($itemSource, $uidOwner); + $fileSource = \OC_FileCache::getId($filePath); + if ($fileSource == -1) { + $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + } else { + $filePath = null; + $fileSource = null; + } + } + $query = \OC_DB::prepare('INSERT INTO *PREFIX*share (item_type, item_source, item_target, parent, share_type, share_with, uid_owner, permissions, stime, file_source, file_target) VALUES (?,?,?,?,?,?,?,?,?,?,?)'); + // Share with a group + if ($shareType == self::SHARE_TYPE_GROUP) { + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner); + // Set group default file target for future use + $parentFolders[0]['folder'] = $groupFileTarget; + } else { + // Get group default file target + $groupFileTarget = $parentFolder[0]['folder'].$itemSource; + $parent = $parentFolder[0]['id']; + unset($parentFolder[0]); + // Only loop through users we know have different file target paths + $uidSharedWith = array_keys($parentFolder); + } + } else { + $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner); + } + } else { + $groupFileTarget = null; + } + $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner); + $uniqueTargets = array(); + // Loop through all users of this group in case we need to add an extra row + foreach ($shareWith['users'] as $uid) { + $itemTarget = self::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid, $uidOwner); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, $uidOwner); + if ($fileTarget != $groupFileTarget) { + $parentFolders[$uid]['folder'] = $fileTarget; + } + } else if (isset($parentFolder[$uid])) { + $fileTarget = $parentFolder[$uid]['folder'].$itemSource; + $parent = $parentFolder[$uid]['id']; + } + } else { + $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, $uidOwner); + } + } else { + $fileTarget = null; + } + // Insert an extra row for the group share if the item or file target is unique for this user + if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { + $uniqueTargets[] = array('uid' => $uid, 'item_target' => $itemTarget, 'file_target' => $fileTarget); + } + } + $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType, $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget)); + // Save this id, any extra rows for this group share will need to reference it + $parent = \OC_DB::insertid('*PREFIX*share'); + foreach ($uniqueTargets as $unique) { + $query->execute(array($itemType, $itemSource, $unique['item_target'], $parent, self::$shareTypeGroupUserUnique, $unique['uid'], $uidOwner, $permissions, time(), $fileSource, $unique['file_target'])); + $id = \OC_DB::insertid('*PREFIX*share'); + if ($parentFolder === true) { + $parentFolders['id'] = $id; + } + } + if ($parentFolder === true) { + // Return parent folders to preserve file target paths for potential children + return $parentFolders; + } + } else { + $itemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner); + $parentFolders['folder'] = $fileTarget; + } else { + $fileTarget = $parentFolder['folder'].$itemSource; + $parent = $parentFolder['id']; + } + } else { + $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner); + } + } else { + $fileTarget = null; + } + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, $permissions, time(), $fileSource, $fileTarget)); + $id = \OC_DB::insertid('*PREFIX*share'); + if ($parentFolder === true) { + $parentFolders['id'] = $id; + // Return parent folder to preserve file target paths for potential children + return $parentFolders; + } + } + return true; + } + + /** + * @brief Generate a unique target for the item + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK + * @param string User or group the item is being shared with + * @return string Item target + * + * TODO Use a suggested item target by default + * + */ + private static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner) { + $backend = self::getBackend($itemType); + if ($shareType == self::SHARE_TYPE_PRIVATE_LINK) { + return $backend->generateTarget($itemSource, false); + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + } else { + $column = 'item_target'; + } + if ($shareType == self::SHARE_TYPE_USER) { + // Share with is a user, so set share type to user and groups + $shareType = self::$shareTypeUserAndGroups; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + } else { + $userAndGroups = false; + } + $exclude = null; + // Backend has 3 opportunities to generate a unique target + for ($i = 0; $i < 2; $i++) { + if ($shareType == self::SHARE_TYPE_GROUP) { + $target = $backend->generateTarget($itemSource, false, $exclude); + } else { + $target = $backend->generateTarget($itemSource, $shareWith, $exclude); + } + if (is_array($exclude) && in_array($target, $exclude)) { + break; + } + // Check if target already exists + if ($checkTarget = self::getItems($itemType, $target, $shareType, $shareWith, null, self::FORMAT_NONE, null, 1)) { + // If matching target is from the same owner, use the same target. The share type will be different so this isn't the same share. + if ($checkTarget['uid_owner'] == $uidOwner) { + return $target; + } + if (!isset($exclude)) { + $exclude = array(); + } + // Find similar targets to improve backend's chances to generate a unqiue target + if ($userAndGroups) { + $checkTargets = \OC_DB::prepare("SELECT ".$column." FROM *PREFIX*share WHERE item_type = ? AND share_type IN (?,?,?) AND share_with IN ('".implode("','", $userAndGroups)."') AND ".$column." LIKE ?"); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, '%'.$target.'%')); + } else { + $checkTargets = \OC_DB::prepare("SELECT ".$column." FROM *PREFIX*share WHERE item_type = ? AND share_type = ? AND share_with = ? AND ".$column." LIKE ?"); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith, '%'.$target.'%')); + } + while ($row = $result->fetchRow()) { + $exclude[] = $row[$column]; + } + } else { + return $target; + } + } + } + $message = 'Sharing backend registered for '.$itemType.' did not generate a unique target for '.$itemSource; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + } + + /** + * @brief Delete all reshares of an item + * @param int Id of item to delete + * @param bool If true, exclude the parent from the delete (optional) + * @param string The user that the parent was shared with (optinal) + */ + private static function delete($parent, $excludeParent = false, $uidOwner = null) { + $ids = array($parent); + $parents = array($parent); + while (!empty($parents)) { + $parents = "'".implode("','", $parents)."'"; + // Check the owner on the first search of reshares, useful for finding and deleting the reshares by a single user of a group share + if (count($ids) == 1 && isset($uidOwner)) { + $query = \OC_DB::prepare('SELECT id FROM *PREFIX*share WHERE parent IN ('.$parents.') AND uid_owner = ?'); + $result = $query->execute(array($uidOwner)); + } else { + $query = \OC_DB::prepare('SELECT id, item_type, item_target, parent, uid_owner FROM *PREFIX*share WHERE parent IN ('.$parents.')'); + $result = $query->execute(); + } + // Reset parents array, only go through loop again if items are found + $parents = array(); + while ($item = $result->fetchRow()) { + // Search for a duplicate parent share, this occurs when an item is shared to the same user through a group and user or the same item is shared by different users + $userAndGroups = array_merge(array($item['uid_owner']), \OC_Group::getUserGroups($item['uid_owner'])); + $query = \OC_DB::prepare("SELECT id, permissions FROM *PREFIX*share WHERE item_type = ? AND item_target = ? AND share_type IN (?,?,?) AND share_with IN ('".implode("','", $userAndGroups)."') AND uid_owner != ? AND id != ?"); + $duplicateParent = $query->execute(array($item['item_type'], $item['item_target'], self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique, $item['uid_owner'], $item['parent']))->fetchRow(); + if ($duplicateParent) { + // Change the parent to the other item id if share permission is granted + if ($duplicateParent['permissions'] & self::PERMISSION_SHARE) { + $query = \OC_DB::prepare('UPDATE *PREFIX*share SET parent = ? WHERE id = ?'); + $query->execute(array($duplicateParent['id'], $item['id'])); + continue; + } + } + $ids[] = $item['id']; + $parents[] = $item['id']; + } + } + if ($excludeParent) { + unset($ids[0]); + } + if (!empty($ids)) { + $ids = "'".implode("','", $ids)."'"; + $query = \OC_DB::prepare('DELETE FROM *PREFIX*share WHERE id IN ('.$ids.')'); + $query->execute(); + } + } + + /** + * Hook Listeners + */ + + public static function post_deleteUser($arguments) { + // Delete any items shared with the deleted user + $query = \OC_DB::prepare('DELETE FROM *PREFIX*share WHERE share_with = ? AND share_type = ? OR share_type = ?'); + $result = $query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique)); + // Delete any items the deleted user shared + $query = \OC_DB::prepare('SELECT id FROM *PREFIX*share WHERE uid_owner = ?'); + $result = $query->execute(array($arguments['uid'])); + while ($item = $result->fetchRow()) { + self::delete($item['id']); + } + } + + public static function post_addToGroup($arguments) { + // TODO + } + + public static function post_removeFromGroup($arguments) { + // TODO Don't call if user deleted? + $query = \OC_DB::prepare('SELECT id, share_type FROM *PREFIX*share WHERE (share_type = ? AND share_with = ?) OR (share_type = ? AND share_with = ?)'); + $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'], self::$shareTypeGroupUserUnique, $arguments['uid'])); + while ($item = $result->fetchRow()) { + if ($item['share_type'] == self::SHARE_TYPE_GROUP) { + // Delete all reshares by this user of the group share + self::delete($item['id'], true, $arguments['uid']); + } else { + self::delete($item['id']); + } + } + } + +} + +/** +* Interface that apps must implement to share content. +*/ +interface Share_Backend { + + /** + * @brief Get the source of the item to be stored in the database + * @param string Item source + * @param string Owner of the item + * @return mixed|array|false Source + * + * Return an array if the item is file dependent, the array needs two keys: 'item' and 'file' + * Return false if the item does not exist for the user + * + * The formatItems() function will translate the source returned back into the item + */ + public function isValidSource($itemSource, $uidOwner); + + /** + * @brief Get a unique name of the item for the specified user + * @param string Item source + * @param string|false User the item is being shared with + * @param array|null List of similar item names already existing as shared items + * @return string Target name + * + * This function needs to verify that the user does not already have an item with this name. + * If it does generate a new name e.g. name_# + */ + public function generateTarget($itemSource, $shareWith, $exclude = null); + + /** + * @brief Converts the shared item sources back into the item in the specified format + * @param array Shared items + * @param int Format + * @return ? + * + * The items array is a 3-dimensional array with the item_source as the first key and the share id as the second key to an array with the share info. + * The key/value pairs included in the share info depend on the function originally called: + * If called by getItem(s)Shared: id, item_type, item, item_source, share_type, share_with, permissions, stime, file_source + * If called by getItem(s)SharedWith: id, item_type, item, item_source, item_target, share_type, share_with, permissions, stime, file_source, file_target + * This function allows the backend to control the output of shared items with custom formats. + * It is only called through calls to the public getItem(s)Shared(With) functions. + */ + public function formatItems($items, $format, $parameters = null); + +} + +/** +* Interface for share backends that share content that is dependent on files. +* Extends the Share_Backend interface. +*/ +interface Share_Backend_File_Dependent extends Share_Backend { + + /** + * @brief Get the file path of the item + * @param + * @param + * @return + */ + public function getFilePath($itemSource, $uidOwner); + +} + +/** +* Interface for collections of of items implemented by another share backend. +* Extends the Share_Backend interface. +*/ +interface Share_Backend_Collection extends Share_Backend { + + /** + * @brief Get the sources of the children of the item + * @param string Item source + * @return array Returns an array of sources + */ + public function getChildren($itemSource); + +} + +?> diff --git a/lib/public/template.php b/lib/public/template.php index 33eefea3b80..a0ed618cb2c 100644 --- a/lib/public/template.php +++ b/lib/public/template.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -104,6 +104,3 @@ function html_select_options($options, $selected, $params=array()) { class Template extends \OC_Template { } - - -?> diff --git a/lib/public/user.php b/lib/public/user.php index d351b001e8f..2fa599488a7 100644 --- a/lib/public/user.php +++ b/lib/public/user.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -51,7 +51,7 @@ class User { * * Get a list of all users. */ - public static function getUsers(){ + public static function getUsers($search = '', $limit = -1, $offset = 0) { return \OC_USER::getUsers(); } @@ -120,6 +120,3 @@ class User { } - - -?> diff --git a/lib/public/userinterface.php b/lib/public/userinterface.php new file mode 100644 index 00000000000..b73a8f8d8b0 --- /dev/null +++ b/lib/public/userinterface.php @@ -0,0 +1,31 @@ +<?php +/** +* ownCloud +* +* @author Arthur Schiwon +* @copyright 2012 Arthur Schiwon blizzz@owncloud.org +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * Public interface of ownCloud for apps to use. + * User Class. + * + */ + +namespace OCP; + +interface UserInterface extends \OC_User_Interface {}
\ No newline at end of file diff --git a/lib/public/util.php b/lib/public/util.php index 08394da575e..9f6f6f32e1e 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -26,7 +26,7 @@ * */ -// use OCP namespace for all classes that are considered public. +// use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; @@ -54,7 +54,7 @@ class Util { /** - * @brief send an email + * @brief send an email * @param string $toaddress * @param string $toname * @param string $subject @@ -145,15 +145,15 @@ class Util { } - /** - * @brief Creates an url - * @param $app app - * @param $file file - * @returns the url - * - * Returns a url to the given app and file. - */ - public static function linkTo( $app, $file ){ + /** + * @brief Creates an url + * @param $app app + * @param $file file + * @returns the url + * + * Returns a url to the given app and file. + */ + public static function linkTo( $app, $file ){ return(\OC_Helper::linkTo( $app, $file )); } @@ -165,21 +165,19 @@ class Util { * reverse proxies */ public static function getServerHost() { - return(\OC_Helper::serverHost()); + return(\OC_Request::serverHost()); } - /** * @brief Returns the server protocol * @returns the server protocol * * Returns the server protocol. It respects reverse proxy servers and load balancers */ - public static function getServerProtocol() { - return(\OC_Helper::serverProtocol()); + public static function getServerProtocol() { + return(\OC_Request::serverProtocol()); } - /** * @brief Creates path to an image * @param $app app @@ -249,6 +247,7 @@ class Util { return(\OC_Hook::emit( $signalclass, $signalname, $params )); } + /** * Register an get/post call. This is important to prevent CSRF attacks * TODO: write example @@ -266,17 +265,70 @@ class Util { return(\OC_Util::callCheck()); } - /**
- * @brief Used to sanitize HTML
- *
- * This function is used to sanitize HTML and should be applied on any string or array of strings before displaying it on a web page.
- *
- * @param string or array of strings
- * @return array with sanitized strings or a single sinitized string, depends on the input parameter.
- */
- public static function sanitizeHTML( $value ){
- return(\OC_Util::sanitizeHTML($value));
+ /** + * @brief Used to sanitize HTML + * + * This function is used to sanitize HTML and should be applied on any string or array of strings before displaying it on a web page. + * + * @param string or array of strings + * @return array with sanitized strings or a single sinitized string, depends on the input parameter. + */ + public static function sanitizeHTML( $value ){ + return(\OC_Util::sanitizeHTML($value)); } -} -?> + /** + * @brief Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. + * + * @param $input The array to work on + * @param $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) + * @param $encoding The encoding parameter is the character encoding. Defaults to UTF-8 + * @return array + * + * + */ + public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8'){ + return(\OC_Helper::mb_array_change_key_case($input, $case, $encoding)); + } + + /** + * @brief replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. + * + * @param $input The input string. .Opposite to the PHP build-in function does not accept an array. + * @param $replacement The replacement string. + * @param $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string. + * @param $length Length of the part to be replaced + * @param $encoding The encoding parameter is the character encoding. Defaults to UTF-8 + * @return string + * + */ + public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { + return(\OC_Helper::mb_substr_replace($string, $replacement, $start, $length, $encoding)); + } + + /** + * @brief Replace all occurrences of the search string with the replacement string + * + * @param $search The value being searched for, otherwise known as the needle. String. + * @param $replace The replacement string. + * @param $subject The string or array being searched and replaced on, otherwise known as the haystack. + * @param $encoding The encoding parameter is the character encoding. Defaults to UTF-8 + * @param $count If passed, this will be set to the number of replacements performed. + * @return string + * + */ + public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { + return(\OC_Helper::mb_str_replace($search, $replace, $subject, $encoding, $count)); + } + + /** + * @brief performs a search in a nested array + * @param haystack the array to be searched + * @param needle the search string + * @param $index optional, only search this key name + * @return the key of the matching field, otherwise false + */ + public static function recursiveArraySearch($haystack, $needle, $index = null) { + return(\OC_Helper::recursiveArraySearch($haystack, $needle, $index)); + } +} diff --git a/lib/request.php b/lib/request.php new file mode 100644 index 00000000000..3fe61fbddcd --- /dev/null +++ b/lib/request.php @@ -0,0 +1,105 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Request { + /** + * @brief Returns the server host + * @returns the server host + * + * Returns the server host, even if the website uses one or more + * reverse proxies + */ + public static function serverHost() { + if(OC::$CLI){ + return 'localhost'; + } + if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { + if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) { + $host = trim(array_pop(explode(",", $_SERVER['HTTP_X_FORWARDED_HOST']))); + } + else{ + $host=$_SERVER['HTTP_X_FORWARDED_HOST']; + } + } + else{ + $host = $_SERVER['HTTP_HOST']; + } + return $host; + } + + + /** + * @brief Returns the server protocol + * @returns the server protocol + * + * Returns the server protocol. It respects reverse proxy servers and load balancers + */ + public static function serverProtocol() { + if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { + $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); + }else{ + if(isset($_SERVER['HTTPS']) and !empty($_SERVER['HTTPS']) and ($_SERVER['HTTPS']!='off')) { + $proto = 'https'; + }else{ + $proto = 'http'; + } + } + return($proto); + } + + /** + * @brief get Path info from request + * @returns string Path info or false when not found + */ + public static function getPathInfo() { + if (array_key_exists('PATH_INFO', $_SERVER)){ + $path_info = $_SERVER['PATH_INFO']; + }else{ + $path_info = substr($_SERVER['REQUEST_URI'], strlen($_SERVER['SCRIPT_NAME'])); + // following is taken from Sabre_DAV_URLUtil::decodePathSegment + $path_info = rawurldecode($path_info); + $encoding = mb_detect_encoding($path_info, array('UTF-8','ISO-8859-1')); + + switch($encoding) { + + case 'ISO-8859-1' : + $path_info = utf8_encode($path_info); + + } + // end copy + } + return $path_info; + } + + /** + * @brief Check if this is a no-cache request + * @returns true for no-cache + */ + static public function isNoCache() { + if (!isset($_SERVER['HTTP_CACHE_CONTROL'])) { + return false; + } + return $_SERVER['HTTP_CACHE_CONTROL'] == 'no-cache'; + } + + /** + * @brief Check if the requestor understands gzip + * @returns true for gzip encoding supported + */ + static public function acceptGZip() { + if (!isset($_SERVER['HTTP_ACCEPT_ENCODING'])) { + return false; + } + $HTTP_ACCEPT_ENCODING = $_SERVER["HTTP_ACCEPT_ENCODING"]; + if( strpos($HTTP_ACCEPT_ENCODING, 'x-gzip') !== false ) + return 'x-gzip'; + else if( strpos($HTTP_ACCEPT_ENCODING,'gzip') !== false ) + return 'gzip'; + return false; + } +} diff --git a/lib/search.php b/lib/search.php index 12055418687..f8a4b8e96eb 100644 --- a/lib/search.php +++ b/lib/search.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/lib/search/provider.php b/lib/search/provider.php index 838ab696d04..b3ee79b4770 100644 --- a/lib/search/provider.php +++ b/lib/search/provider.php @@ -2,13 +2,17 @@ /** * provides search functionalty */ -class OC_Search_Provider { - public function __construct($options){} +abstract class OC_Search_Provider { + private $options; + + public function __construct($options){ + $this->options=$options; + } /** * search for $query * @param string $query * @return array An array of OC_Search_Result's */ - public function search($query){} + abstract public function search($query); } diff --git a/lib/setup.php b/lib/setup.php index 61210560aba..f7e8c6950ce 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -107,7 +107,6 @@ class OC_Setup { } else { $oldUser=OC_Config::getValue('dbuser', false); - $oldPassword=OC_Config::getValue('dbpassword', false); $query="SELECT user FROM mysql.user WHERE user='$dbuser'"; //this should be enough to check for admin rights in mysql if(mysql_query($query, $connection)) { @@ -160,8 +159,11 @@ class OC_Setup { OC_CONFIG::setValue('dbhost', $dbhost); OC_CONFIG::setValue('dbtableprefix', $dbtableprefix); + $e_host = addslashes($dbhost); + $e_user = addslashes($dbuser); + $e_password = addslashes($dbpass); //check if the database user has admin right - $connection_string = "host=$dbhost dbname=postgres user=$dbuser password=$dbpass"; + $connection_string = "host='$e_host' dbname=postgres user='$e_user' password='$e_password'"; $connection = @pg_connect($connection_string); if(!$connection) { $error[] = array( @@ -171,8 +173,9 @@ class OC_Setup { return $error; } else { + $e_user = pg_escape_string($dbuser); //check for roles creation rights in postgresql - $query="SELECT 1 FROM pg_roles WHERE rolcreaterole=TRUE AND rolname='$dbuser'"; + $query="SELECT 1 FROM pg_roles WHERE rolcreaterole=TRUE AND rolname='$e_user'"; $result = pg_query($connection, $query); if($result and pg_num_rows($result) > 0) { //use the admin login data for the new database user @@ -204,7 +207,13 @@ class OC_Setup { // connect to the ownCloud database (dbname=$dbname) an check if it needs to be filled $dbuser = OC_CONFIG::getValue('dbuser'); $dbpass = OC_CONFIG::getValue('dbpassword'); - $connection_string = "host=$dbhost dbname=$dbname user=$dbuser password=$dbpass"; + + $e_host = addslashes($dbhost); + $e_dbname = addslashes($dbname); + $e_user = addslashes($dbuser); + $e_password = addslashes($dbpass); + + $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' password='$e_password'"; $connection = @pg_connect($connection_string); if(!$connection) { $error[] = array( @@ -363,7 +372,7 @@ class OC_Setup { OC_Installer::installShippedApps(); //create htaccess files for apache hosts - if (strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { + if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { self::createHtaccess(); } @@ -401,13 +410,23 @@ class OC_Setup { //we cant use OC_BD functions here because we need to connect as the administrative user. $e_name = pg_escape_string($name); $e_user = pg_escape_string($user); - $query = "CREATE DATABASE \"$e_name\" OWNER \"$e_user\""; + $query = "select datname from pg_database where datname = '$e_name'"; $result = pg_query($connection, $query); if(!$result) { $entry='DB Error: "'.pg_last_error($connection).'"<br />'; $entry.='Offending command was: '.$query.'<br />'; echo($entry); } + if(! pg_fetch_row($result)) { + //The database does not exists... let's create it + $query = "CREATE DATABASE \"$e_name\" OWNER \"$e_user\""; + $result = pg_query($connection, $query); + if(!$result) { + $entry='DB Error: "'.pg_last_error($connection).'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + } $query = "REVOKE ALL PRIVILEGES ON DATABASE \"$e_name\" FROM PUBLIC"; $result = pg_query($connection, $query); } @@ -415,13 +434,33 @@ class OC_Setup { private static function pg_createDBUser($name,$password,$connection) { $e_name = pg_escape_string($name); $e_password = pg_escape_string($password); - $query = "CREATE USER \"$e_name\" CREATEDB PASSWORD '$e_password';"; + $query = "select * from pg_roles where rolname='$e_name';"; $result = pg_query($connection, $query); if(!$result) { $entry='DB Error: "'.pg_last_error($connection).'"<br />'; $entry.='Offending command was: '.$query.'<br />'; echo($entry); } + + if(! pg_fetch_row($result)) { + //user does not exists let's create it :) + $query = "CREATE USER \"$e_name\" CREATEDB PASSWORD '$e_password';"; + $result = pg_query($connection, $query); + if(!$result) { + $entry='DB Error: "'.pg_last_error($connection).'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + } + else { // change password of the existing role + $query = "ALTER ROLE \"$e_name\" WITH PASSWORD '$e_password';"; + $result = pg_query($connection, $query); + if(!$result) { + $entry='DB Error: "'.pg_last_error($connection).'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + } } /** * @@ -529,5 +568,3 @@ class OC_Setup { file_put_contents(OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data').'/index.html', ''); } } - -?> diff --git a/lib/streamwrappers.php b/lib/streamwrappers.php index f1e0fa0e1d9..f502c6170bd 100644 --- a/lib/streamwrappers.php +++ b/lib/streamwrappers.php @@ -1,6 +1,4 @@ <?php -global $FAKEDIRS; -$FAKEDIRS=array(); class OC_FakeDirStream{ public static $dirs=array(); @@ -8,8 +6,6 @@ class OC_FakeDirStream{ private $index; public function dir_opendir($path,$options){ - global $FAKEDIRS; - $url=parse_url($path); $this->name=substr($path,strlen('fakedir://')); $this->index=0; if(!isset(self::$dirs[$this->name])){ @@ -161,7 +157,6 @@ class OC_StaticStreamWrapper { public function stream_write($data) { if (!$this->writable) return 0; $size = strlen($data); - $len = strlen(self::$data[$this->path]); if ($this->stream_eof()) { self::$data[$this->path] .= $data; } else { diff --git a/lib/subadmin.php b/lib/subadmin.php new file mode 100644 index 00000000000..0806f27a6bd --- /dev/null +++ b/lib/subadmin.php @@ -0,0 +1,181 @@ +<?php +/** + * ownCloud + * + * @author Georg Ehrke + * @copyright 2012 Georg Ehrke + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ +OC_Hook::connect('OC_User', 'post_deleteUser', 'OC_SubAdmin', 'post_deleteUser'); +OC_Hook::connect('OC_User', 'post_deleteGroup', 'OC_SubAdmin', 'post_deleteGroup'); +/** + * This class provides all methods needed for managing groups. + * + * Hooks provided: + * post_createSubAdmin($gid) + * post_deleteSubAdmin($gid) + */ +class OC_SubAdmin{ + + /** + * @brief add a SubAdmin + * @param $uid uid of the SubAdmin + * @param $gid gid of the group + * @return boolean + */ + public static function createSubAdmin($uid, $gid){ + $stmt = OC_DB::prepare('INSERT INTO *PREFIX*group_admin (gid,uid) VALUES(?,?)'); + $result = $stmt->execute(array($gid, $uid)); + OC_Hook::emit( "OC_SubAdmin", "post_createSubAdmin", array( "gid" => $gid )); + return true; + } + + /** + * @brief delete a SubAdmin + * @param $uid uid of the SubAdmin + * @param $gid gid of the group + * @return boolean + */ + public static function deleteSubAdmin($uid, $gid){ + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*group_admin WHERE gid = ? AND uid = ?'); + $result = $stmt->execute(array($gid, $uid)); + OC_Hook::emit( "OC_SubAdmin", "post_deleteSubAdmin", array( "gid" => $gid )); + return true; + } + + /** + * @brief get groups of a SubAdmin + * @param $uid uid of the SubAdmin + * @return array + */ + public static function getSubAdminsGroups($uid){ + $stmt = OC_DB::prepare('SELECT gid FROM *PREFIX*group_admin WHERE uid = ?'); + $result = $stmt->execute(array($uid)); + $gids = array(); + while($row = $result->fetchRow()){ + $gids[] = $row['gid']; + } + return $gids; + } + + /** + * @brief get SubAdmins of a group + * @param $gid gid of the group + * @return array + */ + public static function getGroupsSubAdmins($gid){ + $stmt = OC_DB::prepare('SELECT uid FROM *PREFIX*group_admin WHERE gid = ?'); + $result = $stmt->execute(array($gid)); + $uids = array(); + while($row = $result->fetchRow()){ + $uids[] = $row['uid']; + } + return $uids; + } + + /** + * @brief get all SubAdmins + * @return array + */ + public static function getAllSubAdmins(){ + $stmt = OC_DB::prepare('SELECT * FROM *PREFIX*group_admin'); + $result = $stmt->execute(); + $subadmins = array(); + while($row = $result->fetchRow()){ + $subadmins[] = $row; + } + return $subadmins; + } + + /** + * @brief checks if a user is a SubAdmin of a group + * @param $uid uid of the subadmin + * @param $gid gid of the group + * @return bool + */ + public static function isSubAdminofGroup($uid, $gid){ + $stmt = OC_DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*group_admin where uid = ? AND gid = ?'); + $result = $stmt->execute(array($uid, $gid)); + $result = $result->fetchRow(); + if($result['count'] >= 1){ + return true; + } + return false; + } + + /** + * @brief checks if a user is a SubAdmin + * @param $uid uid of the subadmin + * @return bool + */ + public static function isSubAdmin($uid){ + $stmt = OC_DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*group_admin WHERE uid = ?'); + $result = $stmt->execute(array($uid)); + $result = $result->fetchRow(); + if($result['count'] > 0){ + return true; + } + return false; + } + + /** + * @brief checks if a user is a accessible by a subadmin + * @param $subadmin uid of the subadmin + * @param $user uid of the user + * @return bool + */ + public static function isUserAccessible($subadmin, $user){ + if(!self::isSubAdmin($subadmin)){ + return false; + } + $accessiblegroups = self::getSubAdminsGroups($subadmin); + foreach($accessiblegroups as $accessiblegroup){ + if(OC_Group::inGroup($user, $accessiblegroup)){ + return true; + } + } + return false; + } + + /* + * @brief alias for self::isSubAdminofGroup() + */ + public static function isGroupAccessible($subadmin, $group){ + return self::isSubAdminofGroup($subadmin, $group); + } + + /** + * @brief delete all SubAdmins by uid + * @param $parameters + * @return boolean + */ + public static function post_deleteUser($parameters){ + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*group_admin WHERE uid = ?'); + $result = $stmt->execute(array($parameters['uid'])); + return true; + } + + /** + * @brief delete all SubAdmins8 by gid + * @param $parameters + * @return boolean + */ + public static function post_deleteGroup($parameters){ + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*group_admin WHERE gid = ?'); + $result = $stmt->execute(array($parameters['gid'])); + return true; + } +} diff --git a/lib/template.php b/lib/template.php index dd5dbad4a99..a10ddcd5c33 100644 --- a/lib/template.php +++ b/lib/template.php @@ -4,7 +4,7 @@ * * @author Frank Karlitschek * @author Jakob Sack - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -82,7 +82,6 @@ function relative_modified_date($timestamp) { $diffhours = round($diffminutes/60); $diffdays = round($diffhours/24); $diffmonths = round($diffdays/31); - $diffyears = round($diffdays/365); if($timediff < 60) { return $l->t('seconds ago'); } else if($timediff < 120) { return $l->t('1 minute ago'); } @@ -159,17 +158,55 @@ class OC_Template{ $this->vars['requesttoken'] = OC_Util::callRegister(); } $this->l10n = OC_L10N::get($app); - header('X-Frame-Options: Sameorigin'); - header('X-XSS-Protection: 1; mode=block'); - header('X-Content-Type-Options: nosniff'); + header('X-Frame-Options: Sameorigin'); + header('X-XSS-Protection: 1; mode=block'); + header('X-Content-Type-Options: nosniff'); + $this->findTemplate($name); } /** + * autodetects the formfactor of the used device + * default -> the normal desktop browser interface + * mobile -> interface for smartphones + * tablet -> interface for tablets + * standalone -> the default interface but without header, footer and + * sidebar, just the application. Useful to use just a specific + * app on the desktop in a standalone window. + */ + public static function detectFormfactor(){ + // please add more useragent strings for other devices + if(isset($_SERVER['HTTP_USER_AGENT'])){ + if(stripos($_SERVER['HTTP_USER_AGENT'],'ipad')>0) { + $mode='tablet'; + }elseif(stripos($_SERVER['HTTP_USER_AGENT'],'iphone')>0){ + $mode='mobile'; + }elseif((stripos($_SERVER['HTTP_USER_AGENT'],'N9')>0) and (stripos($_SERVER['HTTP_USER_AGENT'],'nokia')>0)){ + $mode='mobile'; + }else{ + $mode='default'; + } + }else{ + $mode='default'; + } + return($mode); + } + + /** * @brief Returns the formfactor extension for current formfactor */ - protected function getFormFactorExtension() + static public function getFormFactorExtension() { + // if the formfactor is not yet autodetected do the + // autodetection now. For possible formfactors check the + // detectFormfactor documentation + if(!isset($_SESSION['formfactor'])){ + $_SESSION['formfactor'] = self::detectFormfactor(); + } + // allow manual override via GET parameter + if(isset($_GET['formfactor'])){ + $_SESSION['formfactor']=$_GET['formfactor']; + } $formfactor=$_SESSION['formfactor']; if($formfactor=='default') { $fext=''; @@ -198,16 +235,16 @@ class OC_Template{ $theme=OC_Config::getValue( "theme" ); // Read the detected formfactor and use the right file name. - $fext = $this->getFormFactorExtension(); + $fext = self::getFormFactorExtension(); $app = $this->application; // Check if it is a app template or not. if( $app != "" ){ // Check if the app is in the app folder or in the root - if( file_exists( OC::$APPSROOT."/apps/$app/templates/" )){ + if( file_exists(OC_App::getAppPath($app)."/templates/" )){ // Check if the template is overwritten by the selected theme if ($this->checkPathForTemplate(OC::$SERVERROOT."/themes/$theme/apps/$app/templates/", $name, $fext)) { - }elseif ($this->checkPathForTemplate(OC::$APPSROOT."/apps/$app/templates/", $name, $fext)) { + }elseif ($this->checkPathForTemplate(OC_App::getAppPath($app)."/templates/", $name, $fext)) { } }else{ // Check if the template is overwritten by the selected theme @@ -261,6 +298,7 @@ class OC_Template{ * @brief Assign variables * @param $key key * @param $value value + * @param $sanitizeHTML false, if data shouldn't get passed through htmlentities * @returns true * * This function assigns a variable. It can be accessed via $_[$key] in @@ -268,8 +306,8 @@ class OC_Template{ * * If the key existed before, it will be overwritten */ - - public function assign( $key, $value ){ + public function assign( $key, $value, $sanitizeHTML=true ){ + if($sanitizeHTML == true) $value=OC_Util::sanitizeHTML($value); $this->vars[$key] = $value; return true; } @@ -320,29 +358,6 @@ class OC_Template{ } } - /* - * @brief append the $file-url if exist at $root - * @param $type of collection to use when appending - * @param $root path to check - * @param $web base for path - * @param $file the filename - */ - public function appendIfExist($type, $root, $web, $file) { - if (is_file($root.'/'.$file)) { - $pathes = explode('/', $file); - if($type == 'cssfiles' && $root == OC::$APPSROOT && $pathes[0] == 'apps'){ - $app = $pathes[1]; - unset($pathes[0]); - unset($pathes[1]); - $path = implode('/', $pathes); - $this->append( $type, OC_Helper::linkTo($app, $path)); - }else{ - $this->append( $type, $web.'/'.$file); - } - return true; - } - return false; - } /** * @brief Proceeds the template * @returns content @@ -354,117 +369,18 @@ class OC_Template{ $data = $this->_fetch(); if( $this->renderas ){ - // Decide which page we show - if( $this->renderas == "user" ){ - $page = new OC_Template( "core", "layout.user" ); - $page->assign('searchurl',OC_Helper::linkTo( 'search', 'index.php' )); + $page = new OC_TemplateLayout($this->renderas); + if($this->renderas == 'user') { $page->assign('requesttoken', $this->vars['requesttoken']); - if(array_search(OC_APP::getCurrentApp(),array('settings','admin','help'))!==false){ - $page->assign('bodyid','body-settings'); - }else{ - $page->assign('bodyid','body-user'); - } - - // Add navigation entry - $navigation = OC_App::getNavigation(); - $page->assign( "navigation", $navigation); - $page->assign( "settingsnavigation", OC_App::getSettingsNavigation()); - foreach($navigation as $entry) { - if ($entry['active']) { - $page->assign( 'application', $entry['name'] ); - break; - } - } - }else{ - $page = new OC_Template( "core", "layout.guest" ); - } - - // Read the selected theme from the config file - $theme=OC_Config::getValue( "theme" ); - - // Read the detected formfactor and use the right file name. - $fext = $this->getFormFactorExtension(); - - // Add the core js files or the js files provided by the selected theme - foreach(OC_Util::$scripts as $script){ - // Is it in 3rd party? - if($page->appendIfExist('jsfiles', OC::$THIRDPARTYROOT, OC::$THIRDPARTYWEBROOT, $script.'.js')) { - - // Is it in apps and overwritten by the theme? - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$script$fext.js" )) { - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$script.js" )) { - - // Is it part of an app? - }elseif($page->appendIfExist('jsfiles', OC::$APPSROOT, OC::$APPSWEBROOT, "apps/$script$fext.js" )) { - }elseif($page->appendIfExist('jsfiles', OC::$APPSROOT, OC::$APPSWEBROOT, "apps/$script.js" )) { - - // Is it in the owncloud root but overwritten by the theme? - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$script$fext.js" )) { - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$script.js" )) { - - // Is it in the owncloud root ? - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "$script$fext.js" )) { - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "$script.js" )) { - - // Is in core but overwritten by a theme? - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$script$fext.js" )) { - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$script.js" )) { - - // Is it in core? - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "core/$script$fext.js" )) { - }elseif($page->appendIfExist('jsfiles', OC::$SERVERROOT, OC::$WEBROOT, "core/$script.js" )) { - - }else{ - echo('js file not found: script:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); - - } - } - // Add the css files - foreach(OC_Util::$styles as $style){ - // is it in 3rdparty? - if($page->appendIfExist('cssfiles', OC::$THIRDPARTYROOT, OC::$THIRDPARTYWEBROOT, $style.'.css')) { - - // or in apps? - }elseif($page->appendIfExist('cssfiles', OC::$APPSROOT, OC::$APPSWEBROOT, "apps/$style$fext.css" )) { - }elseif($page->appendIfExist('cssfiles', OC::$APPSROOT, OC::$APPSWEBROOT, "apps/$style.css" )) { - - // or in the owncloud root? - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "$style$fext.css" )) { - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "$style.css" )) { - - // or in core ? - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "core/$style$fext.css" )) { - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "core/$style.css" )) { - - }else{ - echo('css file not found: style:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); - die(); - } - } - // Add the theme css files. you can override the default values here - if(!empty($theme)) { - foreach(OC_Util::$styles as $style){ - if($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$style$fext.css" )) { - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$style.css" )) { - - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$style$fext.css" )) { - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$style.css" )) { - - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$style$fext.css" )) { - }elseif($page->appendIfExist('cssfiles', OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$style.css" )) { - } - } } // Add custom headers - $page->assign('headers',$this->headers); + $page->assign('headers',$this->headers, false); foreach(OC_Util::$headers as $header){ $page->append('headers',$header); } - // Add css files and js files - $page->assign( "content", $data ); + $page->assign( "content", $data, false ); return $page->fetchPage(); } else{ @@ -509,13 +425,13 @@ class OC_Template{ $_ = array_merge( $additionalparams, $this->vars ); } - // Einbinden + // Include ob_start(); include( $this->path.$file.'.php' ); $data = ob_get_contents(); @ob_end_clean(); - // Daten zurückgeben + // Return data return $data; } @@ -529,7 +445,7 @@ class OC_Template{ public static function printUserPage( $application, $name, $parameters = array() ){ $content = new OC_Template( $application, $name, "user" ); foreach( $parameters as $key => $value ){ - $content->assign( $key, $value ); + $content->assign( $key, $value, false ); } print $content->printPage(); } @@ -544,7 +460,7 @@ class OC_Template{ public static function printAdminPage( $application, $name, $parameters = array() ){ $content = new OC_Template( $application, $name, "admin" ); foreach( $parameters as $key => $value ){ - $content->assign( $key, $value ); + $content->assign( $key, $value, false ); } return $content->printPage(); } @@ -559,7 +475,7 @@ class OC_Template{ public static function printGuestPage( $application, $name, $parameters = array() ){ $content = new OC_Template( $application, $name, "guest" ); foreach( $parameters as $key => $value ){ - $content->assign( $key, $value ); + $content->assign( $key, $value, false ); } return $content->printPage(); } diff --git a/lib/templatelayout.php b/lib/templatelayout.php new file mode 100644 index 00000000000..588a7845997 --- /dev/null +++ b/lib/templatelayout.php @@ -0,0 +1,195 @@ +<?php +/** + * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_TemplateLayout extends OC_Template { + public function __construct( $renderas ){ + // Decide which page we show + + if( $renderas == 'user' ){ + parent::__construct( 'core', 'layout.user' ); + $this->assign('searchurl',OC_Helper::linkTo( 'search', 'index.php' ), false); + if(array_search(OC_APP::getCurrentApp(),array('settings','admin','help'))!==false){ + $this->assign('bodyid','body-settings', false); + }else{ + $this->assign('bodyid','body-user', false); + } + + // Add navigation entry + $navigation = OC_App::getNavigation(); + $this->assign( 'navigation', $navigation, false); + $this->assign( 'settingsnavigation', OC_App::getSettingsNavigation(), false); + foreach($navigation as $entry) { + if ($entry['active']) { + $this->assign( 'application', $entry['name'], false ); + break; + } + } + }else{ + parent::__construct( 'core', 'layout.guest' ); + } + + $apps_paths = array(); + foreach(OC_App::getEnabledApps() as $app){ + $apps_paths[$app] = OC_App::getAppWebPath($app); + } + $this->assign( 'apps_paths', str_replace('\\/', '/',json_encode($apps_paths)),false ); // Ugly unescape slashes waiting for better solution + + // Add the js files + $jsfiles = self::findJavascriptFiles(OC_Util::$scripts); + $this->assign('jsfiles', array(), false); + foreach($jsfiles as $info) { + $root = $info[0]; + $web = $info[1]; + $file = $info[2]; + $this->append( 'jsfiles', $web.'/'.$file); + } + + // Add the css files + $cssfiles = self::findStylesheetFiles(OC_Util::$styles); + + $this->assign('cssfiles', array()); + foreach($cssfiles as $info) { + $root = $info[0]; + $web = $info[1]; + $file = $info[2]; + $paths = explode('/', $file); + + $in_root = false; + foreach(OC::$APPSROOTS as $app_root) { + if($root == $app_root['path']) { + $in_root = true; + break; + } + } + + if($in_root ) { + $app = $paths[0]; + unset($paths[0]); + $path = implode('/', $paths); + $this->append( 'cssfiles', OC_Helper::linkTo($app, $path)); + } + else { + $this->append( 'cssfiles', $web.'/'.$file); + } + } + } + + /* + * @brief append the $file-url if exist at $root + * @param $files array to append file info to + * @param $root path to check + * @param $web base for path + * @param $file the filename + */ + static public function appendIfExist(&$files, $root, $webroot, $file) { + if (is_file($root.'/'.$file)) { + $files[] = array($root, $webroot, $file); + return true; + } + return false; + } + + static public function findStylesheetFiles($styles){ + // Read the selected theme from the config file + $theme=OC_Config::getValue( 'theme' ); + + // Read the detected formfactor and use the right file name. + $fext = self::getFormFactorExtension(); + + $files = array(); + foreach($styles as $style){ + // is it in 3rdparty? + if(self::appendIfExist($files, OC::$THIRDPARTYROOT, OC::$THIRDPARTYWEBROOT, $style.'.css')) { + + // or in the owncloud root? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$style$fext.css" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$style.css" )) { + + // or in core ? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$style$fext.css" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$style.css" )) { + + }else{ + $append = false; + // or in apps? + foreach( OC::$APPSROOTS as $apps_dir) + { + if(self::appendIfExist($files, $apps_dir['path'], $apps_dir['url'], "$style$fext.css")) { $append =true; break; } + elseif(self::appendIfExist($files, $apps_dir['path'], $apps_dir['url'], "$style.css")) { $append =true; break; } + } + if(! $append) { + echo('css file not found: style:'.$style.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); + die(); + } + } + } + // Add the theme css files. you can override the default values here + if(!empty($theme)) { + foreach($styles as $style){ + if(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$style$fext.css" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$style.css" )) { + + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$style$fext.css" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$style.css" )) { + + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$style$fext.css" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$style.css" )) { + } + } + } + return $files; + } + + static public function findJavascriptFiles($scripts){ + // Read the selected theme from the config file + $theme=OC_Config::getValue( 'theme' ); + + // Read the detected formfactor and use the right file name. + $fext = self::getFormFactorExtension(); + + $files = array(); + foreach($scripts as $script){ + // Is it in 3rd party? + if(self::appendIfExist($files, OC::$THIRDPARTYROOT, OC::$THIRDPARTYWEBROOT, $script.'.js')) { + + // Is it in apps and overwritten by the theme? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$script$fext.js" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/apps/$script.js" )) { + + // Is it in the owncloud root but overwritten by the theme? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$script$fext.js" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/$script.js" )) { + + // Is it in the owncloud root ? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$script$fext.js" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "$script.js" )) { + + // Is in core but overwritten by a theme? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$script$fext.js" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "themes/$theme/core/$script.js" )) { + + // Is it in core? + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$script$fext.js" )) { + }elseif(self::appendIfExist($files, OC::$SERVERROOT, OC::$WEBROOT, "core/$script.js" )) { + + }else{ + // Is it part of an app? + $append = false; + foreach( OC::$APPSROOTS as $apps_dir) { + if(self::appendIfExist($files, $apps_dir['path'], OC::$WEBROOT.$apps_dir['url'], "$script$fext.js")) { $append =true; break; } + elseif(self::appendIfExist($files, $apps_dir['path'], OC::$WEBROOT.$apps_dir['url'], "$script.js")) { $append =true; break; } + } + if(! $append) { + echo('js file not found: script:'.$script.' formfactor:'.$fext.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT); + die(); + } + } + } + return $files; + } +} diff --git a/lib/updater.php b/lib/updater.php index 07904ef5f13..967f64c0b30 100644 --- a/lib/updater.php +++ b/lib/updater.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -85,4 +85,3 @@ class OC_Updater{ } } -?> diff --git a/lib/user.php b/lib/user.php index 9e0e82e79fe..06a56b7f4a6 100644 --- a/lib/user.php +++ b/lib/user.php @@ -3,7 +3,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,7 +21,9 @@ */ /** - * This class provides all methods for user management. + * This class provides wrapper methods for user management. Multiple backends are + * supported. User management operations are delegated to the configured backend for + * execution. * * Hooks provided: * pre_createUser(&run, uid, password) @@ -48,8 +50,8 @@ class OC_User { * * Makes a list of backends that can be used by other modules */ - public static function registerBackend( $name ){ - self::$_backends[] = $name; + public static function registerBackend( $backend ){ + self::$_backends[] = $backend; return true; } @@ -81,28 +83,38 @@ class OC_User { * Set the User Authentication Module */ public static function useBackend( $backend = 'database' ){ - // You'll never know what happens - if( null === $backend OR !is_string( $backend )){ - $backend = 'database'; - } + if($backend instanceof OC_User_Interface){ + self::$_usedBackends[get_class($backend)]=$backend; + }else{ + // You'll never know what happens + if( null === $backend OR !is_string( $backend )){ + $backend = 'database'; + } - // Load backend - switch( $backend ){ - case 'database': - case 'mysql': - case 'sqlite': - self::$_usedBackends[$backend] = new OC_User_Database(); - break; - default: - $className = 'OC_USER_' . strToUpper($backend); - self::$_usedBackends[$backend] = new $className(); - break; + // Load backend + switch( $backend ){ + case 'database': + case 'mysql': + case 'sqlite': + self::$_usedBackends[$backend] = new OC_User_Database(); + break; + default: + $className = 'OC_USER_' . strToUpper($backend); + self::$_usedBackends[$backend] = new $className(); + break; + } } - true; } /** + * remove all used backends + */ + public static function clearBackends(){ + self::$_usedBackends=array(); + } + + /** * @brief Create a new user * @param $uid The username of the user to create * @param $password The password of the new user @@ -166,9 +178,7 @@ class OC_User { if( $run ){ //delete the user from all backends foreach(self::$_usedBackends as $backend){ - if($backend->implementsActions(OC_USER_BACKEND_DELETE_USER)){ - $backend->deleteUser($uid); - } + $backend->deleteUser($uid); } // We have to delete the user from all groups foreach( OC_Group::getUserGroups( $uid ) as $i ){ @@ -198,8 +208,9 @@ class OC_User { OC_Hook::emit( "OC_User", "pre_login", array( "run" => &$run, "uid" => $uid )); if( $run ){ - $uid=self::checkPassword( $uid, $password ); - if($uid){ + $uid = self::checkPassword( $uid, $password ); + $enabled = self::isEnabled($uid); + if($uid && $enabled){ session_regenerate_id(true); self::setUserId($uid); OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password )); @@ -240,17 +251,13 @@ class OC_User { * Checks if the user is logged in */ public static function isLoggedIn(){ - static $is_login_checked = null; - if (!is_null($is_login_checked)) { - return $is_login_checked; - } if( isset($_SESSION['user_id']) AND $_SESSION['user_id']) { OC_App::loadApps(array('authentication')); if (self::userExists($_SESSION['user_id']) ){ - return $is_login_checked = true; + return true; } } - return $is_login_checked = false; + return false; } /** @@ -331,14 +338,12 @@ class OC_User { * * Get a list of all users. */ - public static function getUsers(){ - $users=array(); - foreach(self::$_usedBackends as $backend){ - if($backend->implementsActions(OC_USER_BACKEND_GET_USERS)){ - $backendUsers=$backend->getUsers(); - if(is_array($backendUsers)){ - $users=array_merge($users,$backendUsers); - } + public static function getUsers($search = '', $limit = null, $offset = null) { + $users = array(); + foreach (self::$_usedBackends as $backend) { + $backendUsers = $backend->getUsers($search, $limit, $offset); + if (is_array($backendUsers)) { + $users = array_merge($users, $backendUsers); } } asort($users); @@ -352,15 +357,45 @@ class OC_User { */ public static function userExists($uid){ foreach(self::$_usedBackends as $backend){ - if($backend->implementsActions(OC_USER_BACKEND_USER_EXISTS)){ - $result=$backend->userExists($uid); - if($result===true){ - return true; - } + $result=$backend->userExists($uid); + if($result===true){ + return true; } } return false; } + + /** + * disables a user + * @param string $userid the user to disable + */ + public static function disableUser($userid){ + $query = "INSERT INTO `*PREFIX*preferences` (`userid`, `appid`, `configkey`, `configvalue`) VALUES(?, ?, ?, ?)"; + $query = OC_DB::prepare($query); + $query->execute(array($userid, 'core', 'enabled', 'false')); + } + + /** + * enable a user + * @param string $userid + */ + public static function enableUser($userid){ + $query = "DELETE FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? AND `configvalue` = ?"; + $query = OC_DB::prepare($query); + $query->execute(array($userid, 'core', 'enabled', 'false')); + } + + /** + * checks if a user is enabled + * @param string $userid + * @return bool + */ + public static function isEnabled($userid){ + $query = "SELECT `userid` FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ? AND `configkey` = ? AND `configvalue` = ?"; + $query = OC_DB::prepare($query); + $results = $query->execute(array($userid, 'core', 'enabled', 'false')); + return $results->numRows() ? false : true; + } /** * @brief Set cookie value to use in next page load diff --git a/lib/user/backend.php b/lib/user/backend.php index 4afdf152150..f67908cdac0 100644 --- a/lib/user/backend.php +++ b/lib/user/backend.php @@ -5,7 +5,7 @@ * * @author Frank Karlitschek * @author Dominik Schmidt - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * @copyright 2011 Dominik Schmidt dev@dominik-schmidt.de * * This library is free software; you can redistribute it and/or @@ -32,26 +32,22 @@ define('OC_USER_BACKEND_NOT_IMPLEMENTED', -501); * actions that user backends can define */ define('OC_USER_BACKEND_CREATE_USER', 0x000001); -define('OC_USER_BACKEND_DELETE_USER', 0x000010); -define('OC_USER_BACKEND_SET_PASSWORD', 0x000100); -define('OC_USER_BACKEND_CHECK_PASSWORD', 0x001000); -define('OC_USER_BACKEND_GET_USERS', 0x010000); -define('OC_USER_BACKEND_USER_EXISTS', 0x100000); +define('OC_USER_BACKEND_SET_PASSWORD', 0x000010); +define('OC_USER_BACKEND_CHECK_PASSWORD', 0x000100); /** - * abstract base class for user management - * subclass this for your own backends and see OC_User_Example for descriptions + * Abstract base class for user management. Provides methods for querying backend + * capabilities. + * + * Subclass this for your own backends, and see OC_User_Example for descriptions */ -abstract class OC_User_Backend { +abstract class OC_User_Backend implements OC_User_Interface { protected $possibleActions = array( OC_USER_BACKEND_CREATE_USER => 'createUser', - OC_USER_BACKEND_DELETE_USER => 'deleteUser', OC_USER_BACKEND_SET_PASSWORD => 'setPassword', OC_USER_BACKEND_CHECK_PASSWORD => 'checkPassword', - OC_USER_BACKEND_GET_USERS => 'getUsers', - OC_USER_BACKEND_USER_EXISTS => 'userExists' ); /** @@ -83,4 +79,34 @@ abstract class OC_User_Backend { public function implementsActions($actions){ return (bool)($this->getSupportedActions() & $actions); } + + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser( $uid ){ + return false; + } + + /** + * @brief Get a list of all users + * @returns array with all uids + * + * Get a list of all users. + */ + public function getUsers($search = '', $limit = -1, $offset = 0) { + return array(); + } + + /** + * @brief check if a user exists + * @param string $uid the username + * @return boolean + */ + public function userExists($uid){ + return false; + } } diff --git a/lib/user/database.php b/lib/user/database.php index 5464a4abfac..1746c4fe0c0 100644 --- a/lib/user/database.php +++ b/lib/user/database.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -39,7 +39,6 @@ require_once 'phpass/PasswordHash.php'; * Class for user management in a SQL Database (e.g. MySQL, SQLite) */ class OC_User_Database extends OC_User_Backend { - static private $userGroupCache=array(); /** * @var PasswordHash */ @@ -70,7 +69,7 @@ class OC_User_Database extends OC_User_Backend { }else{ $hasher=$this->getHasher(); $hash = $hasher->HashPassword($password.OC_Config::getValue('passwordsalt', '')); - $query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" ); + $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )' ); $result = $query->execute( array( $uid, $hash)); return $result ? true : false; @@ -86,8 +85,8 @@ class OC_User_Database extends OC_User_Backend { */ public function deleteUser( $uid ){ // Delete user-group-relation - $query = OC_DB::prepare( "DELETE FROM `*PREFIX*users` WHERE `uid` = ?" ); - $result = $query->execute( array( $uid )); + $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*users` WHERE uid = ?' ); + $query->execute( array( $uid )); return true; } @@ -103,12 +102,11 @@ class OC_User_Database extends OC_User_Backend { if( $this->userExists($uid) ){ $hasher=$this->getHasher(); $hash = $hasher->HashPassword($password.OC_Config::getValue('passwordsalt', '')); - $query = OC_DB::prepare( "UPDATE `*PREFIX*users` SET `password` = ? WHERE `uid` = ?" ); - $result = $query->execute( array( $hash, $uid )); + $query = OC_DB::prepare( 'UPDATE `*PREFIX*users` SET `password` = ? WHERE `uid` = ?' ); + $query->execute( array( $hash, $uid )); return true; - } - else{ + }else{ return false; } } @@ -123,7 +121,7 @@ class OC_User_Database extends OC_User_Backend { * returns the user id or false */ public function checkPassword( $uid, $password ){ - $query = OC_DB::prepare( "SELECT `uid`, `password` FROM `*PREFIX*users` WHERE `uid` = ?" ); + $query = OC_DB::prepare( 'SELECT `uid`, `password` FROM `*PREFIX*users` WHERE `uid` = ?' ); $result = $query->execute( array( $uid)); $row=$result->fetchRow(); @@ -156,13 +154,12 @@ class OC_User_Database extends OC_User_Backend { * * Get a list of all users. */ - public function getUsers(){ - $query = OC_DB::prepare( "SELECT `uid` FROM `*PREFIX*users`" ); - $result = $query->execute(); - - $users=array(); - while( $row = $result->fetchRow()){ - $users[] = $row["uid"]; + public function getUsers($search = '', $limit = null, $offset = null) { + $query = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users` WHERE `uid` LIKE ? LIMIT',$limit,$offset); + $result = $query->execute(array($search.'%')); + $users = array(); + while ($row = $result->fetchRow()) { + $users[] = $row['uid']; } return $users; } @@ -173,7 +170,7 @@ class OC_User_Database extends OC_User_Backend { * @return boolean */ public function userExists($uid){ - $query = OC_DB::prepare( "SELECT * FROM `*PREFIX*users` WHERE `uid` = ?" ); + $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*users` WHERE `uid` = ?' ); $result = $query->execute( array( $uid )); return $result->numRows() > 0; diff --git a/lib/user/dummy.php b/lib/user/dummy.php index 03d5c3256da..da3edfb2df4 100644 --- a/lib/user/dummy.php +++ b/lib/user/dummy.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek -* @copyright 2010 Frank Karlitschek karlitschek@kde.org +* @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -100,7 +100,7 @@ class OC_User_Dummy extends OC_User_Backend { * * Get a list of all users. */ - public function getUsers(){ + public function getUsers($search = '', $limit = -1, $offset = 0) { return array_keys($this->users); } diff --git a/lib/user/example.php b/lib/user/example.php index b6a2091b685..77246d8136c 100644 --- a/lib/user/example.php +++ b/lib/user/example.php @@ -4,7 +4,7 @@ * ownCloud * * @author Frank Karlitschek - * @copyright 2010 Frank Karlitschek karlitschek@kde.org + * @copyright 2012 Frank Karlitschek frank@owncloud.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -35,20 +35,7 @@ abstract class OC_User_Example extends OC_User_Backend { * Creates a new user. Basic checking of username is done in OC_User * itself, not in its subclasses. */ - public function createUser($uid, $password){ - return OC_USER_BACKEND_NOT_IMPLEMENTED; - } - - /** - * @brief delete a user - * @param $uid The username of the user to delete - * @returns true/false - * - * Deletes a user - */ - public function deleteUser( $uid ){ - return OC_USER_BACKEND_NOT_IMPLEMENTED; - } + abstract public function createUser($uid, $password); /** * @brief Set password @@ -58,9 +45,7 @@ abstract class OC_User_Example extends OC_User_Backend { * * Change the password of a user */ - public function setPassword($uid, $password){ - return OC_USER_BACKEND_NOT_IMPLEMENTED; - } + abstract public function setPassword($uid, $password); /** * @brief Check if the password is correct @@ -71,26 +56,5 @@ abstract class OC_User_Example extends OC_User_Backend { * Check if the password is correct without logging in the user * returns the user id or false */ - public function checkPassword($uid, $password){ - return OC_USER_BACKEND_NOT_IMPLEMENTED; - } - - /** - * @brief Get a list of all users - * @returns array with all uids - * - * Get a list of all users. - */ - public function getUsers(){ - return OC_USER_BACKEND_NOT_IMPLEMENTED; - } - - /** - * @brief check if a user exists - * @param string $uid the username - * @return boolean - */ - public function userExists($uid){ - return OC_USER_BACKEND_NOT_IMPLEMENTED; - } + abstract public function checkPassword($uid, $password); } diff --git a/lib/user/http.php b/lib/user/http.php new file mode 100644 index 00000000000..009aa30c6f5 --- /dev/null +++ b/lib/user/http.php @@ -0,0 +1,93 @@ +<?php + +/** +* ownCloud +* +* @author Frank Karlitschek +* @copyright 2012 Robin Appelman icewind@owncloud.com +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +/** + * user backend using http auth requests + */ +class OC_User_HTTP extends OC_User_Backend { + /** + * split http://user@host/path into a user and url part + * @param string path + * @return array + */ + private function parseUrl($url){ + $parts=parse_url($url); + $url=$parts['scheme'].'://'.$parts['host']; + if(isset($parts['port'])){ + $url.=':'.$parts['port']; + } + $url.=$parts['path']; + if(isset($parts['query'])){ + $url.='?'.$parts['query']; + } + return array($parts['user'],$url); + + } + + /** + * check if an url is a valid login + * @param string url + * @return boolean + */ + private function matchUrl($url){ + return ! is_null(parse_url($url,PHP_URL_USER)); + } + + /** + * @brief Check if the password is correct + * @param $uid The username + * @param $password The password + * @returns string + * + * Check if the password is correct without logging in the user + * returns the user id or false + */ + public function checkPassword($uid, $password){ + if(!$this->matchUrl($uid)){ + return false; + } + list($user,$url)=$this->parseUrl($uid); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_USERPWD, $user.':'.$password); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + curl_exec($ch); + + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + curl_close($ch); + + return $status==200; + } + + /** + * @brief check if a user exists + * @param string $uid the username + * @return boolean + */ + public function userExists($uid){ + return $this->matchUrl($uid); + } +}
\ No newline at end of file diff --git a/lib/user/interface.php b/lib/user/interface.php new file mode 100644 index 00000000000..a4903898fb1 --- /dev/null +++ b/lib/user/interface.php @@ -0,0 +1,60 @@ +<?php + +/** + * ownCloud - user interface + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +interface OC_User_Interface { + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions); + + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid); + + /** + * @brief Get a list of all users + * @returns array with all uids + * + * Get a list of all users. + */ + public function getUsers($search = '', $limit = -1, $offset = 0); + + /** + * @brief check if a user exists + * @param string $uid the username + * @return boolean + */ + public function userExists($uid); + +}
\ No newline at end of file diff --git a/lib/util.php b/lib/util.php index 010aa1f7f09..e63eb5b24c9 100644..100755 --- a/lib/util.php +++ b/lib/util.php @@ -10,42 +10,20 @@ class OC_Util { public static $headers=array(); private static $rootMounted=false; private static $fsSetup=false; + public static $core_styles=array(); + public static $core_scripts=array(); // Can be set up - public static function setupFS( $user = "", $root = "files" ){ // configure the initial filesystem based on the configuration - if(self::$fsSetup){ //setting up the filesystem twice can only lead to trouble + public static function setupFS( $user = '' ){// configure the initial filesystem based on the configuration + if(self::$fsSetup){//setting up the filesystem twice can only lead to trouble return false; } - $CONFIG_DATADIRECTORY_ROOT = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - $CONFIG_BACKUPDIRECTORY = OC_Config::getValue( "backupdirectory", OC::$SERVERROOT."/backup" ); - - // Check if config folder is writable. - if(!is_writable(OC::$SERVERROOT."/config/")) { - $tmpl = new OC_Template( '', 'error', 'guest' ); - $tmpl->assign('errors',array(1=>array('error'=>"Can't write into config directory 'config'",'hint'=>"You can usually fix this by giving the webserver user write access to the config directory in owncloud"))); - $tmpl->printPage(); - exit; - } - - // Check if apps folder is writable. - if(OC_Config::getValue('writable_appsdir', true) && !is_writable(OC::$SERVERROOT."/apps/")) { - $tmpl = new OC_Template( '', 'error', 'guest' ); - $tmpl->assign('errors',array(1=>array('error'=>"Can't write into apps directory 'apps'",'hint'=>"You can usually fix this by giving the webserver user write access to the config directory in owncloud"))); - $tmpl->printPage(); - exit; - } - - - // Create root dir. - if(!is_dir($CONFIG_DATADIRECTORY_ROOT)){ - $success=@mkdir($CONFIG_DATADIRECTORY_ROOT); - if(!$success) { - $tmpl = new OC_Template( '', 'error', 'guest' ); - $tmpl->assign('errors',array(1=>array('error'=>"Can't create data directory (".$CONFIG_DATADIRECTORY_ROOT.")",'hint'=>"You can usually fix this by giving the webserver write access to the ownCloud directory '".OC::$SERVERROOT."' (in a terminal, use the command 'chown -R www-data:www-data /path/to/your/owncloud/install/data' "))); - $tmpl->printPage(); - exit; - } + $CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); + //first set up the local "root" storage + if(!self::$rootMounted){ + OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY),'/'); + self::$rootMounted=true; } // If we are not forced to load a specific user we load the one that is logged in @@ -53,23 +31,28 @@ class OC_Util { $user = OC_User::getUser(); } - //first set up the local "root" storage - if(!self::$rootMounted){ - OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); - self::$rootMounted=true; - } if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem - - OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root"; - if( !is_dir( OC::$CONFIG_DATADIRECTORY )){ - mkdir( OC::$CONFIG_DATADIRECTORY, 0755, true ); + $user_dir = '/'.$user.'/files'; + $userdirectory = $CONFIG_DATADIRECTORY.$user_dir; + if( !is_dir( $userdirectory )){ + mkdir( $userdirectory, 0755, true ); } //jail the user into his "home" directory - OC_Filesystem::init('/'.$user.'/'.$root); + OC_Filesystem::init($user_dir); $quotaProxy=new OC_FileProxy_Quota(); OC_FileProxy::register($quotaProxy); self::$fsSetup=true; + // Load personal mount config + if (is_file($CONFIG_DATADIRECTORY.'/'.$user.'/mount.php')) { + $mountConfig = include($CONFIG_DATADIRECTORY.'/'.$user.'/mount.php'); + if (isset($mountConfig['user'][$user])) { + foreach ($mountConfig['user'][$user] as $mountPoint => $options) { + OC_Filesystem::mount($options['class'], $options['options'], $mountPoint); + } + } + } + OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $user_dir)); } } @@ -83,7 +66,7 @@ class OC_Util { * @return array */ public static function getVersion(){ - return array(4,00,7); + return array(4,82,4); } /** @@ -91,16 +74,16 @@ class OC_Util { * @return string */ public static function getVersionString(){ - return '4.0.7'; + return '5 pre alpha 1'; } - /** - * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise". - * @return string - */ - public static function getEditionString(){ - return ''; - } + /** + * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise". + * @return string + */ + public static function getEditionString(){ + return ''; + } /** * add a javascript file @@ -148,12 +131,12 @@ class OC_Util { self::$headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text); } - /** - * formats a timestamp in the "right" way - * - * @param int timestamp $timestamp - * @param bool dateOnly option to ommit time from the result - */ + /** + * formats a timestamp in the "right" way + * + * @param int timestamp $timestamp + * @param bool dateOnly option to ommit time from the result + */ public static function formatDate( $timestamp,$dateOnly=false){ if(isset($_SESSION['timezone'])){//adjust to clients timezone if we know it $systemTimeZone = intval(date('O')); @@ -200,34 +183,44 @@ class OC_Util { * @return array arrays with error messages and hints */ public static function checkServer(){ - $CONFIG_DATADIRECTORY_ROOT = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); - $CONFIG_BACKUPDIRECTORY = OC_Config::getValue( "backupdirectory", OC::$SERVERROOT."/backup" ); - $CONFIG_INSTALLED = OC_Config::getValue( "installed", false ); $errors=array(); //check for database drivers if(!(is_callable('sqlite_open') or class_exists('SQLite3')) and !is_callable('mysql_connect') and !is_callable('pg_connect')){ $errors[]=array('error'=>'No database drivers (sqlite, mysql, or postgresql) installed.<br/>','hint'=>'');//TODO: sane hint } - $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); - $CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" ); //common hint for all file permissons error messages $permissionsHint="Permissions can usually be fixed by giving the webserver write access to the ownCloud directory"; + // Check if config folder is writable. + if(!is_writable(OC::$SERVERROOT."/config/")) { + $errors[]=array('error'=>"Can't write into config directory 'config'",'hint'=>"You can usually fix this by giving the webserver user write access to the config directory in owncloud"); + } + + // Check if there is a writable install folder. + if(OC_Config::getValue('appstoreenabled', true)) { + if( OC_App::getInstallPath() === null || !is_writable(OC_App::getInstallPath())) { + $errors[]=array('error'=>"Can't write into apps directory",'hint'=>"You can usually fix this by giving the webserver user write access to the apps directory + in owncloud or disabling the appstore in the config file."); + } + } + + $CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); //check for correct file permissions if(!stristr(PHP_OS, 'WIN')){ $permissionsModHint="Please change the permissions to 0770 so that the directory cannot be listed by other users."; - $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY_ROOT)),-3); + $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY)),-3); if(substr($prems,-1)!='0'){ - OC_Helper::chmodr($CONFIG_DATADIRECTORY_ROOT,0770); + OC_Helper::chmodr($CONFIG_DATADIRECTORY,0770); clearstatcache(); - $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY_ROOT)),-3); + $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY)),-3); if(substr($prems,2,1)!='0'){ - $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY_ROOT.') is readable for other users<br/>','hint'=>$permissionsModHint); + $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY.') is readable for other users<br/>','hint'=>$permissionsModHint); } } if( OC_Config::getValue( "enablebackup", false )){ + $CONFIG_BACKUPDIRECTORY = OC_Config::getValue( "backupdirectory", OC::$SERVERROOT."/backup" ); $prems=substr(decoct(@fileperms($CONFIG_BACKUPDIRECTORY)),-3); if(substr($prems,-1)!='0'){ OC_Helper::chmodr($CONFIG_BACKUPDIRECTORY,0770); @@ -241,8 +234,14 @@ class OC_Util { }else{ //TODO: permissions checks for windows hosts } - if(is_dir($CONFIG_DATADIRECTORY_ROOT) and !is_writable($CONFIG_DATADIRECTORY_ROOT)){ - $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY_ROOT.') not writable by ownCloud<br/>','hint'=>$permissionsHint); + // Create root dir. + if(!is_dir($CONFIG_DATADIRECTORY)){ + $success=@mkdir($CONFIG_DATADIRECTORY); + if(!$success) { + $errors[]=array('error'=>"Can't create data directory (".$CONFIG_DATADIRECTORY.")",'hint'=>"You can usually fix this by giving the webserver write access to the ownCloud directory '".OC::$SERVERROOT."' (in a terminal, use the command 'chown -R www-data:www-data /path/to/your/owncloud/install/data' "); + } + } else if(!is_writable($CONFIG_DATADIRECTORY)){ + $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY.') not writable by ownCloud<br/>','hint'=>$permissionsHint); } // check if all required php modules are present @@ -272,15 +271,26 @@ class OC_Util { return $errors; } - public static function displayLoginPage($parameters = array()){ - if(isset($_COOKIE["username"])){ - $parameters["username"] = $_COOKIE["username"]; + public static function displayLoginPage($display_lostpassword) { + $parameters = array(); + $parameters['display_lostpassword'] = $display_lostpassword; + if (!empty($_POST['user'])) { + $parameters["username"] = + OC_Util::sanitizeHTML($_POST['user']).'"'; + $parameters['user_autofocus'] = false; } else { $parameters["username"] = ''; + $parameters['user_autofocus'] = true; } $sectoken=rand(1000000,9999999); $_SESSION['sectoken']=$sectoken; $parameters["sectoken"] = $sectoken; + if (isset($_REQUEST['redirect_url'])) { + $redirect_url = OC_Util::sanitizeHTML($_REQUEST['redirect_url']); + } else { + $redirect_url = $_SERVER['REQUEST_URI']; + } + $parameters['redirect_url'] = $redirect_url; OC_Template::printGuestPage("", "login", $parameters); } @@ -320,23 +330,60 @@ class OC_Util { } /** + * Check if the user is a subadmin, redirects to home if not + * @return array $groups where the current user is subadmin + */ + public static function checkSubAdminUser(){ + // Check if we are a user + self::checkLoggedIn(); + if(OC_Group::inGroup(OC_User::getUser(),'admin')){ + return true; + } + if(!OC_SubAdmin::isSubAdmin(OC_User::getUser())){ + header( 'Location: '.OC_Helper::linkToAbsolute( '', 'index.php' )); + exit(); + } + return true; + } + + /** * Redirect to the user default page */ public static function redirectToDefaultPage(){ - OC_Log::write('core','redirectToDefaultPage',OC_Log::DEBUG); if(isset($_REQUEST['redirect_url']) && (substr($_REQUEST['redirect_url'], 0, strlen(OC::$WEBROOT)) == OC::$WEBROOT || $_REQUEST['redirect_url'][0] == '/')) { - header( 'Location: '.$_REQUEST['redirect_url']); + $location = $_REQUEST['redirect_url']; } else if (isset(OC::$REQUESTEDAPP) && !empty(OC::$REQUESTEDAPP)) { - header( 'Location: '.OC::$WEBROOT.'/?app='.OC::$REQUESTEDAPP ); + $location = OC_Helper::linkToAbsolute( OC::$REQUESTEDAPP, 'index.php' ); } else { - header( 'Location: '.OC::$WEBROOT.'/'.OC_Appconfig::getValue('core', 'defaultpage', '?app=files')); + $defaultpage = OC_Appconfig::getValue('core', 'defaultpage'); + if ($defaultpage) { + $location = OC_Helper::makeURLAbsolute(OC::$WEBROOT.'/'.$defaultpage); + } + else { + $location = OC_Helper::linkToAbsolute( 'files', 'index.php' ); + } } + OC_Log::write('core', 'redirectToDefaultPage: '.$location, OC_Log::DEBUG); + header( 'Location: '.$location ); exit(); } /** + * get an id unqiue for this instance + * @return string + */ + public static function getInstanceId(){ + $id=OC_Config::getValue('instanceid',null); + if(is_null($id)){ + $id=uniqid(); + OC_Config::setValue('instanceid',$id); + } + return $id; + } + + /** * @brief Register an get/post call. This is important to prevent CSRF attacks * Todo: Write howto * @return $token Generated token. @@ -368,6 +415,7 @@ class OC_Util { return($token); } + /** * @brief Check an ajax get/post call if the request token is valid. * @return boolean False if request token is not set or is invalid. @@ -411,10 +459,11 @@ class OC_Util { /** * @brief Public function to sanitize HTML * - * This function is used to sanitize HTML and should be applied on any string or array of strings before displaying it on a web page. - * + * This function is used to sanitize HTML and should be applied on any + * string or array of strings before displaying it on a web page. + * * @param string or array of strings - * @return array with sanitized strings or a single sinitized string, depends on the input parameter. + * @return array with sanitized strings or a single sanitized string, depends on the input parameter. */ public static function sanitizeHTML( &$value ){ if (is_array($value) || is_object($value)) array_walk_recursive($value,'OC_Util::sanitizeHTML'); @@ -423,44 +472,35 @@ class OC_Util { } - - - - /** - * Check if the htaccess file is working buy creating a test file in the data directory and trying to access via http - */ - public static function ishtaccessworking() { - + /** + * Check if the htaccess file is working by creating a test file in the data directory and trying to access via http + */ + public static function ishtaccessworking() { // testdata $filename='/htaccesstest.txt'; $testcontent='testcontent'; // creating a test file - $testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename; - $fp = @fopen($testfile, 'w'); - @fwrite($fp, $testcontent); - @fclose($fp); - + $testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename; + $fp = @fopen($testfile, 'w'); + @fwrite($fp, $testcontent); + @fclose($fp); + // accessing the file via http - $url = OC_Helper::serverProtocol(). '://' . OC_Helper::serverHost() . OC::$WEBROOT.'/data'.$filename; - $fp = @fopen($url, 'r'); - $content=@fread($fp, 2048); - @fclose($fp); - + $url = OC_Helper::makeURLAbsolute(OC::$WEBROOT.'/data'.$filename); + $fp = @fopen($url, 'r'); + $content=@fread($fp, 2048); + @fclose($fp); + // cleanup @unlink($testfile); - + // does it work ? if($content==$testcontent) { return(false); }else{ return(true); } - - } - - - + } } - diff --git a/lib/vcategories.php b/lib/vcategories.php index 724965d0e95..20d9e3b5d61 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -55,7 +55,7 @@ class OC_VCategories { $this->app = $app; $this->user = is_null($user) ? OC_User::getUser() : $user; $categories = trim(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); - $this->categories = $categories != '' ? unserialize($categories) : $defcategories; + $this->categories = $categories != '' ? @unserialize($categories) : $defcategories; } /** @@ -64,6 +64,9 @@ class OC_VCategories { */ public function categories() { //OC_Log::write('core','OC_VCategories::categories: '.print_r($this->categories, true), OC_Log::DEBUG); + if(!$this->categories) { + return array(); + } usort($this->categories, 'strnatcasecmp'); // usort to also renumber the keys return $this->categories; } @@ -128,16 +131,17 @@ class OC_VCategories { * } * $categories->rescan($objects); */ - public function rescan($objects, $sync=true) { - $this->categories = array(); + public function rescan($objects, $sync=true, $reset=true) { + if($reset === true) { + $this->categories = array(); + } foreach($objects as $object) { //OC_Log::write('core','OC_VCategories::rescan: '.substr($object, 0, 100).'(...)', OC_Log::DEBUG); $vobject = OC_VObject::parse($object); if(!is_null($vobject)) { $this->loadFromVObject($vobject, $sync); - unset($vobject); } else { - OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.', '.substr($object, 0, 100).'(...)', OC_Log::DEBUG); + OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.', '.substr($object, 0, 100).'(...)', OC_Log::DEBUG); } } $this->save(); @@ -204,13 +208,18 @@ class OC_VCategories { // case-insensitive in_array private function in_arrayi($needle, $haystack) { + if(!is_array($haystack)) { + return false; + } return in_array(strtolower($needle), array_map('strtolower', $haystack)); } // case-insensitive array_search private function array_searchi($needle, $haystack) { + if(!is_array($haystack)) { + return false; + } return array_search(strtolower($needle),array_map('strtolower',$haystack)); } } -?> |