diff options
Diffstat (limited to 'lib')
131 files changed, 5423 insertions, 2790 deletions
diff --git a/lib/MDB2/Driver/Function/sqlite3.php b/lib/MDB2/Driver/Function/sqlite3.php index 235a106e183..4147a48199f 100644 --- a/lib/MDB2/Driver/Function/sqlite3.php +++ b/lib/MDB2/Driver/Function/sqlite3.php @@ -92,9 +92,9 @@ class MDB2_Driver_Function_sqlite3 extends MDB2_Driver_Function_Common function substring($value, $position = 1, $length = null) { if (!is_null($length)) { - return "substr($value,$position,$length)"; + return "substr($value, $position, $length)"; } - return "substr($value,$position,length($value))"; + return "substr($value, $position, length($value))"; } // }}} diff --git a/lib/MDB2/Driver/Reverse/sqlite3.php b/lib/MDB2/Driver/Reverse/sqlite3.php index 36626478ce8..97037809549 100644 --- a/lib/MDB2/Driver/Reverse/sqlite3.php +++ b/lib/MDB2/Driver/Reverse/sqlite3.php @@ -476,7 +476,7 @@ class MDB2_Driver_Reverse_sqlite3 extends MDB2_Driver_Reverse_Common $definition['unique'] = true; $count = count($column_names); for ($i=0; $i<$count; ++$i) { - $column_name = strtok($column_names[$i]," "); + $column_name = strtok($column_names[$i], " "); $collation = strtok(" "); $definition['fields'][$column_name] = array( 'position' => $i+1 diff --git a/lib/MDB2/Driver/sqlite3.php b/lib/MDB2/Driver/sqlite3.php index 9757e4faf94..fa4c91c1269 100644 --- a/lib/MDB2/Driver/sqlite3.php +++ b/lib/MDB2/Driver/sqlite3.php @@ -153,7 +153,7 @@ class MDB2_Driver_sqlite3 extends MDB2_Driver_Common if($this->connection) { return $this->connection->escapeString($text); }else{ - return str_replace("'","''",$text);//TODO; more + return str_replace("'", "''", $text);//TODO; more } } @@ -276,7 +276,7 @@ class MDB2_Driver_sqlite3 extends MDB2_Driver_Common * @access public * @since 2.1.1 */ - function setTransactionIsolation($isolation,$options=array()) + function setTransactionIsolation($isolation, $options=array()) { $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); switch ($isolation) { @@ -351,7 +351,7 @@ class MDB2_Driver_sqlite3 extends MDB2_Driver_Common } if ($database_file !== ':memory:') { - if(!strpos($database_file,'.db')) { + if(!strpos($database_file, '.db')) { $database_file="$datadir/$database_file.db"; } if (!file_exists($database_file)) { @@ -387,7 +387,7 @@ class MDB2_Driver_sqlite3 extends MDB2_Driver_Common $php_errormsg = ''; $this->connection = new SQLite3($database_file); - if(is_callable(array($this->connection,'busyTimeout'))) {//busy timout is only available in php>=5.3 + if(is_callable(array($this->connection, 'busyTimeout'))) {//busy timout is only available in php>=5.3 $this->connection->busyTimeout(100); } $this->_lasterror = $this->connection->lastErrorMsg(); @@ -397,8 +397,7 @@ class MDB2_Driver_sqlite3 extends MDB2_Driver_Common } if ($this->fix_assoc_fields_names || - $this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) - { + $this->options['portability'] & MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES) { $this->connection->exec("PRAGMA short_column_names = 1"); $this->fix_assoc_fields_names = true; } @@ -1142,9 +1141,9 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common function bindValue($parameter, $value, $type = null) { if($type) { $type=$this->getParamType($type); - $this->statement->bindValue($parameter,$value,$type); + $this->statement->bindValue($parameter, $value, $type); }else{ - $this->statement->bindValue($parameter,$value); + $this->statement->bindValue($parameter, $value); } return MDB2_OK; } @@ -1165,9 +1164,9 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common function bindParam($parameter, &$value, $type = null) { if($type) { $type=$this->getParamType($type); - $this->statement->bindParam($parameter,$value,$type); + $this->statement->bindParam($parameter, $value, $type); }else{ - $this->statement->bindParam($parameter,$value); + $this->statement->bindParam($parameter, $value); } return MDB2_OK; } @@ -1318,7 +1317,7 @@ class MDB2_Statement_sqlite3 extends MDB2_Statement_Common }else{ $types=null; } - $err = $this->bindValueArray($values,$types); + $err = $this->bindValueArray($values, $types); if (PEAR::isError($err)) { return $this->db->raiseError(MDB2_ERROR, null, null, 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); diff --git a/lib/app.php b/lib/app.php index 395230156f6..5d4fbbd9c23 100755 --- a/lib/app.php +++ b/lib/app.php @@ -63,7 +63,7 @@ class OC_App{ if (!defined('DEBUG') || !DEBUG) { if (is_null($types) - && empty(OC_Util::$core_scripts) + && empty(OC_Util::$core_scripts) && empty(OC_Util::$core_styles)) { OC_Util::$core_scripts = OC_Util::$scripts; OC_Util::$scripts = array(); @@ -92,7 +92,7 @@ class OC_App{ * @param string/array $types * @return bool */ - public static function isType($app,$types) { + public static function isType($app, $types) { if(is_string($types)) { $types=array($types); } @@ -185,7 +185,7 @@ class OC_App{ }else{ $download=OC_OCSClient::getApplicationDownload($app, 1); if(isset($download['downloadlink']) and $download['downloadlink']!='') { - $app=OC_Installer::installApp(array('source'=>'http','href'=>$download['downloadlink'])); + $app=OC_Installer::installApp(array('source'=>'http', 'href'=>$download['downloadlink'])); } } } @@ -253,6 +253,8 @@ class OC_App{ * highlighting the current position of the user. */ public static function setActiveNavigationEntry( $id ) { + // load all the apps, to make sure we have all the navigation entries + self::loadApps(); self::$activeapp = $id; return true; } @@ -282,33 +284,33 @@ class OC_App{ // by default, settings only contain the help menu if(OC_Config::getValue('knowledgebaseenabled', true)==true) { $settings = array( - array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "help.php" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" )) + array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkToRoute( "settings_help" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" )) ); } // if the user is logged-in if (OC_User::isLoggedIn()) { // personal menu - $settings[] = array( "id" => "personal", "order" => 1, "href" => OC_Helper::linkTo( "settings", "personal.php" ), "name" => $l->t("Personal"), "icon" => OC_Helper::imagePath( "settings", "personal.svg" )); + $settings[] = array( "id" => "personal", "order" => 1, "href" => OC_Helper::linkToRoute( "settings_personal" ), "name" => $l->t("Personal"), "icon" => OC_Helper::imagePath( "settings", "personal.svg" )); // if there are some settings forms if(!empty(self::$settingsForms)) // 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" )); + $settings[]=array( "id" => "settings", "order" => 1000, "href" => OC_Helper::linkToRoute( "settings_settings" ), "name" => $l->t("Settings"), "icon" => OC_Helper::imagePath( "settings", "settings.svg" )); //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" )); + $settings[] = array( "id" => "core_users", "order" => 2, "href" => OC_Helper::linkToRoute( "settings_users" ), "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" )); + $settings[] = array( "id" => "core_apps", "order" => 3, "href" => OC_Helper::linkToRoute( "settings_apps" ).'?installed', "name" => $l->t("Apps"), "icon" => OC_Helper::imagePath( "settings", "apps.svg" )); - $settings[]=array( "id" => "admin", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "admin.php" ), "name" => $l->t("Admin"), "icon" => OC_Helper::imagePath( "settings", "admin.svg" )); + $settings[]=array( "id" => "admin", "order" => 1000, "href" => OC_Helper::linkToRoute( "settings_admin" ), "name" => $l->t("Admin"), "icon" => OC_Helper::imagePath( "settings", "admin.svg" )); } } @@ -319,7 +321,6 @@ class OC_App{ /// This is private as well. It simply works, so don't ask for more details private static function proceedNavigation( $list ) { foreach( $list as &$naventry ) { - $naventry['subnavigation'] = array(); if( $naventry['id'] == self::$activeapp ) { $naventry['active'] = true; } @@ -390,9 +391,8 @@ class OC_App{ */ public static function getAppVersion($appid) { $file= self::getAppPath($appid).'/appinfo/version'; - $version=@file_get_contents($file); - if($version) { - return trim($version); + if(is_file($file) && $version = trim(file_get_contents($file))) { + return $version; }else{ $appData=self::getAppInfo($appid); return isset($appData['version'])? $appData['version'] : ''; @@ -406,7 +406,7 @@ class OC_App{ * @return array * @note all data is read from info.xml, not just pre-defined fields */ - public static function getAppInfo($appid,$path=false) { + public static function getAppInfo($appid, $path=false) { if($path) { $file=$appid; }else{ @@ -458,7 +458,7 @@ class OC_App{ } } self::$appInfo[$appid]=$data; - + return $data; } @@ -470,8 +470,6 @@ class OC_App{ * entries are sorted by the key 'order' ascending. Additional to the keys * given for each app the following keys exist: * - active: boolean, signals if the user is on this navigation entry - * - children: array that is empty if the key 'active' is false or - * contains the subentries if the key 'active' is true */ public static function getNavigation() { $navigation = self::proceedNavigation( self::$navigation ); @@ -485,6 +483,12 @@ class OC_App{ public static function getCurrentApp() { $script=substr($_SERVER["SCRIPT_NAME"], strlen(OC::$WEBROOT)+1); $topFolder=substr($script, 0, strpos($script, '/')); + if (empty($topFolder)) { + $path_info = OC_Request::getPathInfo(); + if ($path_info) { + $topFolder=substr($path_info, 1, strpos($path_info, '/', 1)-1); + } + } if($topFolder=='apps') { $length=strlen($topFolder); return substr($script, $length+1, strpos($script, '/', $length+1)-$length-1); @@ -521,21 +525,21 @@ class OC_App{ /** * register a settings form to be shown */ - public static function registerSettings($app,$page) { + public static function registerSettings($app, $page) { self::$settingsForms[]= $app.'/'.$page.'.php'; } /** * register an admin form to be shown */ - public static function registerAdmin($app,$page) { + public static function registerAdmin($app, $page) { self::$adminForms[]= $app.'/'.$page.'.php'; } /** * register a personal form to be shown */ - public static function registerPersonal($app,$page) { + public static function registerPersonal($app, $page) { self::$personalForms[]= $app.'/'.$page.'.php'; } @@ -545,34 +549,31 @@ class OC_App{ * @todo: change the name of this method to getInstalledApps, which is more accurate */ public static function getAllApps() { - + $apps=array(); - + foreach ( OC::$APPSROOTS as $apps_dir ) { if(! is_readable($apps_dir['path'])) { - OC_Log::write('core', 'unable to read app folder : ' .$apps_dir['path'] , OC_Log::WARN); + OC_Log::write('core', 'unable to read app folder : ' .$apps_dir['path'], OC_Log::WARN); continue; } $dh = opendir( $apps_dir['path'] ); - + while( $file = readdir( $dh ) ) { - - if ( - $file[0] != '.' - and is_file($apps_dir['path'].'/'.$file.'/appinfo/app.php' ) - ) { - + + if ($file[0] != '.' and is_file($apps_dir['path'].'/'.$file.'/appinfo/app.php')) { + $apps[] = $file; - + } - + } - + } - + return $apps; } - + /** * @brief: get a list of all apps on apps.owncloud.com * @return array, multi-dimensional array of apps. Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description @@ -584,7 +585,7 @@ class OC_App{ if ( ! $categories = array_keys( $catagoryNames ) ) { return false; } - + $page = 0; $remoteApps = OC_OCSClient::getApplications( $categories, $page, $filter ); $app1 = array(); @@ -659,7 +660,7 @@ class OC_App{ $version = OC_Util::getVersion(); foreach($apps as $app) { // check if the app is compatible with this version of ownCloud - $info = OC_App::getAppInfo($app); + $info = OC_App::getAppInfo($app); if(!isset($info['require']) or (($version[0].'.'.$version[1])>$info['require'])) { OC_Log::write('core', 'App "'.$info['name'].'" ('.$app.') can\'t be used because it is not compatible with this version of ownCloud', OC_Log::ERROR); OC_App::disable( $app ); @@ -689,6 +690,10 @@ class OC_App{ * @param string $appid */ public static function updateApp($appid) { + if(file_exists(self::getAppPath($appid).'/appinfo/preupdate.php')) { + self::loadApp($appid); + include self::getAppPath($appid).'/appinfo/preupdate.php'; + } if(file_exists(self::getAppPath($appid).'/appinfo/database.xml')) { OC_DB::updateDbFromStructure(self::getAppPath($appid).'/appinfo/database.xml'); } diff --git a/lib/appconfig.php b/lib/appconfig.php index 6604e854d55..1f2d576af87 100644 --- a/lib/appconfig.php +++ b/lib/appconfig.php @@ -107,7 +107,7 @@ class OC_Appconfig{ * @param string $key * @return bool */ - public static function hasKey($app,$key) { + public static function hasKey($app, $key) { $exists = self::getKeys( $app ); return in_array( $key, $exists ); } @@ -123,7 +123,7 @@ class OC_Appconfig{ */ public static function setValue( $app, $key, $value ) { // Does the key exist? yes: update. No: insert - if(! self::hasKey($app,$key)) { + if(! self::hasKey($app, $key)) { $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*appconfig` ( `appid`, `configkey`, `configvalue` ) VALUES( ?, ?, ? )' ); $query->execute( array( $app, $key, $value )); } @@ -170,7 +170,7 @@ class OC_Appconfig{ * @param key * @return array */ - public static function getValues($app,$key) { + public static function getValues($app, $key) { if($app!==false and $key!==false) { return false; } diff --git a/lib/archive.php b/lib/archive.php index a9c245eaf43..61239c82076 100644 --- a/lib/archive.php +++ b/lib/archive.php @@ -42,14 +42,14 @@ abstract class OC_Archive{ * @param string source either a local file or string data * @return bool */ - abstract function addFile($path,$source=''); + abstract function addFile($path, $source=''); /** * rename a file or folder in the archive * @param string source * @param string dest * @return bool */ - abstract function rename($source,$dest); + abstract function rename($source, $dest); /** * get the uncompressed size of a file in the archive * @param string path @@ -85,7 +85,7 @@ abstract class OC_Archive{ * @param string dest * @return bool */ - abstract function extractFile($path,$dest); + abstract function extractFile($path, $dest); /** * extract the archive * @param string path @@ -111,14 +111,14 @@ abstract class OC_Archive{ * @param string mode * @return resource */ - abstract function getStream($path,$mode); + 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) { + function addRecursive($path, $source) { if($dh=opendir($source)) { $this->addFolder($path); while($file=readdir($dh)) { diff --git a/lib/archive/tar.php b/lib/archive/tar.php index 86d39b88968..0fa633c6038 100644 --- a/lib/archive/tar.php +++ b/lib/archive/tar.php @@ -6,7 +6,7 @@ * See the COPYING-README file. */ -require_once '3rdparty/Archive/Tar.php'; +require_once 'Archive/Tar.php'; class OC_Archive_TAR extends OC_Archive{ const PLAIN=0; @@ -23,7 +23,7 @@ class OC_Archive_TAR extends OC_Archive{ private $path; function __construct($source) { - $types=array(null,'gz','bz'); + $types=array(null, 'gz', 'bz'); $this->path=$source; $this->tar=new Archive_Tar($source, $types[self::getTarType($source)]); } @@ -84,7 +84,7 @@ class OC_Archive_TAR extends OC_Archive{ * @param string source either a local file or string data * @return bool */ - function addFile($path,$source='') { + function addFile($path, $source='') { if($this->fileExists($path)) { $this->remove($path); } @@ -107,7 +107,7 @@ class OC_Archive_TAR extends OC_Archive{ * @param string dest * @return bool */ - function rename($source,$dest) { + function rename($source, $dest) { //no proper way to delete, rename entire archive, rename file and remake archive $tmp=OCP\Files::tmpFolder(); $this->tar->extract($tmp); @@ -130,8 +130,7 @@ class OC_Archive_TAR extends OC_Archive{ if( $file == $header['filename'] or $file.'/' == $header['filename'] or '/'.$file.'/' == $header['filename'] - or '/'.$file == $header['filename']) - { + or '/'.$file == $header['filename']) { return $header; } } @@ -214,7 +213,7 @@ class OC_Archive_TAR extends OC_Archive{ * @param string dest * @return bool */ - function extractFile($path,$dest) { + function extractFile($path, $dest) { $tmp=OCP\Files::tmpFolder(); if(!$this->fileExists($path)) { return false; @@ -294,7 +293,7 @@ class OC_Archive_TAR extends OC_Archive{ * @param string mode * @return resource */ - function getStream($path,$mode) { + function getStream($path, $mode) { if(strrpos($path, '.')!==false) { $ext=substr($path, strrpos($path, '.')); }else{ @@ -309,7 +308,7 @@ class OC_Archive_TAR extends OC_Archive{ if($mode=='r' or $mode=='rb') { return fopen($tmpFile, $mode); }else{ - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); self::$tempFiles[$tmpFile]=$path; return fopen('close://'.$tmpFile, $mode); } @@ -334,7 +333,7 @@ class OC_Archive_TAR extends OC_Archive{ $this->tar->_close(); $this->tar=null; } - $types=array(null,'gz','bz'); + $types=array(null, 'gz', 'bz'); $this->tar=new Archive_Tar($this->path, $types[self::getTarType($this->path)]); } } diff --git a/lib/archive/zip.php b/lib/archive/zip.php index d016c692e35..1c967baa08f 100644 --- a/lib/archive/zip.php +++ b/lib/archive/zip.php @@ -35,7 +35,7 @@ class OC_Archive_ZIP extends OC_Archive{ * @param string source either a local file or string data * @return bool */ - function addFile($path,$source='') { + function addFile($path, $source='') { if($source and $source[0]=='/' and file_exists($source)) { $result=$this->zip->addFile($source, $path); }else{ @@ -53,7 +53,7 @@ class OC_Archive_ZIP extends OC_Archive{ * @param string dest * @return bool */ - function rename($source,$dest) { + function rename($source, $dest) { $source=$this->stripPath($source); $dest=$this->stripPath($dest); $this->zip->renameName($source, $dest); @@ -119,7 +119,7 @@ class OC_Archive_ZIP extends OC_Archive{ * @param string dest * @return bool */ - function extractFile($path,$dest) { + function extractFile($path, $dest) { $fp = $this->zip->getStream($path); file_put_contents($dest, $fp); } @@ -158,7 +158,7 @@ class OC_Archive_ZIP extends OC_Archive{ * @param string mode * @return resource */ - function getStream($path,$mode) { + function getStream($path, $mode) { if($mode=='r' or $mode=='rb') { return $this->zip->getStream($path); } else { @@ -171,7 +171,7 @@ class OC_Archive_ZIP extends OC_Archive{ $ext=''; } $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); if($this->fileExists($path)) { $this->extractFile($path, $tmpFile); } diff --git a/lib/backgroundjob.php b/lib/backgroundjob.php new file mode 100644 index 00000000000..28b5ce3af20 --- /dev/null +++ b/lib/backgroundjob.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 does the dirty work. + */ +class OC_BackgroundJob{ + /** + * @brief get the execution type of background jobs + * @return string + * + * This method returns the type how background jobs are executed. If the user + * did not select something, the type is ajax. + */ + public static function getExecutionType() { + return OC_Appconfig::getValue( 'core', 'backgroundjobs_mode', 'ajax' ); + } + + /** + * @brief sets the background jobs execution type + * @param $type execution type + * @return boolean + * + * This method sets the execution type of the background jobs. Possible types + * are "none", "ajax", "webcron", "cron" + */ + public static function setExecutionType( $type ) { + if( !in_array( $type, array('none', 'ajax', 'webcron', 'cron'))) { + return false; + } + return OC_Appconfig::setValue( 'core', 'backgroundjobs_mode', $type ); + } +} diff --git a/lib/base.php b/lib/base.php index ad485342d2d..d9a0e2c757f 100644 --- a/lib/base.php +++ b/lib/base.php @@ -20,6 +20,8 @@ * */ +require_once 'public/constants.php'; + /** * Class that is a namespace for all global OC variables * No, we can not put this class in its own file because it is used by @@ -67,6 +69,10 @@ class OC{ * check if owncloud runs in cli mode */ public static $CLI = false; + /* + * OC router + */ + protected static $router = null; /** * SPL autoload */ @@ -99,6 +105,12 @@ class OC{ elseif(strpos($className, 'Doctrine\\DBAL')===0) { $path = 'doctrine-dbal/lib/'.str_replace('\\', '/', $className) . '.php'; } + elseif(strpos($className, 'Symfony\\Component\\Routing\\')===0) { + $path = 'symfony/routing/'.str_replace('\\', '/', $className) . '.php'; + } + elseif(strpos($className, 'Sabre\\VObject')===0) { + $path = str_replace('\\', '/', $className) . '.php'; + } elseif(strpos($className, 'Test_')===0) { $path = 'tests/lib/'.strtolower(str_replace('_', '/', substr($className, 5)) . '.php'); }else{ @@ -182,7 +194,7 @@ class OC{ OC::$SERVERROOT.'/lib'.PATH_SEPARATOR. OC::$SERVERROOT.'/config'.PATH_SEPARATOR. OC::$THIRDPARTYROOT.'/3rdparty'.PATH_SEPARATOR. - implode($paths,PATH_SEPARATOR).PATH_SEPARATOR. + implode($paths, PATH_SEPARATOR).PATH_SEPARATOR. get_include_path().PATH_SEPARATOR. OC::$SERVERROOT ); @@ -217,6 +229,14 @@ class OC{ $installedVersion=OC_Config::getValue('version', '0.0.0'); $currentVersion=implode('.', OC_Util::getVersion()); if (version_compare($currentVersion, $installedVersion, '>')) { + // Check if the .htaccess is existing - this is needed for upgrades from really old ownCloud versions + if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { + if(!OC_Util::ishtaccessworking()) { + if(!file_exists(OC::$SERVERROOT.'/data/.htaccess')) { + OC_Setup::protectDataDirectory(); + } + } + } OC_Log::write('core', 'starting upgrade from '.$installedVersion.' to '.$currentVersion, OC_Log::DEBUG); $result=OC_DB::updateDbFromStructure(OC::$SERVERROOT.'/db_structure.xml'); if(!$result) { @@ -225,7 +245,7 @@ class OC{ } if(file_exists(OC::$SERVERROOT."/config/config.php") and !is_writable(OC::$SERVERROOT."/config/config.php")) { $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->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; } @@ -246,16 +266,15 @@ class OC{ OC_Util::addScript( "jquery-1.7.2.min" ); OC_Util::addScript( "jquery-ui-1.8.16.custom.min" ); OC_Util::addScript( "jquery-showpassword" ); - OC_Util::addScript( "jquery.infieldlabel.min" ); + OC_Util::addScript( "jquery.infieldlabel" ); OC_Util::addScript( "jquery-tipsy" ); OC_Util::addScript( "oc-dialogs" ); OC_Util::addScript( "js" ); - // request protection token MUST be defined after the jquery library but before any $('document').ready() - OC_Util::addScript( "requesttoken" ); OC_Util::addScript( "eventsource" ); OC_Util::addScript( "config" ); //OC_Util::addScript( "multiselect" ); OC_Util::addScript('search', 'result'); + OC_Util::addScript('router'); if( OC_Config::getValue( 'installed', false )) { if( OC_Appconfig::getValue( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax' ) { @@ -270,13 +289,44 @@ class OC{ } public static function initSession() { + // prevents javascript from accessing php session cookies ini_set('session.cookie_httponly', '1;'); + + // (re)-initialize session session_start(); + + // regenerate session id periodically to avoid session fixation + if (!isset($_SESSION['SID_CREATED'])) { + $_SESSION['SID_CREATED'] = time(); + } else if (time() - $_SESSION['SID_CREATED'] > 900) { + session_regenerate_id(true); + $_SESSION['SID_CREATED'] = time(); + } + + // session timeout + if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 3600)) { + if (isset($_COOKIE[session_name()])) { + setcookie(session_name(), '', time() - 42000, '/'); + } + session_unset(); + session_destroy(); + session_start(); + } + $_SESSION['LAST_ACTIVITY'] = time(); + } + + public static function getRouter() { + if (!isset(OC::$router)) { + OC::$router = new OC_Router(); + OC::$router->loadRoutes(); + } + + return OC::$router; } public static function init() { // register autoloader - spl_autoload_register(array('OC','autoload')); + spl_autoload_register(array('OC', 'autoload')); setlocale(LC_ALL, 'en_US.UTF-8'); // set some stuff @@ -291,7 +341,7 @@ class OC{ ini_set('arg_separator.output', '&'); // try to switch magic quotes off. - if(function_exists('set_magic_quotes_runtime')) { + if(get_magic_quotes_gpc()) { @set_magic_quotes_runtime(false); } @@ -312,6 +362,10 @@ class OC{ //try to set the session lifetime to 60min @ini_set('gc_maxlifetime', '3600'); + //copy http auth headers for apache+php-fcgid work around + if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) { + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_XAUTHORIZATION']; + } //set http auth headers for apache+php-cgi work around if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches)) { @@ -329,6 +383,10 @@ class OC{ self::initPaths(); + register_shutdown_function(array('OC_Log', 'onShutdown')); + set_error_handler(array('OC_Log', 'onError')); + set_exception_handler(array('OC_Log', 'onException')); + // set debug mode if an xdebug session is active if (!defined('DEBUG') || !DEBUG) { if(isset($_COOKIE['XDEBUG_SESSION'])) { @@ -381,16 +439,12 @@ class OC{ //setup extra user backends OC_User::setupBackends(); - // register cache cleanup jobs - OC_BackgroundJob_RegularTask::register('OC_Cache_FileGlobal', 'gc'); - OC_Hook::connect('OC_User', 'post_login', 'OC_Cache_File', 'loginListener'); - - // Check for blacklisted files - OC_Hook::connect('OC_Filesystem', 'write', 'OC_Filesystem', 'isBlacklisted'); - OC_Hook::connect('OC_Filesystem', 'rename', 'OC_Filesystem', 'isBlacklisted'); + self::registerCacheHooks(); + self::registerFilesystemHooks(); + self::registerShareHooks(); //make sure temporary files are cleaned up - register_shutdown_function(array('OC_Helper','cleanTmp')); + 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')); @@ -423,21 +477,39 @@ class OC{ } /** + * register hooks for the cache + */ + public static function registerCacheHooks() { + // register cache cleanup jobs + OC_BackgroundJob_RegularTask::register('OC_Cache_FileGlobal', 'gc'); + OC_Hook::connect('OC_User', 'post_login', 'OC_Cache_File', 'loginListener'); + } + + /** + * register hooks for the filesystem + */ + public static function registerFilesystemHooks() { + // Check for blacklisted files + OC_Hook::connect('OC_Filesystem', 'write', 'OC_Filesystem', 'isBlacklisted'); + OC_Hook::connect('OC_Filesystem', 'rename', 'OC_Filesystem', 'isBlacklisted'); + } + + /** + * register hooks for sharing + */ + public static function registerShareHooks() { + 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'); + OC_Hook::connect('OC_User', 'post_deleteGroup', 'OCP\Share', 'post_deleteGroup'); + } + + /** * @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'; + require_once 'core/setup.php'; exit(); } // Handle WebDAV @@ -445,9 +517,21 @@ class OC{ header('location: '.OC_Helper::linkToRemote('webdav')); return; } + try { + OC::getRouter()->match(OC_Request::getPathInfo()); + return; + } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { + //header('HTTP/1.0 404 Not Found'); + } catch (Symfony\Component\Routing\Exception\MethodNotAllowedException $e) { + OC_Response::setStatus(405); + return; + } + $app = OC::$REQUESTEDAPP; + $file = OC::$REQUESTEDFILE; + $param = array('app' => $app, 'file' => $file); // Handle app css files - if(substr(OC::$REQUESTEDFILE, -3) == 'css') { - self::loadCSSFile(); + if(substr($file, -3) == 'css') { + self::loadCSSFile($param); return; } // Someone is logged in : @@ -455,16 +539,16 @@ class OC{ OC_App::loadApps(); OC_User::setupBackends(); if(isset($_GET["logout"]) and ($_GET["logout"])) { + OC_Preferences::deleteKey(OC_User::getUser(), 'login_token', $_COOKIE['oc_token']); OC_User::logout(); header("Location: ".OC::$WEBROOT.'/'); }else{ - $app = OC::$REQUESTEDAPP; - $file = OC::$REQUESTEDFILE; if(is_null($file)) { - $file = 'index.php'; + $param['file'] = 'index.php'; } - $file_ext = substr($file, -3); - if ($file_ext != 'php'|| !self::loadAppScriptFile($app, $file)) { + $file_ext = substr($param['file'], -3); + if ($file_ext != 'php' + || !self::loadAppScriptFile($param)) { header('HTTP/1.0 404 Not Found'); } } @@ -474,7 +558,10 @@ class OC{ self::handleLogin(); } - protected static function loadAppScriptFile($app, $file) { + public static function loadAppScriptFile($param) { + OC_App::loadApps(); + $app = $param['app']; + $file = $param['file']; $app_path = OC_App::getAppPath($app); $file = $app_path . '/' . $file; unset($app, $app_path); @@ -485,9 +572,9 @@ class OC{ return false; } - protected static function loadCSSFile() { - $app = OC::$REQUESTEDAPP; - $file = OC::$REQUESTEDFILE; + public static function loadCSSFile($param) { + $app = $param['app']; + $file = $param['file']; $app_path = OC_App::getAppPath($app); if (file_exists($app_path . '/' . $file)) { $app_web_path = OC_App::getAppWebPath($app); @@ -500,28 +587,38 @@ class OC{ protected static function handleLogin() { OC_App::loadApps(array('prelogin')); - $error = false; + $error = array(); // remember was checked after last login if (OC::tryRememberLogin()) { - // nothing more to do + $error[] = 'invalidcookie'; // Someone wants to log in : } elseif (OC::tryFormLogin()) { - $error = true; + $error[] = 'invalidpassword'; // The user is already authenticated using Apaches AuthType Basic... very usable in combination with LDAP } elseif (OC::tryBasicAuthLogin()) { - $error = true; + $error[] = 'invalidpassword'; + } + OC_Util::displayLoginPage(array_unique($error)); + } + + protected static function cleanupLoginTokens($user) { + $cutoff = time() - OC_Config::getValue('remember_login_cookie_lifetime', 60*60*24*15); + $tokens = OC_Preferences::getKeys($user, 'login_token'); + foreach($tokens as $token) { + $time = OC_Preferences::getValue($user, 'login_token', $token); + if ($time < $cutoff) { + OC_Preferences::deleteKey($user, 'login_token', $token); + } } - 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"]) - { + || !$_COOKIE["oc_remember_login"]) { return false; } OC_App::loadApps(array('authentication')); @@ -529,15 +626,30 @@ class OC{ 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(); + if(isset($_COOKIE['oc_token']) && OC_User::userExists($_COOKIE['oc_username'])) { + // delete outdated cookies + self::cleanupLoginTokens($_COOKIE['oc_username']); + // get stored tokens + $tokens = OC_Preferences::getKeys($_COOKIE['oc_username'], 'login_token'); + // test cookies token against stored tokens + if (in_array($_COOKIE['oc_token'], $tokens, true)) { + // replace successfully used token with a new one + OC_Preferences::deleteKey($_COOKIE['oc_username'], 'login_token', $_COOKIE['oc_token']); + $token = OC_Util::generate_random_bytes(32); + OC_Preferences::setValue($_COOKIE['oc_username'], 'login_token', $token, time()); + OC_User::setMagicInCookie($_COOKIE['oc_username'], $token); + // login + OC_User::setUserId($_COOKIE['oc_username']); + OC_Util::redirectToDefaultPage(); + // doesn't return + } + // if you reach this point you have changed your password + // or you are an attacker + // we can not delete tokens here because users may reach + // this point multiple times after a password change + OC_Log::write('core', 'Authentication cookie rejected for user '.$_COOKIE['oc_username'], OC_Log::WARN); } + OC_User::unsetMagicInCookie(); return true; } @@ -552,12 +664,13 @@ class OC{ OC_User::setupBackends(); if(OC_User::login($_POST["user"], $_POST["password"])) { + self::cleanupLoginTokens($_POST['user']); 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); + $token = OC_Util::generate_random_bytes(32); + OC_Preferences::setValue($_POST['user'], 'login_token', $token, time()); OC_User::setMagicInCookie($_POST["user"], $token); } else { @@ -576,7 +689,7 @@ class OC{ } 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_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(); diff --git a/lib/cache.php b/lib/cache.php index 62003793d5f..bc74ed83f8b 100644 --- a/lib/cache.php +++ b/lib/cache.php @@ -144,4 +144,13 @@ class OC_Cache { return self::$isFast; } + static public function generateCacheKeyFromFiles($files) { + $key = ''; + sort($files); + foreach($files as $file) { + $stat = stat($file); + $key .= $file.$stat['mtime'].$stat['size']; + } + return md5($key); + } } diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php index 0c34c7ea29f..db8f005745a 100644 --- a/lib/connector/sabre/auth.php +++ b/lib/connector/sabre/auth.php @@ -37,7 +37,7 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { } else { OC_Util::setUpFS();//login hooks may need early access to the filesystem if(OC_User::login($username, $password)) { - OC_Util::setUpFS($username); + OC_Util::setUpFS(OC_User::getUser()); return true; } else{ diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 413efef73b7..6076aed6fcd 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -116,7 +116,6 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * @return Sabre_DAV_INode[] */ public function getChildren() { - $folder_content = OC_Files::getDirectoryContent($this->path); $paths = array(); foreach($folder_content as $info) { @@ -124,15 +123,22 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } $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; + // + // the number of arguments within IN conditions are limited in most databases + // we chunk $paths into arrays of 200 items each to meet this criteria + // + $chunks = array_chunk($paths, 200, false); + foreach ($chunks as $pack) { + $placeholders = join(',', array_fill(0, count($pack), '?')); + $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ?' . ' AND `propertypath` IN ('.$placeholders.')' ); + array_unshift($pack, OC_User::getUser()); // prepend userid + $result = $query->execute( $pack ); + while($row = $result->fetchRow()) { + $propertypath = $row['propertypath']; + $propertyname = $row['propertyname']; + $propertyvalue = $row['propertyvalue']; + $properties[$propertypath][$propertyname] = $propertyvalue; + } } } @@ -200,7 +206,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function getProperties($properties) { $props = parent::getProperties($properties); if (in_array(self::GETETAG_PROPERTYNAME, $properties) && !isset($props[self::GETETAG_PROPERTYNAME])) { - $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 5bd38240d44..8d963a1cf8d 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -45,7 +45,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function put($data) { - OC_Filesystem::file_put_contents($this->path,$data); + OC_Filesystem::file_put_contents($this->path, $data); return OC_Connector_Sabre_Node::getETagPropertyForPath($this->path); } @@ -57,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,'rb'); + return OC_Filesystem::fopen($this->path, 'rb'); } diff --git a/lib/connector/sabre/locks.php b/lib/connector/sabre/locks.php index dbcc57558e0..a72d003bc72 100644 --- a/lib/connector/sabre/locks.php +++ b/lib/connector/sabre/locks.php @@ -45,10 +45,10 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract { // 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); + $params = array(OC_User::getUser(), $uri); // We need to check locks for every part in the uri. - $uriParts = explode('/',$uri); + $uriParts = explode('/', $uri); // We already covered the last part of the uri array_pop($uriParts); @@ -102,14 +102,14 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract { * @param Sabre_DAV_Locks_LockInfo $lockInfo * @return bool */ - public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + public function lock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) { // We're making the lock timeout 5 minutes $lockInfo->timeout = 300; $lockInfo->created = time(); $lockInfo->uri = $uri; - $locks = $this->getLocks($uri,false); + $locks = $this->getLocks($uri, false); $exists = false; foreach($locks as $lock) { if ($lock->token == $lockInfo->token) $exists = true; @@ -134,10 +134,10 @@ class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract { * @param Sabre_DAV_Locks_LockInfo $lockInfo * @return bool */ - public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + public function unlock($uri, Sabre_DAV_Locks_LockInfo $lockInfo) { $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*locks` WHERE `userid` = ? AND `uri` = ? AND `token` = ?' ); - $result = $query->execute( array(OC_User::getUser(),$uri,$lockInfo->token)); + $result = $query->execute( array(OC_User::getUser(), $uri, $lockInfo->token)); return $result->numRows() === 1; diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index 72de9723774..52350072fb2 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -26,6 +26,13 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; /** + * Allow configuring the method used to generate Etags + * + * @var array(class_name, function_name) + */ + public static $ETagFunction = null; + + /** * The path to the current node * * @var string @@ -43,8 +50,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr protected $property_cache = null; /** - * Sets up the node, expects a full path name - * + * @brief Sets up the node, expects a full path name * @param string $path * @return void */ @@ -55,8 +61,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr /** - * Returns the name of the node - * + * @brief Returns the name of the node * @return string */ public function getName() { @@ -67,8 +72,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Renames the node - * + * @brief Renames the node * @param string $name The new name * @return void */ @@ -80,12 +84,12 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr $newPath = $parentPath . '/' . $newName; $oldPath = $this->path; - OC_Filesystem::rename($this->path,$newPath); + OC_Filesystem::rename($this->path, $newPath); $this->path = $newPath; $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ? WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( $newPath,OC_User::getUser(), $oldPath )); + $query->execute( array( $newPath, OC_User::getUser(), $oldPath )); } @@ -95,7 +99,8 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Make sure the fileinfo cache is filled. Uses OC_FileCache or a direct stat + * @brief Ensure that the fileinfo cache is filled + & @note Uses OC_FileCache or a direct stat */ protected function getFileinfoCache() { if (!isset($this->fileinfo_cache)) { @@ -114,8 +119,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Returns the last modification time, as a unix timestamp - * + * @brief Returns the last modification time, as a unix timestamp * @return int */ public function getLastModified() { @@ -134,8 +138,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Updates properties on this node, - * + * @brief Updates properties on this node, * @param array $mutations * @see Sabre_DAV_IProperties::updateProperties * @return bool|array @@ -156,10 +159,10 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } else { if(!array_key_exists( $propertyName, $existing )) { $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties` (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' ); - $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue )); + $query->execute( array( OC_User::getUser(), $this->path, $propertyName, $propertyValue )); } else { $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ? WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' ); - $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName )); + $query->execute( array( $propertyValue, OC_User::getUser(), $this->path, $propertyName )); } } } @@ -170,15 +173,13 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * 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 - * + * @brief Returns a list of properties for this nodes.; * @param array $properties - * @return void + * @return array + * @note 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 */ public function getProperties($properties) { if (is_null($this->property_cache)) { @@ -204,16 +205,21 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Creates a ETag for this path. + * @brief 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); + if(self::$ETagFunction) { + $hash = call_user_func(self::$ETagFunction, $path); + return $hash; + }else{ + return uniqid('', true); + } } /** - * Returns the ETag surrounded by double-quotes for this path. + * @brief 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 */ @@ -229,7 +235,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Remove the ETag from the cache. + * @brief Remove the ETag from the cache. * @param string $path Path of the file */ static public function removeETagPropertyForPath($path) { diff --git a/lib/connector/sabre/principal.php b/lib/connector/sabre/principal.php index ee95ae63306..04be410ac85 100644 --- a/lib/connector/sabre/principal.php +++ b/lib/connector/sabre/principal.php @@ -46,7 +46,7 @@ class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend { * @return array */ public function getPrincipalByPath($path) { - list($prefix,$name) = explode('/', $path); + list($prefix, $name) = explode('/', $path); if ($prefix == 'principals' && OC_User::userExists($name)) { return array( @@ -83,7 +83,7 @@ class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend { * @return array */ public function getGroupMembership($principal) { - list($prefix,$name) = Sabre_DAV_URLUtil::splitPath($principal); + list($prefix, $name) = Sabre_DAV_URLUtil::splitPath($principal); $group_membership = array(); if ($prefix == 'principals') { @@ -115,11 +115,11 @@ class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend { public function setGroupMemberSet($principal, array $members) { throw new Sabre_DAV_Exception('Setting members of the group is not supported yet'); } - + function updatePrincipal($path, $mutations) { return 0; } - + function searchPrincipals($prefixPath, array $searchProperties) { return 0; } diff --git a/lib/connector/sabre/quotaplugin.php b/lib/connector/sabre/quotaplugin.php new file mode 100644 index 00000000000..a56a65ad863 --- /dev/null +++ b/lib/connector/sabre/quotaplugin.php @@ -0,0 +1,59 @@ +<?php + +/** + * This plugin check user quota and deny creating files when they exceeds the quota. + * + * @copyright Copyright (C) 2012 entreCables S.L. All rights reserved. + * @author Sergio Cambra + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { + + /** + * Reference to main server object + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * This initializes the plugin. + * + * This function is called by Sabre_DAV_Server, after + * addPlugin is called. + * + * This method should set up the requires event subscriptions. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeWriteContent', array($this, 'checkQuota'), 10); + $this->server->subscribeEvent('beforeCreateFile', array($this, 'checkQuota'), 10); + + } + + /** + * This method is called before any HTTP method and forces users to be authenticated + * + * @param string $method + * @throws Sabre_DAV_Exception + * @return bool + */ + public function checkQuota($uri, $data = null) { + $expected = $this->server->httpRequest->getHeader('X-Expected-Entity-Length'); + $length = $expected ? $expected : $this->server->httpRequest->getHeader('Content-Length'); + if ($length) { + if (substr($uri, 0, 1)!=='/') { + $uri='/'.$uri; + } + list($parentUri, $newName) = Sabre_DAV_URLUtil::splitPath($uri); + if ($length > OC_Filesystem::free_space($parentUri)) { + throw new Sabre_DAV_Exception('Quota exceeded. File is too big.'); + } + } + return true; + } +} diff --git a/lib/db.php b/lib/db.php index a6500a2e3bd..2e21a11d545 100644 --- a/lib/db.php +++ b/lib/db.php @@ -92,8 +92,8 @@ class OC_DB { $pass = OC_Config::getValue( "dbpassword", "" ); $type = OC_Config::getValue( "dbtype", "sqlite" ); if(strpos($host, ':')) { - list($host, $port)=explode(':', $host,2); - }else{ + list($host, $port)=explode(':', $host, 2); + } else { $port=false; } @@ -192,9 +192,9 @@ class OC_DB { }catch(PDOException $e) { $entry = 'DB Error: "'.$e->getMessage().'"<br />'; $entry .= 'Offending command was: '.htmlentities($query).'<br />'; - OC_Log::write('core', $entry,OC_Log::FATAL); + OC_Log::write('core', $entry, OC_Log::FATAL); error_log('DB error: '.$entry); - die( $entry ); + OC_Template::printErrorPage( $entry ); } $result=new DoctrineStatementWrapper($result); } @@ -213,12 +213,19 @@ class OC_DB { */ public static function insertid($table=null) { self::connect(); - if($table !== null) { - $prefix = OC_Config::getValue( "dbtableprefix", "oc_" ); - $suffix = OC_Config::getValue( "dbsequencesuffix", "_id_seq" ); - $table = str_replace( '*PREFIX*', $prefix, $table ).$suffix; + $type = OC_Config::getValue( "dbtype", "sqlite" ); + if( $type == 'pgsql' ) { + $query = self::prepare('SELECT lastval() AS id'); + $row = $query->execute()->fetchRow(); + return $row['id']; + } else { + if($table !== null) { + $prefix = OC_Config::getValue( "dbtableprefix", "oc_" ); + $suffix = OC_Config::getValue( "dbsequencesuffix", "_id_seq" ); + $table = str_replace( '*PREFIX*', $prefix, $table ).$suffix; + } + return self::$connection->lastInsertId($table); } - return self::$connection->lastInsertId($table); } /** @@ -301,6 +308,78 @@ class OC_DB { } /** + * @brief Insert a row if a matching row doesn't exists. + * @param string $table. The table to insert into in the form '*PREFIX*tableName' + * @param array $input. An array of fieldname/value pairs + * @returns The return value from PDOStatementWrapper->execute() + */ + public static function insertIfNotExist($table, $input) { + self::connect(); + $prefix = OC_Config::getValue( "dbtableprefix", "oc_" ); + $table = str_replace( '*PREFIX*', $prefix, $table ); + + if(is_null(self::$type)) { + self::$type=OC_Config::getValue( "dbtype", "sqlite" ); + } + $type = self::$type; + + $query = ''; + // differences in escaping of table names ('`' for mysql) and getting the current timestamp + if( $type == 'sqlite' || $type == 'sqlite3' ) { + // NOTE: For SQLite we have to use this clumsy approach + // otherwise all fieldnames used must have a unique key. + $query = 'SELECT * FROM "' . $table . '" WHERE '; + foreach($input as $key => $value) { + $query .= $key . " = '" . $value . '\' AND '; + } + $query = substr($query, 0, strlen($query) - 5); + try { + $stmt = self::prepare($query); + $result = $stmt->execute(); + } catch(PDOException $e) { + $entry = 'DB Error: "'.$e->getMessage() . '"<br />'; + $entry .= 'Offending command was: ' . $query . '<br />'; + OC_Log::write('core', $entry, OC_Log::FATAL); + error_log('DB error: '.$entry); + OC_Template::printErrorPage( $entry ); + } + + if($result->numRows() == 0) { + $query = 'INSERT INTO "' . $table . '" ("' + . implode('","', array_keys($input)) . '") VALUES("' + . implode('","', array_values($input)) . '")'; + } else { + return true; + } + } elseif( $type == 'pgsql' || $type == 'oci' || $type == 'mysql') { + $query = 'INSERT INTO `' .$table . '` (' + . implode(',', array_keys($input)) . ') SELECT \'' + . implode('\',\'', array_values($input)) . '\' FROM ' . $table . ' WHERE '; + + foreach($input as $key => $value) { + $query .= $key . " = '" . $value . '\' AND '; + } + $query = substr($query, 0, strlen($query) - 5); + $query .= ' HAVING COUNT(*) = 0'; + } + + // TODO: oci should be use " (quote) instead of ` (backtick). + //OC_Log::write('core', __METHOD__ . ', type: ' . $type . ', query: ' . $query, OC_Log::DEBUG); + + try { + $result = self::prepare($query); + } catch(PDOException $e) { + $entry = 'DB Error: "'.$e->getMessage() . '"<br />'; + $entry .= 'Offending command was: ' . $query.'<br />'; + OC_Log::write('core', $entry, OC_Log::FATAL); + error_log('DB error: ' . $entry); + OC_Template::printErrorPage( $entry ); + } + + return $result->execute(); + } + + /** * @brief does minor changes to query * @param string $query Query string * @return string corrected query string @@ -403,7 +482,7 @@ class OC_DB { return false; } } - + /** * returns the error code and message as a string for logging * works with DoctrineException diff --git a/lib/eventsource.php b/lib/eventsource.php index 900b5b101e6..1b8033943a1 100644 --- a/lib/eventsource.php +++ b/lib/eventsource.php @@ -32,13 +32,13 @@ class OC_EventSource{ private $fallBackId=0; public function __construct() { - @ob_end_clean(); + OC_Util::obEnd(); header('Cache-Control: no-cache'); $this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true'; if($this->fallback) { $this->fallBackId=$_GET['fallback_id']; header("Content-Type: text/html"); - echo str_repeat('<span></span>'.PHP_EOL,10); //dummy data to keep IE happy + echo str_repeat('<span></span>'.PHP_EOL, 10); //dummy data to keep IE happy }else{ header("Content-Type: text/event-stream"); } @@ -56,7 +56,7 @@ class OC_EventSource{ * * if only one paramater is given, a typeless message will be send with that paramater as data */ - public function send($type,$data=null) { + public function send($type, $data=null) { if(is_null($data)) { $data=$type; $type=null; @@ -78,6 +78,6 @@ class OC_EventSource{ * close the connection of the even source */ public function close() { - $this->send('__internal__','close');//server side closing can be an issue, let the client do it + $this->send('__internal__', 'close');//server side closing can be an issue, let the client do it } -}
\ No newline at end of file +} diff --git a/lib/filecache.php b/lib/filecache.php index 8fcb6fd9404..7bf98f43a37 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -28,6 +28,7 @@ * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache */ class OC_FileCache{ + /** * get the filesystem info from the cache * @param string path @@ -42,15 +43,15 @@ class OC_FileCache{ * - encrypted * - versioned */ - public static function get($path,$root=false) { - if(OC_FileCache_Update::hasUpdated($path,$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)); + OC_Hook::emit('OC_Filesystem', 'post_write', array('path'=>$path)); }else{ - OC_FileCache_Update::update($path,$root); + OC_FileCache_Update::update($path, $root); } } - return OC_FileCache_Cached::get($path,$root); + return OC_FileCache_Cached::get($path, $root); } /** @@ -58,22 +59,22 @@ class OC_FileCache{ * @param string $path * @param array data * @param string root (optional) - * - * $data is an assiciative array in the same format as returned by get + * @note $data is an associative array in the same format as returned + * by get */ - public static function put($path,$data,$root=false) { + public static function put($path, $data, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } - $fullpath=$root.$path; + $fullpath=OC_Filesystem::normalizePath($root.'/'.$path); $parent=self::getParentId($fullpath); - $id=self::getId($fullpath,''); + $id=self::getId($fullpath, ''); if(isset(OC_FileCache_Cached::$savedData[$fullpath])) { - $data=array_merge(OC_FileCache_Cached::$savedData[$fullpath],$data); + $data=array_merge(OC_FileCache_Cached::$savedData[$fullpath], $data); unset(OC_FileCache_Cached::$savedData[$fullpath]); } if($id!=-1) { - self::update($id,$data); + self::update($id, $data); return; } @@ -102,9 +103,9 @@ class OC_FileCache{ $data['versioned']=(int)$data['versioned']; $user=OC_User::getUser(); $query=OC_DB::prepare('INSERT INTO `*PREFIX*fscache`(`parent`, `name`, `path`, `path_hash`, `size`, `mtime`, `ctime`, `mimetype`, `mimepart`,`user`,`writable`,`encrypted`,`versioned`) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)'); - $result=$query->execute(array($parent,basename($fullpath),$fullpath,md5($fullpath),$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned'])); + $result=$query->execute(array($parent, basename($fullpath), $fullpath, md5($fullpath), $data['size'], $data['mtime'], $data['ctime'], $data['mimetype'], $mimePart, $user, $data['writable'], $data['encrypted'], $data['versioned'])); if(OC_DB::isError($result)) { - OC_Log::write('files','error while writing file('.$fullpath.') to cache',OC_Log::ERROR); + OC_Log::write('files', 'error while writing file('.$fullpath.') to cache', OC_Log::ERROR); } if($cache=OC_Cache::getUserCache(true)) { @@ -117,10 +118,10 @@ class OC_FileCache{ * @param int $id * @param array $data */ - private static function update($id,$data) { + private static function update($id, $data) { $arguments=array(); $queryParts=array(); - foreach(array('size','mtime','ctime','mimetype','encrypted','versioned','writable') as $attribute) { + foreach(array('size','mtime','ctime','mimetype','encrypted','versioned', 'writable') as $attribute) { if(isset($data[$attribute])) { //Convert to int it args are false if($data[$attribute] === false) { @@ -137,11 +138,13 @@ class OC_FileCache{ } $arguments[]=$id; - $sql = 'UPDATE `*PREFIX*fscache` SET '.implode(' , ',$queryParts).' WHERE `id`=?'; - $query=OC_DB::prepare($sql); - $result=$query->execute($arguments); - if(OC_DB::isError($result)) { - OC_Log::write('files','error while updating file('.$id.') in cache',OC_Log::ERROR); + if(!empty($queryParts)) { + $sql = 'UPDATE `*PREFIX*fscache` SET '.implode(' , ', $queryParts).' WHERE `id`=?'; + $query=OC_DB::prepare($sql); + $result=$query->execute($arguments); + if(OC_DB::isError($result)) { + OC_Log::write('files', 'error while updating file('.$id.') in cache', OC_Log::ERROR); + } } } @@ -151,7 +154,7 @@ class OC_FileCache{ * @param string newPath * @param string root (optional) */ - public static function move($oldPath,$newPath,$root=false) { + public static function move($oldPath, $newPath, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } @@ -163,10 +166,10 @@ class OC_FileCache{ $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))); + $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->set('fileid/'.$newPath, $cache->get('fileid/'.$oldPath)); $cache->remove('fileid/'.$oldPath); } @@ -175,11 +178,11 @@ class OC_FileCache{ $updateQuery=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `path`=?, `path_hash`=? WHERE `path_hash`=?'); while($row= $query->execute(array($oldPath.'/%'))->fetchRow()) { $old=$row['path']; - $new=$newPath.substr($old,$oldLength); - $updateQuery->execute(array($new,md5($new),md5($old))); + $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->set('fileid/'.$new, $cache->get('fileid/'.$old)); $cache->remove('fileid/'.$old); } } @@ -190,7 +193,7 @@ class OC_FileCache{ * @param string path * @param string root (optional) */ - public static function delete($path,$root=false) { + public static function delete($path, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } @@ -203,7 +206,7 @@ class OC_FileCache{ OC_Cache::remove('fileid/'.$root.$path); } - + /** * return array of filenames matching the querty * @param string $query @@ -211,23 +214,29 @@ class OC_FileCache{ * @param string root (optional) * @return array of filepaths */ - public static function search($search,$returnData=false,$root=false) { + public static function search($search, $returnData=false, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } $rootLen=strlen($root); if(!$returnData) { - $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `name` LIKE ? AND `user`=?'); + $select = '`path`'; }else{ - $query=OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` WHERE `name` LIKE ? AND `user`=?'); + $select = '*'; + } + if (OC_Config::getValue('dbtype') === 'oci8') { + $where = 'LOWER(`name`) LIKE LOWER(?) AND `user`=?'; + } else { + $where = '`name` LIKE ? AND `user`=?'; } - $result=$query->execute(array("%$search%",OC_User::getUser())); + $query=OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*fscache` WHERE '.$where); + $result=$query->execute(array("%$search%", OC_User::getUser())); $names=array(); while($row=$result->fetchRow()) { if(!$returnData) { - $names[]=substr($row['path'],$rootLen); + $names[]=substr($row['path'], $rootLen); }else{ - $row['path']=substr($row['path'],$rootLen); + $row['path']=substr($row['path'], $rootLen); $names[]=$row; } } @@ -249,11 +258,11 @@ class OC_FileCache{ * - encrypted * - versioned */ - public static function getFolderContent($path,$root=false,$mimetype_filter='') { - if(OC_FileCache_Update::hasUpdated($path,$root,true)) { - OC_FileCache_Update::updateFolder($path,$root); + 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); + return OC_FileCache_Cached::getFolderContent($path, $root, $mimetype_filter); } /** @@ -262,8 +271,8 @@ class OC_FileCache{ * @param string root (optional) * @return bool */ - public static function inCache($path,$root=false) { - return self::getId($path,$root)!=-1; + public static function inCache($path, $root=false) { + return self::getId($path, $root)!=-1; } /** @@ -272,7 +281,7 @@ class OC_FileCache{ * @param string root (optional) * @return int */ - public static function getId($path,$root=false) { + public static function getId($path, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } @@ -285,7 +294,7 @@ class OC_FileCache{ $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); + OC_Log::write('files', 'error while getting file id of '.$path, OC_Log::ERROR); return -1; } @@ -296,7 +305,7 @@ class OC_FileCache{ $id=-1; } if($cache=OC_Cache::getUserCache(true)) { - $cache->set('fileid/'.$fullPath,$id); + $cache->set('fileid/'.$fullPath, $id); } return $id; @@ -308,19 +317,19 @@ class OC_FileCache{ * @param string user (optional) * @return string */ - public static function getPath($id,$user='') { + public static function getPath($id, $user='') { if(!$user) { $user=OC_User::getUser(); } $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `id`=? AND `user`=?'); - $result=$query->execute(array($id,$user)); + $result=$query->execute(array($id, $user)); $row=$result->fetchRow(); $path=$row['path']; $root='/'.$user.'/files'; - if(substr($path,0,strlen($root))!=$root) { + if(substr($path, 0, strlen($root))!=$root) { return false; } - return substr($path,strlen($root)); + return substr($path, strlen($root)); } /** @@ -332,7 +341,7 @@ class OC_FileCache{ if($path=='/') { return -1; }else{ - return self::getId(dirname($path),''); + return self::getId(dirname($path), ''); } } @@ -342,27 +351,40 @@ class OC_FileCache{ * @param int $sizeDiff * @param string root (optinal) */ - public static function increaseSize($path,$sizeDiff, $root=false) { + public static function increaseSize($path, $sizeDiff, $root=false) { if($sizeDiff==0) return; - $id=self::getId($path,$root); + $item = OC_FileCache_Cached::get($path); + //stop walking up the filetree if we hit a non-folder + if($item['mimetype'] !== 'httpd/unix-directory'){ + return; + } + $id = $item['id']; 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); + $query->execute(array($sizeDiff, $id)); + if($path == '' or $path =='/'){ + return; + } $path=dirname($path); + $parent = OC_FileCache_Cached::get($path); + $id = $parent['id']; + //stop walking up the filetree if we hit a non-folder + if($parent['mimetype'] !== 'httpd/unix-directory'){ + return; + } } } /** * recursively scan the filesystem and fill the cache * @param string $path - * @param OC_EventSource $enventSource (optional) - * @param int count (optional) - * @param string root (optional) + * @param OC_EventSource $eventSource (optional) + * @param int $count (optional) + * @param string $root (optional) */ - public static function scan($path,$eventSource=false,&$count=0,$root=false) { + public static function scan($path, $eventSource=false,&$count=0, $root=false) { if($eventSource) { - $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); + $eventSource->send('scanning', array('file'=>$path, 'count'=>$count)); } $lastSend=$count; // NOTE: Ugly hack to prevent shared files from going into the cache (the source already exists somewhere in the cache) @@ -374,7 +396,7 @@ class OC_FileCache{ }else{ $view=new OC_FilesystemView($root); } - self::scanFile($path,$root); + self::scanFile($path, $root); $dh=$view->opendir($path.'/'); $totalSize=0; if($dh) { @@ -382,21 +404,21 @@ class OC_FileCache{ if($filename != '.' and $filename != '..') { $file=$path.'/'.$filename; if($view->is_dir($file.'/')) { - self::scan($file,$eventSource,$count,$root); + self::scan($file, $eventSource, $count, $root); }else{ - $totalSize+=self::scanFile($file,$root); + $totalSize+=self::scanFile($file, $root); $count++; if($count>$lastSend+25 and $eventSource) { $lastSend=$count; - $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); + $eventSource->send('scanning', array('file'=>$path, 'count'=>$count)); } } } } } - OC_FileCache_Update::cleanFolder($path,$root); - self::increaseSize($path,$totalSize,$root); + OC_FileCache_Update::cleanFolder($path, $root); + self::increaseSize($path, $totalSize, $root); } /** @@ -405,7 +427,7 @@ class OC_FileCache{ * @param string root (optional) * @return int size of the scanned file */ - public static function scanFile($path,$root=false) { + 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; @@ -430,7 +452,7 @@ class OC_FileCache{ if($path=='/') { $path=''; } - self::put($path,$stat,$root); + self::put($path, $stat, $root); return $stat['size']; } @@ -442,12 +464,12 @@ class OC_FileCache{ * @return array of file paths * * $part1 and $part2 together form the complete mimetype. - * e.g. searchByMime('text','plain') + * e.g. searchByMime('text', 'plain') * * seccond mimetype part can be ommited * e.g. searchByMime('audio') */ - public static function searchByMime($part1,$part2=null,$root=false) { + public static function searchByMime($part1, $part2=null, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } @@ -456,14 +478,14 @@ class OC_FileCache{ $user=OC_User::getUser(); if(!$part2) { $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `mimepart`=? AND `user`=? AND `path` LIKE ?'); - $result=$query->execute(array($part1,$user, $root)); + $result=$query->execute(array($part1, $user, $root)); }else{ $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `mimetype`=? AND `user`=? AND `path` LIKE ? '); - $result=$query->execute(array($part1.'/'.$part2,$user, $root)); + $result=$query->execute(array($part1.'/'.$part2, $user, $root)); } $names=array(); while($row=$result->fetchRow()) { - $names[]=substr($row['path'],$rootLen); + $names[]=substr($row['path'], $rootLen); } return $names; } @@ -494,18 +516,19 @@ class OC_FileCache{ * trigger an update for the cache by setting the mtimes to 0 * @param string $user (optional) */ - public static function triggerUpdate($user=''){ + public static function triggerUpdate($user='') { if($user) { - $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `mtime`=0 WHERE `user`=? AND `mimetype`="httpd/unix-directory"'); - $query->execute(array($user)); + $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `mtime`=0 WHERE `user`=? AND `mimetype`= ? '); + $query->execute(array($user,'httpd/unix-directory')); }else{ - $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `mtime`=0 AND `mimetype`="httpd/unix-directory"'); - $query->execute(); + $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `mtime`=0 AND `mimetype`= ? '); + $query->execute(array('httpd/unix-directory')); } } } //watch for changes and try to keep the cache up to date -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'); +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'); +OC_Hook::connect('OC_User', 'post_deleteUser', 'OC_FileCache_Update', 'deleteFromUser'); diff --git a/lib/filecache/cached.php b/lib/filecache/cached.php index 9b1eb4f7803..5e0a00746b9 100644 --- a/lib/filecache/cached.php +++ b/lib/filecache/cached.php @@ -13,12 +13,12 @@ class OC_FileCache_Cached{ public static $savedData=array(); - public static function get($path,$root=false) { + public static function get($path, $root=false) { if($root===false) { $root=OC_Filesystem::getRoot(); } $path=$root.$path; - $stmt=OC_DB::prepare('SELECT `path`,`ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); + $stmt=OC_DB::prepare('SELECT `id`, `path`,`ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); if ( ! OC_DB::isError($stmt) ) { $result=$stmt->execute(array(md5($path))); if ( ! OC_DB::isError($result) ) { @@ -61,7 +61,7 @@ class OC_FileCache_Cached{ * - encrypted * - versioned */ - public static function getFolderContent($path,$root=false,$mimetype_filter='') { + public static function getFolderContent($path, $root=false, $mimetype_filter='') { if($root===false) { $root=OC_Filesystem::getRoot(); } @@ -78,4 +78,4 @@ class OC_FileCache_Cached{ return false; } } -}
\ No newline at end of file +} diff --git a/lib/filecache/update.php b/lib/filecache/update.php index 4a5ea873b17..bc403113e7c 100644 --- a/lib/filecache/update.php +++ b/lib/filecache/update.php @@ -18,7 +18,7 @@ class OC_FileCache_Update{ * @param boolean folder * @return bool */ - public static function hasUpdated($path,$root=false,$folder=false) { + public static function hasUpdated($path, $root=false, $folder=false) { if($root===false) { $view=OC_Filesystem::getView(); }else{ @@ -46,14 +46,14 @@ class OC_FileCache_Update{ /** * delete non existing files from the cache */ - public static function cleanFolder($path,$root=false) { + 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); + $cachedContent=OC_FileCache_Cached::getFolderContent($path, $root); foreach($cachedContent as $fileData) { $path=$fileData['path']; $file=$view->getRelativePath($path); @@ -72,7 +72,7 @@ class OC_FileCache_Update{ * @param string path * @param string root (optional) */ - public static function updateFolder($path,$root=false) { + public static function updateFolder($path, $root=false) { if($root===false) { $view=OC_Filesystem::getView(); }else{ @@ -85,7 +85,7 @@ class OC_FileCache_Update{ $file=$path.'/'.$filename; $isDir=$view->is_dir($file); if(self::hasUpdated($file, $root, $isDir)) { - if($isDir){ + if($isDir) { self::updateFolder($file, $root); }elseif($root===false) {//filesystem hooks are only valid for the default root OC_Hook::emit('OC_Filesystem', 'post_write', array('path'=>$file)); @@ -143,7 +143,7 @@ class OC_FileCache_Update{ * @param string path * @param string root (optional) */ - public static function update($path,$root=false) { + public static function update($path, $root=false) { if($root===false) { $view=OC_Filesystem::getView(); }else{ @@ -153,7 +153,7 @@ class OC_FileCache_Update{ $mimetype=$view->getMimeType($path); $size=0; - $cached=OC_FileCache_Cached::get($path,$root); + $cached=OC_FileCache_Cached::get($path, $root); $cachedSize=isset($cached['size'])?$cached['size']:0; if($view->is_dir($path.'/')) { @@ -165,7 +165,7 @@ class OC_FileCache_Update{ $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)); + 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); @@ -174,7 +174,7 @@ class OC_FileCache_Update{ }else{ $size=OC_FileCache::scanFile($path, $root); } - if($path !== '' and $path !== '/'){ + if($path !== '' and $path !== '/') { OC_FileCache::increaseSize(dirname($path), $size-$cachedSize, $root); } } @@ -184,7 +184,7 @@ class OC_FileCache_Update{ * @param string path * @param string root (optional) */ - public static function delete($path,$root=false) { + public static function delete($path, $root=false) { $cached=OC_FileCache_Cached::get($path, $root); if(!isset($cached['size'])) { return; @@ -200,7 +200,7 @@ class OC_FileCache_Update{ * @param string newPath * @param string root (optional) */ - public static function rename($oldPath,$newPath,$root=false) { + public static function rename($oldPath, $newPath, $root=false) { if(!OC_FileCache::inCache($oldPath, $root)) { return; } @@ -216,4 +216,12 @@ class OC_FileCache_Update{ OC_FileCache::increaseSize(dirname($newPath), $oldSize, $root); OC_FileCache::move($oldPath, $newPath); } + + /** + * delete files owned by user from the cache + * @param string $parameters$parameters["uid"]) + */ + public static function deleteFromUser($parameters) { + OC_FileCache::clear($parameters["uid"]); + } } diff --git a/lib/filechunking.php b/lib/filechunking.php index 5ab33c77ad7..55a4d730430 100644 --- a/lib/filechunking.php +++ b/lib/filechunking.php @@ -59,7 +59,7 @@ class OC_FileChunking { for($i=0; $i < $this->info['chunkcount']; $i++) { $chunk = $cache->get($prefix.$i); $cache->remove($prefix.$i); - $count += fwrite($f,$chunk); + $count += fwrite($f, $chunk); } return $count; } diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 17380c656a3..2f81bde64a1 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -51,8 +51,8 @@ class OC_FileProxy{ * * this implements a dummy proxy for all operations */ - public function __call($function,$arguments) { - if(substr($function,0,3)=='pre') { + public function __call($function, $arguments) { + if(substr($function, 0, 3)=='pre') { return true; }else{ return $arguments[1]; @@ -70,7 +70,7 @@ class OC_FileProxy{ public static function getProxies($operation) { $proxies=array(); foreach(self::$proxies as $proxy) { - if(method_exists($proxy,$operation)) { + if(method_exists($proxy, $operation)) { $proxies[]=$proxy; } } @@ -85,7 +85,7 @@ class OC_FileProxy{ $proxies=self::getProxies($operation); foreach($proxies as $proxy) { if(!is_null($filepath2)) { - if($proxy->$operation($filepath,$filepath2)===false) { + if($proxy->$operation($filepath, $filepath2)===false) { return false; } }else{ @@ -97,14 +97,14 @@ class OC_FileProxy{ return true; } - public static function runPostProxies($operation,$path,$result) { + public static function runPostProxies($operation, $path, $result) { if(!self::$enabled) { return $result; } $operation='post'.$operation; $proxies=self::getProxies($operation); foreach($proxies as $proxy) { - $result=$proxy->$operation($path,$result); + $result=$proxy->$operation($path, $result); } return $result; } diff --git a/lib/fileproxy/fileoperations.php b/lib/fileproxy/fileoperations.php index fe7edafad2b..516629adaec 100644 --- a/lib/fileproxy/fileoperations.php +++ b/lib/fileproxy/fileoperations.php @@ -1,37 +1,37 @@ <?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/>.
- *
- */
-
-/**
- * check if standard file operations
+/** + * 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_FileProxy_FileOperations extends OC_FileProxy{
- static $rootView;
- public function premkdir($path) {
- if(!self::$rootView){
- self::$rootView = new OC_FilesystemView('');
+/** + * check if standard file operations + */ + +class OC_FileProxy_FileOperations extends OC_FileProxy{ + static $rootView; + + public function premkdir($path) { + if(!self::$rootView) { + self::$rootView = new OC_FilesystemView(''); } - return !self::$rootView->file_exists($path);
+ return !self::$rootView->file_exists($path); } - + }
\ No newline at end of file diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php index 5a0dbdb6fe2..742e02d471b 100644 --- a/lib/fileproxy/quota.php +++ b/lib/fileproxy/quota.php @@ -27,77 +27,83 @@ class OC_FileProxy_Quota extends OC_FileProxy{ static $rootView; - private $userQuota=-1; + private $userQuota=array(); /** - * get the quota for the current user + * get the quota for the user + * @param user * @return int */ - private function getQuota() { - if($this->userQuota!=-1) { - return $this->userQuota; + private function getQuota($user) { + if(in_array($user, $this->userQuota)) { + return $this->userQuota[$user]; } - $userQuota=OC_Preferences::getValue(OC_User::getUser(),'files','quota','default'); + $userQuota=OC_Preferences::getValue($user, 'files', 'quota', 'default'); if($userQuota=='default') { - $userQuota=OC_AppConfig::getValue('files','default_quota','none'); + $userQuota=OC_AppConfig::getValue('files', 'default_quota', 'none'); } if($userQuota=='none') { - $this->userQuota=0; + $this->userQuota[$user]=-1; }else{ - $this->userQuota=OC_Helper::computerFileSize($userQuota); + $this->userQuota[$user]=OC_Helper::computerFileSize($userQuota); } - return $this->userQuota; + return $this->userQuota[$user]; } /** - * get the free space in the users home folder + * get the free space in the path's owner home folder + * @param path * @return int */ - private function getFreeSpace() { - $rootInfo=OC_FileCache_Cached::get(''); + private function getFreeSpace($path) { + $storage=OC_Filesystem::getStorage($path); + $owner=$storage->getOwner($path); + + $totalSpace=$this->getQuota($owner); + if($totalSpace==-1) { + return -1; + } + + $rootInfo=OC_FileCache::get('', "/".$owner."/files"); // TODO Remove after merge of share_api - if (OC_FileCache::inCache('/Shared')) { - $sharedInfo=OC_FileCache_Cached::get('/Shared'); + if (OC_FileCache::inCache('/Shared', "/".$owner."/files")) { + $sharedInfo=OC_FileCache::get('/Shared', "/".$owner."/files"); } else { $sharedInfo = null; } $usedSpace=isset($rootInfo['size'])?$rootInfo['size']:0; $usedSpace=isset($sharedInfo['size'])?$usedSpace-$sharedInfo['size']:$usedSpace; - $totalSpace=$this->getQuota(); - if($totalSpace==0) { - return 0; - } return $totalSpace-$usedSpace; } - - public function postFree_space($path,$space) { - $free=$this->getFreeSpace(); - if($free==0) { + + public function postFree_space($path, $space) { + $free=$this->getFreeSpace($path); + if($free==-1) { return $space; } - return min($free,$space); + return min($free, $space); } - public function preFile_put_contents($path,$data) { + public function preFile_put_contents($path, $data) { if (is_resource($data)) { $data = '';//TODO: find a way to get the length of the stream without emptying it } - return (strlen($data)<$this->getFreeSpace() or $this->getFreeSpace()==0); + return (strlen($data)<$this->getFreeSpace($path) or $this->getFreeSpace($path)==-1); } - public function preCopy($path1,$path2) { - if(!self::$rootView){ + public function preCopy($path1, $path2) { + if(!self::$rootView) { self::$rootView = new OC_FilesystemView(''); } - return (self::$rootView->filesize($path1)<$this->getFreeSpace() or $this->getFreeSpace()==0); + return (self::$rootView->filesize($path1)<$this->getFreeSpace($path2) or $this->getFreeSpace($path2)==-1); } - public function preFromTmpFile($tmpfile,$path) { - return (filesize($tmpfile)<$this->getFreeSpace() or $this->getFreeSpace()==0); + public function preFromTmpFile($tmpfile, $path) { + return (filesize($tmpfile)<$this->getFreeSpace($path) or $this->getFreeSpace($path)==-1); } - public function preFromUploadedFile($tmpfile,$path) { - return (filesize($tmpfile)<$this->getFreeSpace() or $this->getFreeSpace()==0); + public function preFromUploadedFile($tmpfile, $path) { + return (filesize($tmpfile)<$this->getFreeSpace($path) or $this->getFreeSpace($path)==-1); } } diff --git a/lib/files.php b/lib/files.php index 2b2b8b42dc4..b4a4145a493 100644 --- a/lib/files.php +++ b/lib/files.php @@ -42,16 +42,20 @@ class OC_Files { * - versioned */ public static function getFileInfo($path) { + $path = OC_Filesystem::normalizePath($path); if (($path == '/Shared' || substr($path, 0, 8) == '/Shared/') && OC_App::isEnabled('files_sharing')) { if ($path == '/Shared') { list($info) = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); - }else{ - $info['size'] = OC_Filesystem::filesize($path); - $info['mtime'] = OC_Filesystem::filemtime($path); - $info['ctime'] = OC_Filesystem::filectime($path); - $info['mimetype'] = OC_Filesystem::getMimeType($path); - $info['encrypted'] = false; - $info['versioned'] = false; + } else { + $info = array(); + if (OC_Filesystem::file_exists($path)) { + $info['size'] = OC_Filesystem::filesize($path); + $info['mtime'] = OC_Filesystem::filemtime($path); + $info['ctime'] = OC_Filesystem::filectime($path); + $info['mimetype'] = OC_Filesystem::getMimeType($path); + $info['encrypted'] = false; + $info['versioned'] = false; + } } } else { $info = OC_FileCache::get($path); @@ -87,16 +91,16 @@ class OC_Files { foreach ($files as &$file) { $file['directory'] = $directory; $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $permissions = OCP\Share::PERMISSION_READ; + $permissions = OCP\PERMISSION_READ; // NOTE: Remove check when new encryption is merged if (!$file['encrypted']) { - $permissions |= OCP\Share::PERMISSION_SHARE; + $permissions |= OCP\PERMISSION_SHARE; } if ($file['type'] == 'dir' && $file['writable']) { - $permissions |= OCP\Share::PERMISSION_CREATE; + $permissions |= OCP\PERMISSION_CREATE; } if ($file['writable']) { - $permissions |= OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE; + $permissions |= OCP\PERMISSION_UPDATE | OCP\PERMISSION_DELETE; } $file['permissions'] = $permissions; } @@ -135,18 +139,27 @@ class OC_Files { * @param file $file ; seperated list of files to download * @param boolean $only_header ; boolean to only send header of the request */ - public static function get($dir,$files, $only_header = false) { - if(strpos($files,';')) { - $files=explode(';',$files); + public static function get($dir, $files, $only_header = false) { + $xsendfile = false; + if (isset($_SERVER['MOD_X_SENDFILE_ENABLED']) || + isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) { + $xsendfile = true; + } + if(strpos($files, ';')) { + $files=explode(';', $files); } if(is_array($files)) { - self::validateZipDownload($dir,$files); + self::validateZipDownload($dir, $files); $executionTime = intval(ini_get('max_execution_time')); set_time_limit(0); $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) { + if ($xsendfile) { + $filename = OC_Helper::tmpFileNoClean('.zip'); + }else{ + $filename = OC_Helper::tmpFile('.zip'); + } + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { exit("cannot open <$filename>\n"); } foreach($files as $file) { @@ -154,31 +167,35 @@ class OC_Files { if(OC_Filesystem::is_file($file)) { $tmpFile=OC_Filesystem::toTmpFile($file); self::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile,basename($file)); + $zip->addFile($tmpFile, basename($file)); }elseif(OC_Filesystem::is_dir($file)) { - self::zipAddDir($file,$zip); + self::zipAddDir($file, $zip); } } $zip->close(); set_time_limit($executionTime); }elseif(OC_Filesystem::is_dir($dir.'/'.$files)) { - self::validateZipDownload($dir,$files); + self::validateZipDownload($dir, $files); $executionTime = intval(ini_get('max_execution_time')); set_time_limit(0); $zip = new ZipArchive(); - $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) { + if ($xsendfile) { + $filename = OC_Helper::tmpFileNoClean('.zip'); + }else{ + $filename = OC_Helper::tmpFile('.zip'); + } + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { exit("cannot open <$filename>\n"); } $file=$dir.'/'.$files; - self::zipAddDir($file,$zip); + self::zipAddDir($file, $zip); $zip->close(); set_time_limit($executionTime); }else{ $zip=false; $filename=$dir.'/'.$files; } - @ob_end_clean(); + OC_Util::obEnd(); if($zip or OC_Filesystem::is_readable($filename)) { header('Content-Disposition: attachment; filename="'.basename($filename).'"'); header('Content-Transfer-Encoding: binary'); @@ -187,13 +204,18 @@ class OC_Files { ini_set('zlib.output_compression', 'off'); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($filename)); + self::addSendfileHeader($filename); }else{ header('Content-Type: '.OC_Filesystem::getMimeType($filename)); + $storage = OC_Filesystem::getStorage($filename); + if ($storage instanceof OC_Filestorage_Local) { + self::addSendfileHeader(OC_Filesystem::getLocalFile($filename)); + } } }elseif($zip or !OC_Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template( '', '404', 'guest' ); - $tmpl->assign('file',$filename); + $tmpl->assign('file', $filename); $tmpl->printPage(); }else{ header("HTTP/1.0 403 Forbidden"); @@ -205,7 +227,7 @@ class OC_Files { return ; } if($zip) { - $handle=fopen($filename,'r'); + $handle=fopen($filename, 'r'); if ($handle) { $chunkSize = 8*1024;// 1 MB chunks while (!feof($handle)) { @@ -213,7 +235,9 @@ class OC_Files { flush(); } } - unlink($filename); + if (!$xsendfile) { + unlink($filename); + } }else{ OC_Filesystem::readfile($filename); } @@ -224,20 +248,29 @@ class OC_Files { } } - public static function zipAddDir($dir,$zip,$internalDir='') { + private static function addSendfileHeader($filename) { + if (isset($_SERVER['MOD_X_SENDFILE_ENABLED'])) { + header("X-Sendfile: " . $filename); + } + if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) { + header("X-Accel-Redirect: " . $filename); + } + } + + public static function zipAddDir($dir, $zip, $internalDir='') { $dirname=basename($dir); $zip->addEmptyDir($internalDir.$dirname); $internalDir.=$dirname.='/'; - $files=OC_Files::getdirectorycontent($dir); + $files=OC_Files::getDirectoryContent($dir); foreach($files as $file) { $filename=$file['name']; $file=$dir.'/'.$filename; if(OC_Filesystem::is_file($file)) { $tmpFile=OC_Filesystem::toTmpFile($file); OC_Files::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile,$internalDir.$filename); + $zip->addFile($tmpFile, $internalDir.$filename); }elseif(OC_Filesystem::is_dir($file)) { - self::zipAddDir($file,$zip,$internalDir); + self::zipAddDir($file, $zip, $internalDir); } } } @@ -249,11 +282,11 @@ class OC_Files { * @param dir $targetDir * @param file $target */ - public static function move($sourceDir,$source,$targetDir,$target) { + public static function move($sourceDir, $source, $targetDir, $target) { if(OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared')) { $targetFile=self::normalizePath($targetDir.'/'.$target); $sourceFile=self::normalizePath($sourceDir.'/'.$source); - return OC_Filesystem::rename($sourceFile,$targetFile); + return OC_Filesystem::rename($sourceFile, $targetFile); } else { return false; } @@ -267,11 +300,11 @@ class OC_Files { * @param dir $targetDir * @param file $target */ - public static function copy($sourceDir,$source,$targetDir,$target) { + public static function copy($sourceDir, $source, $targetDir, $target) { if(OC_User::isLoggedIn()) { $targetFile=$targetDir.'/'.$target; $sourceFile=$sourceDir.'/'.$source; - return OC_Filesystem::copy($sourceFile,$targetFile); + return OC_Filesystem::copy($sourceFile, $targetFile); } } @@ -282,7 +315,7 @@ class OC_Files { * @param file $name * @param type $type */ - public static function newFile($dir,$name,$type) { + public static function newFile($dir, $name, $type) { if(OC_User::isLoggedIn()) { $file=$dir.'/'.$name; if($type=='dir') { @@ -305,7 +338,7 @@ class OC_Files { * @param dir $dir * @param file $name */ - public static function delete($dir,$file) { + public static function delete($dir, $file) { if(OC_User::isLoggedIn() && ($dir!= '' || $file != 'Shared')) { $file=$dir.'/'.$file; return OC_Filesystem::unlink($file); @@ -389,12 +422,12 @@ class OC_Files { * @param string file * @return string guessed mime type */ - static function pull($source,$token,$dir,$file) { - $tmpfile=tempnam(get_temp_dir(),'remoteCloudFile'); - $fp=fopen($tmpfile,'w+'); + static function pull($source, $token, $dir, $file) { + $tmpfile=tempnam(get_temp_dir(), 'remoteCloudFile'); + $fp=fopen($tmpfile, 'w+'); $url=$source.="/files/pull.php?token=$token"; $ch=curl_init(); - curl_setopt($ch,CURLOPT_URL,$url); + curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FILE, $fp); curl_exec($ch); fclose($fp); @@ -402,7 +435,7 @@ class OC_Files { $httpCode=$info['http_code']; curl_close($ch); if($httpCode==200 or $httpCode==0) { - OC_Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file); + OC_Filesystem::fromTmpFile($tmpfile, $dir.'/'.$file); return true; }else{ return false; @@ -423,8 +456,8 @@ class OC_Files { $size -=1; } else { $size=OC_Helper::humanFileSize($size); - $size=substr($size,0,-1);//strip the B - $size=str_replace(' ','',$size); //remove the space between the size and the postfix + $size=substr($size, 0, -1);//strip the B + $size=str_replace(' ', '', $size); //remove the space between the size and the postfix } //don't allow user to break his config -- broken or malicious size input @@ -447,7 +480,7 @@ class OC_Files { $setting = 'php_value '.$key.' '.$size; $hasReplaced = 0; $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced); - if($content !== NULL) { + if($content !== null) { $htaccess = $content; } if($hasReplaced == 0) { @@ -459,7 +492,7 @@ class OC_Files { 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); } + } else { OC_Log::write('files', 'Can\'t write upload limit to '.OC::$SERVERROOT.'/.htaccess. Please check the file permissions', OC_Log::WARN); } return false; } @@ -474,18 +507,18 @@ class OC_Files { $old=''; while($old!=$path) {//replace any multiplicity of slashes with a single one $old=$path; - $path=str_replace('//','/',$path); + $path=str_replace('//', '/', $path); } return $path; } } -function fileCmp($a,$b) { +function fileCmp($a, $b) { if($a['type']=='dir' and $b['type']!='dir') { return -1; }elseif($a['type']!='dir' and $b['type']=='dir') { return 1; }else{ - return strnatcasecmp($a['name'],$b['name']); + return strnatcasecmp($a['name'], $b['name']); } } diff --git a/lib/filestorage.php b/lib/filestorage.php index 5bfd09253d5..dd65f4421b7 100644 --- a/lib/filestorage.php +++ b/lib/filestorage.php @@ -42,13 +42,13 @@ abstract class OC_Filestorage{ abstract public function filectime($path); abstract public function filemtime($path); abstract public function file_get_contents($path); - abstract public function file_put_contents($path,$data); + abstract public function file_put_contents($path, $data); abstract public function unlink($path); - abstract public function rename($path1,$path2); - abstract public function copy($path1,$path2); - abstract public function fopen($path,$mode); + abstract public function rename($path1, $path2); + abstract public function copy($path1, $path2); + abstract public function fopen($path, $mode); abstract public function getMimeType($path); - abstract public function hash($type,$path,$raw = false); + abstract public function hash($type, $path, $raw = false); abstract public function free_space($path); abstract public function search($query); abstract public function touch($path, $mtime=null); @@ -62,5 +62,6 @@ abstract class OC_Filestorage{ * 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); + abstract public function hasUpdated($path, $time); + abstract public function getOwner($path); } diff --git a/lib/filestorage/common.php b/lib/filestorage/common.php index 351714437c5..b97eb79d8d4 100644 --- a/lib/filestorage/common.php +++ b/lib/filestorage/common.php @@ -89,25 +89,25 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { } return fread($handle, $size); } - public function file_put_contents($path,$data) { + public function file_put_contents($path, $data) { $handle = $this->fopen($path, "w"); return fwrite($handle, $data); } // abstract public function unlink($path); - public function rename($path1,$path2) { - if($this->copy($path1,$path2)) { + public function rename($path1, $path2) { + if($this->copy($path1, $path2)) { return $this->unlink($path1); }else{ return false; } } - public function copy($path1,$path2) { - $source=$this->fopen($path1,'r'); - $target=$this->fopen($path2,'w'); - $count=OC_Helper::streamCopy($source,$target); + public function copy($path1, $path2) { + $source=$this->fopen($path1, 'r'); + $target=$this->fopen($path2, 'w'); + $count=OC_Helper::streamCopy($source, $target); return $count>0; } -// abstract public function fopen($path,$mode); +// abstract public function fopen($path, $mode); /** * @brief Deletes all files and folders recursively within a directory @@ -188,25 +188,25 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { if($this->is_dir($path)) { return 'httpd/unix-directory'; } - $source=$this->fopen($path,'r'); + $source=$this->fopen($path, 'r'); if(!$source) { return false; } - $head=fread($source,8192);//8kb should suffice to determine a mimetype - if($pos=strrpos($path,'.')) { - $extension=substr($path,$pos); + $head=fread($source, 8192);//8kb should suffice to determine a mimetype + if($pos=strrpos($path, '.')) { + $extension=substr($path, $pos); }else{ $extension=''; } $tmpFile=OC_Helper::tmpFile($extension); - file_put_contents($tmpFile,$head); + file_put_contents($tmpFile, $head); $mime=OC_Helper::getMimeType($tmpFile); unlink($tmpFile); return $mime; } - public function hash($type,$path,$raw = false) { + public function hash($type, $path, $raw = false) { $tmpFile=$this->getLocalFile(); - $hash=hash($type,$tmpFile,$raw); + $hash=hash($type, $tmpFile, $raw); unlink($tmpFile); return $hash; } @@ -218,35 +218,35 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { return $this->toTmpFile($path); } private function toTmpFile($path) {//no longer in the storage api, still usefull here - $source=$this->fopen($path,'r'); + $source=$this->fopen($path, 'r'); if(!$source) { return false; } - if($pos=strrpos($path,'.')) { - $extension=substr($path,$pos); + if($pos=strrpos($path, '.')) { + $extension=substr($path, $pos); }else{ $extension=''; } $tmpFile=OC_Helper::tmpFile($extension); - $target=fopen($tmpFile,'w'); - OC_Helper::streamCopy($source,$target); + $target=fopen($tmpFile, 'w'); + OC_Helper::streamCopy($source, $target); return $tmpFile; } public function getLocalFolder($path) { $baseDir=OC_Helper::tmpFolder(); - $this->addLocalFolder($path,$baseDir); + $this->addLocalFolder($path, $baseDir); return $baseDir; } - private function addLocalFolder($path,$target) { + 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); + $this->addLocalFolder($path.'/'.$file, $target.'/'.$file); }else{ $tmp=$this->toTmpFile($path.'/'.$file); - rename($tmp,$target.'/'.$file); + rename($tmp, $target.'/'.$file); } } } @@ -254,17 +254,17 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { } // abstract public function touch($path, $mtime=null); - protected function searchInDir($query,$dir='') { + protected function searchInDir($query, $dir='') { $files=array(); $dh=$this->opendir($dir); if($dh) { while($item=readdir($dh)) { if ($item == '.' || $item == '..') continue; - if(strstr(strtolower($item),strtolower($query))!==false) { + if(strstr(strtolower($item), strtolower($query))!==false) { $files[]=$dir.'/'.$item; } if($this->is_dir($dir.'/'.$item)) { - $files=array_merge($files,$this->searchInDir($query,$dir.'/'.$item)); + $files=array_merge($files, $this->searchInDir($query, $dir.'/'.$item)); } } } @@ -276,7 +276,16 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { * @param int $time * @return bool */ - public function hasUpdated($path,$time) { + public function hasUpdated($path, $time) { return $this->filemtime($path)>$time; } + + /** + * get the owner of a path + * @param $path The path to get the owner + * @return string uid or false + */ + public function getOwner($path) { + return OC_User::getUser(); + } } diff --git a/lib/filestorage/commontest.php b/lib/filestorage/commontest.php index b88bb232c36..3b038b3fda9 100644 --- a/lib/filestorage/commontest.php +++ b/lib/filestorage/commontest.php @@ -63,13 +63,13 @@ class OC_Filestorage_CommonTest extends OC_Filestorage_Common{ public function unlink($path) { return $this->storage->unlink($path); } - public function fopen($path,$mode) { - return $this->storage->fopen($path,$mode); + public function fopen($path, $mode) { + return $this->storage->fopen($path, $mode); } public function free_space($path) { return $this->storage->free_space($path); } public function touch($path, $mtime=null) { - return $this->storage->touch($path,$mtime); + return $this->storage->touch($path, $mtime); } }
\ No newline at end of file diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index 80aa548047c..6fe45acf8c5 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -6,7 +6,7 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ protected $datadir; public function __construct($arguments) { $this->datadir=$arguments['datadir']; - if(substr($this->datadir,-1)!=='/') { + if(substr($this->datadir, -1)!=='/') { $this->datadir.='/'; } } @@ -20,8 +20,8 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ return opendir($this->datadir.$path); } public function is_dir($path) { - if(substr($path,-1)=='/') { - $path=substr($path,0,-1); + if(substr($path, -1)=='/') { + $path=substr($path, 0, -1); } return is_dir($this->datadir.$path); } @@ -78,38 +78,38 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ public function file_get_contents($path) { return file_get_contents($this->datadir.$path); } - public function file_put_contents($path,$data) { - return file_put_contents($this->datadir.$path,$data); + public function file_put_contents($path, $data) { + return file_put_contents($this->datadir.$path, $data); } public function unlink($path) { return $this->delTree($path); } - public function rename($path1,$path2) { + public function rename($path1, $path2) { if (!$this->isUpdatable($path1)) { - OC_Log::write('core','unable to rename, file is not writable : '.$path1,OC_Log::ERROR); + OC_Log::write('core', 'unable to rename, file is not writable : '.$path1, OC_Log::ERROR); return false; } if(! $this->file_exists($path1)) { - OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR); + OC_Log::write('core', 'unable to rename, file does not exists : '.$path1, OC_Log::ERROR); return false; } - if($return=rename($this->datadir.$path1,$this->datadir.$path2)) { + if($return=rename($this->datadir.$path1, $this->datadir.$path2)) { } return $return; } - public function copy($path1,$path2) { + public function copy($path1, $path2) { if($this->is_dir($path2)) { if(!$this->file_exists($path2)) { $this->mkdir($path2); } - $source=substr($path1,strrpos($path1,'/')+1); + $source=substr($path1, strrpos($path1, '/')+1); $path2.=$source; } - return copy($this->datadir.$path1,$this->datadir.$path2); + return copy($this->datadir.$path1, $this->datadir.$path2); } - public function fopen($path,$mode) { - if($return=fopen($this->datadir.$path,$mode)) { + public function fopen($path, $mode) { + if($return=fopen($this->datadir.$path, $mode)) { switch($mode) { case 'r': break; @@ -156,8 +156,8 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ return $return; } - public function hash($path,$type,$raw=false) { - return hash_file($type,$this->datadir.$path,$raw); + public function hash($path, $type, $raw=false) { + return hash_file($type, $this->datadir.$path, $raw); } public function free_space($path) { @@ -174,15 +174,15 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ return $this->datadir.$path; } - protected function searchInDir($query,$dir='') { + protected function searchInDir($query, $dir='') { $files=array(); foreach (scandir($this->datadir.$dir) as $item) { if ($item == '.' || $item == '..') continue; - if(strstr(strtolower($item),strtolower($query))!==false) { + if(strstr(strtolower($item), strtolower($query))!==false) { $files[]=$dir.'/'.$item; } if(is_dir($this->datadir.$dir.'/'.$item)) { - $files=array_merge($files,$this->searchInDir($query,$dir.'/'.$item)); + $files=array_merge($files, $this->searchInDir($query, $dir.'/'.$item)); } } return $files; @@ -193,7 +193,7 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ * @param int $time * @return bool */ - public function hasUpdated($path,$time) { + public function hasUpdated($path, $time) { return $this->filemtime($path)>$time; } } diff --git a/lib/filesystem.php b/lib/filesystem.php index da524d7f181..aa03593908d 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -35,10 +35,10 @@ * post_create(path) * delete(path, &run) * post_delete(path) - * rename(oldpath,newpath, &run) - * post_rename(oldpath,newpath) - * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order) - * post_rename(oldpath,newpath) + * rename(oldpath, newpath, &run) + * post_rename(oldpath, newpath) + * copy(oldpath, newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order) + * post_rename(oldpath, newpath) * * the &run parameter can be set to false to prevent the operation from occuring */ @@ -46,6 +46,7 @@ class OC_Filesystem{ static private $storages=array(); static private $mounts=array(); + static private $loadedUsers=array(); public static $loaded=false; /** * @var OC_Filestorage $defaultInstance @@ -147,21 +148,21 @@ class OC_Filesystem{ * @return string */ static public function getMountPoint($path) { - OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path)); + OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path'=>$path)); if(!$path) { $path='/'; } if($path[0]!=='/') { $path='/'.$path; } - $path=str_replace('//', '/',$path); + $path=str_replace('//', '/', $path); $foundMountPoint=''; $mountPoints=array_keys(OC_Filesystem::$mounts); foreach($mountPoints as $mountpoint) { if($mountpoint==$path) { return $mountpoint; } - if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)) { + if(strpos($path, $mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)) { $foundMountPoint=$mountpoint; } } @@ -175,75 +176,95 @@ class OC_Filesystem{ */ static public function getInternalPath($path) { $mountPoint=self::getMountPoint($path); - $internalPath=substr($path,strlen($mountPoint)); + $internalPath=substr($path, strlen($mountPoint)); return $internalPath; } + + static private function mountPointsLoaded($user) { + return in_array($user, self::$loadedUsers); + } + /** * get the storage object for a path * @param string path * @return OC_Filestorage */ static public function getStorage($path) { + $user = ltrim(substr($path, 0, strpos($path, '/', 1)), '/'); + // check mount points if file was shared from a different user + if ($user != OC_User::getUser() && !self::mountPointsLoaded($user)) { + OC_Util::loadUserMountPoints($user); + self::loadSystemMountPoints($user); + self::$loadedUsers[] = $user; + } + $mountpoint=self::getMountPoint($path); if($mountpoint) { if(!isset(OC_Filesystem::$storages[$mountpoint])) { $mount=OC_Filesystem::$mounts[$mountpoint]; - OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']); + OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'], $mount['arguments']); } return OC_Filesystem::$storages[$mountpoint]; } } - static public function init($root) { - if(self::$defaultInstance) { - return false; - } - self::$defaultInstance=new OC_FilesystemView($root); - - //load custom mount config + static private function loadSystemMountPoints($user) { if(is_file(OC::$SERVERROOT.'/config/mount.php')) { - $mountConfig=include(OC::$SERVERROOT.'/config/mount.php'); + $mountConfig=include OC::$SERVERROOT.'/config/mount.php'; if(isset($mountConfig['global'])) { foreach($mountConfig['global'] as $mountPoint=>$options) { - self::mount($options['class'],$options['options'],$mountPoint); + self::mount($options['class'], $options['options'], $mountPoint); } } - + if(isset($mountConfig['group'])) { foreach($mountConfig['group'] as $group=>$mounts) { - if(OC_Group::inGroup(OC_User::getUser(),$group)) { + if(OC_Group::inGroup($user, $group)) { foreach($mounts as $mountPoint=>$options) { - $mountPoint=self::setUserVars($mountPoint); + $mountPoint=self::setUserVars($mountPoint, $user); foreach($options as &$option) { - $option=self::setUserVars($option); + $option=self::setUserVars($option, $user); } - self::mount($options['class'],$options['options'],$mountPoint); + self::mount($options['class'], $options['options'], $mountPoint); } } } } - + if(isset($mountConfig['user'])) { - foreach($mountConfig['user'] as $user=>$mounts) { - if($user==='all' or strtolower($user)===strtolower(OC_User::getUser())) { + foreach($mountConfig['user'] as $mountUser=>$mounts) { + if($user==='all' or strtolower($mountUser)===strtolower($user)) { foreach($mounts as $mountPoint=>$options) { - $mountPoint=self::setUserVars($mountPoint); + $mountPoint=self::setUserVars($mountPoint, $user); foreach($options as &$option) { - $option=self::setUserVars($option); + $option=self::setUserVars($option, $user); } - self::mount($options['class'],$options['options'],$mountPoint); + self::mount($options['class'], $options['options'], $mountPoint); } } } } - + $mtime=filemtime(OC::$SERVERROOT.'/config/mount.php'); - $previousMTime=OC_Appconfig::getValue('files','mountconfigmtime',0); + $previousMTime=OC_Appconfig::getValue('files', 'mountconfigmtime', 0); if($mtime>$previousMTime) {//mount config has changed, filecache needs to be updated OC_FileCache::triggerUpdate(); - OC_Appconfig::setValue('files','mountconfigmtime',$mtime); + OC_Appconfig::setValue('files', 'mountconfigmtime', $mtime); } + } + } + + static public function init($root, $user = '') { + if(self::$defaultInstance) { + return false; } + self::$defaultInstance=new OC_FilesystemView($root); + + //load custom mount config + if (!isset($user)) { + $user = OC_User::getUser(); + } + self::loadSystemMountPoints($user); self::$loaded=true; } @@ -253,8 +274,12 @@ class OC_Filesystem{ * @param string intput * @return string */ - private static function setUserVars($input) { - return str_replace('$user',OC_User::getUser(),$input); + private static function setUserVars($input, $user) { + if (isset($user)) { + return str_replace('$user', $user, $input); + } else { + return str_replace('$user', OC_User::getUser(), $input); + } } /** @@ -278,7 +303,7 @@ class OC_Filesystem{ * @param array arguments * @return OC_Filestorage */ - static private function createStorage($class,$arguments) { + static private function createStorage($class, $arguments) { if(class_exists($class)) { try { return new $class($arguments); @@ -287,7 +312,7 @@ class OC_Filesystem{ return false; } }else{ - OC_Log::write('core','storage backend '.$class.' not found',OC_Log::ERROR); + OC_Log::write('core', 'storage backend '.$class.' not found', OC_Log::ERROR); return false; } } @@ -324,14 +349,14 @@ class OC_Filesystem{ * @param OC_Filestorage storage * @param string mountpoint */ - static public function mount($class,$arguments,$mountpoint) { + static public function mount($class, $arguments, $mountpoint) { if($mountpoint[0]!='/') { $mountpoint='/'.$mountpoint; } - if(substr($mountpoint,-1)!=='/') { + if(substr($mountpoint, -1)!=='/') { $mountpoint=$mountpoint.'/'; } - self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments); + self::$mounts[$mountpoint]=array('class'=>$class, 'arguments'=>$arguments); } /** @@ -371,10 +396,14 @@ class OC_Filesystem{ * @return bool */ static public function isValidPath($path) { + $path = self::normalizePath($path); if(!$path || $path[0]!=='/') { $path='/'.$path; } - if(strstr($path,'/../') || strrchr($path, '/') === '/..' ) { + if(strstr($path, '/../') || strrchr($path, '/') === '/..' ) { + return false; + } + if(self::isFileBlacklisted($path)) { return false; } return true; @@ -386,20 +415,22 @@ class OC_Filesystem{ * @param array $data from hook */ static public function isBlacklisted($data) { - $blacklist = array('.htaccess'); if (isset($data['path'])) { $path = $data['path']; } else if (isset($data['newpath'])) { $path = $data['newpath']; } if (isset($path)) { - $filename = strtolower(basename($path)); - if (in_array($filename, $blacklist)) { - $data['run'] = false; - } + $data['run'] = !self::isFileBlacklisted($path); } } + static public function isFileBlacklisted($path) { + $blacklist = array('.htaccess'); + $filename = strtolower(basename($path)); + return in_array($filename, $blacklist); + } + /** * following functions are equivilent to their php buildin equivilents for arguments/return values. */ @@ -475,33 +506,33 @@ class OC_Filesystem{ static public function file_get_contents($path) { return self::$defaultInstance->file_get_contents($path); } - static public function file_put_contents($path,$data) { - return self::$defaultInstance->file_put_contents($path,$data); + static public function file_put_contents($path, $data) { + return self::$defaultInstance->file_put_contents($path, $data); } static public function unlink($path) { return self::$defaultInstance->unlink($path); } - static public function rename($path1,$path2) { - return self::$defaultInstance->rename($path1,$path2); + static public function rename($path1, $path2) { + return self::$defaultInstance->rename($path1, $path2); } - static public function copy($path1,$path2) { - return self::$defaultInstance->copy($path1,$path2); + static public function copy($path1, $path2) { + return self::$defaultInstance->copy($path1, $path2); } - static public function fopen($path,$mode) { - return self::$defaultInstance->fopen($path,$mode); + static public function fopen($path, $mode) { + return self::$defaultInstance->fopen($path, $mode); } static public function toTmpFile($path) { return self::$defaultInstance->toTmpFile($path); } - static public function fromTmpFile($tmpFile,$path) { - return self::$defaultInstance->fromTmpFile($tmpFile,$path); + static public function fromTmpFile($tmpFile, $path) { + return self::$defaultInstance->fromTmpFile($tmpFile, $path); } static public function getMimeType($path) { return self::$defaultInstance->getMimeType($path); } - static public function hash($type,$path, $raw = false) { - return self::$defaultInstance->hash($type,$path, $raw); + static public function hash($type, $path, $raw = false) { + return self::$defaultInstance->hash($type, $path, $raw); } static public function free_space($path='/') { @@ -517,8 +548,8 @@ class OC_Filesystem{ * @param int $time * @return bool */ - static public function hasUpdated($path,$time) { - return self::$defaultInstance->hasUpdated($path,$time); + static public function hasUpdated($path, $time) { + return self::$defaultInstance->hasUpdated($path, $time); } static public function removeETagHook($params, $root = false) { @@ -544,23 +575,23 @@ class OC_Filesystem{ * @param bool $stripTrailingSlash * @return string */ - public static function normalizePath($path,$stripTrailingSlash=true) { + public static function normalizePath($path, $stripTrailingSlash=true) { if($path=='') { return '/'; } //no windows style slashes - $path=str_replace('\\','/',$path); + $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); + 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); + while(strpos($path, '//')!==false) { + $path=str_replace('//', '/', $path); } //normalize unicode if possible if(class_exists('Normalizer')) { @@ -569,9 +600,9 @@ class OC_Filesystem{ 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_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 2950ced5f9e..e944ae5045d 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -47,11 +47,8 @@ class OC_FilesystemView { $this->fakeRoot=$root; } - public function getAbsolutePath($path) { - if(!$path) { - $path='/'; - } - if($path[0]!=='/') { + public function getAbsolutePath($path = '/') { + if(!$path || $path[0]!=='/') { $path='/'.$path; } return $this->fakeRoot.$path; @@ -142,7 +139,7 @@ class OC_FilesystemView { * @return string */ public function getLocalFile($path) { - $parent=substr($path, 0, strrpos($path,'/')); + $parent=substr($path, 0, strrpos($path, '/')); if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { return $storage->getLocalFile($this->getInternalPath($path)); } @@ -152,7 +149,7 @@ class OC_FilesystemView { * @return string */ public function getLocalFolder($path) { - $parent=substr($path, 0, strrpos($path,'/')); + $parent=substr($path, 0, strrpos($path, '/')); if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { return $storage->getLocalFolder($this->getInternalPath($path)); } @@ -198,7 +195,7 @@ class OC_FilesystemView { return $this->basicOperation('filesize', $path); } public function readfile($path) { - @ob_end_clean(); + OC_Util::obEnd(); $handle=$this->fopen($path, 'rb'); if ($handle) { $chunkSize = 8192;// 8 MB chunks @@ -215,13 +212,13 @@ class OC_FilesystemView { * @deprecated Replaced by isReadable() as part of CRUDS */ public function is_readable($path) { - return $this->basicOperation('isReadable',$path); + return $this->basicOperation('isReadable', $path); } /** * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS */ public function is_writable($path) { - return $this->basicOperation('isUpdatable',$path); + return $this->basicOperation('isUpdatable', $path); } public function isCreatable($path) { return $this->basicOperation('isCreatable', $path); @@ -251,6 +248,9 @@ class OC_FilesystemView { return $this->basicOperation('filemtime', $path); } public function touch($path, $mtime=null) { + if(!is_null($mtime) and !is_numeric($mtime)) { + $mtime = strtotime($mtime); + } return $this->basicOperation('touch', $path, array('write'), $mtime); } public function file_get_contents($path) { @@ -263,7 +263,7 @@ class OC_FilesystemView { $path = $this->getRelativePath($absolutePath); $exists = $this->file_exists($path); $run = true; - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if( $this->fakeRoot==OC_Filesystem::getRoot() ) { if(!$exists) { OC_Hook::emit( OC_Filesystem::CLASSNAME, @@ -291,7 +291,7 @@ class OC_FilesystemView { $count=OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if( $this->fakeRoot==OC_Filesystem::getRoot() ) { if(!$exists) { OC_Hook::emit( OC_Filesystem::CLASSNAME, @@ -322,8 +322,8 @@ class OC_FilesystemView { return $this->basicOperation( 'deleteAll', $directory, array('delete'), $empty ); } public function rename($path1, $path2) { - $postFix1=(substr($path1,-1,1)==='/')?'/':''; - $postFix2=(substr($path2,-1,1)==='/')?'/':''; + $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)) { @@ -334,7 +334,7 @@ class OC_FilesystemView { return false; } $run=true; - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if( $this->fakeRoot==OC_Filesystem::getRoot() ) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( @@ -359,7 +359,7 @@ class OC_FilesystemView { $storage1->unlink($this->getInternalPath($path1.$postFix1)); $result = $count>0; } - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if( $this->fakeRoot==OC_Filesystem::getRoot() ) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, @@ -374,8 +374,8 @@ class OC_FilesystemView { } } public function copy($path1, $path2) { - $postFix1=(substr($path1,-1,1)==='/')?'/':''; - $postFix2=(substr($path2,-1,1)==='/')?'/':''; + $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)) { @@ -386,7 +386,7 @@ class OC_FilesystemView { return false; } $run=true; - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if( $this->fakeRoot==OC_Filesystem::getRoot() ) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, @@ -430,7 +430,10 @@ class OC_FilesystemView { $target = $this->fopen($path2.$postFix2, 'w'); $result = OC_Helper::streamCopy($source, $target); } - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if( $this->fakeRoot==OC_Filesystem::getRoot() ) { + // If the file to be copied originates within + // the user's data directory + OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, @@ -451,11 +454,33 @@ class OC_FilesystemView { OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path2) ); - } else { // no real copy, file comes from somewhere else, e.g. version rollback -> just update the file cache and the webdav properties without all the other post_write actions - OC_FileCache_Update::update($path2, $this->fakeRoot); + + } else { + // If this is not a normal file copy operation + // and the file originates somewhere else + // (e.g. a version rollback operation), do not + // perform all the other post_write actions + + // Update webdav properties OC_Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); + + $splitPath2 = explode( '/', $path2 ); + + // Only cache information about files + // that are being copied from within + // the user files directory. Caching + // other files, like VCS backup files, + // serves no purpose + if ( $splitPath2[1] == 'files' ) { + + OC_FileCache_Update::update($path2, $this->fakeRoot); + + } + } + return $result; + } } } @@ -486,7 +511,7 @@ class OC_FilesystemView { $hooks[]='write'; break; default: - OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR); + OC_Log::write('core', 'invalid mode ('.$mode.') for '.$path, OC_Log::ERROR); } return $this->basicOperation('fopen', $path, $hooks, $mode); @@ -498,7 +523,7 @@ class OC_FilesystemView { $extension=''; $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); @@ -527,7 +552,7 @@ class OC_FilesystemView { return $this->basicOperation('getMimeType', $path); } public function hash($type, $path, $raw = false) { - $postFix=(substr($path,-1,1)==='/')?'/':''; + $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); @@ -567,7 +592,7 @@ class OC_FilesystemView { * OC_Filestorage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks=array(), $extraParam=null) { - $postFix=(substr($path,-1,1)==='/')?'/':''; + $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); @@ -575,7 +600,7 @@ class OC_FilesystemView { return false; } $internalPath = $this->getInternalPath($path.$postFix); - $run=$this->runHooks($hooks,$path); + $run=$this->runHooks($hooks, $path); if($run and $storage = $this->getStorage($path.$postFix)) { if(!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); @@ -585,7 +610,7 @@ class OC_FilesystemView { $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); + $this->runHooks($hooks, $path, true); } } return $result; @@ -594,7 +619,7 @@ class OC_FilesystemView { return null; } - private function runHooks($hooks,$path,$post=false) { + private function runHooks($hooks, $path, $post=false) { $prefix=($post)?'post_':''; $run=true; if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { diff --git a/lib/group.php b/lib/group.php index 66892a99b60..ed9482418bd 100644 --- a/lib/group.php +++ b/lib/group.php @@ -65,15 +65,8 @@ class OC_Group { * * Tries to create a new group. If the group name already exists, false will * be returned. Basic checking of Group name - * - * Allowed characters in the username are: "a-z", "A-Z", "0-9" and "_.@-" */ public static function createGroup( $gid ) { - // Check the name for bad characters - // Allowed are: "a-z", "A-Z", "0-9" and "_.@-" - if( preg_match( '/[^a-zA-Z0-9 _\.@\-]/', $gid )) { - return false; - } // No empty group names! if( !$gid ) { return false; @@ -146,7 +139,7 @@ class OC_Group { */ public static function inGroup( $uid, $gid ) { foreach(self::$_usedBackends as $backend) { - if($backend->inGroup($uid,$gid)) { + if($backend->inGroup($uid, $gid)) { return true; } } @@ -230,7 +223,7 @@ class OC_Group { public static function getUserGroups( $uid ) { $groups=array(); foreach(self::$_usedBackends as $backend) { - $groups=array_merge($backend->getUserGroups($uid),$groups); + $groups=array_merge($backend->getUserGroups($uid), $groups); } asort($groups); return $groups; diff --git a/lib/group/dummy.php b/lib/group/dummy.php index 8116dcbd675..9516fd52ff8 100644 --- a/lib/group/dummy.php +++ b/lib/group/dummy.php @@ -69,7 +69,7 @@ class OC_Group_Dummy extends OC_Group_Backend { */ public function inGroup($uid, $gid) { if(isset($this->groups[$gid])) { - return (array_search($uid,$this->groups[$gid])!==false); + return (array_search($uid, $this->groups[$gid])!==false); }else{ return false; } @@ -85,7 +85,7 @@ class OC_Group_Dummy extends OC_Group_Backend { */ public function addToGroup($uid, $gid) { if(isset($this->groups[$gid])) { - if(array_search($uid,$this->groups[$gid])===false) { + if(array_search($uid, $this->groups[$gid])===false) { $this->groups[$gid][]=$uid; return true; }else{ @@ -104,9 +104,9 @@ class OC_Group_Dummy extends OC_Group_Backend { * * removes the user from a group. */ - public function removeFromGroup($uid,$gid) { + public function removeFromGroup($uid, $gid) { if(isset($this->groups[$gid])) { - if(($index=array_search($uid,$this->groups[$gid]))!==false) { + if(($index=array_search($uid, $this->groups[$gid]))!==false) { unset($this->groups[$gid][$index]); }else{ return false; @@ -128,7 +128,7 @@ class OC_Group_Dummy extends OC_Group_Backend { $groups=array(); $allGroups=array_keys($this->groups); foreach($allGroups as $group) { - if($this->inGroup($uid,$group)) { + if($this->inGroup($uid, $group)) { $groups[]=$group; } } diff --git a/lib/group/example.php b/lib/group/example.php index 76d12629763..3519b9ed92f 100644 --- a/lib/group/example.php +++ b/lib/group/example.php @@ -73,7 +73,7 @@ abstract class OC_Group_Example { * * removes the user from a group. */ - abstract public static function removeFromGroup($uid,$gid); + abstract public static function removeFromGroup($uid, $gid); /** * @brief Get all groups a user belongs to diff --git a/lib/helper.php b/lib/helper.php index 2c221ddf195..5dec7fadfb4 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -29,6 +29,20 @@ class OC_Helper { private static $tmpFiles=array(); /** + * @brief Creates an url using a defined route + * @param $route + * @param $parameters + * @param $args array with param=>value, will be appended to the returned url + * @returns the url + * + * Returns a url to the given app and file. + */ + public static function linkToRoute( $route, $parameters = array() ) { + $urlLinkTo = OC::getRouter()->generate($route, $parameters); + return $urlLinkTo; + } + + /** * @brief Creates an url * @param string $app app * @param string $file file @@ -44,8 +58,8 @@ class OC_Helper { // Check if the app is in the app folder 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):''; + $urlLinkTo = OC::$WEBROOT . '/index.php/apps/' . $app; + $urlLinkTo .= ($file!='index.php') ? '/' . $file : ''; }else{ $urlLinkTo = OC_App::getAppWebPath($app) . '/' . $file; } @@ -101,6 +115,17 @@ class OC_Helper { } /** + * @brief Creates an url for remote use + * @param string $service id + * @return string the url + * + * Returns a url to the given service. + */ + public static function linkToRemoteBase( $service ) { + return self::linkTo( '', 'remote.php') . '/' . $service; + } + + /** * @brief Creates an absolute url for remote use * @param string $service id * @return string the url @@ -108,7 +133,7 @@ class OC_Helper { * Returns a absolute url to the given service. */ public static function linkToRemote( $service, $add_slash = true ) { - return self::linkToAbsolute( '', 'remote.php') . '/' . $service . (($add_slash && $service[strlen($service)-1]!='/')?'/':''); + return self::makeURLAbsolute(self::linkToRemoteBase($service)) . (($add_slash && $service[strlen($service)-1]!='/')?'/':''); } /** @@ -178,7 +203,7 @@ class OC_Helper { return OC::$WEBROOT."/core/img/filetypes/$mimetype.png"; } //try only the first part of the filetype - $mimetype=substr($mimetype,0,strpos($mimetype,'-')); + $mimetype=substr($mimetype, 0, strpos($mimetype, '-')); if( file_exists( OC::$SERVERROOT."/core/img/filetypes/$mimetype.png" )) { return OC::$WEBROOT."/core/img/filetypes/$mimetype.png"; } @@ -263,18 +288,18 @@ class OC_Helper { if($file != '.' && $file != '..') { $fullpath = $path.'/'.$file; if(is_link($fullpath)) - return FALSE; + return false; elseif(!is_dir($fullpath) && !@chmod($fullpath, $filemode)) - return FALSE; + return false; elseif(!self::chmodr($fullpath, $filemode)) - return FALSE; + return false; } } closedir($dh); if(@chmod($path, $filemode)) - return TRUE; + return true; else - return FALSE; + return false; } /** @@ -294,7 +319,7 @@ class OC_Helper { self::copyr("$src/$file", "$dest/$file"); } } - }elseif(file_exists($src)) { + }elseif(file_exists($src) && !OC_Filesystem::isFileBlacklisted($src)) { copy($src, $dest); } } @@ -330,29 +355,29 @@ class OC_Helper { * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead */ static function getMimeType($path) { - $isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://'); + $isWrapped=(strpos($path, '://')!==false) and (substr($path, 0, 7)=='file://'); if (@is_dir($path)) { // directories are easy return "httpd/unix-directory"; } - if(strpos($path,'.')) { + 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'); + 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 . + $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)); + $info = @strtolower(finfo_file($finfo, $path)); if($info) { - $mimeType=substr($info,0,strpos($info,';')); + $mimeType=substr($info, 0, strpos($info, ';')); } finfo_close($finfo); } @@ -362,20 +387,15 @@ class OC_Helper { } if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) { // it looks like we have a 'file' command, - // lets see it it does have mime support + // lets see if it does have mime support $path=escapeshellarg($path); $fp = popen("file -i -b $path 2>/dev/null", "r"); $reply = fgets($fp); pclose($fp); - //trim the character set from the end of the response - $mimeType=substr($reply,0,strrpos($reply,' ')); - $mimeType=substr($mimeType,0,strrpos($mimeType,"\n")); - - //trim ; - if (strpos($mimeType, ';') !== false) { - $mimeType = strstr($mimeType, ';', true); - } + // we have smth like 'text/x-c++; charset=us-ascii\n' + // and need to eliminate everything starting with semicolon including trailing LF + $mimeType = preg_replace('/;.*/ms', '', trim($reply)); } return $mimeType; @@ -392,8 +412,8 @@ class OC_Helper { return finfo_buffer($finfo, $data); }else{ $tmpFile=OC_Helper::tmpFile(); - $fh=fopen($tmpFile,'wb'); - fwrite($fh,$data,8024); + $fh=fopen($tmpFile, 'wb'); + fwrite($fh, $data, 8024); fclose($fh); $mime=self::getMimeType($tmpFile); unset($tmpFile); @@ -455,16 +475,16 @@ class OC_Helper { $dirs = explode(PATH_SEPARATOR, $path); // WARNING : We have to check if open_basedir is enabled : $obd = ini_get('open_basedir'); - if($obd != "none"){ + if($obd != "none") { $obd_values = explode(PATH_SEPARATOR, $obd); - if(count($obd_values) > 0 and $obd_values[0]){ + if(count($obd_values) > 0 and $obd_values[0]) { // open_basedir is in effect ! // We need to check if the program is in one of these dirs : $dirs = $obd_values; } } - foreach($dirs as $dir){ - foreach($exts as $ext){ + foreach($dirs as $dir) { + foreach($exts as $ext) { if($check_fn("$dir/$name".$ext)) return true; } @@ -478,13 +498,13 @@ class OC_Helper { * @param resource $target * @return int the number of bytes copied */ - public static function streamCopy($source,$target) { + public static function streamCopy($source, $target) { if(!$source or !$target) { return false; } $count=0; while(!feof($source)) { - $count+=fwrite($target,fread($source,8192)); + $count+=fwrite($target, fread($source, 8192)); } return $count; } @@ -498,13 +518,34 @@ class OC_Helper { */ public static function tmpFile($postfix='') { $file=get_temp_dir().'/'.md5(time().rand()).$postfix; - $fh=fopen($file,'w'); + $fh=fopen($file, 'w'); fclose($fh); self::$tmpFiles[]=$file; return $file; } /** + * create a temporary file with an unique filename. It will not be deleted + * automatically + * @param string $postfix + * @return string + * + */ + public static function tmpFileNoClean($postfix='') { + $tmpDirNoClean=get_temp_dir().'/oc-noclean/'; + if (!file_exists($tmpDirNoClean) || !is_dir($tmpDirNoClean)) { + if (file_exists($tmpDirNoClean)) { + unlink($tmpDirNoClean); + } + mkdir($tmpDirNoClean); + } + $file=$tmpDirNoClean.md5(time().rand()).$postfix; + $fh=fopen($file,'w'); + fclose($fh); + return $file; + } + + /** * create a temporary folder with an unique filename * @return string * @@ -540,6 +581,16 @@ class OC_Helper { } /** + * remove all files created by self::tmpFileNoClean + */ + public static function cleanTmpNoClean() { + $tmpDirNoCleanFile=get_temp_dir().'/oc-noclean/'; + if(file_exists($tmpDirNoCleanFile)) { + self::rmdirr($tmpDirNoCleanFile); + } + } + + /** * Adds a suffix to the name in case the file exists * * @param $path @@ -692,4 +743,19 @@ class OC_Helper { return false; } + + /** + * Shortens str to maxlen by replacing characters in the middle with '...', eg. + * ellipsis('a very long string with lots of useless info to make a better example', 14) becomes 'a very ...example' + * @param string $str the string + * @param string $maxlen the maximum length of the result + * @return string with at most maxlen characters + */ + public static function ellipsis($str, $maxlen) { + if (strlen($str) > $maxlen) { + $characters = floor($maxlen / 2); + return substr($str, 0, $characters) . '...' . substr($str, -1 * $characters); + } + return $str; + } } diff --git a/lib/image.php b/lib/image.php index 861353e039d..2043a452541 100644 --- a/lib/image.php +++ b/lib/image.php @@ -20,32 +20,13 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ - -//From user comments at http://dk2.php.net/manual/en/function.exif-imagetype.php -if ( ! function_exists( 'exif_imagetype' ) ) { - function exif_imagetype ( $filename ) { - if ( ( $info = getimagesize( $filename ) ) !== false ) { - return $info[2]; - } - return false; - } -} - -function ellipsis($str, $maxlen) { - if (strlen($str) > $maxlen) { - $characters = floor($maxlen / 2); - return substr($str, 0, $characters) . '...' . substr($str, -1 * $characters); - } - return $str; -} - /** * Class for basic image manipulation - * */ class OC_Image { protected $resource = false; // tmp resource. protected $imagetype = IMAGETYPE_PNG; // Default to png if file type isn't evident. + protected $bit_depth = 24; protected $filepath = null; /** @@ -66,7 +47,7 @@ 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')) { - OC_Log::write('core',__METHOD__.'(): GD module not installed', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): GD module not installed', OC_Log::ERROR); return false; } if(!is_null($imageref)) { @@ -112,7 +93,7 @@ class OC_Image { */ public function widthTopLeft() { $o = $this->getOrientation(); - OC_Log::write('core','OC_Image->widthTopLeft() Orientation: '.$o, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->widthTopLeft() Orientation: '.$o, OC_Log::DEBUG); switch($o) { case -1: case 1: @@ -137,7 +118,7 @@ class OC_Image { */ public function heightTopLeft() { $o = $this->getOrientation(); - OC_Log::write('core','OC_Image->heightTopLeft() Orientation: '.$o, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->heightTopLeft() Orientation: '.$o, OC_Log::DEBUG); switch($o) { case -1: case 1: @@ -172,7 +153,7 @@ class OC_Image { public function save($filepath=null) { if($filepath === null && $this->filepath === null) { - OC_Log::write('core',__METHOD__.'(): called with no path.', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): called with no path.', OC_Log::ERROR); return false; } elseif($filepath === null && $this->filepath !== null) { $filepath = $this->filepath; @@ -188,10 +169,10 @@ class OC_Image { if (!file_exists(dirname($filepath))) mkdir(dirname($filepath), 0777, true); if(!is_writable(dirname($filepath))) { - OC_Log::write('core',__METHOD__.'(): Directory \''.dirname($filepath).'\' is not writable.', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): Directory \''.dirname($filepath).'\' is not writable.', OC_Log::ERROR); return false; } elseif(is_writable(dirname($filepath)) && file_exists($filepath) && !is_writable($filepath)) { - OC_Log::write('core',__METHOD__.'(): File \''.$filepath.'\' is not writable.', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): File \''.$filepath.'\' is not writable.', OC_Log::ERROR); return false; } } @@ -214,9 +195,11 @@ class OC_Image { $retval = imagexbm($this->resource, $filepath); break; case IMAGETYPE_WBMP: - case IMAGETYPE_BMP: $retval = imagewbmp($this->resource, $filepath); break; + case IMAGETYPE_BMP: + $retval = imagebmp($this->resource, $filepath, $this->bit_depth); + break; default: $retval = imagepng($this->resource, $filepath); } @@ -244,7 +227,7 @@ class OC_Image { ob_start(); $res = imagepng($this->resource); if (!$res) { - OC_Log::write('core','OC_Image->data. Error getting image data.',OC_Log::ERROR); + OC_Log::write('core', 'OC_Image->data. Error getting image data.', OC_Log::ERROR); } return ob_get_clean(); } @@ -263,15 +246,15 @@ class OC_Image { */ public function getOrientation() { if(!is_callable('exif_read_data')) { - OC_Log::write('core','OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG); return -1; } if(!$this->valid()) { - OC_Log::write('core','OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG); 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); + OC_Log::write('core', 'OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG); return -1; } $exif = @exif_read_data($this->filepath, 'IFD0'); @@ -291,7 +274,7 @@ class OC_Image { */ public function fixOrientation() { $o = $this->getOrientation(); - OC_Log::write('core','OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG); $rotate = 0; $flip = false; switch($o) { @@ -341,15 +324,15 @@ class OC_Image { $this->resource = $res; return true; } else { - OC_Log::write('core','OC_Image->fixOrientation() Error during alphasaving.', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->fixOrientation() Error during alphasaving.', OC_Log::DEBUG); return false; } } else { - OC_Log::write('core','OC_Image->fixOrientation() Error during alphablending.', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->fixOrientation() Error during alphablending.', OC_Log::DEBUG); return false; } } else { - OC_Log::write('core','OC_Image->fixOrientation() Error during oriention fixing.', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->fixOrientation() Error during oriention fixing.', OC_Log::DEBUG); return false; } } @@ -365,7 +348,7 @@ class OC_Image { if(get_resource_type($imageref) == 'gd') { $this->resource = $imageref; return $this->resource; - } elseif(in_array(get_resource_type($imageref), array('file','stream'))) { + } elseif(in_array(get_resource_type($imageref), array('file', 'stream'))) { return $this->loadFromFileHandle($imageref); } } elseif($this->loadFromFile($imageref) !== false) { @@ -375,7 +358,7 @@ class OC_Image { } elseif($this->loadFromData($imageref) !== false) { return $this->resource; } else { - OC_Log::write('core',__METHOD__.'(): couldn\'t load anything. Giving up!', OC_Log::DEBUG); + OC_Log::write('core', __METHOD__.'(): couldn\'t load anything. Giving up!', OC_Log::DEBUG); return false; } } @@ -387,7 +370,7 @@ class OC_Image { * @returns An image resource or false on error */ public function loadFromFileHandle($handle) { - OC_Log::write('core',__METHOD__.'(): Trying', OC_Log::DEBUG); + OC_Log::write('core', __METHOD__.'(): Trying', OC_Log::DEBUG); $contents = stream_get_contents($handle); if($this->loadFromData($contents)) { return $this->resource; @@ -402,7 +385,7 @@ class OC_Image { public function loadFromFile($imagepath=false) { if(!is_file($imagepath) || !file_exists($imagepath) || !is_readable($imagepath)) { // Debug output disabled because this method is tried before loadFromBase64? - OC_Log::write('core','OC_Image->loadFromFile, couldn\'t load: '.ellipsis($imagepath, 50), OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, couldn\'t load: '.$imagepath, OC_Log::DEBUG); return false; } $itype = exif_imagetype($imagepath); @@ -411,38 +394,40 @@ class OC_Image { if (imagetypes() & IMG_GIF) { $this->resource = imagecreatefromgif($imagepath); } else { - OC_Log::write('core','OC_Image->loadFromFile, GIF images not supported: '.$imagepath, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, GIF images not supported: '.$imagepath, OC_Log::DEBUG); } break; case IMAGETYPE_JPEG: if (imagetypes() & IMG_JPG) { $this->resource = imagecreatefromjpeg($imagepath); } else { - OC_Log::write('core','OC_Image->loadFromFile, JPG images not supported: '.$imagepath, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, JPG images not supported: '.$imagepath, OC_Log::DEBUG); } break; case IMAGETYPE_PNG: if (imagetypes() & IMG_PNG) { $this->resource = imagecreatefrompng($imagepath); } else { - OC_Log::write('core','OC_Image->loadFromFile, PNG images not supported: '.$imagepath, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, PNG images not supported: '.$imagepath, OC_Log::DEBUG); } break; case IMAGETYPE_XBM: if (imagetypes() & IMG_XPM) { $this->resource = imagecreatefromxbm($imagepath); } else { - OC_Log::write('core','OC_Image->loadFromFile, XBM/XPM images not supported: '.$imagepath, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, XBM/XPM images not supported: '.$imagepath, OC_Log::DEBUG); } break; case IMAGETYPE_WBMP: - case IMAGETYPE_BMP: if (imagetypes() & IMG_WBMP) { $this->resource = imagecreatefromwbmp($imagepath); } else { - OC_Log::write('core','OC_Image->loadFromFile, (W)BMP images not supported: '.$imagepath, OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, WBMP images not supported: '.$imagepath, OC_Log::DEBUG); } break; + case IMAGETYPE_BMP: + $this->resource = $this->imagecreatefrombmp($imagepath); + break; /* case IMAGETYPE_TIFF_II: // (intel byte order) break; @@ -472,7 +457,7 @@ class OC_Image { // this is mostly file created from encrypted file $this->resource = imagecreatefromstring(\OC_Filesystem::file_get_contents(\OC_Filesystem::getLocalPath($imagepath))); $itype = IMAGETYPE_PNG; - OC_Log::write('core','OC_Image->loadFromFile, Default', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromFile, Default', OC_Log::DEBUG); break; } if($this->valid()) { @@ -493,7 +478,7 @@ class OC_Image { } $this->resource = @imagecreatefromstring($str); if(!$this->resource) { - OC_Log::write('core','OC_Image->loadFromData, couldn\'t load', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromData, couldn\'t load', OC_Log::DEBUG); return false; } return $this->resource; @@ -512,7 +497,7 @@ class OC_Image { if($data) { // try to load from string data $this->resource = @imagecreatefromstring($data); if(!$this->resource) { - OC_Log::write('core','OC_Image->loadFromBase64, couldn\'t load', OC_Log::DEBUG); + OC_Log::write('core', 'OC_Image->loadFromBase64, couldn\'t load', OC_Log::DEBUG); return false; } return $this->resource; @@ -522,13 +507,154 @@ class OC_Image { } /** + * Create a new image from file or URL + * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm + * @version 1.00 + * @param string $filename <p> + * Path to the BMP image. + * </p> + * @return resource an image resource identifier on success, <b>FALSE</b> on errors. + */ + private function imagecreatefrombmp($filename) { + if (!($fh = fopen($filename, 'rb'))) { + trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING); + return false; + } + // read file header + $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); + // check for bitmap + if ($meta['type'] != 19778) { + trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING); + return false; + } + // read image header + $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); + // read additional 16bit header + if ($meta['bits'] == 16) { + $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); + } + // set bytes and padding + $meta['bytes'] = $meta['bits'] / 8; + $this->bit_depth = $meta['bits']; //remember the bit depth for the imagebmp call + $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4))); + if ($meta['decal'] == 4) { + $meta['decal'] = 0; + } + // obtain imagesize + if ($meta['imagesize'] < 1) { + $meta['imagesize'] = $meta['filesize'] - $meta['offset']; + // in rare cases filesize is equal to offset so we need to read physical size + if ($meta['imagesize'] < 1) { + $meta['imagesize'] = @filesize($filename) - $meta['offset']; + if ($meta['imagesize'] < 1) { + trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING); + return false; + } + } + } + // calculate colors + $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; + // read color palette + $palette = array(); + if ($meta['bits'] < 16) { + $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); + // in rare cases the color value is signed + if ($palette[1] < 0) { + foreach ($palette as $i => $color) { + $palette[$i] = $color + 16777216; + } + } + } + // create gd image + $im = imagecreatetruecolor($meta['width'], $meta['height']); + $data = fread($fh, $meta['imagesize']); + $p = 0; + $vide = chr(0); + $y = $meta['height'] - 1; + $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!'; + // loop through the image data beginning with the lower left corner + while ($y >= 0) { + $x = 0; + while ($x < $meta['width']) { + switch ($meta['bits']) { + case 32: + case 24: + if (!($part = substr($data, $p, 3))) { + trigger_error($error, E_USER_WARNING); + return $im; + } + $color = unpack('V', $part . $vide); + break; + case 16: + if (!($part = substr($data, $p, 2))) { + trigger_error($error, E_USER_WARNING); + return $im; + } + $color = unpack('v', $part); + $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); + break; + case 8: + $color = unpack('n', $vide . substr($data, $p, 1)); + $color[1] = $palette[ $color[1] + 1 ]; + break; + case 4: + $color = unpack('n', $vide . substr($data, floor($p), 1)); + $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; + $color[1] = $palette[ $color[1] + 1 ]; + break; + case 1: + $color = unpack('n', $vide . substr($data, floor($p), 1)); + switch (($p * 8) % 8) { + case 0: + $color[1] = $color[1] >> 7; + break; + case 1: + $color[1] = ($color[1] & 0x40) >> 6; + break; + case 2: + $color[1] = ($color[1] & 0x20) >> 5; + break; + case 3: + $color[1] = ($color[1] & 0x10) >> 4; + break; + case 4: + $color[1] = ($color[1] & 0x8) >> 3; + break; + case 5: + $color[1] = ($color[1] & 0x4) >> 2; + break; + case 6: + $color[1] = ($color[1] & 0x2) >> 1; + break; + case 7: + $color[1] = ($color[1] & 0x1); + break; + } + $color[1] = $palette[ $color[1] + 1 ]; + break; + default: + trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING); + return false; + } + imagesetpixel($im, $x, $y, $color[1]); + $x++; + $p += $meta['bytes']; + } + $y--; + $p += $meta['decal']; + } + fclose($fh); + return $im; + } + + /** * @brief Resizes the image preserving ratio. * @param $maxsize The maximum size of either the width or height. * @returns bool */ public function resize($maxsize) { if(!$this->valid()) { - OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR); return false; } $width_orig=imageSX($this->resource); @@ -549,7 +675,7 @@ class OC_Image { public function preciseResize($width, $height) { if (!$this->valid()) { - OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR); return false; } $width_orig=imageSX($this->resource); @@ -557,14 +683,14 @@ class OC_Image { $process = imagecreatetruecolor($width, $height); if ($process == false) { - OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): Error creating true color image', OC_Log::ERROR); imagedestroy($process); return false; } imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig); if ($process == false) { - OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$width.'x'.$height,OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): Error resampling process image '.$width.'x'.$height, OC_Log::ERROR); imagedestroy($process); return false; } @@ -580,7 +706,7 @@ class OC_Image { */ public function centerCrop($size=0) { if(!$this->valid()) { - OC_Log::write('core','OC_Image->centerCrop, No image loaded', OC_Log::ERROR); + OC_Log::write('core', 'OC_Image->centerCrop, No image loaded', OC_Log::ERROR); return false; } $width_orig=imageSX($this->resource); @@ -607,13 +733,13 @@ class OC_Image { } $process = imagecreatetruecolor($targetWidth, $targetHeight); if ($process == false) { - OC_Log::write('core','OC_Image->centerCrop. Error creating true color image',OC_Log::ERROR); + OC_Log::write('core', 'OC_Image->centerCrop. Error creating true color image', OC_Log::ERROR); imagedestroy($process); return false; } imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height); if ($process == false) { - OC_Log::write('core','OC_Image->centerCrop. Error resampling process image '.$width.'x'.$height,OC_Log::ERROR); + OC_Log::write('core', 'OC_Image->centerCrop. Error resampling process image '.$width.'x'.$height, OC_Log::ERROR); imagedestroy($process); return false; } @@ -627,23 +753,23 @@ class OC_Image { * @param $x Horizontal position * @param $y Vertical position * @param $w Width - * @param $h Hight + * @param $h Height * @returns bool for success or failure */ public function crop($x, $y, $w, $h) { if(!$this->valid()) { - OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR); return false; } $process = imagecreatetruecolor($w, $h); if ($process == false) { - OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): Error creating true color image', OC_Log::ERROR); imagedestroy($process); return false; } imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); if ($process == false) { - OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$w.'x'.$h,OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): Error resampling process image '.$w.'x'.$h, OC_Log::ERROR); imagedestroy($process); return false; } @@ -660,7 +786,7 @@ class OC_Image { */ public function fitIn($maxWidth, $maxHeight) { if(!$this->valid()) { - OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR); + OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR); return false; } $width_orig=imageSX($this->resource); @@ -669,7 +795,7 @@ class OC_Image { $newWidth = min($maxWidth, $ratio*$maxHeight); $newHeight = min($maxHeight, $maxWidth/$ratio); - + $this->preciseResize(round($newWidth), round($newHeight)); return true; } @@ -685,3 +811,138 @@ class OC_Image { $this->destroy(); } } +if ( ! function_exists( 'imagebmp') ) { + /** + * Output a BMP image to either the browser or a file + * @link http://www.ugia.cn/wp-data/imagebmp.php + * @author legend <legendsky@hotmail.com> + * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm + * @author mgutt <marc@gutt.it> + * @version 1.00 + * @param resource $image + * @param string $filename [optional] <p>The path to save the file to.</p> + * @param int $bit [optional] <p>Bit depth, (default is 24).</p> + * @param int $compression [optional] + * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure. + */ + function imagebmp($im, $filename='', $bit=24, $compression=0) { + if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) { + $bit = 24; + } + else if ($bit == 32) { + $bit = 24; + } + $bits = pow(2, $bit); + imagetruecolortopalette($im, true, $bits); + $width = imagesx($im); + $height = imagesy($im); + $colors_num = imagecolorstotal($im); + $rgb_quad = ''; + if ($bit <= 8) { + for ($i = 0; $i < $colors_num; $i++) { + $colors = imagecolorsforindex($im, $i); + $rgb_quad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0"; + } + $bmp_data = ''; + if ($compression == 0 || $bit < 8) { + $compression = 0; + $extra = ''; + $padding = 4 - ceil($width / (8 / $bit)) % 4; + if ($padding % 4 != 0) { + $extra = str_repeat("\0", $padding); + } + for ($j = $height - 1; $j >= 0; $j --) { + $i = 0; + while ($i < $width) { + $bin = 0; + $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0; + for ($k = 8 - $bit; $k >= $limit; $k -= $bit) { + $index = imagecolorat($im, $i, $j); + $bin |= $index << $k; + $i++; + } + $bmp_data .= chr($bin); + } + $bmp_data .= $extra; + } + } + // RLE8 + else if ($compression == 1 && $bit == 8) { + for ($j = $height - 1; $j >= 0; $j--) { + $last_index = "\0"; + $same_num = 0; + for ($i = 0; $i <= $width; $i++) { + $index = imagecolorat($im, $i, $j); + if ($index !== $last_index || $same_num > 255) { + if ($same_num != 0) { + $bmp_data .= chr($same_num) . chr($last_index); + } + $last_index = $index; + $same_num = 1; + } + else { + $same_num++; + } + } + $bmp_data .= "\0\0"; + } + $bmp_data .= "\0\1"; + } + $size_quad = strlen($rgb_quad); + $size_data = strlen($bmp_data); + } + else { + $extra = ''; + $padding = 4 - ($width * ($bit / 8)) % 4; + if ($padding % 4 != 0) { + $extra = str_repeat("\0", $padding); + } + $bmp_data = ''; + for ($j = $height - 1; $j >= 0; $j--) { + for ($i = 0; $i < $width; $i++) { + $index = imagecolorat($im, $i, $j); + $colors = imagecolorsforindex($im, $index); + if ($bit == 16) { + $bin = 0 << $bit; + $bin |= ($colors['red'] >> 3) << 10; + $bin |= ($colors['green'] >> 3) << 5; + $bin |= $colors['blue'] >> 3; + $bmp_data .= pack("v", $bin); + } + else { + $bmp_data .= pack("c*", $colors['blue'], $colors['green'], $colors['red']); + } + } + $bmp_data .= $extra; + } + $size_quad = 0; + $size_data = strlen($bmp_data); + $colors_num = 0; + } + $file_header = 'BM' . pack('V3', 54 + $size_quad + $size_data, 0, 54 + $size_quad); + $info_header = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $size_data, 0, 0, $colors_num, 0); + if ($filename != '') { + $fp = fopen($filename, 'wb'); + fwrite($fp, $file_header . $info_header . $rgb_quad . $bmp_data); + fclose($fp); + return true; + } + echo $file_header . $info_header. $rgb_quad . $bmp_data; + return true; + } +} + +if ( ! function_exists( 'exif_imagetype' ) ) { + /** + * Workaround if exif_imagetype does not exist + * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383 + * @param string $filename + * @return string|boolean + */ + function exif_imagetype ( $filename ) { + if ( ( $info = getimagesize( $filename ) ) !== false ) { + return $info[2]; + } + return false; + } +} diff --git a/lib/installer.php b/lib/installer.php index 9135c60fc05..7dc8b0cef8d 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -57,7 +57,7 @@ class OC_Installer{ */ public static function installApp( $data = array()) { if(!isset($data['source'])) { - OC_Log::write('core','No source specified when installing app',OC_Log::ERROR); + OC_Log::write('core', 'No source specified when installing app', OC_Log::ERROR); return false; } @@ -65,13 +65,13 @@ class OC_Installer{ if($data['source']=='http') { $path=OC_Helper::tmpFile(); if(!isset($data['href'])) { - OC_Log::write('core','No href specified when installing app from http',OC_Log::ERROR); + OC_Log::write('core', 'No href specified when installing app from http', OC_Log::ERROR); return false; } - copy($data['href'],$path); + copy($data['href'], $path); }else{ if(!isset($data['path'])) { - OC_Log::write('core','No path specified when installing app from local file',OC_Log::ERROR); + OC_Log::write('core', 'No path specified when installing app from local file', OC_Log::ERROR); return false; } $path=$data['path']; @@ -80,13 +80,13 @@ class OC_Installer{ //detect the archive type $mime=OC_Helper::getMimeType($path); if($mime=='application/zip') { - rename($path,$path.'.zip'); + rename($path, $path.'.zip'); $path.='.zip'; }elseif($mime=='application/x-gzip') { - rename($path,$path.'.tgz'); + rename($path, $path.'.tgz'); $path.='.tgz'; }else{ - OC_Log::write('core','Archives of type '.$mime.' are not supported',OC_Log::ERROR); + OC_Log::write('core', 'Archives of type '.$mime.' are not supported', OC_Log::ERROR); return false; } @@ -97,7 +97,7 @@ class OC_Installer{ if($archive=OC_Archive::open($path)) { $archive->extract($extractDir); } else { - OC_Log::write('core','Failed to open archive when installing app',OC_Log::ERROR); + OC_Log::write('core', 'Failed to open archive when installing app', OC_Log::ERROR); OC_Helper::rmdirr($extractDir); if($data['source']=='http') { unlink($path); @@ -118,17 +118,17 @@ class OC_Installer{ } } if(!is_file($extractDir.'/appinfo/info.xml')) { - OC_Log::write('core','App does not provide an info.xml file',OC_Log::ERROR); + OC_Log::write('core', 'App does not provide an info.xml file', OC_Log::ERROR); OC_Helper::rmdirr($extractDir); if($data['source']=='http') { unlink($path); } return false; } - $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml',true); + $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true); // 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); + 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; } @@ -136,14 +136,14 @@ class OC_Installer{ // 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_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; } //check if an app with the same id is already installed if(self::isInstalled( $info['id'] )) { - OC_Log::write('core','App already installed',OC_Log::WARN); + OC_Log::write('core', 'App already installed', OC_Log::WARN); OC_Helper::rmdirr($extractDir); if($data['source']=='http') { unlink($path); @@ -154,7 +154,7 @@ class OC_Installer{ $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); + OC_Log::write('core', 'App directory already exists', OC_Log::WARN); OC_Helper::rmdirr($extractDir); if($data['source']=='http') { unlink($path); @@ -168,14 +168,14 @@ class OC_Installer{ //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); + OC_Log::write('core', 'Can\'t create app folder. Please fix permissions. ('.$basedir.')', OC_Log::ERROR); OC_Helper::rmdirr($extractDir); if($data['source']=='http') { unlink($path); } return false; } - OC_Helper::copyr($extractDir,$basedir); + OC_Helper::copyr($extractDir, $basedir); //remove temporary files OC_Helper::rmdirr($extractDir); @@ -187,12 +187,12 @@ class OC_Installer{ //run appinfo/install.php if((!isset($data['noinstall']) or $data['noinstall']==false) and file_exists($basedir.'/appinfo/install.php')) { - include($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'); + 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) { @@ -248,7 +248,7 @@ class OC_Installer{ * -# including appinfo/upgrade.php * -# setting the installed version * - * upgrade.php can determine the current installed version of the app using "OC_Appconfig::getValue($appid,'installed_version')" + * upgrade.php can determine the current installed version of the app using "OC_Appconfig::getValue($appid, 'installed_version')" */ public static function upgradeApp( $data = array()) { // TODO: write function @@ -296,7 +296,7 @@ class OC_Installer{ $enabled = isset($info['default_enable']); if( $enabled ) { OC_Installer::installShippedApp($filename); - OC_Appconfig::setValue($filename,'enabled','yes'); + OC_Appconfig::setValue($filename, 'enabled', 'yes'); } } } @@ -320,10 +320,10 @@ class OC_Installer{ //run appinfo/install.php if(is_file(OC_App::getAppPath($app)."/appinfo/install.php")) { - include(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)); + OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); //set remote/public handelers foreach($info['remote'] as $name=>$path) { @@ -344,7 +344,7 @@ class OC_Installer{ * @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) { + public static function checkCode($appname, $folder) { $blacklist=array( 'exec(', @@ -360,7 +360,7 @@ class OC_Installer{ // check if grep is installed $grep = exec('which grep'); if($grep=='') { - OC_Log::write('core','grep not installed. So checking the code of the app "'.$appname.'" was not possible',OC_Log::ERROR); + OC_Log::write('core', 'grep not installed. So checking the code of the app "'.$appname.'" was not possible', OC_Log::ERROR); return true; } @@ -370,7 +370,7 @@ class OC_Installer{ $result = exec($cmd); // bad pattern found if($result<>'') { - OC_Log::write('core','App "'.$appname.'" is using a not allowed call "'.$bl.'". Installation refused.',OC_Log::ERROR); + OC_Log::write('core', 'App "'.$appname.'" is using a not allowed call "'.$bl.'". Installation refused.', OC_Log::ERROR); return false; } } diff --git a/lib/json.php b/lib/json.php index 518c3c87c49..204430411c0 100644 --- a/lib/json.php +++ b/lib/json.php @@ -58,6 +58,7 @@ class OC_JSON{ */ public static function checkAdminUser() { self::checkLoggedIn(); + self::verifyUser(); if( !OC_Group::inGroup( OC_User::getUser(), 'admin' )) { $l = OC_L10N::get('lib'); self::error(array( 'data' => array( 'message' => $l->t('Authentication error') ))); @@ -70,7 +71,8 @@ class OC_JSON{ */ public static function checkSubAdminUser() { self::checkLoggedIn(); - if(!OC_Group::inGroup(OC_User::getUser(),'admin') && !OC_SubAdmin::isSubAdmin(OC_User::getUser())) { + self::verifyUser(); + if(!OC_Group::inGroup(OC_User::getUser(), 'admin') && !OC_SubAdmin::isSubAdmin(OC_User::getUser())) { $l = OC_L10N::get('lib'); self::error(array( 'data' => array( 'message' => $l->t('Authentication error') ))); exit(); @@ -78,6 +80,19 @@ class OC_JSON{ } /** + * Check if the user verified the login with his password + */ + public static function verifyUser() { + if(OC_Config::getValue('enhancedauth', false) === true) { + if(!isset($_SESSION['verifiedLogin']) OR $_SESSION['verifiedLogin'] < time()) { + $l = OC_L10N::get('lib'); + self::error(array( 'data' => array( 'message' => $l->t('Authentication error') ))); + exit(); + } + } + } + + /** * Send json error msg */ public static function error($data = array()) { @@ -105,7 +120,7 @@ class OC_JSON{ /** * Encode and print $data in json format */ - public static function encodedPrint($data,$setContentType=true) { + public static function encodedPrint($data, $setContentType=true) { // Disable mimesniffing, don't move this to setContentTypeHeader! header( 'X-Content-Type-Options: nosniff' ); if($setContentType) { diff --git a/lib/l10n.php b/lib/l10n.php index 4eb4c323d88..f172710e5d7 100644 --- a/lib/l10n.php +++ b/lib/l10n.php @@ -58,22 +58,24 @@ class OC_L10N{ * Localization */ private $localizations = array( - 'date' => 'd.m.Y', - 'datetime' => 'd.m.Y H:i:s', - 'time' => 'H:i:s'); + 'jsdate' => 'dd.mm.yy', + 'date' => '%d.%m.%Y', + 'datetime' => '%d.%m.%Y %H:%M:%S', + 'time' => '%H:%M:%S', + 'firstday' => 0); /** * get an L10N instance * @return OC_L10N */ - public static function get($app,$lang=null) { + public static function get($app, $lang=null) { if(is_null($lang)) { if(!isset(self::$instances[$app])) { self::$instances[$app]=new OC_L10N($app); } return self::$instances[$app]; }else{ - return new OC_L10N($app,$lang); + return new OC_L10N($app, $lang); } } @@ -118,7 +120,7 @@ class OC_L10N{ OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/lib/l10n/') || OC_Helper::issubdirectory($i18ndir.$lang.'.php', OC::$SERVERROOT.'/settings')) && file_exists($i18ndir.$lang.'.php')) { // Include the file, save the data from $CONFIG - include(strip_tags($i18ndir).strip_tags($lang).'.php'); + include strip_tags($i18ndir).strip_tags($lang).'.php'; if(isset($TRANSLATIONS) && is_array($TRANSLATIONS)) { $this->translations = $TRANSLATIONS; } @@ -126,7 +128,7 @@ class OC_L10N{ if(file_exists(OC::$SERVERROOT.'/core/l10n/l10n-'.$lang.'.php')) { // Include the file, save the data from $CONFIG - include(OC::$SERVERROOT.'/core/l10n/l10n-'.$lang.'.php'); + include OC::$SERVERROOT.'/core/l10n/l10n-'.$lang.'.php'; if(isset($LOCALIZATIONS) && is_array($LOCALIZATIONS)) { $this->localizations = array_merge($this->localizations, $LOCALIZATIONS); } @@ -165,7 +167,7 @@ class OC_L10N{ * */ public function tA($textArray) { - OC_Log::write('core', 'DEPRECATED: the method tA is deprecated and will be removed soon.',OC_Log::WARN); + 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] = (string)$this->t($text); @@ -216,8 +218,21 @@ class OC_L10N{ case 'time': if($data instanceof DateTime) return $data->format($this->localizations[$type]); elseif(is_string($data)) $data = strtotime($data); - return date($this->localizations[$type], $data); + $locales = array(self::findLanguage()); + if (strlen($locales[0]) == 2) { + $locales[] = $locales[0].'_'.strtoupper($locales[0]); + } + setlocale(LC_TIME, $locales); + $format = $this->localizations[$type]; + // Check for Windows to find and replace the %e modifier correctly + if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { + $format = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $format); + } + return strftime($format, $data); break; + case 'firstday': + case 'jsdate': + return $this->localizations[$type]; default: return false; } diff --git a/lib/l10n/ar.php b/lib/l10n/ar.php new file mode 100644 index 00000000000..3ae226f04fd --- /dev/null +++ b/lib/l10n/ar.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Help" => "المساعدة", +"Personal" => "شخصي", +"Settings" => "تعديلات", +"Users" => "المستخدمين", +"Authentication error" => "لم يتم التأكد من الشخصية بنجاح", +"Files" => "الملفات", +"Text" => "معلومات إضافية" +); diff --git a/lib/l10n/bg_BG.php b/lib/l10n/bg_BG.php new file mode 100644 index 00000000000..3eb0660d944 --- /dev/null +++ b/lib/l10n/bg_BG.php @@ -0,0 +1,4 @@ +<?php $TRANSLATIONS = array( +"Personal" => "Лично", +"Authentication error" => "Проблем с идентификацията" +); diff --git a/lib/l10n/ca.php b/lib/l10n/ca.php index 031207227ec..b3321ef82e1 100644 --- a/lib/l10n/ca.php +++ b/lib/l10n/ca.php @@ -12,17 +12,23 @@ "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.", +"Files" => "Fitxers", +"Text" => "Text", +"Images" => "Imatges", "seconds ago" => "segons enrere", "1 minute ago" => "fa 1 minut", "%d minutes ago" => "fa %d minuts", +"1 hour ago" => "fa 1 hora", +"%d hours ago" => "fa %d hores", "today" => "avui", "yesterday" => "ahir", "%d days ago" => "fa %d dies", "last month" => "el mes passat", -"months ago" => "mesos enrere", +"%d months ago" => "fa %d mesos", "last year" => "l'any passat", "years ago" => "fa anys", "%s is available. Get <a href=\"%s\">more information</a>" => "%s està disponible. Obtén <a href=\"%s\">més informació</a>", "up to date" => "actualitzat", -"updates check is disabled" => "la comprovació d'actualitzacions està desactivada" +"updates check is disabled" => "la comprovació d'actualitzacions està desactivada", +"Could not find category \"%s\"" => "No s'ha trobat la categoria \"%s\"" ); diff --git a/lib/l10n/cs_CZ.php b/lib/l10n/cs_CZ.php index 00815f97533..fa11e886774 100644 --- a/lib/l10n/cs_CZ.php +++ b/lib/l10n/cs_CZ.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Aplikace není povolena", "Authentication error" => "Chyba ověření", "Token expired. Please reload page." => "Token vypršel. Obnovte prosím stránku.", +"Files" => "Soubory", +"Text" => "Text", +"Images" => "Obrázky", "seconds ago" => "před vteřinami", "1 minute ago" => "před 1 minutou", "%d minutes ago" => "před %d minutami", +"1 hour ago" => "před hodinou", +"%d hours ago" => "před %d hodinami", "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", +"%d months ago" => "Před %d měsíci", "last year" => "loni", "years ago" => "před lety", "%s is available. Get <a href=\"%s\">more information</a>" => "%s je dostupná. Získat <a href=\"%s\">více informací</a>", "up to date" => "aktuální", -"updates check is disabled" => "kontrola aktualizací je vypnuta" +"updates check is disabled" => "kontrola aktualizací je vypnuta", +"Could not find category \"%s\"" => "Nelze nalézt kategorii \"%s\"" ); diff --git a/lib/l10n/da.php b/lib/l10n/da.php index 09124c18290..7458b329782 100644 --- a/lib/l10n/da.php +++ b/lib/l10n/da.php @@ -12,6 +12,8 @@ "Application is not enabled" => "Programmet er ikke aktiveret", "Authentication error" => "Adgangsfejl", "Token expired. Please reload page." => "Adgang er udløbet. Genindlæs siden.", +"Files" => "Filer", +"Text" => "SMS", "seconds ago" => "sekunder siden", "1 minute ago" => "1 minut siden", "%d minutes ago" => "%d minutter siden", @@ -19,7 +21,6 @@ "yesterday" => "I går", "%d days ago" => "%d dage siden", "last month" => "Sidste måned", -"months ago" => "måneder siden", "last year" => "Sidste år", "years ago" => "år siden", "%s is available. Get <a href=\"%s\">more information</a>" => "%s er tilgængelig. Få <a href=\"%s\">mere information</a>", diff --git a/lib/l10n/de.php b/lib/l10n/de.php index 8c81be16582..7724d8c684f 100644 --- a/lib/l10n/de.php +++ b/lib/l10n/de.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Die Anwendung ist nicht aktiviert", "Authentication error" => "Authentifizierungs-Fehler", "Token expired. Please reload page." => "Token abgelaufen. Bitte lade die Seite neu.", -"seconds ago" => "Vor wenigen Sekunden", +"Files" => "Dateien", +"Text" => "Text", +"Images" => "Bilder", +"seconds ago" => "Gerade eben", "1 minute ago" => "Vor einer Minute", "%d minutes ago" => "Vor %d Minuten", +"1 hour ago" => "Vor einer Stunde", +"%d hours ago" => "Vor %d Stunden", "today" => "Heute", "yesterday" => "Gestern", "%d days ago" => "Vor %d Tag(en)", "last month" => "Letzten Monat", -"months ago" => "Vor Monaten", +"%d months ago" => "Vor %d Monaten", "last year" => "Letztes Jahr", -"years ago" => "Vor Jahren", +"years ago" => "Vor wenigen Jahren", "%s is available. Get <a href=\"%s\">more information</a>" => "%s ist verfügbar. <a href=\"%s\">Weitere Informationen</a>", "up to date" => "aktuell", -"updates check is disabled" => "Die Update-Überprüfung ist ausgeschaltet" +"updates check is disabled" => "Die Update-Überprüfung ist ausgeschaltet", +"Could not find category \"%s\"" => "Die Kategorie \"%s\" konnte nicht gefunden werden." ); diff --git a/lib/l10n/de_DE.php b/lib/l10n/de_DE.php new file mode 100644 index 00000000000..95596a7a33a --- /dev/null +++ b/lib/l10n/de_DE.php @@ -0,0 +1,34 @@ +<?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.", +"Files" => "Dateien", +"Text" => "Text", +"Images" => "Bilder", +"seconds ago" => "Gerade eben", +"1 minute ago" => "Vor einer Minute", +"%d minutes ago" => "Vor %d Minuten", +"1 hour ago" => "Vor einer Stunde", +"%d hours ago" => "Vor %d Stunden", +"today" => "Heute", +"yesterday" => "Gestern", +"%d days ago" => "Vor %d Tag(en)", +"last month" => "Letzten Monat", +"%d months ago" => "Vor %d Monaten", +"last year" => "Letztes Jahr", +"years ago" => "Vor wenigen Jahren", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s ist verfügbar. <a href=\"%s\">Weitere Informationen</a>", +"up to date" => "aktuell", +"updates check is disabled" => "Die Update-Überprüfung ist ausgeschaltet", +"Could not find category \"%s\"" => "Die Kategorie \"%s\" konnte nicht gefunden werden." +); diff --git a/lib/l10n/el.php b/lib/l10n/el.php index e4e12490711..315b995ecc9 100644 --- a/lib/l10n/el.php +++ b/lib/l10n/el.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Δεν ενεργοποιήθηκε η εφαρμογή", "Authentication error" => "Σφάλμα πιστοποίησης", "Token expired. Please reload page." => "Το αναγνωριστικό έληξε. Παρακαλώ φορτώστε ξανά την σελίδα.", +"Files" => "Αρχεία", +"Text" => "Κείμενο", +"Images" => "Εικόνες", "seconds ago" => "δευτερόλεπτα πριν", "1 minute ago" => "1 λεπτό πριν", "%d minutes ago" => "%d λεπτά πριν", +"1 hour ago" => "1 ώρα πριν", +"%d hours ago" => "%d ώρες πριν", "today" => "σήμερα", "yesterday" => "χθές", "%d days ago" => "%d ημέρες πριν", "last month" => "τον προηγούμενο μήνα", -"months ago" => "μήνες πριν", +"%d months ago" => "%d μήνες πριν", "last year" => "τον προηγούμενο χρόνο", "years ago" => "χρόνια πριν", "%s is available. Get <a href=\"%s\">more information</a>" => "%s είναι διαθέσιμα. Δείτε <a href=\"%s\">περισσότερες πληροφορίες</a>", "up to date" => "ενημερωμένο", -"updates check is disabled" => "ο έλεγχος ενημερώσεων είναι απενεργοποιημένος" +"updates check is disabled" => "ο έλεγχος ενημερώσεων είναι απενεργοποιημένος", +"Could not find category \"%s\"" => "Αδυναμία εύρεσης κατηγορίας \"%s\"" ); diff --git a/lib/l10n/eo.php b/lib/l10n/eo.php index b3c1c52ecee..dac11ffe7e6 100644 --- a/lib/l10n/eo.php +++ b/lib/l10n/eo.php @@ -12,17 +12,23 @@ "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.", +"Files" => "Dosieroj", +"Text" => "Teksto", +"Images" => "Bildoj", "seconds ago" => "sekundojn antaŭe", "1 minute ago" => "antaŭ 1 minuto", "%d minutes ago" => "antaŭ %d minutoj", +"1 hour ago" => "antaŭ 1 horo", +"%d hours ago" => "antaŭ %d horoj", "today" => "hodiaŭ", "yesterday" => "hieraŭ", "%d days ago" => "antaŭ %d tagoj", "last month" => "lasta monato", -"months ago" => "monatojn antaŭe", +"%d months ago" => "antaŭ %d monatoj", "last year" => "lasta jaro", "years ago" => "jarojn antaŭe", "%s is available. Get <a href=\"%s\">more information</a>" => "%s haveblas. Ekhavu <a href=\"%s\">pli da informo</a>", "up to date" => "ĝisdata", -"updates check is disabled" => "ĝisdateckontrolo estas malkapabligita" +"updates check is disabled" => "ĝisdateckontrolo estas malkapabligita", +"Could not find category \"%s\"" => "Ne troviĝis kategorio “%s”" ); diff --git a/lib/l10n/es.php b/lib/l10n/es.php index 6d2a310ca3b..f843c42dfd3 100644 --- a/lib/l10n/es.php +++ b/lib/l10n/es.php @@ -12,17 +12,23 @@ "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.", +"Files" => "Archivos", +"Text" => "Texto", +"Images" => "Imágenes", "seconds ago" => "hace segundos", "1 minute ago" => "hace 1 minuto", "%d minutes ago" => "hace %d minutos", +"1 hour ago" => "Hace 1 hora", +"%d hours ago" => "Hace %d horas", "today" => "hoy", "yesterday" => "ayer", "%d days ago" => "hace %d días", "last month" => "este mes", -"months ago" => "hace meses", +"%d months ago" => "Hace %d meses", "last year" => "este año", "years ago" => "hace años", "%s is available. Get <a href=\"%s\">more information</a>" => "%s está disponible. Obtén <a href=\"%s\">más información</a>", "up to date" => "actualizado", -"updates check is disabled" => "comprobar actualizaciones está desactivado" +"updates check is disabled" => "comprobar actualizaciones está desactivado", +"Could not find category \"%s\"" => "No puede encontrar la categoria \"%s\"" ); diff --git a/lib/l10n/es_AR.php b/lib/l10n/es_AR.php index fd50027d8a1..2bbffd39e9e 100644 --- a/lib/l10n/es_AR.php +++ b/lib/l10n/es_AR.php @@ -12,17 +12,23 @@ "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, recargá la página.", +"Files" => "Archivos", +"Text" => "Texto", +"Images" => "Imágenes", "seconds ago" => "hace unos segundos", "1 minute ago" => "hace 1 minuto", "%d minutes ago" => "hace %d minutos", +"1 hour ago" => "1 hora atrás", +"%d hours ago" => "%d horas atrás", "today" => "hoy", "yesterday" => "ayer", "%d days ago" => "hace %d días", "last month" => "este mes", -"months ago" => "hace meses", +"%d months ago" => "%d meses atrás", "last year" => "este año", "years ago" => "hace años", "%s is available. Get <a href=\"%s\">more information</a>" => "%s está disponible. Conseguí <a href=\"%s\">más información</a>", "up to date" => "actualizado", -"updates check is disabled" => "comprobar actualizaciones está desactivado" +"updates check is disabled" => "comprobar actualizaciones está desactivado", +"Could not find category \"%s\"" => "No fue posible encontrar la categoría \"%s\"" ); diff --git a/lib/l10n/et_EE.php b/lib/l10n/et_EE.php index 87f222af838..906abf9430a 100644 --- a/lib/l10n/et_EE.php +++ b/lib/l10n/et_EE.php @@ -12,6 +12,9 @@ "Application is not enabled" => "Rakendus pole sisse lülitatud", "Authentication error" => "Autentimise viga", "Token expired. Please reload page." => "Kontrollkood aegus. Paelun lae leht uuesti.", +"Files" => "Failid", +"Text" => "Tekst", +"Images" => "Pildid", "seconds ago" => "sekundit tagasi", "1 minute ago" => "1 minut tagasi", "%d minutes ago" => "%d minutit tagasi", @@ -19,7 +22,6 @@ "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", "%s is available. Get <a href=\"%s\">more information</a>" => "%s on saadaval. Vaata <a href=\"%s\">lisainfot</a>", diff --git a/lib/l10n/eu.php b/lib/l10n/eu.php index 461bf458778..5d47ecbda23 100644 --- a/lib/l10n/eu.php +++ b/lib/l10n/eu.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Aplikazioa ez dago gaituta", "Authentication error" => "Autentikazio errorea", "Token expired. Please reload page." => "Tokena iraungitu da. Mesedez birkargatu orria.", +"Files" => "Fitxategiak", +"Text" => "Testua", +"Images" => "Irudiak", "seconds ago" => "orain dela segundu batzuk", "1 minute ago" => "orain dela minutu 1", "%d minutes ago" => "orain dela %d minutu", +"1 hour ago" => "orain dela ordu bat", +"%d hours ago" => "orain dela %d ordu", "today" => "gaur", "yesterday" => "atzo", "%d days ago" => "orain dela %d egun", "last month" => "joan den hilabetea", -"months ago" => "orain dela hilabete batzuk", +"%d months ago" => "orain dela %d hilabete", "last year" => "joan den urtea", "years ago" => "orain dela urte batzuk", "%s is available. Get <a href=\"%s\">more information</a>" => "%s eskuragarri dago. Lortu <a href=\"%s\">informazio gehiago</a>", "up to date" => "eguneratuta", -"updates check is disabled" => "eguneraketen egiaztapena ez dago gaituta" +"updates check is disabled" => "eguneraketen egiaztapena ez dago gaituta", +"Could not find category \"%s\"" => "Ezin da \"%s\" kategoria aurkitu" ); diff --git a/lib/l10n/fa.php b/lib/l10n/fa.php index 3579329820f..ce7c7c6e970 100644 --- a/lib/l10n/fa.php +++ b/lib/l10n/fa.php @@ -4,13 +4,15 @@ "Settings" => "تنظیمات", "Users" => "کاربران", "Admin" => "مدیر", +"Authentication error" => "خطا در اعتبار سنجی", +"Files" => "پروندهها", +"Text" => "متن", "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 index 6f0ebcd16e6..6a5734e978d 100644 --- a/lib/l10n/fi_FI.php +++ b/lib/l10n/fi_FI.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Sovellusta ei ole otettu käyttöön", "Authentication error" => "Todennusvirhe", "Token expired. Please reload page." => "Valtuutus vanheni. Lataa sivu uudelleen.", +"Files" => "Tiedostot", +"Text" => "Teksti", +"Images" => "Kuvat", "seconds ago" => "sekuntia sitten", "1 minute ago" => "1 minuutti sitten", "%d minutes ago" => "%d minuuttia sitten", +"1 hour ago" => "1 tunti sitten", +"%d hours ago" => "%d tuntia sitten", "today" => "tänään", "yesterday" => "eilen", "%d days ago" => "%d päivää sitten", "last month" => "viime kuussa", -"months ago" => "kuukautta sitten", +"%d months ago" => "%d kuukautta sitten", "last year" => "viime vuonna", "years ago" => "vuotta sitten", "%s is available. Get <a href=\"%s\">more information</a>" => "%s on saatavilla. Lue <a href=\"%s\">lisätietoja</a>", "up to date" => "ajan tasalla", -"updates check is disabled" => "päivitysten tarkistus on pois käytöstä" +"updates check is disabled" => "päivitysten tarkistus on pois käytöstä", +"Could not find category \"%s\"" => "Luokkaa \"%s\" ei löytynyt" ); diff --git a/lib/l10n/fr.php b/lib/l10n/fr.php index c10259e6376..218c22c1d53 100644 --- a/lib/l10n/fr.php +++ b/lib/l10n/fr.php @@ -12,17 +12,23 @@ "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.", +"Files" => "Fichiers", +"Text" => "Texte", +"Images" => "Images", "seconds ago" => "à l'instant", "1 minute ago" => "il y a 1 minute", "%d minutes ago" => "il y a %d minutes", +"1 hour ago" => "Il y a une heure", +"%d hours ago" => "Il y a %d heures", "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", +"%d months ago" => "Il y a %d mois", "last year" => "l'année dernière", "years ago" => "il y a plusieurs années", "%s is available. Get <a href=\"%s\">more information</a>" => "%s est disponible. Obtenez <a href=\"%s\">plus d'informations</a>", "up to date" => "À jour", -"updates check is disabled" => "la vérification des mises à jour est désactivée" +"updates check is disabled" => "la vérification des mises à jour est désactivée", +"Could not find category \"%s\"" => "Impossible de trouver la catégorie \"%s\"" ); diff --git a/lib/l10n/gl.php b/lib/l10n/gl.php index 7a9de627c2d..fd59cff02fa 100644 --- a/lib/l10n/gl.php +++ b/lib/l10n/gl.php @@ -1,28 +1,34 @@ <?php $TRANSLATIONS = array( "Help" => "Axuda", "Personal" => "Personal", -"Settings" => "Preferencias", +"Settings" => "Configuracións", "Users" => "Usuarios", -"Apps" => "Apps", +"Apps" => "Aplicativos", "Admin" => "Administración", -"ZIP download is turned off." => "Descargas ZIP está deshabilitadas", -"Files need to be downloaded one by one." => "Os ficheiros necesitan ser descargados de un en un", -"Back to Files" => "Voltar a ficheiros", -"Selected files too large to generate zip file." => "Os ficheiros seleccionados son demasiado grandes para xerar un ficheiro ZIP", -"Application is not enabled" => "O aplicativo non está habilitado", +"ZIP download is turned off." => "As descargas ZIP están desactivadas", +"Files need to be downloaded one by one." => "Os ficheiros necesitan ser descargados de un en un.", +"Back to Files" => "Volver aos ficheiros", +"Selected files too large to generate zip file." => "Os ficheiros seleccionados son demasiado grandes como para xerar un ficheiro zip.", +"Application is not enabled" => "O aplicativo non está activado", "Authentication error" => "Erro na autenticación", -"Token expired. Please reload page." => "Testemuño caducado. Por favor recargue a páxina.", +"Token expired. Please reload page." => "Token caducado. Recarga a páxina.", +"Files" => "Ficheiros", +"Text" => "Texto", +"Images" => "Imaxes", "seconds ago" => "hai segundos", "1 minute ago" => "hai 1 minuto", "%d minutes ago" => "hai %d minutos", +"1 hour ago" => "1 hora antes", +"%d hours ago" => "%d horas antes", "today" => "hoxe", "yesterday" => "onte", "%d days ago" => "hai %d días", "last month" => "último mes", -"months ago" => "meses atrás", +"%d months ago" => "%d meses antes", "last year" => "último ano", "years ago" => "anos atrás", -"%s is available. Get <a href=\"%s\">more information</a>" => "%s está dispoñible. Obteña <a href=\"%s\">máis información</a>", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s está dispoñible. Obtén <a href=\"%s\">máis información</a>", "up to date" => "ao día", -"updates check is disabled" => "comprobación de actualizacións está deshabilitada" +"updates check is disabled" => "a comprobación de actualizacións está desactivada", +"Could not find category \"%s\"" => "Non se puido atopar a categoría «%s»" ); diff --git a/lib/l10n/he.php b/lib/l10n/he.php index 149637d09d2..078a731afc0 100644 --- a/lib/l10n/he.php +++ b/lib/l10n/he.php @@ -12,17 +12,23 @@ "Application is not enabled" => "יישומים אינם מופעלים", "Authentication error" => "שגיאת הזדהות", "Token expired. Please reload page." => "פג תוקף. נא לטעון שוב את הדף.", +"Files" => "קבצים", +"Text" => "טקסט", +"Images" => "תמונות", "seconds ago" => "שניות", "1 minute ago" => "לפני דקה אחת", "%d minutes ago" => "לפני %d דקות", +"1 hour ago" => "לפני שעה", +"%d hours ago" => "לפני %d שעות", "today" => "היום", "yesterday" => "אתמול", "%d days ago" => "לפני %d ימים", "last month" => "חודש שעבר", -"months ago" => "חודשים", +"%d months ago" => "לפני %d חודשים", "last year" => "שנה שעברה", "years ago" => "שנים", "%s is available. Get <a href=\"%s\">more information</a>" => "%s זמין. קבלת <a href=\"%s\">מידע נוסף</a>", "up to date" => "עדכני", -"updates check is disabled" => "בדיקת עדכונים מנוטרלת" +"updates check is disabled" => "בדיקת עדכונים מנוטרלת", +"Could not find category \"%s\"" => "לא ניתן למצוא את הקטגוריה „%s“" ); diff --git a/lib/l10n/hr.php b/lib/l10n/hr.php new file mode 100644 index 00000000000..62305c15711 --- /dev/null +++ b/lib/l10n/hr.php @@ -0,0 +1,15 @@ +<?php $TRANSLATIONS = array( +"Help" => "Pomoć", +"Personal" => "Osobno", +"Settings" => "Postavke", +"Users" => "Korisnici", +"Authentication error" => "Greška kod autorizacije", +"Files" => "Datoteke", +"Text" => "Tekst", +"seconds ago" => "sekundi prije", +"today" => "danas", +"yesterday" => "jučer", +"last month" => "prošli mjesec", +"last year" => "prošlu godinu", +"years ago" => "godina" +); diff --git a/lib/l10n/hu_HU.php b/lib/l10n/hu_HU.php index eb074b79c61..63704a978c5 100644 --- a/lib/l10n/hu_HU.php +++ b/lib/l10n/hu_HU.php @@ -12,6 +12,8 @@ "Application is not enabled" => "Az alkalmazás nincs engedélyezve", "Authentication error" => "Hitelesítési hiba", "Token expired. Please reload page." => "A token lejárt. Frissítsd az oldalt.", +"Files" => "Fájlok", +"Text" => "Szöveg", "seconds ago" => "másodperccel ezelőtt", "1 minute ago" => "1 perccel ezelőtt", "%d minutes ago" => "%d perccel ezelőtt", @@ -19,7 +21,6 @@ "yesterday" => "tegnap", "%d days ago" => "%d évvel ezelőtt", "last month" => "múlt hónapban", -"months ago" => "hónappal ezelőtt", "last year" => "tavaly", "years ago" => "évvel ezelőtt" ); diff --git a/lib/l10n/ia.php b/lib/l10n/ia.php new file mode 100644 index 00000000000..05b2c88e1ed --- /dev/null +++ b/lib/l10n/ia.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Help" => "Adjuta", +"Personal" => "Personal", +"Settings" => "Configurationes", +"Users" => "Usatores", +"Files" => "Files", +"Text" => "Texto" +); diff --git a/lib/l10n/id.php b/lib/l10n/id.php new file mode 100644 index 00000000000..e31b4caf4f5 --- /dev/null +++ b/lib/l10n/id.php @@ -0,0 +1,28 @@ +<?php $TRANSLATIONS = array( +"Help" => "bantu", +"Personal" => "perseorangan", +"Settings" => "pengaturan", +"Users" => "pengguna", +"Apps" => "aplikasi", +"Admin" => "admin", +"ZIP download is turned off." => "download ZIP sedang dimatikan", +"Files need to be downloaded one by one." => "file harus di unduh satu persatu", +"Back to Files" => "kembali ke daftar file", +"Selected files too large to generate zip file." => "file yang dipilih terlalu besar untuk membuat file zip", +"Application is not enabled" => "aplikasi tidak diaktifkan", +"Authentication error" => "autentikasi bermasalah", +"Token expired. Please reload page." => "token kadaluarsa.mohon perbaharui laman.", +"Text" => "teks", +"seconds ago" => "beberapa detik yang lalu", +"1 minute ago" => "1 menit lalu", +"%d minutes ago" => "%d menit lalu", +"today" => "hari ini", +"yesterday" => "kemarin", +"%d days ago" => "%d hari lalu", +"last month" => "bulan kemarin", +"last year" => "tahun kemarin", +"years ago" => "beberapa tahun lalu", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s tersedia. dapatkan <a href=\"%s\"> info lebih lanjut</a>", +"up to date" => "terbaru", +"updates check is disabled" => "pengecekan pembaharuan sedang non-aktifkan" +); diff --git a/lib/l10n/it.php b/lib/l10n/it.php index c4c7d90610b..c0fb0babfb3 100644 --- a/lib/l10n/it.php +++ b/lib/l10n/it.php @@ -12,17 +12,23 @@ "Application is not enabled" => "L'applicazione non è abilitata", "Authentication error" => "Errore di autenticazione", "Token expired. Please reload page." => "Token scaduto. Ricarica la pagina.", +"Files" => "File", +"Text" => "Testo", +"Images" => "Immagini", "seconds ago" => "secondi fa", "1 minute ago" => "1 minuto fa", "%d minutes ago" => "%d minuti fa", +"1 hour ago" => "1 ora fa", +"%d hours ago" => "%d ore fa", "today" => "oggi", "yesterday" => "ieri", "%d days ago" => "%d giorni fa", "last month" => "il mese scorso", -"months ago" => "mesi fa", +"%d months ago" => "%d mesi fa", "last year" => "l'anno scorso", "years ago" => "anni fa", "%s is available. Get <a href=\"%s\">more information</a>" => "%s è disponibile. Ottieni <a href=\"%s\">ulteriori informazioni</a>", "up to date" => "aggiornato", -"updates check is disabled" => "il controllo degli aggiornamenti è disabilitato" +"updates check is disabled" => "il controllo degli aggiornamenti è disabilitato", +"Could not find category \"%s\"" => "Impossibile trovare la categoria \"%s\"" ); diff --git a/lib/l10n/ja_JP.php b/lib/l10n/ja_JP.php index 10f7276703a..854734c9764 100644 --- a/lib/l10n/ja_JP.php +++ b/lib/l10n/ja_JP.php @@ -12,17 +12,23 @@ "Application is not enabled" => "アプリケーションは無効です", "Authentication error" => "認証エラー", "Token expired. Please reload page." => "トークンが無効になりました。ページを再読込してください。", +"Files" => "ファイル", +"Text" => "TTY TDD", +"Images" => "画像", "seconds ago" => "秒前", "1 minute ago" => "1分前", "%d minutes ago" => "%d 分前", +"1 hour ago" => "1 時間前", +"%d hours ago" => "%d 時間前", "today" => "今日", "yesterday" => "昨日", "%d days ago" => "%d 日前", "last month" => "先月", -"months ago" => "月前", +"%d months ago" => "%d 分前", "last year" => "昨年", "years ago" => "年前", "%s is available. Get <a href=\"%s\">more information</a>" => "%s が利用可能です。<a href=\"%s\">詳細情報</a> を確認ください", "up to date" => "最新です", -"updates check is disabled" => "更新チェックは無効です" +"updates check is disabled" => "更新チェックは無効です", +"Could not find category \"%s\"" => "カテゴリ \"%s\" が見つかりませんでした" ); diff --git a/lib/l10n/ka_GE.php b/lib/l10n/ka_GE.php new file mode 100644 index 00000000000..ff623827216 --- /dev/null +++ b/lib/l10n/ka_GE.php @@ -0,0 +1,20 @@ +<?php $TRANSLATIONS = array( +"Help" => "დახმარება", +"Personal" => "პირადი", +"Settings" => "პარამეტრები", +"Users" => "მომხმარებელი", +"Apps" => "აპლიკაციები", +"Admin" => "ადმინისტრატორი", +"Authentication error" => "ავთენტიფიკაციის შეცდომა", +"Files" => "ფაილები", +"Text" => "ტექსტი", +"seconds ago" => "წამის წინ", +"1 minute ago" => "1 წუთის წინ", +"today" => "დღეს", +"yesterday" => "გუშინ", +"last month" => "გასულ თვეში", +"last year" => "ბოლო წელს", +"years ago" => "წლის წინ", +"up to date" => "განახლებულია", +"updates check is disabled" => "განახლების ძებნა გათიშულია" +); diff --git a/lib/l10n/ko.php b/lib/l10n/ko.php new file mode 100644 index 00000000000..6f32e3b54ec --- /dev/null +++ b/lib/l10n/ko.php @@ -0,0 +1,34 @@ +<?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." => "토큰 만료. 페이지를 새로고침 해주세요.", +"Files" => "파일", +"Text" => "문자 번호", +"Images" => "그림", +"seconds ago" => "초 전", +"1 minute ago" => "1분 전", +"%d minutes ago" => "%d 분 전", +"1 hour ago" => "1 시간 전", +"%d hours ago" => "%d 시간 전", +"today" => "오늘", +"yesterday" => "어제", +"%d days ago" => "%d 일 전", +"last month" => "지난 달", +"%d months ago" => "%d 달 전", +"last year" => "지난 해", +"years ago" => "작년", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s은 가능합니다. 더 자세한 정보는 <a href=\"%s\">이곳</a>으로..", +"up to date" => "최신", +"updates check is disabled" => "업데이트 확인이 비활성화 되어있습니다.", +"Could not find category \"%s\"" => "\"%s\" 카테고리를 찾을 수 없습니다." +); diff --git a/lib/l10n/ku_IQ.php b/lib/l10n/ku_IQ.php new file mode 100644 index 00000000000..f89871f23c9 --- /dev/null +++ b/lib/l10n/ku_IQ.php @@ -0,0 +1,5 @@ +<?php $TRANSLATIONS = array( +"Help" => "یارمەتی", +"Settings" => "دهستكاری", +"Users" => "بهكارهێنهر" +); diff --git a/lib/l10n/lb.php b/lib/l10n/lb.php new file mode 100644 index 00000000000..baee630e897 --- /dev/null +++ b/lib/l10n/lb.php @@ -0,0 +1,6 @@ +<?php $TRANSLATIONS = array( +"Personal" => "Perséinlech", +"Settings" => "Astellungen", +"Authentication error" => "Authentifikatioun's Fehler", +"Text" => "SMS" +); diff --git a/lib/l10n/lt_LT.php b/lib/l10n/lt_LT.php index c6702a62287..b84c155633b 100644 --- a/lib/l10n/lt_LT.php +++ b/lib/l10n/lt_LT.php @@ -11,11 +11,19 @@ "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", +"Token expired. Please reload page." => "Sesija baigėsi. Prašome perkrauti puslapį.", +"Files" => "Failai", +"Text" => "Žinučių", +"seconds ago" => "prieš kelias sekundes", "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" +"last year" => "pereitais metais", +"years ago" => "prieš metus", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s yra galimas. Platesnė <a href=\"%s\">informacija čia</a>", +"up to date" => "pilnai atnaujinta", +"updates check is disabled" => "atnaujinimų tikrinimas išjungtas" ); diff --git a/lib/l10n/lv.php b/lib/l10n/lv.php new file mode 100644 index 00000000000..3330d0e6b70 --- /dev/null +++ b/lib/l10n/lv.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Help" => "Palīdzība", +"Personal" => "Personīgi", +"Settings" => "Iestatījumi", +"Users" => "Lietotāji", +"Authentication error" => "Ielogošanās kļūme", +"Files" => "Faili" +); diff --git a/lib/l10n/mk.php b/lib/l10n/mk.php new file mode 100644 index 00000000000..a06073e808a --- /dev/null +++ b/lib/l10n/mk.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Help" => "Помош", +"Personal" => "Лично", +"Settings" => "Параметри", +"Users" => "Корисници", +"Files" => "Датотеки", +"Text" => "Текст" +); diff --git a/lib/l10n/ms_MY.php b/lib/l10n/ms_MY.php new file mode 100644 index 00000000000..86c7e51b486 --- /dev/null +++ b/lib/l10n/ms_MY.php @@ -0,0 +1,8 @@ +<?php $TRANSLATIONS = array( +"Personal" => "Peribadi", +"Settings" => "Tetapan", +"Users" => "Pengguna", +"Authentication error" => "Ralat pengesahan", +"Files" => "Fail-fail", +"Text" => "Teks" +); diff --git a/lib/l10n/nb_NO.php b/lib/l10n/nb_NO.php index f751a41d5eb..b01e0979889 100644 --- a/lib/l10n/nb_NO.php +++ b/lib/l10n/nb_NO.php @@ -12,6 +12,9 @@ "Application is not enabled" => "Applikasjon er ikke påslått", "Authentication error" => "Autentiseringsfeil", "Token expired. Please reload page." => "Symbol utløpt. Vennligst last inn siden på nytt.", +"Files" => "Filer", +"Text" => "Tekst", +"Images" => "Bilder", "seconds ago" => "sekunder siden", "1 minute ago" => "1 minuitt siden", "%d minutes ago" => "%d minutter siden", @@ -19,7 +22,9 @@ "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" +"years ago" => "år siden", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s er tilgjengelig. Få <a href=\"%s\">mer informasjon</a>", +"up to date" => "oppdatert", +"updates check is disabled" => "versjonssjekk er avslått" ); diff --git a/lib/l10n/nl.php b/lib/l10n/nl.php index c650071c03c..087cf23a627 100644 --- a/lib/l10n/nl.php +++ b/lib/l10n/nl.php @@ -4,7 +4,7 @@ "Settings" => "Instellingen", "Users" => "Gebruikers", "Apps" => "Apps", -"Admin" => "Administrator", +"Admin" => "Beheerder", "ZIP download is turned off." => "ZIP download is uitgeschakeld.", "Files need to be downloaded one by one." => "Bestanden moeten één voor één worden gedownload.", "Back to Files" => "Terug naar bestanden", @@ -12,17 +12,23 @@ "Application is not enabled" => "De applicatie is niet actief", "Authentication error" => "Authenticatie fout", "Token expired. Please reload page." => "Token verlopen. Herlaad de pagina.", +"Files" => "Bestanden", +"Text" => "Tekst", +"Images" => "Afbeeldingen", "seconds ago" => "seconden geleden", "1 minute ago" => "1 minuut geleden", "%d minutes ago" => "%d minuten geleden", +"1 hour ago" => "1 uur geleden", +"%d hours ago" => "%d uren geleden", "today" => "vandaag", "yesterday" => "gisteren", "%d days ago" => "%d dagen geleden", "last month" => "vorige maand", -"months ago" => "maanden geleden", +"%d months ago" => "%d maanden geleden", "last year" => "vorig jaar", "years ago" => "jaar geleden", "%s is available. Get <a href=\"%s\">more information</a>" => "%s is beschikbaar. Verkrijg <a href=\"%s\">meer informatie</a>", "up to date" => "bijgewerkt", -"updates check is disabled" => "Meest recente versie controle is uitgeschakeld" +"updates check is disabled" => "Meest recente versie controle is uitgeschakeld", +"Could not find category \"%s\"" => "Kon categorie \"%s\" niet vinden" ); diff --git a/lib/l10n/nn_NO.php b/lib/l10n/nn_NO.php new file mode 100644 index 00000000000..faf7440320a --- /dev/null +++ b/lib/l10n/nn_NO.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Help" => "Hjelp", +"Personal" => "Personleg", +"Settings" => "Innstillingar", +"Users" => "Brukarar", +"Authentication error" => "Feil i autentisering", +"Files" => "Filer", +"Text" => "Tekst" +); diff --git a/lib/l10n/oc.php b/lib/l10n/oc.php index ffc0588becc..89161393380 100644 --- a/lib/l10n/oc.php +++ b/lib/l10n/oc.php @@ -9,6 +9,7 @@ "Files need to be downloaded one by one." => "Los fichièrs devan èsser avalcargats un per un.", "Back to Files" => "Torna cap als fichièrs", "Authentication error" => "Error d'autentificacion", +"Files" => "Fichièrs", "seconds ago" => "segonda a", "1 minute ago" => "1 minuta a", "%d minutes ago" => "%d minutas a", @@ -16,7 +17,6 @@ "yesterday" => "ièr", "%d days ago" => "%d jorns a", "last month" => "mes passat", -"months ago" => "meses a", "last year" => "an passat", "years ago" => "ans a", "up to date" => "a jorn", diff --git a/lib/l10n/pl.php b/lib/l10n/pl.php index 087aaa227d3..6f84a328ed9 100644 --- a/lib/l10n/pl.php +++ b/lib/l10n/pl.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Aplikacja nie jest włączona", "Authentication error" => "Błąd uwierzytelniania", "Token expired. Please reload page." => "Token wygasł. Proszę ponownie załadować stronę.", +"Files" => "Pliki", +"Text" => "Połączenie tekstowe", +"Images" => "Obrazy", "seconds ago" => "sekund temu", "1 minute ago" => "1 minutę temu", "%d minutes ago" => "%d minut temu", +"1 hour ago" => "1 godzine temu", +"%d hours ago" => "%d godzin temu", "today" => "dzisiaj", "yesterday" => "wczoraj", "%d days ago" => "%d dni temu", "last month" => "ostatni miesiąc", -"months ago" => "miesięcy temu", +"%d months ago" => "%d miesiecy temu", "last year" => "ostatni rok", "years ago" => "lat temu", "%s is available. Get <a href=\"%s\">more information</a>" => "%s jest dostępna. Uzyskaj <a href=\"%s\">więcej informacji</a>", "up to date" => "Aktualne", -"updates check is disabled" => "wybór aktualizacji jest wyłączony" +"updates check is disabled" => "wybór aktualizacji jest wyłączony", +"Could not find category \"%s\"" => "Nie można odnaleźć kategorii \"%s\"" ); diff --git a/lib/l10n/pl_PL.php b/lib/l10n/pl_PL.php new file mode 100644 index 00000000000..67cf0a33259 --- /dev/null +++ b/lib/l10n/pl_PL.php @@ -0,0 +1,3 @@ +<?php $TRANSLATIONS = array( +"Settings" => "Ustawienia" +); diff --git a/lib/l10n/pt_BR.php b/lib/l10n/pt_BR.php index 1455eabbc94..fb7087d35d7 100644 --- a/lib/l10n/pt_BR.php +++ b/lib/l10n/pt_BR.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Aplicação não está habilitada", "Authentication error" => "Erro de autenticação", "Token expired. Please reload page." => "Token expirou. Por favor recarregue a página.", +"Files" => "Arquivos", +"Text" => "Texto", +"Images" => "Imagens", "seconds ago" => "segundos atrás", "1 minute ago" => "1 minuto atrás", "%d minutes ago" => "%d minutos atrás", +"1 hour ago" => "1 hora atrás", +"%d hours ago" => "%d horas atrás", "today" => "hoje", "yesterday" => "ontem", "%d days ago" => "%d dias atrás", "last month" => "último mês", -"months ago" => "meses atrás", +"%d months ago" => "%d meses atrás", "last year" => "último ano", "years ago" => "anos atrás", "%s is available. Get <a href=\"%s\">more information</a>" => "%s está disponível. Obtenha <a href=\"%s\">mais informações</a>", "up to date" => "atualizado", -"updates check is disabled" => "checagens de atualização estão desativadas" +"updates check is disabled" => "checagens de atualização estão desativadas", +"Could not find category \"%s\"" => "Impossível localizar categoria \"%s\"" ); diff --git a/lib/l10n/pt_PT.php b/lib/l10n/pt_PT.php index c3cee207a16..84867c4c37c 100644 --- a/lib/l10n/pt_PT.php +++ b/lib/l10n/pt_PT.php @@ -12,17 +12,23 @@ "Application is not enabled" => "A aplicação não está activada", "Authentication error" => "Erro na autenticação", "Token expired. Please reload page." => "O token expirou. Por favor recarregue a página.", +"Files" => "Ficheiros", +"Text" => "Texto", +"Images" => "Imagens", "seconds ago" => "há alguns segundos", "1 minute ago" => "há 1 minuto", "%d minutes ago" => "há %d minutos", +"1 hour ago" => "Há 1 horas", +"%d hours ago" => "Há %d horas", "today" => "hoje", "yesterday" => "ontem", "%d days ago" => "há %d dias", "last month" => "mês passado", -"months ago" => "há meses", +"%d months ago" => "Há %d meses atrás", "last year" => "ano passado", "years ago" => "há anos", "%s is available. Get <a href=\"%s\">more information</a>" => "%s está disponível. Obtenha <a href=\"%s\">mais informação</a>", "up to date" => "actualizado", -"updates check is disabled" => "a verificação de actualizações está desligada" +"updates check is disabled" => "a verificação de actualizações está desligada", +"Could not find category \"%s\"" => "Não foi encontrado a categoria \"%s\"" ); diff --git a/lib/l10n/ro.php b/lib/l10n/ro.php index 5fffeec2335..27912550e17 100644 --- a/lib/l10n/ro.php +++ b/lib/l10n/ro.php @@ -12,6 +12,8 @@ "Application is not enabled" => "Aplicația nu este activată", "Authentication error" => "Eroare la autentificare", "Token expired. Please reload page." => "Token expirat. Te rugăm să reîncarci pagina.", +"Files" => "Fișiere", +"Text" => "Text", "seconds ago" => "secunde în urmă", "1 minute ago" => "1 minut în urmă", "%d minutes ago" => "%d minute în urmă", @@ -19,7 +21,6 @@ "yesterday" => "ieri", "%d days ago" => "%d zile în urmă", "last month" => "ultima lună", -"months ago" => "luni în urmă", "last year" => "ultimul an", "years ago" => "ani în urmă", "%s is available. Get <a href=\"%s\">more information</a>" => "%s este disponibil. Vezi <a href=\"%s\">mai multe informații</a>", diff --git a/lib/l10n/ru.php b/lib/l10n/ru.php index 74425f0e134..3ed55f8e9dc 100644 --- a/lib/l10n/ru.php +++ b/lib/l10n/ru.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Приложение не разрешено", "Authentication error" => "Ошибка аутентификации", "Token expired. Please reload page." => "Токен просрочен. Перезагрузите страницу.", +"Files" => "Файлы", +"Text" => "Текст", +"Images" => "Изображения", "seconds ago" => "менее минуты", "1 minute ago" => "1 минуту назад", "%d minutes ago" => "%d минут назад", +"1 hour ago" => "час назад", +"%d hours ago" => "%d часов назад", "today" => "сегодня", "yesterday" => "вчера", "%d days ago" => "%d дней назад", "last month" => "в прошлом месяце", -"months ago" => "месяцы назад", +"%d months ago" => "%d месяцев назад", "last year" => "в прошлом году", "years ago" => "годы назад", "%s is available. Get <a href=\"%s\">more information</a>" => "Возможно обновление до %s. <a href=\"%s\">Подробнее</a>", "up to date" => "актуальная версия", -"updates check is disabled" => "проверка обновлений отключена" +"updates check is disabled" => "проверка обновлений отключена", +"Could not find category \"%s\"" => "Категория \"%s\" не найдена" ); diff --git a/lib/l10n/ru_RU.php b/lib/l10n/ru_RU.php index decf63efb97..ba7d39f9eb0 100644 --- a/lib/l10n/ru_RU.php +++ b/lib/l10n/ru_RU.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Приложение не запущено", "Authentication error" => "Ошибка аутентификации", "Token expired. Please reload page." => "Маркер истек. Пожалуйста, перезагрузите страницу.", +"Files" => "Файлы", +"Text" => "Текст", +"Images" => "Изображения", "seconds ago" => "секунд назад", "1 minute ago" => "1 минуту назад", "%d minutes ago" => "%d минут назад", +"1 hour ago" => "1 час назад", +"%d hours ago" => "%d часов назад", "today" => "сегодня", "yesterday" => "вчера", "%d days ago" => "%d дней назад", "last month" => "в прошлом месяце", -"months ago" => "месяц назад", +"%d months ago" => "%d месяцев назад", "last year" => "в прошлом году", "years ago" => "год назад", "%s is available. Get <a href=\"%s\">more information</a>" => "%s доступно. Получите <a href=\"%s\">more information</a>", "up to date" => "до настоящего времени", -"updates check is disabled" => "Проверка обновлений отключена" +"updates check is disabled" => "Проверка обновлений отключена", +"Could not find category \"%s\"" => "Не удалось найти категорию \"%s\"" ); diff --git a/lib/l10n/si_LK.php b/lib/l10n/si_LK.php new file mode 100644 index 00000000000..25624acf705 --- /dev/null +++ b/lib/l10n/si_LK.php @@ -0,0 +1,30 @@ +<?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." => "ටෝකනය කල් ඉකුත් වී ඇත. පිටුව නැවුම් කරන්න", +"Files" => "ගොනු", +"Text" => "පෙළ", +"Images" => "අනු රූ", +"seconds ago" => "තත්පරයන්ට පෙර", +"1 minute ago" => "1 මිනිත්තුවකට පෙර", +"%d minutes ago" => "%d මිනිත්තුවන්ට පෙර", +"today" => "අද", +"yesterday" => "ඊයේ", +"%d days ago" => "%d දිනකට පෙර", +"last month" => "පෙර මාසයේ", +"last year" => "පෙර අවුරුද්දේ", +"years ago" => "අවුරුදු කීපයකට පෙර", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s යොදාගත හැක. <a href=\"%s\">තව විස්තර</a> ලබාගන්න", +"up to date" => "යාවත්කාලීනයි", +"updates check is disabled" => "යාවත්කාලීන බව පරීක්ෂණය අක්රියයි" +); diff --git a/lib/l10n/sk_SK.php b/lib/l10n/sk_SK.php index 33b329c30bb..98a5b5ca677 100644 --- a/lib/l10n/sk_SK.php +++ b/lib/l10n/sk_SK.php @@ -11,15 +11,24 @@ "Selected files too large to generate zip file." => "Zvolené súbory sú príliž veľké na vygenerovanie zip súboru.", "Application is not enabled" => "Aplikácia nie je zapnutá", "Authentication error" => "Chyba autentifikácie", +"Token expired. Please reload page." => "Token vypršal. Obnovte, prosím, stránku.", +"Files" => "Súbory", +"Text" => "Text", +"Images" => "Obrázky", +"seconds ago" => "pred sekundami", "1 minute ago" => "pred 1 minútou", "%d minutes ago" => "pred %d minútami", +"1 hour ago" => "Pred 1 hodinou", +"%d hours ago" => "Pred %d hodinami.", "today" => "dnes", "yesterday" => "včera", "%d days ago" => "pred %d dňami", "last month" => "minulý mesiac", -"months ago" => "pred mesiacmi", +"%d months ago" => "Pred %d mesiacmi.", "last year" => "minulý rok", "years ago" => "pred rokmi", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s je dostupné. Získať <a href=\"%s\">viac informácií</a>", "up to date" => "aktuálny", -"updates check is disabled" => "sledovanie aktualizácií je vypnuté" +"updates check is disabled" => "sledovanie aktualizácií je vypnuté", +"Could not find category \"%s\"" => "Nemožno nájsť danú kategóriu \"%s\"" ); diff --git a/lib/l10n/sl.php b/lib/l10n/sl.php index eac839e78f3..391d932c4ee 100644 --- a/lib/l10n/sl.php +++ b/lib/l10n/sl.php @@ -3,26 +3,32 @@ "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.", +"Apps" => "Programi", +"Admin" => "Skrbništvo", +"ZIP download is turned off." => "Prejem datotek ZIP je onemogočen.", +"Files need to be downloaded one by one." => "Datoteke je mogoče prejeti le posamič.", "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", +"Selected files too large to generate zip file." => "Izbrane datoteke so prevelike za ustvarjanje datoteke arhiva zip.", +"Application is not enabled" => "Program ni omogočen", "Authentication error" => "Napaka overitve", -"Token expired. Please reload page." => "Žeton je potekel. Prosimo, če spletno stran znova naložite.", +"Token expired. Please reload page." => "Žeton je potekel. Spletišče je traba znova naložiti.", +"Files" => "Datoteke", +"Text" => "Besedilo", +"Images" => "Slike", "seconds ago" => "pred nekaj sekundami", "1 minute ago" => "pred minuto", "%d minutes ago" => "pred %d minutami", +"1 hour ago" => "Pred 1 uro", +"%d hours ago" => "Pred %d urami", "today" => "danes", "yesterday" => "včeraj", "%d days ago" => "pred %d dnevi", "last month" => "prejšnji mesec", -"months ago" => "pred nekaj meseci", +"%d months ago" => "Pred %d meseci", "last year" => "lani", "years ago" => "pred nekaj leti", -"%s is available. Get <a href=\"%s\">more information</a>" => "%s je na voljo. <a href=\"%s\">Več informacij.</a>", -"up to date" => "ažuren", -"updates check is disabled" => "preverjanje za posodobitve je onemogočeno" +"%s is available. Get <a href=\"%s\">more information</a>" => "%s je na voljo. <a href=\"%s\">Več podrobnosti.</a>", +"up to date" => "posodobljeno", +"updates check is disabled" => "preverjanje za posodobitve je onemogočeno", +"Could not find category \"%s\"" => "Kategorije \"%s\" ni bilo mogoče najti." ); diff --git a/lib/l10n/sr.php b/lib/l10n/sr.php new file mode 100644 index 00000000000..2ae7400ba79 --- /dev/null +++ b/lib/l10n/sr.php @@ -0,0 +1,34 @@ +<?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." => "Жетон је истекао. Поново учитајте страницу.", +"Files" => "Датотеке", +"Text" => "Текст", +"Images" => "Слике", +"seconds ago" => "пре неколико секунди", +"1 minute ago" => "пре 1 минут", +"%d minutes ago" => "пре %d минута", +"1 hour ago" => "пре 1 сат", +"%d hours ago" => "пре %d сата/и", +"today" => "данас", +"yesterday" => "јуче", +"%d days ago" => "пре %d дана", +"last month" => "прошлог месеца", +"%d months ago" => "пре %d месеца/и", +"last year" => "прошле године", +"years ago" => "година раније", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s је доступна. Погледајте <a href=\"%s\">више информација</a>.", +"up to date" => "је ажурна.", +"updates check is disabled" => "провера ажурирања је онемогућена.", +"Could not find category \"%s\"" => "Не могу да пронађем категорију „%s“." +); diff --git a/lib/l10n/sr@latin.php b/lib/l10n/sr@latin.php new file mode 100644 index 00000000000..3fc1f61eafa --- /dev/null +++ b/lib/l10n/sr@latin.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Help" => "Pomoć", +"Personal" => "Lično", +"Settings" => "Podešavanja", +"Users" => "Korisnici", +"Authentication error" => "Greška pri autentifikaciji", +"Files" => "Fajlovi", +"Text" => "Tekst" +); diff --git a/lib/l10n/sv.php b/lib/l10n/sv.php index 3d377133f22..5799e2dd1a8 100644 --- a/lib/l10n/sv.php +++ b/lib/l10n/sv.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Applikationen är inte aktiverad", "Authentication error" => "Fel vid autentisering", "Token expired. Please reload page." => "Ogiltig token. Ladda om sidan.", +"Files" => "Filer", +"Text" => "Text", +"Images" => "Bilder", "seconds ago" => "sekunder sedan", "1 minute ago" => "1 minut sedan", "%d minutes ago" => "%d minuter sedan", +"1 hour ago" => "1 timme sedan", +"%d hours ago" => "%d timmar sedan", "today" => "idag", "yesterday" => "igår", "%d days ago" => "%d dagar sedan", "last month" => "förra månaden", -"months ago" => "månader sedan", +"%d months ago" => "%d månader sedan", "last year" => "förra året", "years ago" => "år sedan", "%s is available. Get <a href=\"%s\">more information</a>" => "%s finns. Få <a href=\"%s\">mer information</a>", "up to date" => "uppdaterad", -"updates check is disabled" => "uppdateringskontroll är inaktiverad" +"updates check is disabled" => "uppdateringskontroll är inaktiverad", +"Could not find category \"%s\"" => "Kunde inte hitta kategorin \"%s\"" ); diff --git a/lib/l10n/ta_LK.php b/lib/l10n/ta_LK.php new file mode 100644 index 00000000000..c76394bcb4f --- /dev/null +++ b/lib/l10n/ta_LK.php @@ -0,0 +1,34 @@ +<?php $TRANSLATIONS = array( +"Help" => "உதவி", +"Personal" => "தனிப்பட்ட", +"Settings" => "அமைப்புகள்", +"Users" => "பயனாளர்கள்", +"Apps" => "செயலிகள்", +"Admin" => "நிர்வாகம்", +"ZIP download is turned off." => "வீசொலிப் பூட்டு பதிவிறக்கம் நிறுத்தப்பட்டுள்ளது.", +"Files need to be downloaded one by one." => "கோப்புகள்ஒன்றன் பின் ஒன்றாக பதிவிறக்கப்படவேண்டும்.", +"Back to Files" => "கோப்புகளுக்கு செல்க", +"Selected files too large to generate zip file." => "வீ சொலிக் கோப்புகளை உருவாக்குவதற்கு தெரிவுசெய்யப்பட்ட கோப்புகள் மிகப்பெரியவை", +"Application is not enabled" => "செயலி இயலுமைப்படுத்தப்படவில்லை", +"Authentication error" => "அத்தாட்சிப்படுத்தலில் வழு", +"Token expired. Please reload page." => "அடையாளவில்லை காலாவதியாகிவிட்டது. தயவுசெய்து பக்கத்தை மீள் ஏற்றுக.", +"Files" => "கோப்புகள்", +"Text" => "உரை", +"Images" => "படங்கள்", +"seconds ago" => "செக்கன்களுக்கு முன்", +"1 minute ago" => "1 நிமிடத்திற்கு முன் ", +"%d minutes ago" => "%d நிமிடங்களுக்கு முன்", +"1 hour ago" => "1 மணித்தியாலத்திற்கு முன்", +"%d hours ago" => "%d மணித்தியாலத்திற்கு முன்", +"today" => "இன்று", +"yesterday" => "நேற்று", +"%d days ago" => "%d நாட்களுக்கு முன்", +"last month" => "கடந்த மாதம்", +"%d months ago" => "%d மாதத்திற்கு முன்", +"last year" => "கடந்த வருடம்", +"years ago" => "வருடங்களுக்கு முன்", +"%s is available. Get <a href=\"%s\">more information</a>" => "%s இன்னும் இருக்கின்றன. <a href=\"%s\">மேலதிக தகவல்களுக்கு</a> எடுக்க", +"up to date" => "நவீன", +"updates check is disabled" => "இற்றைப்படுத்தலை சரிபார்ப்பதை செயலற்றதாக்குக", +"Could not find category \"%s\"" => "பிரிவு \"%s\" ஐ கண்டுப்பிடிக்க முடியவில்லை" +); diff --git a/lib/l10n/th_TH.php b/lib/l10n/th_TH.php index 2aa2ffaba8c..75fa02f84b0 100644 --- a/lib/l10n/th_TH.php +++ b/lib/l10n/th_TH.php @@ -12,17 +12,23 @@ "Application is not enabled" => "แอพพลิเคชั่นดังกล่าวยังไม่ได้เปิดใช้งาน", "Authentication error" => "เกิดข้อผิดพลาดในสิทธิ์การเข้าใช้งาน", "Token expired. Please reload page." => "รหัสยืนยันความถูกต้องหมดอายุแล้ว กรุณาโหลดหน้าเว็บใหม่อีกครั้ง", +"Files" => "ไฟล์", +"Text" => "ข้อความ", +"Images" => "รูปภาพ", "seconds ago" => "วินาทีที่ผ่านมา", "1 minute ago" => "1 นาทีมาแล้ว", "%d minutes ago" => "%d นาทีที่ผ่านมา", +"1 hour ago" => "1 ชั่วโมงก่อนหน้านี้", +"%d hours ago" => "%d ชั่วโมงก่อนหน้านี้", "today" => "วันนี้", "yesterday" => "เมื่อวานนี้", "%d days ago" => "%d วันที่ผ่านมา", "last month" => "เดือนที่แล้ว", -"months ago" => "เดือนมาแล้ว", +"%d months ago" => "%d เดือนมาแล้ว", "last year" => "ปีที่แล้ว", "years ago" => "ปีที่ผ่านมา", "%s is available. Get <a href=\"%s\">more information</a>" => "%s พร้อมให้ใช้งานได้แล้ว. <a href=\"%s\">ดูรายละเอียดเพิ่มเติม</a>", "up to date" => "ทันสมัย", -"updates check is disabled" => "การตรวจสอบชุดอัพเดทถูกปิดใช้งานไว้" +"updates check is disabled" => "การตรวจสอบชุดอัพเดทถูกปิดใช้งานไว้", +"Could not find category \"%s\"" => "ไม่พบหมวดหมู่ \"%s\"" ); diff --git a/lib/l10n/tr.php b/lib/l10n/tr.php new file mode 100644 index 00000000000..69067d7ec57 --- /dev/null +++ b/lib/l10n/tr.php @@ -0,0 +1,9 @@ +<?php $TRANSLATIONS = array( +"Help" => "Yardı", +"Personal" => "Kişisel", +"Settings" => "Ayarlar", +"Users" => "Kullanıcılar", +"Authentication error" => "Kimlik doğrulama hatası", +"Files" => "Dosyalar", +"Text" => "Metin" +); diff --git a/lib/l10n/uk.php b/lib/l10n/uk.php index 423aa12b2d7..f5d52f8682d 100644 --- a/lib/l10n/uk.php +++ b/lib/l10n/uk.php @@ -11,15 +11,24 @@ "Selected files too large to generate zip file." => "Вибрані фали завеликі для генерування zip файлу.", "Application is not enabled" => "Додаток не увімкнений", "Authentication error" => "Помилка автентифікації", +"Token expired. Please reload page." => "Строк дії токена скінчився. Будь ласка, перезавантажте сторінку.", +"Files" => "Файли", +"Text" => "Текст", +"Images" => "Зображення", "seconds ago" => "секунди тому", "1 minute ago" => "1 хвилину тому", "%d minutes ago" => "%d хвилин тому", +"1 hour ago" => "1 годину тому", +"%d hours ago" => "%d годин тому", "today" => "сьогодні", "yesterday" => "вчора", "%d days ago" => "%d днів тому", "last month" => "минулого місяця", -"months ago" => "місяці тому", +"%d months ago" => "%d місяців тому", "last year" => "минулого року", "years ago" => "роки тому", -"updates check is disabled" => "перевірка оновлень відключена" +"%s is available. Get <a href=\"%s\">more information</a>" => "%s доступно. Отримати <a href=\"%s\">детальну інформацію</a>", +"up to date" => "оновлено", +"updates check is disabled" => "перевірка оновлень відключена", +"Could not find category \"%s\"" => "Не вдалося знайти категорію \"%s\"" ); diff --git a/lib/l10n/vi.php b/lib/l10n/vi.php index fc41d69819a..8b7242ae611 100644 --- a/lib/l10n/vi.php +++ b/lib/l10n/vi.php @@ -12,17 +12,23 @@ "Application is not enabled" => "Ứng dụng không được BẬT", "Authentication error" => "Lỗi xác thực", "Token expired. Please reload page." => "Mã Token đã hết hạn. Hãy tải lại trang.", +"Files" => "Các tập tin", +"Text" => "Văn bản", +"Images" => "Hình ảnh", "seconds ago" => "1 giây trước", "1 minute ago" => "1 phút trước", "%d minutes ago" => "%d phút trước", +"1 hour ago" => "1 giờ trước", +"%d hours ago" => "%d giờ trước", "today" => "hôm nay", "yesterday" => "hôm qua", "%d days ago" => "%d ngày trước", "last month" => "tháng trước", -"months ago" => "tháng trước", +"%d months ago" => "%d tháng trước", "last year" => "năm trước", "years ago" => "năm trước", "%s is available. Get <a href=\"%s\">more information</a>" => "%s có sẵn. <a href=\"%s\">xem thêm ở đây</a>", "up to date" => "đến ngày", -"updates check is disabled" => "đã TĂT chức năng cập nhật " +"updates check is disabled" => "đã TĂT chức năng cập nhật ", +"Could not find category \"%s\"" => "không thể tìm thấy mục \"%s\"" ); diff --git a/lib/l10n/zh_CN.GB2312.php b/lib/l10n/zh_CN.GB2312.php index 4b0a5e9f4d2..08975e44598 100644 --- a/lib/l10n/zh_CN.GB2312.php +++ b/lib/l10n/zh_CN.GB2312.php @@ -12,6 +12,9 @@ "Application is not enabled" => "应用未启用", "Authentication error" => "验证错误", "Token expired. Please reload page." => "会话过期。请刷新页面。", +"Files" => "文件", +"Text" => "文本", +"Images" => "图片", "seconds ago" => "秒前", "1 minute ago" => "1 分钟前", "%d minutes ago" => "%d 分钟前", @@ -19,7 +22,6 @@ "yesterday" => "昨天", "%d days ago" => "%d 天前", "last month" => "上个月", -"months ago" => "月前", "last year" => "去年", "years ago" => "年前", "%s is available. Get <a href=\"%s\">more information</a>" => "%s 不可用。获知 <a href=\"%s\">详情</a>", diff --git a/lib/l10n/zh_CN.php b/lib/l10n/zh_CN.php index 8229c77d2dd..c3af288b727 100644 --- a/lib/l10n/zh_CN.php +++ b/lib/l10n/zh_CN.php @@ -12,17 +12,23 @@ "Application is not enabled" => "不需要程序", "Authentication error" => "认证错误", "Token expired. Please reload page." => "Token 过期,请刷新页面。", +"Files" => "文件", +"Text" => "文本", +"Images" => "图像", "seconds ago" => "几秒前", "1 minute ago" => "1分钟前", "%d minutes ago" => "%d 分钟前", +"1 hour ago" => "1小时前", +"%d hours ago" => "%d小时前", "today" => "今天", "yesterday" => "昨天", "%d days ago" => "%d 天前", "last month" => "上月", -"months ago" => "几月前", +"%d months ago" => "%d 月前", "last year" => "上年", "years ago" => "几年前", "%s is available. Get <a href=\"%s\">more information</a>" => "%s 已存在. 点此 <a href=\"%s\">获取更多信息</a>", "up to date" => "已更新。", -"updates check is disabled" => "检查更新功能被关闭。" +"updates check is disabled" => "检查更新功能被关闭。", +"Could not find category \"%s\"" => "无法找到分类 \"%s\"" ); diff --git a/lib/l10n/zh_TW.php b/lib/l10n/zh_TW.php index c9a26a53b2a..4dbf89c2e0e 100644 --- a/lib/l10n/zh_TW.php +++ b/lib/l10n/zh_TW.php @@ -12,17 +12,23 @@ "Application is not enabled" => "應用程式未啟用", "Authentication error" => "認證錯誤", "Token expired. Please reload page." => "Token 過期. 請重新整理頁面", +"Files" => "檔案", +"Text" => "文字", +"Images" => "圖片", "seconds ago" => "幾秒前", "1 minute ago" => "1 分鐘前", "%d minutes ago" => "%d 分鐘前", +"1 hour ago" => "1小時之前", +"%d hours ago" => "%d小時之前", "today" => "今天", "yesterday" => "昨天", "%d days ago" => "%d 天前", "last month" => "上個月", -"months ago" => "幾個月前", +"%d months ago" => "%d個月之前", "last year" => "去年", "years ago" => "幾年前", "%s is available. Get <a href=\"%s\">more information</a>" => "%s 已經可用. 取得 <a href=\"%s\">更多資訊</a>", "up to date" => "最新的", -"updates check is disabled" => "檢查更新已停用" +"updates check is disabled" => "檢查更新已停用", +"Could not find category \"%s\"" => "找不到分類-\"%s\"" ); diff --git a/lib/log.php b/lib/log.php index 6de99b4ea6b..e9cededa5c0 100644 --- a/lib/log.php +++ b/lib/log.php @@ -39,4 +39,29 @@ class OC_Log { $log_class::write($app, $message, $level); } } + + //Fatal errors handler + public static function onShutdown() { + $error = error_get_last(); + if($error) { + //ob_end_clean(); + self::write('PHP', $error['message'] . ' at ' . $error['file'] . '#' . $error['line'], self::FATAL); + } else { + return true; + } + } + + // Uncaught exception handler + public static function onException($exception) { + self::write('PHP', $exception->getMessage() . ' at ' . $exception->getFile() . '#' . $exception->getLine(), self::FATAL); + } + + //Recoverable errors handler + public static function onError($number, $message, $file, $line) { + if (error_reporting() === 0) { + return; + } + self::write('PHP', $message . ' at ' . $file . '#' . $line, self::WARN); + + } } diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php index d4644163ad5..ec43208d833 100644 --- a/lib/log/owncloud.php +++ b/lib/log/owncloud.php @@ -44,9 +44,9 @@ class OC_Log_Owncloud { * @param int level */ public static function write($app, $message, $level) { - $minLevel=min(OC_Config::getValue( "loglevel", OC_Log::WARN ),OC_Log::ERROR); + $minLevel=min(OC_Config::getValue( "loglevel", OC_Log::WARN ), OC_Log::ERROR); if($level>=$minLevel) { - $entry=array('app'=>$app, 'message'=>$message, 'level'=>$level,'time'=>time()); + $entry=array('app'=>$app, 'message'=>$message, 'level'=>$level, 'time'=>time()); $fh=fopen(self::$logFile, 'a'); fwrite($fh, json_encode($entry)."\n"); fclose($fh); diff --git a/lib/mail.php b/lib/mail.php index 8d30fff9f28..c78fcce88d4 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -27,7 +27,7 @@ class OC_Mail { * @param string $fromname * @param bool $html */ - public static function send($toaddress,$toname,$subject,$mailtext,$fromaddress,$fromname,$html=0,$altbody='',$ccaddress='',$ccname='',$bcc='') { + public static function send($toaddress,$toname,$subject,$mailtext,$fromaddress,$fromname,$html=0,$altbody='',$ccaddress='',$ccname='', $bcc='') { $SMTPMODE = OC_Config::getValue( 'mail_smtpmode', 'sendmail' ); $SMTPHOST = OC_Config::getValue( 'mail_smtphost', '127.0.0.1' ); @@ -56,13 +56,13 @@ class OC_Mail { $mailo->From =$fromaddress; $mailo->FromName = $fromname;; $mailo->Sender =$fromaddress; - $a=explode(' ',$toaddress); + $a=explode(' ', $toaddress); try { foreach($a as $ad) { - $mailo->AddAddress($ad,$toname); + $mailo->AddAddress($ad, $toname); } - if($ccaddress<>'') $mailo->AddCC($ccaddress,$ccname); + if($ccaddress<>'') $mailo->AddCC($ccaddress, $ccname); if($bcc<>'') $mailo->AddBCC($bcc); $mailo->AddReplyTo($fromaddress, $fromname); diff --git a/lib/migrate.php b/lib/migrate.php index 611a935ee5d..2cc0a3067b8 100644 --- a/lib/migrate.php +++ b/lib/migrate.php @@ -66,7 +66,7 @@ class OC_Migrate{ foreach($apps as $app) { $path = OC_App::getAppPath($app) . '/appinfo/migrate.php'; if( file_exists( $path ) ) { - include( $path ); + include $path; } } } @@ -91,7 +91,7 @@ class OC_Migrate{ if( self::$exporttype == 'user' ) { // Check user exists self::$uid = is_null($uid) ? OC_User::getUser() : $uid; - if(!OC_User::userExists(self::$uid)){ + if(!OC_User::userExists(self::$uid)) { return json_encode( array( 'success' => false) ); } } @@ -200,7 +200,7 @@ class OC_Migrate{ $scan = scandir( $extractpath ); // Check for export_info.json if( !in_array( 'export_info.json', $scan ) ) { - OC_Log::write( 'migration', 'Invalid import file, export_info.json note found', OC_Log::ERROR ); + OC_Log::write( 'migration', 'Invalid import file, export_info.json not found', OC_Log::ERROR ); return json_encode( array( 'success' => false ) ); } $json = json_decode( file_get_contents( $extractpath . 'export_info.json' ) ); @@ -235,12 +235,19 @@ class OC_Migrate{ return json_encode( array( 'success' => false ) ); } // Copy data - if( !self::copy_r( $extractpath . $json->exporteduser, $datadir . '/' . self::$uid ) ) { - return json_encode( array( 'success' => false ) ); + $userfolder = $extractpath . $json->exporteduser; + $newuserfolder = $datadir . '/' . self::$uid; + foreach(scandir($userfolder) as $file){ + if($file !== '.' && $file !== '..' && is_dir($file)) { + // Then copy the folder over + OC_Helper::copyr($userfolder.'/'.$file, $newuserfolder.'/'.$file); + } } // Import user app data - if( !$appsimported = self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ) { - return json_encode( array( 'success' => false ) ); + if(file_exists($extractpath . $json->exporteduser . '/migration.db')) { + if( !$appsimported = self::importAppData( $extractpath . $json->exporteduser . '/migration.db', $json, self::$uid ) ) { + return json_encode( array( 'success' => false ) ); + } } // All done! if( !self::unlink_r( $extractpath ) ) { @@ -305,37 +312,6 @@ class OC_Migrate{ } /** - * @brief copies recursively - * @param $path string path to source folder - * @param $dest string path to destination - * @return bool - */ - private static function copy_r( $path, $dest ) { - if( is_dir($path) ) { - @mkdir( $dest ); - $objects = scandir( $path ); - if( sizeof( $objects ) > 0 ) { - foreach( $objects as $file ) { - if( $file == "." || $file == ".." || $file == ".htaccess") - continue; - // go on - if( is_dir( $path . '/' . $file ) ) { - self::copy_r( $path .'/' . $file, $dest . '/' . $file ); - } else { - copy( $path . '/' . $file, $dest . '/' . $file ); - } - } - } - return true; - } - elseif( is_file( $path ) ) { - return copy( $path, $dest ); - } else { - return false; - } - } - - /** * @brief tries to extract the import zip * @param $path string path to the zip * @return string path to extract location (with a trailing slash) or false on failure @@ -347,7 +323,7 @@ class OC_Migrate{ OC_Log::write( 'migration', 'Zip not found', OC_Log::ERROR ); return false; } - if ( self::$zip->open( $path ) != TRUE ) { + if ( self::$zip->open( $path ) != true ) { OC_Log::write( 'migration', "Failed to open zip file", OC_Log::ERROR ); return false; } @@ -576,7 +552,7 @@ class OC_Migrate{ OC_Log::write('migration', 'createZip() called but $zip and/or $zippath have not been set', OC_Log::ERROR); return false; } - if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE ) !== TRUE ) { + if ( self::$zip->open( self::$zippath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE ) !== true ) { OC_Log::write('migration', 'Failed to create the zip with error: '.self::$zip->getStatusString(), OC_Log::ERROR); return false; } else { @@ -611,11 +587,11 @@ class OC_Migrate{ if( file_exists( $db ) ) { // Connect to the db if(!self::connectDB( $db )) { - OC_Log::write('migration','Failed to connect to migration.db',OC_Log::ERROR); + OC_Log::write('migration', 'Failed to connect to migration.db', OC_Log::ERROR); return false; } } else { - OC_Log::write('migration','Migration.db not found at: '.$db, OC_Log::FATAL ); + OC_Log::write('migration', 'Migration.db not found at: '.$db, OC_Log::FATAL ); return false; } diff --git a/lib/migration/content.php b/lib/migration/content.php index 89b1e782d86..00df62f0c7f 100644 --- a/lib/migration/content.php +++ b/lib/migration/content.php @@ -48,16 +48,16 @@ class OC_Migration_Content{ // @brief prepares the db // @param $query the sql query to prepare public function prepare( $query ) { - + // Only add database to tmpfiles if actually used if( !is_null( $this->db ) ) { // Get db path $db = $this->db->getDatabase(); - if(!in_array($db, $this->tmpfiles)){ + if(!in_array($db, $this->tmpfiles)) { $this->tmpfiles[] = $db; } } - + // Optimize the query $query = $this->processQuery( $query ); @@ -152,7 +152,7 @@ class OC_Migration_Content{ $sql = "INSERT INTO `" . $options['table'] . '` ( `'; $fieldssql = implode( '`, `', $fields ); $sql .= $fieldssql . "` ) VALUES( "; - $valuessql = substr( str_repeat( '?, ', count( $fields ) ),0,-2 ); + $valuessql = substr( str_repeat( '?, ', count( $fields ) ), 0, -2 ); $sql .= $valuessql . " )"; // Make the query $query = $this->prepare( $sql ); @@ -205,7 +205,7 @@ class OC_Migration_Content{ } closedir($dirhandle); } else { - OC_Log::write('admin_export',"Was not able to open directory: " . $dir,OC_Log::ERROR); + OC_Log::write('admin_export', "Was not able to open directory: " . $dir, OC_Log::ERROR); return false; } return true; diff --git a/lib/minimizer.php b/lib/minimizer.php index d50ab0d2397..db522de74dc 100644 --- a/lib/minimizer.php +++ b/lib/minimizer.php @@ -2,14 +2,11 @@ abstract class OC_Minimizer { public function generateETag($files) { - $etag = ''; - sort($files); + $fullpath_files = array(); foreach($files as $file_info) { - $file = $file_info[0] . '/' . $file_info[2]; - $stat = stat($file); - $etag .= $file.$stat['mtime'].$stat['size']; + $fullpath_files[] = $file_info[0] . '/' . $file_info[2]; } - return md5($etag); + return OC_Cache::generateCacheKeyFromFiles($fullpath_files); } abstract public function minimizeFiles($files); @@ -33,6 +30,12 @@ abstract class OC_Minimizer { $cache->set($cache_key.'.gz', $gzout); OC_Response::setETagHeader($etag); } + // on some systems (e.g. SLES 11, but not Ubuntu) mod_deflate and zlib compression will compress the output twice. + // This results in broken core.css and core.js. To avoid it, we switch off zlib compression. + // Since mod_deflate is still active, Apache will compress what needs to be compressed, i.e. no disadvantage. + if(function_exists('apache_get_modules') && ini_get('zlib.output_compression') && in_array('mod_deflate', apache_get_modules())) { + ini_set('zlib.output_compression', 'Off'); + } if ($encoding = OC_Request::acceptGZip()) { header('Content-Encoding: '.$encoding); $out = $gzout; @@ -51,11 +54,11 @@ abstract class OC_Minimizer { } if (!function_exists('gzdecode')) { - function gzdecode($data,$maxlength=null,&$filename='',&$error='') + function gzdecode($data, $maxlength=null, &$filename='', &$error='') { - if (strcmp(substr($data,0,9),"\x1f\x8b\x8\0\0\0\0\0\0")) { + 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)); + return gzinflate(substr($data, 10, -8)); } } diff --git a/lib/ocs.php b/lib/ocs.php index 7350c3c8821..1a0abf0e367 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -23,7 +23,8 @@ * */ - +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; /** * Class to handle open collaboration services API requests @@ -92,91 +93,144 @@ class OC_OCS { 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 + $router = new OC_Router(); + $router->useCollection('root'); // CONFIG - // apiconfig - GET - CONFIG - if(($method=='get') and ($ex[$paracount-3] == 'v1.php') and ($ex[$paracount-2] == 'config')) { - OC_OCS::apiconfig($format); + $router->create('config', '/config.{format}') + ->defaults(array('format' => $format)) + ->action('OC_OCS', 'apiConfig') + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('person_check', '/person/check.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $login = OC_OCS::readData('post', 'login', 'text'); + $passwd = OC_OCS::readData('post', 'password', 'text'); + OC_OCS::personCheck($format, $login, $passwd); + }) + ->requirements(array('format'=>'xml|json')); // 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); - + $router->create('activity_get', '/activity.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $page = OC_OCS::readData('get', 'page', 'int', 0); + $pagesize = OC_OCS::readData('get', 'pagesize', 'int', 10); + if($pagesize<1 or $pagesize>100) $pagesize=10; + OC_OCS::activityGet($format, $page, $pagesize); + }) + ->requirements(array('format'=>'xml|json')); // 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); - + $router->create('activity_put', '/activity.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $message = OC_OCS::readData('post', 'message', 'text'); + OC_OCS::activityPut($format, $message); + }) + ->requirements(array('format'=>'xml|json')); // 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); - + $router->create('privatedata_get', + '/privatedata/getattribute/{app}/{key}.{format}') + ->defaults(array('app' => '', 'key' => '', 'format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + OC_OCS::privateDataGet($format, $app, $key); + }) + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('privatedata_set', + '/privatedata/setattribute/{app}/{key}.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + $value=OC_OCS::readData('post', 'value', 'text'); + OC_OCS::privateDataSet($format, $app, $key, $value); + }) + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('privatedata_delete', + '/privatedata/deleteattribute/{app}/{key}.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + OC_OCS::privateDataDelete($format, $app, $key); + }) + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('system_webapps', + '/cloud/system/webapps.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + OC_OCS::systemwebapps($format); + }) + ->requirements(array('format'=>'xml|json')); // 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); - + $router->create('quota_get', + '/cloud/user/{user}.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $user = $parameters['user']; + OC_OCS::quotaGet($format, $user); + }) + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('quota_set', + '/cloud/user/{user}.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $user = $parameters['user']; + $quota = self::readData('post', 'quota', 'int'); + OC_OCS::quotaSet($format, $user, $quota); + }) + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('keygetpublic', + '/cloud/user/{user}/publickey.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $user = $parameters['user']; + OC_OCS::publicKeyGet($format, $user); + }) + ->requirements(array('format'=>'xml|json')); // 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); + $router->create('keygetpublic', + '/cloud/user/{user}/privatekey.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $user = $parameters['user']; + OC_OCS::privateKeyGet($format, $user); + }) + ->requirements(array('format'=>'xml|json')); // add more calls here @@ -190,13 +244,17 @@ class OC_OCS { // 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"; + try { + $router->match($_SERVER['PATH_INFO']); + } catch (ResourceNotFoundException $e) { + $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)); + } catch (MethodNotAllowedException $e) { + OC_Response::setStatus(405); } exit(); } @@ -268,7 +326,7 @@ class OC_OCS { * @param int $itemsperpage * @return string xml/json */ - private static function generateXml($format,$status,$statuscode,$message,$data=array(),$tag='',$tagattribute='',$dimension=-1,$itemscount='',$itemsperpage='') { + private static function generateXml($format, $status, $statuscode, $message, $data=array(), $tag='', $tagattribute='', $dimension=-1, $itemscount='', $itemsperpage='') { if($format=='json') { $json=array(); $json['status']=$status; @@ -288,7 +346,7 @@ class OC_OCS { 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($itemscount<>'') xmlwriter_write_element($writer, 'totalitems', $itemscount); if(!empty($itemsperpage)) xmlwriter_write_element($writer, 'itemsperpage', $itemsperpage); xmlwriter_end_element($writer); if($dimension=='0') { @@ -303,7 +361,7 @@ class OC_OCS { xmlwriter_end_element($writer); }elseif($dimension=='2') { - xmlwriter_start_element($writer,'data'); + xmlwriter_start_element($writer, 'data'); foreach($data as $entry) { xmlwriter_start_element($writer, $tag); if(!empty($tagattribute)) { @@ -358,14 +416,14 @@ class OC_OCS { } } - public static function toXml($writer,$data,$node) { + 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); + OC_OCS::toxml($writer, $value, $node); xmlwriter_end_element($writer); }else{ xmlwriter_write_element($writer, $key, $value); @@ -378,7 +436,8 @@ class OC_OCS { * @param string $format * @return string xml/json */ - private static function apiConfig($format) { + public static function apiConfig($parameters) { + $format = $parameters['format']; $user=OC_OCS::checkpassword(false); $url=substr(OCP\Util::getServerHost().$_SERVER['SCRIPT_NAME'], 0, -11).''; @@ -397,7 +456,7 @@ class OC_OCS { * @param string $passwd * @return string xml/json */ - private static function personCheck($format,$login,$passwd) { + private static function personCheck($format, $login, $passwd) { if($login<>'') { if(OC_User::login($login, $passwd)) { $xml['person']['personid']=$login; @@ -424,7 +483,7 @@ class OC_OCS { //TODO - $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'activity', 'full', 2, $totalcount,$pagesize); + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'activity', 'full', 2, $totalcount, $pagesize); echo($txt); } @@ -434,7 +493,7 @@ class OC_OCS { * @param string $message * @return string xml/json */ - private static function activityPut($format,$message) { + private static function activityPut($format, $message) { // not implemented in ownCloud $user=OC_OCS::checkpassword(); echo(OC_OCS::generatexml($format, 'ok', 100, '')); @@ -565,7 +624,7 @@ class OC_OCS { foreach($apps as $app) { $info=OC_App::getAppInfo($app); if(isset($info['standalone'])) { - $newvalue=array('name'=>$info['name'],'url'=>OC_Helper::linkToAbsolute($app,''),'icon'=>''); + $newvalue=array('name'=>$info['name'], 'url'=>OC_Helper::linkToAbsolute($app, ''), 'icon'=>''); $values[]=$newvalue; } @@ -582,7 +641,7 @@ class OC_OCS { * @param string $user * @return string xml/json */ - private static function quotaGet($format,$user) { + private static function quotaGet($format, $user) { $login=OC_OCS::checkpassword(); if(OC_Group::inGroup($login, 'admin') or ($login==$user)) { @@ -621,7 +680,7 @@ class OC_OCS { * @param string $quota * @return string xml/json */ - private static function quotaSet($format,$user,$quota) { + private static function quotaSet($format, $user, $quota) { $login=OC_OCS::checkpassword(); if(OC_Group::inGroup($login, 'admin')) { @@ -644,7 +703,7 @@ class OC_OCS { * @param string $user * @return string xml/json */ - private static function publicKeyGet($format,$user) { + private static function publicKeyGet($format, $user) { $login=OC_OCS::checkpassword(); if(OC_User::userExists($user)) { @@ -662,7 +721,7 @@ class OC_OCS { * @param string $user * @return string xml/json */ - private static function privateKeyGet($format,$user) { + private static function privateKeyGet($format, $user) { $login=OC_OCS::checkpassword(); if(OC_Group::inGroup($login, 'admin') or ($login==$user)) { diff --git a/lib/ocsclient.php b/lib/ocsclient.php index 794bc972f57..12e5026a877 100644 --- a/lib/ocsclient.php +++ b/lib/ocsclient.php @@ -55,20 +55,11 @@ class OC_OCSClient{ * This function calls an OCS server and returns the response. It also sets a sane timeout */ private static function getOCSresponse($url) { - // set a sensible timeout of 10 sec to stay responsive even if the server is down. - $ctx = stream_context_create( - array( - 'http' => array( - 'timeout' => 10 - ) - ) - ); - $data=@file_get_contents($url, 0, $ctx); + $data = \OC_Util::getUrlContent($url); return($data); } - - /** + /** * @brief Get all the categories from the OCS server * @returns array with category ids * @note returns NULL if config value appstoreenabled is set to false @@ -76,12 +67,12 @@ class OC_OCSClient{ */ public static function getCategories() { if(OC_Config::getValue('appstoreenabled', true)==false) { - return NULL; + return null; } $url=OC_OCSClient::getAppStoreURL().'/content/categories'; $xml=OC_OCSClient::getOCSresponse($url); - if($xml==FALSE) { - return NULL; + if($xml==false) { + return null; } $data=simplexml_load_string($xml); @@ -105,25 +96,25 @@ class OC_OCSClient{ * * This function returns a list of all the applications on the OCS server */ - public static function getApplications($categories,$page,$filter) { + public static function getApplications($categories, $page, $filter) { if(OC_Config::getValue('appstoreenabled', true)==false) { return(array()); } if(is_array($categories)) { - $categoriesstring=implode('x',$categories); + $categoriesstring=implode('x', $categories); }else{ $categoriesstring=$categories; } - $version='&version='.implode('x',\OC_Util::getVersion()); + $version='&version='.implode('x', \OC_Util::getVersion()); $filterurl='&filter='.urlencode($filter); $url=OC_OCSClient::getAppStoreURL().'/content/data?categories='.urlencode($categoriesstring).'&sortmode=new&page='.urlencode($page).'&pagesize=100'.$filterurl.$version; $apps=array(); $xml=OC_OCSClient::getOCSresponse($url); - if($xml==FALSE) { - return NULL; + if($xml==false) { + return null; } $data=simplexml_load_string($xml); @@ -156,14 +147,14 @@ class OC_OCSClient{ */ public static function getApplication($id) { if(OC_Config::getValue('appstoreenabled', true)==false) { - return NULL; + return null; } $url=OC_OCSClient::getAppStoreURL().'/content/data/'.urlencode($id); $xml=OC_OCSClient::getOCSresponse($url); - if($xml==FALSE) { - OC_Log::write('core','Unable to parse OCS content',OC_Log::FATAL); - return NULL; + if($xml==false) { + OC_Log::write('core', 'Unable to parse OCS content', OC_Log::FATAL); + return null; } $data=simplexml_load_string($xml); @@ -192,16 +183,16 @@ class OC_OCSClient{ * * This function returns an download url for an applications from the OCS server */ - public static function getApplicationDownload($id,$item) { + public static function getApplicationDownload($id, $item) { if(OC_Config::getValue('appstoreenabled', true)==false) { - return NULL; + return null; } $url=OC_OCSClient::getAppStoreURL().'/content/download/'.urlencode($id).'/'.urlencode($item); $xml=OC_OCSClient::getOCSresponse($url); - if($xml==FALSE) { - OC_Log::write('core','Unable to parse OCS content',OC_Log::FATAL); - return NULL; + if($xml==false) { + OC_Log::write('core', 'Unable to parse OCS content', OC_Log::FATAL); + return null; } $data=simplexml_load_string($xml); @@ -222,40 +213,35 @@ class OC_OCSClient{ * * This function returns a list of all the knowledgebase entries from the OCS server */ - public static function getKnownledgebaseEntries($page,$pagesize,$search='') { - if(OC_Config::getValue('knowledgebaseenabled', true)==false) { - $kbe=array(); - $kbe['totalitems']=0; - return $kbe; - } - - $p= (int) $page; - $s= (int) $pagesize; - if($search<>'') $searchcmd='&search='.urlencode($search); else $searchcmd=''; - $url=OC_OCSClient::getKBURL().'/knowledgebase/data?type=150&page='.$p.'&pagesize='.$s.$searchcmd; - - $kbe=array(); - $xml=OC_OCSClient::getOCSresponse($url); - - if($xml==FALSE) { - OC_Log::write('core','Unable to parse knowledgebase content',OC_Log::FATAL); - return NULL; - } - $data=simplexml_load_string($xml); - - $tmp=$data->data->content; - for($i = 0; $i < count($tmp); $i++) { - $kb=array(); - $kb['id']=$tmp[$i]->id; - $kb['name']=$tmp[$i]->name; - $kb['description']=$tmp[$i]->description; - $kb['answer']=$tmp[$i]->answer; - $kb['preview1']=$tmp[$i]->smallpreviewpic1; - $kb['detailpage']=$tmp[$i]->detailpage; - $kbe[]=$kb; + public static function getKnownledgebaseEntries($page, $pagesize, $search='') { + $kbe = array('totalitems' => 0); + if(OC_Config::getValue('knowledgebaseenabled', true)) { + $p = (int) $page; + $s = (int) $pagesize; + $searchcmd = ''; + if ($search) { + $searchcmd = '&search='.urlencode($search); + } + $url = OC_OCSClient::getKBURL().'/knowledgebase/data?type=150&page='. $p .'&pagesize='. $s . $searchcmd; + $xml = OC_OCSClient::getOCSresponse($url); + $data = @simplexml_load_string($xml); + if($data===false) { + OC_Log::write('core', 'Unable to parse knowledgebase content', OC_Log::FATAL); + return null; + } + $tmp = $data->data->content; + for($i = 0; $i < count($tmp); $i++) { + $kbe[] = array( + 'id' => $tmp[$i]->id, + 'name' => $tmp[$i]->name, + 'description' => $tmp[$i]->description, + 'answer' => $tmp[$i]->answer, + 'preview1' => $tmp[$i]->smallpreviewpic1, + 'detailpage' => $tmp[$i]->detailpage + ); + } + $kbe['totalitems'] = $data->meta->totalitems; } - $total=$data->meta->totalitems; - $kbe['totalitems']=$total; return $kbe; } diff --git a/lib/preferences.php b/lib/preferences.php index b198a18415c..6270457834d 100644 --- a/lib/preferences.php +++ b/lib/preferences.php @@ -139,7 +139,7 @@ class OC_Preferences{ public static function setValue( $user, $app, $key, $value ) { // Check if the key does exist $query = OC_DB::prepare( 'SELECT `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?' ); - $values=$query->execute(array($user,$app,$key))->fetchAll(); + $values=$query->execute(array($user, $app, $key))->fetchAll(); $exists=(count($values)>0); if( !$exists ) { diff --git a/lib/public/backgroundjob.php b/lib/public/backgroundjob.php index aba7d2b7620..601046fe691 100644 --- a/lib/public/backgroundjob.php +++ b/lib/public/backgroundjob.php @@ -47,6 +47,29 @@ namespace OCP; */ class BackgroundJob { /** + * @brief get the execution type of background jobs + * @return string + * + * This method returns the type how background jobs are executed. If the user + * did not select something, the type is ajax. + */ + public static function getExecutionType() { + return \OC_BackgroundJob::getExecutionType(); + } + + /** + * @brief sets the background jobs execution type + * @param $type execution type + * @return boolean + * + * This method sets the execution type of the background jobs. Possible types + * are "none", "ajax", "webcron", "cron" + */ + public static function setExecutionType( $type ) { + return \OC_BackgroundJob::setExecutionType( $type ); + } + + /** * @brief creates a regular task * @param $klass class name * @param $method method name diff --git a/lib/public/constants.php b/lib/public/constants.php new file mode 100644 index 00000000000..bc979c9031f --- /dev/null +++ b/lib/public/constants.php @@ -0,0 +1,38 @@ +<?php +/** + * ownCloud + * + * @author Thomas Tanghus + * @copyright 2012 Thomas Tanghus (thomas@tanghus.net) + * + * 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 file defines common constants used in ownCloud + */ + +namespace OCP; + +/** + * CRUDS permissions. + */ +const PERMISSION_CREATE = 4; +const PERMISSION_READ = 1; +const PERMISSION_UPDATE = 2; +const PERMISSION_DELETE = 8; +const PERMISSION_SHARE = 16; +const PERMISSION_ALL = 31; + diff --git a/lib/public/contacts.php b/lib/public/contacts.php new file mode 100644 index 00000000000..5762fd28e02 --- /dev/null +++ b/lib/public/contacts.php @@ -0,0 +1,103 @@ +<?php +/** + * ownCloud + * + * @author Thomas Müller + * @copyright 2012 Thomas Müller thomas.mueller@tmit.eu + * + * 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. + * Contacts Class + * + */ + +// 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 access to the contacts app. Use this class exclusively if you want to access contacts. + * + * Contacts in general will be expressed as an array of key-value-pairs. + * The keys will match the property names defined in https://tools.ietf.org/html/rfc2426#section-1 + * + * Proposed workflow for working with contacts: + * - search for the contacts + * - manipulate the results array + * - createOrUpdate will save the given contacts overwriting the existing data + * + * For updating it is mandatory to keep the id. + * Without an id a new contact will be created. + * + */ +class Contacts +{ + /** + * This function is used to search and find contacts within the users address books. + * In case $pattern is empty all contacts will be returned. + * + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - for future use. One should always have options! + * @return array of contacts which are arrays of key-value-pairs + */ + public static function search($pattern, $searchProperties = array(), $options = array()) { + + // dummy results + return array( + array('id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', 'GEO' => '37.386013;-122.082932'), + array('id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => array('d@e.f', 'g@h.i')), + ); + } + + /** + * This function can be used to delete the contact identified by the given id + * + * @param object $id the unique identifier to a contact + * @return bool successful or not + */ + public static function delete($id) { + return false; + } + + /** + * This function is used to create a new contact if 'id' is not given or not present. + * Otherwise the contact will be updated by replacing the entire data set. + * + * @param array $properties this array if key-value-pairs defines a contact + * @return array representing the contact just created or updated + */ + public static function createOrUpdate($properties) { + + // dummy + return array('id' => 0, 'FN' => 'Thomas Müller', 'EMAIL' => 'a@b.c', + 'PHOTO' => 'VALUE=uri:http://www.abc.com/pub/photos/jqpublic.gif', + 'ADR' => ';;123 Main Street;Any Town;CA;91921-1234' + ); + } + + /** + * Check if contacts are available (e.g. contacts app enabled) + * + * @return bool true if enabled, false if not + */ + public static function isEnabled() { + return false; + } + +} diff --git a/lib/public/db.php b/lib/public/db.php index 6ce62b27ca2..92ff8f93a22 100644 --- a/lib/public/db.php +++ b/lib/public/db.php @@ -42,10 +42,31 @@ class DB { * SQL query via MDB2 prepare(), needs to be execute()'d! */ static public function prepare( $query, $limit=null, $offset=null ) { - return(\OC_DB::prepare($query,$limit,$offset)); + return(\OC_DB::prepare($query, $limit, $offset)); } /** + * @brief Insert a row if a matching row doesn't exists. + * @param $table string The table name (will replace *PREFIX*) to perform the replace on. + * @param $input array + * + * The input array if in the form: + * + * array ( 'id' => array ( 'value' => 6, + * 'key' => true + * ), + * 'name' => array ('value' => 'Stoyan'), + * 'family' => array ('value' => 'Stefanov'), + * 'birth_date' => array ('value' => '1975-06-20') + * ); + * @returns true/false + * + */ + public static function insertIfNotExist($table, $input) { + return(\OC_DB::insertIfNotExist($table, $input)); + } + + /** * @brief gets last value of autoincrement * @param $table string The optional table name (will replace *PREFIX*) and add sequence suffix * @returns id diff --git a/lib/public/share.php b/lib/public/share.php index 1db3a0b2c1d..d736871d244 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -1,1251 +1,1318 @@ -<?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');
-\OC_Hook::connect('OC_User', 'post_deleteGroup', 'OCP\Share', 'post_deleteGroup');
-
-/**
-* 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_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 (self::isEnabled()) {
- 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 Check if the Share API is enabled
- * @return Returns true if enabled or false
- *
- * The Share API is enabled by default if not configured
- *
- */
- public static function isEnabled() {
- if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') {
- return true;
- }
- 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 item of item type shared by a link
- * @param string Item type
- * @param string Item source
- * @param string Owner of link
- * @return Item
- */
- public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) {
- return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, null, 1);
- }
-
- /**
- * @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_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();
- $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global');
- // 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);
- }
- if ($sharingPolicy == 'groups_only') {
- $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 ($sharingPolicy == 'groups_only' && !\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_LINK) {
- if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') {
- if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, null, 1)) {
- // If password is set delete the old link
- if (isset($shareWith)) {
- self::delete($checkExists['id']);
- } else {
- $message = 'Sharing '.$itemSource.' failed, because this item is already shared with a link';
- \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
- throw new \Exception($message);
- }
- }
- // Generate hash of password - same method as user passwords
- if (isset($shareWith)) {
- $forcePortable = (CRYPT_BLOWFISH != 1);
- $hasher = new \PasswordHash(8, $forcePortable);
- $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', ''));
- }
- return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions);
- }
- $message = 'Sharing '.$itemSource.' failed, because sharing with links is not allowed';
- \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR);
- throw new \Exception($message);
- return false;
-// } 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 {
- // Check file extension for an equivalent item type to convert to
- $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;
- }
- }
- // 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($itemType, $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_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) {
- // Insert an extra row for the group share and set permission to 0 to prevent it from showing up for the user
- $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 (?,?,?,?,?,?,?,?,?,?,?)');
- $query->execute(array($item['item_type'], $item['item_source'], $item['item_target'], $item['id'], self::$shareTypeGroupUserUnique, \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'], $item['file_target']));
- \OC_DB::insertid('*PREFIX*share');
- // Delete all reshares by this user of the group share
- self::delete($item['id'], true, \OC_User::getUser());
- } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) {
- // Set permission to 0 to prevent it from showing up for the user
- $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?');
- $query->execute(array(0, $item['id']));
- self::delete($item['id'], true);
- } else {
- self::delete($item['id']);
- }
- return 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_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` = ?', 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);
- }
-
- public static function setExpirationDate($itemType, $itemSource, $date) {
- if ($items = self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), self::FORMAT_NONE, null, -1, false)) {
- if (!empty($items)) {
- if ($date == '') {
- $date = null;
- } else {
- $date = new \DateTime($date);
- $date = date('Y-m-d H:i', $date->format('U') - $date->getOffset());
- }
- $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?');
- foreach ($items as $item) {
- $query->execute(array($date, $item['id']));
- }
- return true;
- }
- }
- return false;
- }
-
- /**
- * @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 (!self::getBackend($itemType) instanceof Share_Backend_Collection) {
- unset($collectionTypes[0]);
- }
- // Return array if collections were found or the item type is a collection itself - collections can be inside collections
- if (count($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_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) {
- if (!self::isEnabled()) {
- if ($limit == 1 || (isset($uidOwner) && isset($item))) {
- return false;
- } else {
- return array();
- }
- }
- $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
- if (!in_array($itemType, $collectionTypes)) {
- $itemTypes = array_merge(array($itemType), $collectionTypes);
- } else {
- $itemTypes = $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)) {
- // Include all user and group items
- if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) {
- $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` = ?';
- $queryArgs[] = $shareType;
- if (isset($shareWith)) {
- $where .= ' AND `share_with` = ?';
- $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 ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) {
- $where .= ' AND (';
- } else {
- $where .= ' AND';
- }
- // 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 .= ' `file_source` = ?';
- $column = 'file_source';
- } else {
- $where .= ' `item_source` = ?';
- $column = 'item_source';
- }
- } else {
- if ($itemType == 'file' || $itemType == 'folder') {
- $where .= ' `file_target` = ?';
- $item = \OC_Filesystem::normalizePath($item);
- } else {
- $where .= ' `item_target` = ?';
- }
- }
- $queryArgs[] = $item;
- if ($includeCollections && $collectionTypes) {
- $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) {
- $queryLimit = 3;
- } else {
- $queryLimit = $limit;
- }
- } else {
- $queryLimit = null;
- }
- // TODO Optimize selects
- if ($format == self::FORMAT_STATUSES) {
- if ($itemType == 'file' || $itemType == 'folder') {
- $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `share_type`, `file_source`, `path`, `expiration`';
- } else {
- $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`';
- }
- } else {
- if (isset($uidOwner)) {
- if ($itemType == 'file' || $itemType == 'folder') {
- $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`, `expiration`';
- } else {
- $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`, `stime`, `file_source`, `expiration`';
- }
- } 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`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `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`, `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`';
- }
- } else {
- $select = '*';
- }
- }
- }
- $root = strlen($root);
- $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit);
- $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']]);
- if ($row['permissions'] == 0) {
- continue;
- }
- } 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['path'])) {
- if (isset($row['parent'])) {
- $row['path'] = '/Shared/'.basename($row['path']);
- } else {
- $row['path'] = substr($row['path'], $root);
- }
- }
- if (isset($row['expiration'])) {
- $time = new \DateTime();
- if ($row['expiration'] < date('Y-m-d H:i', $time->format('U') - $time->getOffset())) {
- self::delete($row['id']);
- continue;
- }
- }
- $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 && $collectionTypes && in_array($row['item_type'], $collectionTypes)) {
- if (($collectionBackend = self::getBackend($row['item_type'])) && $collectionBackend instanceof Share_Backend_Collection) {
- // Collections can be inside collections, check if the item is a collection
- if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) {
- $collectionItems[] = $row;
- } else {
- $collection = array();
- $collection['item_type'] = $row['item_type'];
- if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
- $collection['path'] = basename($row['path']);
- }
- $row['collection'] = $collection;
- // Fetch all of the children sources
- $children = $collectionBackend->getChildren($row[$column]);
- foreach ($children as $child) {
- $childItem = $row;
- $childItem['item_type'] = $itemType;
- if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') {
- $childItem['item_source'] = $child['source'];
- $childItem['item_target'] = $child['target'];
- }
- if ($backend instanceof Share_Backend_File_Dependent) {
- if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') {
- $childItem['file_source'] = $child['source'];
- } else {
- $childItem['file_source'] = \OC_FileCache::getId($child['file_path']);
- }
- $childItem['file_target'] = \OC_Filesystem::normalizePath($child['file_path']);
- }
- if (isset($item)) {
- if ($childItem[$column] == $item) {
- // Return only the item instead of a 2-dimensional array
- if ($limit == 1) {
- if ($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;
- }
- }
- } 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();
- // Switch column to path for files and folders, used for determining statuses inside of folders
- if ($itemType == 'file' || $itemType == 'folder') {
- $column = 'path';
- }
- foreach ($items as $item) {
- if ($item['share_type'] == self::SHARE_TYPE_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_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) {
- $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 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'];
- $fileSource = $checkReshare['file_source'];
- $suggestedItemTarget = $checkReshare['item_target'];
- $suggestedFileTarget = $checkReshare['file_target'];
- $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;
- $suggestedItemTarget = null;
- $suggestedFileTarget = 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) {
- $filePath = $backend->getFilePath($itemSource, $uidOwner);
- if ($itemType == 'file' || $itemType == 'folder') {
- $fileSource = $itemSource;
- } else {
- $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) {
- $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget);
- if (isset($fileSource)) {
- if ($parentFolder) {
- if ($parentFolder === true) {
- $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner, $suggestedFileTarget);
- // 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'];
- }
- } else {
- $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner, $suggestedFileTarget);
- }
- } else {
- $groupFileTarget = null;
- }
- $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');
- // 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, $suggestedItemTarget, $parent);
- if (isset($fileSource)) {
- if ($parentFolder) {
- if ($parentFolder === true) {
- $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, $uidOwner, $suggestedFileTarget, $parent);
- 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, $suggestedFileTarget, $parent);
- }
- } 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)) {
- $query->execute(array($itemType, $itemSource, $itemTarget, $parent, self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), $fileSource, $fileTarget));
- \OC_DB::insertid('*PREFIX*share');
- }
- }
- 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, $suggestedItemTarget);
- if (isset($fileSource)) {
- if ($parentFolder) {
- if ($parentFolder === true) {
- $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, $suggestedFileTarget);
- $parentFolders['folder'] = $fileTarget;
- } else {
- $fileTarget = $parentFolder['folder'].$itemSource;
- $parent = $parentFolder['id'];
- }
- } else {
- $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, $suggestedFileTarget);
- }
- } 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_LINK
- * @param string User or group the item is being shared with
- * @param string The suggested target originating from a reshare (optional)
- * @param int The id of the parent group share (optional)
- * @return string Item target
- */
- private static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedTarget = null, $groupParent = null) {
- $backend = self::getBackend($itemType);
- if ($shareType == self::SHARE_TYPE_LINK) {
- if (isset($suggestedTarget)) {
- return $suggestedTarget;
- }
- return $backend->generateTarget($itemSource, false);
- } else {
- if ($itemType == 'file' || $itemType == 'folder') {
- $column = 'file_target';
- $columnSource = 'file_source';
- } else {
- $column = 'item_target';
- $columnSource = 'item_source';
- }
- 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++) {
- // Check if suggested target exists first
- if ($i == 0 && isset($suggestedTarget)) {
- $target = $suggestedTarget;
- } else {
- 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
- $checkTarget = self::getItems($itemType, $target, $shareType, $shareWith);
- if (!empty($checkTarget)) {
- foreach ($checkTarget as $item) {
- // Skip item if it is the group parent row
- if (isset($groupParent) && $item['id'] == $groupParent) {
- if (count($checkTarget) == 1) {
- return $target;
- } else {
- continue;
- }
- }
- if ($item['uid_owner'] == $uidOwner) {
- if ($itemType == 'file' || $itemType == 'folder') {
- if ($item['file_source'] == \OC_FileCache::getId($itemSource)) {
- return $target;
- }
- } else if ($item['item_source'] == $itemSource) {
- return $target;
- }
- }
- }
- if (!isset($exclude)) {
- $exclude = array();
- }
- // Find similar targets to improve backend's chances to generate a unqiue target
- if ($userAndGroups) {
- if ($column == 'file_target') {
- $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` IN (\'file\', \'folder\') AND `share_type` IN (?,?,?) AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')');
- $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique));
- } else {
- $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` = ? AND `share_type` IN (?,?,?) AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')');
- $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique));
- }
- } else {
- if ($column == 'file_target') {
- $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` IN (\'file\', \'folder\') AND `share_type` = ? AND `share_with` = ?');
- $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith));
- } else {
- $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?');
- $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith));
- }
- }
- 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`, `uid_owner`, `item_type`, `item_target`, `parent` 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) {
- // Find the group shares and check if the user needs a unique target
- $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?');
- $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid']));
- $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 (?,?,?,?,?,?,?,?,?,?,?)');
- while ($item = $result->fetchRow()) {
- if ($item['item_type'] == 'file' || $item['item_type'] == 'file') {
- $itemTarget = null;
- } else {
- $itemTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']);
- }
- if (isset($item['file_source'])) {
- $fileTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']);
- } else {
- $fileTarget = null;
- }
- // Insert an extra row for the group share if the item or file target is unique for this user
- if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) {
- $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], $item['stime'], $item['file_source'], $fileTarget));
- \OC_DB::insertid('*PREFIX*share');
- }
- }
- }
-
- 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']);
- }
- }
- }
-
- public static function post_deleteGroup($arguments) {
- $query = \OC_DB::prepare('SELECT id FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?');
- $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid']));
- while ($item = $result->fetchRow()) {
- 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 children each inside an array with the keys: source, target, and file_path if applicable
- */
- public function getChildren($itemSource);
-
-}
+<?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; + +/** +* 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. +* +* It provides the following hooks: +* - post_shared +*/ +class Share { + + const SHARE_TYPE_USER = 0; + const SHARE_TYPE_GROUP = 1; + const SHARE_TYPE_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 + * @see lib/public/constants.php + */ + + const FORMAT_NONE = -1; + const FORMAT_STATUSES = -2; + const FORMAT_SOURCES = -3; + + const TOKEN_LENGTH = 32; // see db_structure.xml + + 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 (self::isEnabled()) { + 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 Check if the Share API is enabled + * @return Returns true if enabled or false + * + * The Share API is enabled by default if not configured + * + */ + public static function isEnabled() { + if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') == 'yes') { + return true; + } + 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 item of item type shared by a link + * @param string Item type + * @param string Item source + * @param string Owner of link + * @return Item + */ + public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner) { + return self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, null, 1); + } + + /** + * @brief Get the item shared by a token + * @param string token + * @return Item + */ + public static function getShareByToken($token) { + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `token` = ?',1); + $result = $query->execute(array($token)); + if (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR); + } + return $result->fetchRow(); + } + + /** + * @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_LINK + * @param string User or group the item is being shared with + * @param int CRUDS permissions + * @return bool|string Returns true on success or false on failure, Returns token on success for links + */ + public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions) { + $uidOwner = \OC_User::getUser(); + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + // 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); + } + if ($sharingPolicy == 'groups_only') { + $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 ($sharingPolicy == 'groups_only' && !\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_LINK) { + if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { + // when updating a link share + if ($checkExists = self::getItems($itemType, $itemSource, self::SHARE_TYPE_LINK, null, $uidOwner, self::FORMAT_NONE, null, 1)) { + // remember old token + $oldToken = $checkExists['token']; + //delete the old share + self::delete($checkExists['id']); + } + + // Generate hash of password - same method as user passwords + if (isset($shareWith)) { + $forcePortable = (CRYPT_BLOWFISH != 1); + $hasher = new \PasswordHash(8, $forcePortable); + $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', '')); + } + + // Generate token + if (isset($oldToken)) { + $token = $oldToken; + } else { + $token = \OC_Util::generate_random_bytes(self::TOKEN_LENGTH); + } + $result = self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, null, $token); + if ($result) { + return $token; + } else { + return false; + } + } + $message = 'Sharing '.$itemSource.' failed, because sharing with links is not allowed'; + \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); + throw new \Exception($message); + return false; +// } 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 { + // Check file extension for an equivalent item type to convert to + $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; + } + } + // 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($itemType, $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_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 from all users, groups, and remove all links + * @param string Item type + * @param string Item source + * @return Returns true on success or false on failure + */ + public static function unshareAll($itemType, $itemSource) { + if ($shares = self::getItemShared($itemType, $itemSource)) { + foreach ($shares as $share) { + self::delete($share['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) { + // Insert an extra row for the group share and set permission to 0 to prevent it from showing up for the user + $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 (?,?,?,?,?,?,?,?,?,?,?)'); + $query->execute(array($item['item_type'], $item['item_source'], $item['item_target'], $item['id'], self::$shareTypeGroupUserUnique, \OC_User::getUser(), $item['uid_owner'], 0, $item['stime'], $item['file_source'], $item['file_target'])); + \OC_DB::insertid('*PREFIX*share'); + // Delete all reshares by this user of the group share + self::delete($item['id'], true, \OC_User::getUser()); + } else if ((int)$item['share_type'] === self::$shareTypeGroupUserUnique) { + // Set permission to 0 to prevent it from showing up for the user + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `permissions` = ? WHERE `id` = ?'); + $query->execute(array(0, $item['id'])); + self::delete($item['id'], true); + } else { + self::delete($item['id']); + } + return 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_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` = ?', 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'] & PERMISSION_SHARE) && (~$permissions & 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); + } + + public static function setExpirationDate($itemType, $itemSource, $date) { + if ($items = self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), self::FORMAT_NONE, null, -1, false)) { + if (!empty($items)) { + if ($date == '') { + $date = null; + } else { + $date = new \DateTime($date); + $date = date('Y-m-d H:i', $date->format('U') - $date->getOffset()); + } + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `id` = ?'); + foreach ($items as $item) { + $query->execute(array($date, $item['id'])); + } + return true; + } + } + return false; + } + + /** + * @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 (!self::getBackend($itemType) instanceof Share_Backend_Collection) { + unset($collectionTypes[0]); + } + // Return array if collections were found or the item type is a collection itself - collections can be inside collections + if (count($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_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) { + if (!self::isEnabled()) { + if ($limit == 1 || (isset($uidOwner) && isset($item))) { + return false; + } else { + return array(); + } + } + $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 + if (!in_array($itemType, $collectionTypes)) { + $itemTypes = array_merge(array($itemType), $collectionTypes); + } else { + $itemTypes = $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)) { + // Include all user and group items + if ($shareType == self::$shareTypeUserAndGroups && isset($shareWith)) { + $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` = ?'; + $queryArgs[] = $shareType; + if (isset($shareWith)) { + $where .= ' AND `share_with` = ?'; + $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 ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) { + $where .= ' AND ('; + } else { + $where .= ' AND'; + } + // 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 .= ' `file_source` = ?'; + $column = 'file_source'; + } else { + $where .= ' `item_source` = ?'; + $column = 'item_source'; + } + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $where .= ' `file_target` = ?'; + $item = \OC_Filesystem::normalizePath($item); + } else { + $where .= ' `item_target` = ?'; + } + } + $queryArgs[] = $item; + if ($includeCollections && $collectionTypes) { + $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) { + $queryLimit = 3; + } else { + $queryLimit = $limit; + } + } else { + $queryLimit = null; + } + // TODO Optimize selects + if ($format == self::FORMAT_STATUSES) { + if ($itemType == 'file' || $itemType == 'folder') { + $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `share_type`, `file_source`, `path`, `expiration`'; + } else { + $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`'; + } + } else { + if (isset($uidOwner)) { + if ($itemType == 'file' || $itemType == 'folder') { + $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`, `expiration`, `token`'; + } else { + $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`, `stime`, `file_source`, `expiration`, `token`'; + } + } 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`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `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`, `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`'; + } + } else { + $select = '*'; + } + } + } + $root = strlen($root); + $query = \OC_DB::prepare('SELECT '.$select.' FROM `*PREFIX*share` '.$where, $queryLimit); + $result = $query->execute($queryArgs); + if (\OC_DB::isError($result)) { + \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', select=' . $select . ' where=' . $where, \OC_Log::ERROR); + } + $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']]); + if ($row['permissions'] == 0) { + continue; + } + } 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'] & PERMISSION_SHARE && (int)$row['permissions'] & 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['path'])) { + if (isset($row['parent'])) { + $row['path'] = '/Shared/'.basename($row['path']); + } else { + $row['path'] = substr($row['path'], $root); + } + } + if (isset($row['expiration'])) { + $time = new \DateTime(); + if ($row['expiration'] < date('Y-m-d H:i', $time->format('U') - $time->getOffset())) { + self::delete($row['id']); + continue; + } + } + $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 && $collectionTypes && in_array($row['item_type'], $collectionTypes)) { + if (($collectionBackend = self::getBackend($row['item_type'])) && $collectionBackend instanceof Share_Backend_Collection) { + // Collections can be inside collections, check if the item is a collection + if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) { + $collectionItems[] = $row; + } else { + $collection = array(); + $collection['item_type'] = $row['item_type']; + if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { + $collection['path'] = basename($row['path']); + } + $row['collection'] = $collection; + // Fetch all of the children sources + $children = $collectionBackend->getChildren($row[$column]); + foreach ($children as $child) { + $childItem = $row; + $childItem['item_type'] = $itemType; + if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') { + $childItem['item_source'] = $child['source']; + $childItem['item_target'] = $child['target']; + } + if ($backend instanceof Share_Backend_File_Dependent) { + if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { + $childItem['file_source'] = $child['source']; + } else { + $childItem['file_source'] = \OC_FileCache::getId($child['file_path']); + } + $childItem['file_target'] = \OC_Filesystem::normalizePath($child['file_path']); + } + if (isset($item)) { + if ($childItem[$column] == $item) { + // Return only the item instead of a 2-dimensional array + if ($limit == 1) { + if ($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; + } + } + } 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(); + // Switch column to path for files and folders, used for determining statuses inside of folders + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'path'; + } + foreach ($items as $item) { + if ($item['share_type'] == self::SHARE_TYPE_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_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, $token = null) { + $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 share permissions is granted + if ((int)$checkReshare['permissions'] & 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']; + $fileSource = $checkReshare['file_source']; + $suggestedItemTarget = $checkReshare['item_target']; + $suggestedFileTarget = $checkReshare['file_target']; + $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; + $suggestedItemTarget = null; + $suggestedFileTarget = 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) { + $filePath = $backend->getFilePath($itemSource, $uidOwner); + if ($itemType == 'file' || $itemType == 'folder') { + $fileSource = $itemSource; + } else { + $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`, `token`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + // Share with a group + if ($shareType == self::SHARE_TYPE_GROUP) { + $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner, $suggestedFileTarget); + // 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']; + } + } else { + $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith['group'], $uidOwner, $suggestedFileTarget); + } + } else { + $groupFileTarget = null; + } + $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType, $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget, $token)); + // Save this id, any extra rows for this group share will need to reference it + $parent = \OC_DB::insertid('*PREFIX*share'); + // 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, $suggestedItemTarget, $parent); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid, $uidOwner, $suggestedFileTarget, $parent); + 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, $suggestedFileTarget, $parent); + } + } else { + $fileTarget = null; + } + \OC_Hook::emit('OCP\Share', 'post_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'parent' => $parent, + 'shareType' => self::$shareTypeGroupUserUnique, + 'shareWith' => $uid, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'fileTarget' => $fileTarget, + 'id' => $parent, + 'token' => $token + )); + // 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)) { + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), $fileSource, $fileTarget, $token)); + $id = \OC_DB::insertid('*PREFIX*share'); + } + } + 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, $suggestedItemTarget); + if (isset($fileSource)) { + if ($parentFolder) { + if ($parentFolder === true) { + $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, $suggestedFileTarget); + $parentFolders['folder'] = $fileTarget; + } else { + $fileTarget = $parentFolder['folder'].$itemSource; + $parent = $parentFolder['id']; + } + } else { + $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith, $uidOwner, $suggestedFileTarget); + } + } else { + $fileTarget = null; + } + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, $permissions, time(), $fileSource, $fileTarget, $token)); + $id = \OC_DB::insertid('*PREFIX*share'); + \OC_Hook::emit('OCP\Share', 'post_shared', array( + 'itemType' => $itemType, + 'itemSource' => $itemSource, + 'itemTarget' => $itemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $shareWith, + 'uidOwner' => $uidOwner, + 'permissions' => $permissions, + 'fileSource' => $fileSource, + 'fileTarget' => $fileTarget, + 'id' => $id, + 'token' => $token + )); + 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_LINK + * @param string User or group the item is being shared with + * @param string The suggested target originating from a reshare (optional) + * @param int The id of the parent group share (optional) + * @return string Item target + */ + private static function generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedTarget = null, $groupParent = null) { + $backend = self::getBackend($itemType); + if ($shareType == self::SHARE_TYPE_LINK) { + if (isset($suggestedTarget)) { + return $suggestedTarget; + } + return $backend->generateTarget($itemSource, false); + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + $columnSource = 'file_source'; + } else { + $column = 'item_target'; + $columnSource = 'item_source'; + } + 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++) { + // Check if suggested target exists first + if ($i == 0 && isset($suggestedTarget)) { + $target = $suggestedTarget; + } else { + 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 + $checkTarget = self::getItems($itemType, $target, $shareType, $shareWith); + if (!empty($checkTarget)) { + foreach ($checkTarget as $item) { + // Skip item if it is the group parent row + if (isset($groupParent) && $item['id'] == $groupParent) { + if (count($checkTarget) == 1) { + return $target; + } else { + continue; + } + } + if ($item['uid_owner'] == $uidOwner) { + if ($itemType == 'file' || $itemType == 'folder') { + if ($item['file_source'] == \OC_FileCache::getId($itemSource)) { + return $target; + } + } else if ($item['item_source'] == $itemSource) { + return $target; + } + } + } + if (!isset($exclude)) { + $exclude = array(); + } + // Find similar targets to improve backend's chances to generate a unqiue target + if ($userAndGroups) { + if ($column == 'file_target') { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` IN (\'file\', \'folder\') AND `share_type` IN (?,?,?) AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); + $result = $checkTargets->execute(array(self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); + } else { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` = ? AND `share_type` IN (?,?,?) AND `share_with` IN (\''.implode('\',\'', $userAndGroups).'\')'); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_USER, self::SHARE_TYPE_GROUP, self::$shareTypeGroupUserUnique)); + } + } else { + if ($column == 'file_target') { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` IN (\'file\', \'folder\') AND `share_type` = ? AND `share_with` = ?'); + $result = $checkTargets->execute(array(self::SHARE_TYPE_GROUP, $shareWith)); + } else { + $checkTargets = \OC_DB::prepare('SELECT `'.$column.'` FROM `*PREFIX*share` WHERE `item_type` = ? AND `share_type` = ? AND `share_with` = ?'); + $result = $checkTargets->execute(array($itemType, self::SHARE_TYPE_GROUP, $shareWith)); + } + } + 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`, `uid_owner`, `item_type`, `item_target`, `parent` 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'] & 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) { + // Find the group shares and check if the user needs a unique target + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); + $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + $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 (?,?,?,?,?,?,?,?,?,?,?)'); + while ($item = $result->fetchRow()) { + if ($item['item_type'] == 'file' || $item['item_type'] == 'file') { + $itemTarget = null; + } else { + $itemTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'], $item['uid_owner'], $item['item_target'], $item['id']); + } + if (isset($item['file_source'])) { + $fileTarget = self::generateTarget($item['item_type'], $item['item_source'], self::SHARE_TYPE_USER, $arguments['uid'], $item['uid_owner'], $item['file_target'], $item['id']); + } else { + $fileTarget = null; + } + // Insert an extra row for the group share if the item or file target is unique for this user + if ($itemTarget != $item['item_target'] || $fileTarget != $item['file_target']) { + $query->execute(array($item['item_type'], $item['item_source'], $itemTarget, $item['id'], self::$shareTypeGroupUserUnique, $arguments['uid'], $item['uid_owner'], $item['permissions'], $item['stime'], $item['file_source'], $fileTarget)); + \OC_DB::insertid('*PREFIX*share'); + } + } + } + + 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']); + } + } + } + + public static function post_deleteGroup($arguments) { + $query = \OC_DB::prepare('SELECT id FROM `*PREFIX*share` WHERE `share_type` = ? AND `share_with` = ?'); + $result = $query->execute(array(self::SHARE_TYPE_GROUP, $arguments['gid'])); + while ($item = $result->fetchRow()) { + 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 children each inside an array with the keys: source, target, and file_path if applicable + */ + public function getChildren($itemSource); + +} diff --git a/lib/public/util.php b/lib/public/util.php index 38da7e82171..7b5b1abbded 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -61,7 +61,7 @@ class Util { */ public static function sendMail( $toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, $html=0, $altbody='', $ccaddress='', $ccname='', $bcc='') { // call the internal mail class - \OC_MAIL::send( $toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, $html=0, $altbody='', $ccaddress='', $ccname='', $bcc=''); + \OC_MAIL::send($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, $html = 0, $altbody = '', $ccaddress = '', $ccname = '', $bcc = ''); } /** @@ -107,8 +107,8 @@ class Util { * @param int timestamp $timestamp * @param bool dateOnly option to ommit time from the result */ - public static function formatDate( $timestamp,$dateOnly=false) { - return(\OC_Util::formatDate( $timestamp,$dateOnly )); + public static function formatDate( $timestamp, $dateOnly=false) { + return(\OC_Util::formatDate( $timestamp, $dateOnly )); } /** diff --git a/lib/request.php b/lib/request.php index 87262d98625..c975c84a711 100644..100755 --- a/lib/request.php +++ b/lib/request.php @@ -18,6 +18,9 @@ class OC_Request { if(OC::$CLI) { return 'localhost'; } + if(OC_Config::getValue('overwritehost', '')<>''){ + return OC_Config::getValue('overwritehost'); + } 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']))); @@ -40,6 +43,9 @@ class OC_Request { * Returns the server protocol. It respects reverse proxy servers and load balancers */ public static function serverProtocol() { + if(OC_Config::getValue('overwriteprotocol', '')<>''){ + return OC_Config::getValue('overwriteprotocol'); + } if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); }else{ @@ -63,7 +69,7 @@ class OC_Request { $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')); + $encoding = mb_detect_encoding($path_info, array('UTF-8', 'ISO-8859-1')); switch($encoding) { @@ -98,7 +104,7 @@ class OC_Request { $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 ) + else if( strpos($HTTP_ACCEPT_ENCODING, 'gzip') !== false ) return 'gzip'; return false; } diff --git a/lib/route.php b/lib/route.php new file mode 100644 index 00000000000..5901717c094 --- /dev/null +++ b/lib/route.php @@ -0,0 +1,116 @@ +<?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. + */ + +use Symfony\Component\Routing\Route; + +class OC_Route extends Route { + /** + * Specify the method when this route is to be used + * + * @param string $method HTTP method (uppercase) + */ + public function method($method) { + $this->setRequirement('_method', strtoupper($method)); + return $this; + } + + /** + * Specify POST as the method to use with this route + */ + public function post() { + $this->method('POST'); + return $this; + } + + /** + * Specify GET as the method to use with this route + */ + public function get() { + $this->method('GET'); + return $this; + } + + /** + * Specify PUT as the method to use with this route + */ + public function put() { + $this->method('PUT'); + return $this; + } + + /** + * Specify DELETE as the method to use with this route + */ + public function delete() { + $this->method('DELETE'); + return $this; + } + + /** + * Defaults to use for this route + * + * @param array $defaults The defaults + */ + public function defaults($defaults) { + $action = $this->getDefault('action'); + $this->setDefaults($defaults); + if (isset($defaults['action'])) { + $action = $defaults['action']; + } + $this->action($action); + return $this; + } + + /** + * Requirements for this route + * + * @param array $requirements The requirements + */ + public function requirements($requirements) { + $method = $this->getRequirement('_method'); + $this->setRequirements($requirements); + if (isset($requirements['_method'])) { + $method = $requirements['_method']; + } + if ($method) { + $this->method($method); + } + return $this; + } + + /** + * The action to execute when this route matches + * @param string|callable $class the class or a callable + * @param string $function the function to use with the class + * + * This function is called with $class set to a callable or + * to the class with $function + */ + public function action($class, $function = null) { + $action = array($class, $function); + if (is_null($function)) { + $action = $class; + } + $this->setDefault('action', $action); + return $this; + } + + /** + * The action to execute when this route matches, includes a file like + * it is called directly + * @param $file + */ + public function actionInclude($file) { + $function = create_function('$param', + 'unset($param["_route"]);' + .'$_GET=array_merge($_GET, $param);' + .'unset($param);' + .'require_once "'.$file.'";'); + $this->action($function); + } +} diff --git a/lib/router.php b/lib/router.php new file mode 100644 index 00000000000..8cb8fd4f33b --- /dev/null +++ b/lib/router.php @@ -0,0 +1,173 @@ +<?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. + */ + +use Symfony\Component\Routing\Matcher\UrlMatcher; +use Symfony\Component\Routing\Generator\UrlGenerator; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; +//use Symfony\Component\Routing\Route; + +class OC_Router { + protected $collections = array(); + protected $collection = null; + protected $root = null; + + protected $generator = null; + protected $routing_files; + protected $cache_key; + + public function __construct() { + $baseUrl = OC_Helper::linkTo('', 'index.php'); + $method = $_SERVER['REQUEST_METHOD']; + $host = OC_Request::serverHost(); + $schema = OC_Request::serverProtocol(); + $this->context = new RequestContext($baseUrl, $method, $host, $schema); + // TODO cache + $this->root = $this->getCollection('root'); + } + + public function getRoutingFiles() { + if (!isset($this->routing_files)) { + $this->routing_files = array(); + foreach(OC_APP::getEnabledApps() as $app) { + $file = OC_App::getAppPath($app).'/appinfo/routes.php'; + if(file_exists($file)) { + $this->routing_files[$app] = $file; + } + } + } + return $this->routing_files; + } + + public function getCacheKey() { + if (!isset($this->cache_key)) { + $files = $this->getRoutingFiles(); + $files[] = 'settings/routes.php'; + $files[] = 'core/routes.php'; + $this->cache_key = OC_Cache::generateCacheKeyFromFiles($files); + } + return $this->cache_key; + } + + /** + * loads the api routes + */ + public function loadRoutes() { + foreach($this->getRoutingFiles() as $app => $file) { + $this->useCollection($app); + require_once $file; + $collection = $this->getCollection($app); + $this->root->addCollection($collection, '/apps/'.$app); + } + $this->useCollection('root'); + require_once 'settings/routes.php'; + require_once 'core/routes.php'; + } + + protected function getCollection($name) { + if (!isset($this->collections[$name])) { + $this->collections[$name] = new RouteCollection(); + } + return $this->collections[$name]; + } + + /** + * Sets the collection to use for adding routes + * + * @param string $name Name of the colletion to use. + */ + public function useCollection($name) { + $this->collection = $this->getCollection($name); + } + + /** + * Create a OC_Route. + * + * @param string $name Name of the route to create. + * @param string $pattern The pattern to match + * @param array $defaults An array of default parameter values + * @param array $requirements An array of requirements for parameters (regexes) + */ + public function create($name, $pattern, array $defaults = array(), array $requirements = array()) { + $route = new OC_Route($pattern, $defaults, $requirements); + $this->collection->add($name, $route); + return $route; + } + + /** + * Find the route matching $url. + * + * @param string $url The url to find + */ + public function match($url) { + $matcher = new UrlMatcher($this->root, $this->context); + $parameters = $matcher->match($url); + if (isset($parameters['action'])) { + $action = $parameters['action']; + if (!is_callable($action)) { + var_dump($action); + throw new Exception('not a callable action'); + } + unset($parameters['action']); + call_user_func($action, $parameters); + } elseif (isset($parameters['file'])) { + include $parameters['file']; + } else { + throw new Exception('no action available'); + } + } + + /** + * Get the url generator + * + */ + public function getGenerator() + { + if (null !== $this->generator) { + return $this->generator; + } + + return $this->generator = new UrlGenerator($this->root, $this->context); + } + + /** + * Generate url based on $name and $parameters + * + * @param string $name Name of the route to use. + * @param array $parameters Parameters for the route + */ + public function generate($name, $parameters = array(), $absolute = false) + { + return $this->getGenerator()->generate($name, $parameters, $absolute); + } + + /** + * Generate JSON response for routing in javascript + */ + public static function JSRoutes() + { + $router = OC::getRouter(); + + $etag = $router->getCacheKey(); + OC_Response::enableCaching(); + OC_Response::setETagHeader($etag); + + $root = $router->getCollection('root'); + $routes = array(); + foreach($root->all() as $name => $route) { + $compiled_route = $route->compile(); + $defaults = $route->getDefaults(); + unset($defaults['action']); + $routes[$name] = array( + 'tokens' => $compiled_route->getTokens(), + 'defaults' => $defaults, + ); + } + OCP\JSON::success ( array( 'data' => $routes ) ); + } +} diff --git a/lib/search.php b/lib/search.php index 0b6ad050024..3c3378ad13c 100644 --- a/lib/search.php +++ b/lib/search.php @@ -40,8 +40,8 @@ class OC_Search{ * register a new search provider to be used * @param string $provider class name of a OC_Search_Provider */ - public static function registerProvider($class,$options=array()) { - self::$registeredProviders[]=array('class'=>$class,'options'=>$options); + public static function registerProvider($class, $options=array()) { + self::$registeredProviders[]=array('class'=>$class, 'options'=>$options); } /** diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php index 24832296c59..ea536ef77de 100644 --- a/lib/search/provider/file.php +++ b/lib/search/provider/file.php @@ -2,8 +2,9 @@ class OC_Search_Provider_File extends OC_Search_Provider{ function search($query) { - $files=OC_FileCache::search($query,true); + $files=OC_FileCache::search($query, true); $results=array(); + $l=OC_L10N::get('lib'); foreach($files as $fileData) { $path = $fileData['path']; $mime = $fileData['mimetype']; @@ -13,25 +14,25 @@ class OC_Search_Provider_File extends OC_Search_Provider{ $skip = false; if($mime=='httpd/unix-directory') { $link = OC_Helper::linkTo( 'files', 'index.php', array('dir' => $path)); - $type = 'Files'; + $type = (string)$l->t('Files'); }else{ - $link = OC_Helper::linkTo( 'files', 'download.php', array('file' => $path)); + $link = OC_Helper::linkToRoute( 'download', array('file' => $path)); $mimeBase = $fileData['mimepart']; switch($mimeBase) { case 'audio': $skip = true; break; case 'text': - $type = 'Text'; + $type = (string)$l->t('Text'); break; case 'image': - $type = 'Images'; + $type = (string)$l->t('Images'); break; default: if($mime=='application/xml') { - $type = 'Text'; + $type = (string)$l->t('Text'); }else{ - $type = 'Files'; + $type = (string)$l->t('Files'); } } } diff --git a/lib/search/result.php b/lib/search/result.php index 63b5cfabce6..08beaea151c 100644 --- a/lib/search/result.php +++ b/lib/search/result.php @@ -15,7 +15,7 @@ class OC_Search_Result{ * @param string $link link for the result * @param string $type the type of result as human readable string ('File', 'Music', etc) */ - public function __construct($name,$text,$link,$type) { + public function __construct($name, $text, $link, $type) { $this->name=$name; $this->text=$text; $this->link=$link; diff --git a/lib/setup.php b/lib/setup.php index 16b9ec68df6..264cd55795e 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -1,38 +1,5 @@ <?php -$hasSQLite = (is_callable('sqlite_open') or class_exists('SQLite3')); -$hasMySQL = is_callable('mysql_connect'); -$hasPostgreSQL = is_callable('pg_connect'); -$hasOracle = is_callable('oci_connect'); -$datadir = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data'); -$opts = array( - 'hasSQLite' => $hasSQLite, - 'hasMySQL' => $hasMySQL, - 'hasPostgreSQL' => $hasPostgreSQL, - 'hasOracle' => $hasOracle, - 'directory' => $datadir, - 'errors' => array(), -); - -if(isset($_POST['install']) AND $_POST['install']=='true') { - // We have to launch the installation process : - $e = OC_Setup::install($_POST); - $errors = array('errors' => $e); - - if(count($e) > 0) { - //OC_Template::printGuestPage("", "error", array("errors" => $errors)); - $options = array_merge($_POST, $opts, $errors); - OC_Template::printGuestPage("", "installation", $options); - } - else { - header("Location: ".OC::$WEBROOT.'/'); - exit(); - } -} -else { - OC_Template::printGuestPage("", "installation", $opts); -} - class OC_Setup { public static function install($options) { $error = array(); @@ -63,6 +30,9 @@ class OC_Setup { if(empty($options['dbname'])) { $error[] = "$dbprettyname enter the database name."; } + if(substr_count($options['dbname'], '.') >= 1) { + $error[] = "$dbprettyname you may not use dots in the database name"; + } if($dbtype != 'oci' && empty($options['dbhost'])) { $error[] = "$dbprettyname set the database host."; } @@ -85,69 +55,27 @@ class OC_Setup { //write the config file OC_Config::setValue('datadirectory', $datadir); OC_Config::setValue('dbtype', $dbtype); - OC_Config::setValue('version',implode('.',OC_Util::getVersion())); + OC_Config::setValue('version', implode('.', OC_Util::getVersion())); if($dbtype == 'mysql') { $dbuser = $options['dbuser']; $dbpass = $options['dbpass']; $dbname = $options['dbname']; $dbhost = $options['dbhost']; $dbtableprefix = isset($options['dbtableprefix']) ? $options['dbtableprefix'] : 'oc_'; + OC_Config::setValue('dbname', $dbname); OC_Config::setValue('dbhost', $dbhost); OC_Config::setValue('dbtableprefix', $dbtableprefix); - //check if the database user has admin right - $connection = @mysql_connect($dbhost, $dbuser, $dbpass); - if(!$connection) { + try { + self::setupMySQLDatabase($dbhost, $dbuser, $dbpass, $dbname, $dbtableprefix, $username); + } catch (Exception $e) { $error[] = array( 'error' => 'MySQL username and/or password not valid', 'hint' => 'You need to enter either an existing account or the administrator.' ); return($error); } - else { - $oldUser=OC_Config::getValue('dbuser', 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)) { - //use the admin login data for the new database user - - //add prefix to the mysql user name to prevent collisions - $dbusername=substr('oc_'.$username,0,16); - if($dbusername!=$oldUser) { - //hash the password so we don't need to store the admin config in the config file - $dbpassword=md5(time().$password); - - self::createDBUser($dbusername, $dbpassword, $connection); - - OC_Config::setValue('dbuser', $dbusername); - OC_Config::setValue('dbpassword', $dbpassword); - } - - //create the database - self::createDatabase($dbname, $dbusername, $connection); - } - else { - if($dbuser!=$oldUser) { - OC_Config::setValue('dbuser', $dbuser); - OC_Config::setValue('dbpassword', $dbpass); - } - - //create the database - self::createDatabase($dbname, $dbuser, $connection); - } - - //fill the database if needed - $query="select count(*) from information_schema.tables where table_schema='$dbname' AND table_name = '{$dbtableprefix}users';"; - $result = mysql_query($query,$connection); - if($result) { - $row=mysql_fetch_row($result); - } - if(!$result or $row[0]==0) { - OC_DB::createDbFromStructure('db_structure.xml'); - } - mysql_close($connection); - } } elseif($dbtype == 'pgsql') { $dbuser = $options['dbuser']; @@ -155,82 +83,20 @@ class OC_Setup { $dbname = $options['dbname']; $dbhost = $options['dbhost']; $dbtableprefix = isset($options['dbtableprefix']) ? $options['dbtableprefix'] : 'oc_'; - OC_CONFIG::setValue('dbname', $dbname); - 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='$e_host' dbname=postgres user='$e_user' password='$e_password'"; - $connection = @pg_connect($connection_string); - if(!$connection) { + + OC_Config::setValue('dbname', $dbname); + OC_Config::setValue('dbhost', $dbhost); + OC_Config::setValue('dbtableprefix', $dbtableprefix); + + try { + self::setupPostgreSQLDatabase($dbhost, $dbuser, $dbpass, $dbname, $dbtableprefix, $username); + } catch (Exception $e) { $error[] = array( 'error' => 'PostgreSQL username and/or password not valid', 'hint' => 'You need to enter either an existing account or the administrator.' ); 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='$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 - - //add prefix to the postgresql user name to prevent collisions - $dbusername='oc_'.$username; - //create a new password so we don't need to store the admin config in the config file - $dbpassword=md5(time()); - - self::pg_createDBUser($dbusername, $dbpassword, $connection); - - OC_CONFIG::setValue('dbuser', $dbusername); - OC_CONFIG::setValue('dbpassword', $dbpassword); - - //create the database - self::pg_createDatabase($dbname, $dbusername, $connection); - } - else { - OC_CONFIG::setValue('dbuser', $dbuser); - OC_CONFIG::setValue('dbpassword', $dbpass); - - //create the database - self::pg_createDatabase($dbname, $dbuser, $connection); - } - - // the connection to dbname=postgres is not needed anymore - pg_close($connection); - - // 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'); - - $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( - 'error' => 'PostgreSQL username and/or password not valid', - 'hint' => 'You need to enter either an existing account or the administrator.' - ); - } else { - $query = "select count(*) FROM pg_class WHERE relname='{$dbtableprefix}users' limit 1"; - $result = pg_query($connection, $query); - if($result) { - $row = pg_fetch_row($result); - } - if(!$result or $row[0]==0) { - OC_DB::createDbFromStructure('db_structure.xml'); - } - } - } } elseif($dbtype == 'oci') { $dbuser = $options['dbuser']; @@ -239,116 +105,20 @@ class OC_Setup { $dbtablespace = $options['dbtablespace']; $dbhost = isset($options['dbhost'])?$options['dbhost']:''; $dbtableprefix = isset($options['dbtableprefix']) ? $options['dbtableprefix'] : 'oc_'; - OC_CONFIG::setValue('dbname', $dbname); - OC_CONFIG::setValue('dbtablespace', $dbtablespace); - OC_CONFIG::setValue('dbhost', $dbhost); - OC_CONFIG::setValue('dbtableprefix', $dbtableprefix); - - $e_host = addslashes($dbhost); - $e_dbname = addslashes($dbname); - //check if the database user has admin right - if ($e_host == '') { - $easy_connect_string = $e_dbname; // use dbname as easy connect name - } else { - $easy_connect_string = '//'.$e_host.'/'.$e_dbname; - } - $connection = @oci_connect($dbuser, $dbpass, $easy_connect_string); - if(!$connection) { - $e = oci_error(); + + OC_Config::setValue('dbname', $dbname); + OC_Config::setValue('dbtablespace', $dbtablespace); + OC_Config::setValue('dbhost', $dbhost); + OC_Config::setValue('dbtableprefix', $dbtableprefix); + + try { + self::setupOCIDatabase($dbhost, $dbuser, $dbpass, $dbname, $dbtableprefix, $dbtablespace, $username); + } catch (Exception $e) { $error[] = array( 'error' => 'Oracle username and/or password not valid', 'hint' => 'You need to enter either an existing account or the administrator.' ); return $error; - } else { - //check for roles creation rights in oracle - - $query="SELECT count(*) FROM user_role_privs, role_sys_privs WHERE user_role_privs.granted_role = role_sys_privs.role AND privilege = 'CREATE ROLE'"; - $stmt = oci_parse($connection, $query); - if (!$stmt) { - $entry='DB Error: "'.oci_last_error($connection).'"<br />'; - $entry.='Offending command was: '.$query.'<br />'; - echo($entry); - } - $result = oci_execute($stmt); - if($result) { - $row = oci_fetch_row($stmt); - } - if($result and $row[0] > 0) { - //use the admin login data for the new database user - - //add prefix to the oracle user name to prevent collisions - $dbusername='oc_'.$username; - //create a new password so we don't need to store the admin config in the config file - $dbpassword=md5(time().$dbpass); - - //oracle passwords are treated as identifiers: - // must start with aphanumeric char - // needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length. - $dbpassword=substr($dbpassword, 0, 30); - - self::oci_createDBUser($dbusername, $dbpassword, $dbtablespace, $connection); - - OC_CONFIG::setValue('dbuser', $dbusername); - OC_CONFIG::setValue('dbname', $dbusername); - OC_CONFIG::setValue('dbpassword', $dbpassword); - - //create the database not neccessary, oracle implies user = schema - //self::oci_createDatabase($dbname, $dbusername, $connection); - } else { - - OC_CONFIG::setValue('dbuser', $dbuser); - OC_CONFIG::setValue('dbname', $dbname); - OC_CONFIG::setValue('dbpassword', $dbpass); - - //create the database not neccessary, oracle implies user = schema - //self::oci_createDatabase($dbname, $dbuser, $connection); - } - - //FIXME check tablespace exists: select * from user_tablespaces - - // the connection to dbname=oracle is not needed anymore - oci_close($connection); - - // connect to the oracle database (schema=$dbuser) an check if the schema needs to be filled - $dbuser = OC_CONFIG::getValue('dbuser'); - //$dbname = OC_CONFIG::getValue('dbname'); - $dbpass = OC_CONFIG::getValue('dbpassword'); - - $e_host = addslashes($dbhost); - $e_dbname = addslashes($dbname); - - if ($e_host == '') { - $easy_connect_string = $e_dbname; // use dbname as easy connect name - } else { - $easy_connect_string = '//'.$e_host.'/'.$e_dbname; - } - $connection = @oci_connect($dbuser, $dbpass, $easy_connect_string); - if(!$connection) { - $error[] = array( - 'error' => 'Oracle username and/or password not valid', - 'hint' => 'You need to enter either an existing account or the administrator.' - ); - return $error; - } else { - $query = "SELECT count(*) FROM user_tables WHERE table_name = :un"; - $stmt = oci_parse($connection, $query); - $un = $dbtableprefix.'users'; - oci_bind_by_name($stmt, ':un', $un); - if (!$stmt) { - $entry='DB Error: "'.oci_last_error($connection).'"<br />'; - $entry.='Offending command was: '.$query.'<br />'; - echo($entry); - } - $result = oci_execute($stmt); - - if($result) { - $row = oci_fetch_row($stmt); - } - if(!$result or $row[0]==0) { - OC_DB::createDbFromStructure('db_structure.xml'); - } - } } } else { @@ -369,8 +139,8 @@ class OC_Setup { } if(count($error) == 0) { - OC_Appconfig::setValue('core', 'installedat',microtime(true)); - OC_Appconfig::setValue('core', 'lastupdatedat',microtime(true)); + OC_Appconfig::setValue('core', 'installedat', microtime(true)); + OC_Appconfig::setValue('core', 'lastupdatedat', microtime(true)); OC_Group::createGroup('admin'); OC_Group::addToGroup($username, 'admin'); @@ -383,7 +153,7 @@ class OC_Setup { if (isset($_SERVER['SERVER_SOFTWARE']) && strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) { self::createHtaccess(); } - + //and we are done OC_Config::setValue('installed', true); } @@ -392,7 +162,56 @@ class OC_Setup { return $error; } - public static function createDatabase($name,$user,$connection) { + private static function setupMySQLDatabase($dbhost, $dbuser, $dbpass, $dbname, $dbtableprefix, $username) { + //check if the database user has admin right + $connection = @mysql_connect($dbhost, $dbuser, $dbpass); + if(!$connection) { + throw new Exception('MySQL username and/or password not valid'); + } + $oldUser=OC_Config::getValue('dbuser', 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)) { + //use the admin login data for the new database user + + //add prefix to the mysql user name to prevent collisions + $dbusername=substr('oc_'.$username, 0, 16); + if($dbusername!=$oldUser) { + //hash the password so we don't need to store the admin config in the config file + $dbpassword=md5(time().$dbpass); + + self::createDBUser($dbusername, $dbpassword, $connection); + + OC_Config::setValue('dbuser', $dbusername); + OC_Config::setValue('dbpassword', $dbpassword); + } + + //create the database + self::createMySQLDatabase($dbname, $dbusername, $connection); + } + else { + if($dbuser!=$oldUser) { + OC_Config::setValue('dbuser', $dbuser); + OC_Config::setValue('dbpassword', $dbpass); + } + + //create the database + self::createMySQLDatabase($dbname, $dbuser, $connection); + } + + //fill the database if needed + $query="select count(*) from information_schema.tables where table_schema='$dbname' AND table_name = '{$dbtableprefix}users';"; + $result = mysql_query($query, $connection); + if($result) { + $row=mysql_fetch_row($result); + } + if(!$result or $row[0]==0) { + OC_DB::createDbFromStructure('db_structure.xml'); + } + mysql_close($connection); + } + + private static function createMySQLDatabase($name, $user, $connection) { //we cant use OC_BD functions here because we need to connect as the administrative user. $query = "CREATE DATABASE IF NOT EXISTS `$name`"; $result = mysql_query($query, $connection); @@ -405,7 +224,7 @@ class OC_Setup { $result = mysql_query($query, $connection); //this query will fail if there aren't the right permissons, ignore the error } - private static function createDBUser($name,$password,$connection) { + private static function createDBUser($name, $password, $connection) { // we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one, // the anonymous user would take precedence when there is one. $query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'"; @@ -414,7 +233,73 @@ class OC_Setup { $result = mysql_query($query, $connection); } - public static function pg_createDatabase($name,$user,$connection) { + private static function setupPostgreSQLDatabase($dbhost, $dbuser, $dbpass, $dbname, $dbtableprefix, $username) { + $e_host = addslashes($dbhost); + $e_user = addslashes($dbuser); + $e_password = addslashes($dbpass); + + //check if the database user has admin rights + $connection_string = "host='$e_host' dbname=postgres user='$e_user' password='$e_password'"; + $connection = @pg_connect($connection_string); + if(!$connection) { + throw new Exception('PostgreSQL username and/or password not valid'); + } + $e_user = pg_escape_string($dbuser); + //check for roles creation rights in postgresql + $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 + + //add prefix to the postgresql user name to prevent collisions + $dbusername='oc_'.$username; + //create a new password so we don't need to store the admin config in the config file + $dbpassword=md5(time()); + + self::pg_createDBUser($dbusername, $dbpassword, $connection); + + OC_Config::setValue('dbuser', $dbusername); + OC_Config::setValue('dbpassword', $dbpassword); + + //create the database + self::pg_createDatabase($dbname, $dbusername, $connection); + } + else { + OC_Config::setValue('dbuser', $dbuser); + OC_Config::setValue('dbpassword', $dbpass); + + //create the database + self::pg_createDatabase($dbname, $dbuser, $connection); + } + + // the connection to dbname=postgres is not needed anymore + pg_close($connection); + + // connect to the ownCloud database (dbname=$dbname) and check if it needs to be filled + $dbuser = OC_Config::getValue('dbuser'); + $dbpass = OC_Config::getValue('dbpassword'); + + $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) { + throw new Exception('PostgreSQL username and/or password not valid'); + } + $query = "select count(*) FROM pg_class WHERE relname='{$dbtableprefix}users' limit 1"; + $result = pg_query($connection, $query); + if($result) { + $row = pg_fetch_row($result); + } + if(!$result or $row[0]==0) { + OC_DB::createDbFromStructure('db_structure.xml'); + } + } + + private static function pg_createDatabase($name, $user, $connection) { //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); @@ -439,7 +324,7 @@ class OC_Setup { $result = pg_query($connection, $query); } - private static function pg_createDBUser($name,$password,$connection) { + private static function pg_createDBUser($name, $password, $connection) { $e_name = pg_escape_string($name); $e_password = pg_escape_string($password); $query = "select * from pg_roles where rolname='$e_name';"; @@ -470,6 +355,106 @@ class OC_Setup { } } } + + private static function setupOCIDatabase($dbhost, $dbuser, $dbpass, $dbname, $dbtableprefix, $dbtablespace, $username) { + $e_host = addslashes($dbhost); + $e_dbname = addslashes($dbname); + //check if the database user has admin right + if ($e_host == '') { + $easy_connect_string = $e_dbname; // use dbname as easy connect name + } else { + $easy_connect_string = '//'.$e_host.'/'.$e_dbname; + } + $connection = @oci_connect($dbuser, $dbpass, $easy_connect_string); + if(!$connection) { + $e = oci_error(); + throw new Exception('Oracle username and/or password not valid'); + } + //check for roles creation rights in oracle + + $query="SELECT count(*) FROM user_role_privs, role_sys_privs WHERE user_role_privs.granted_role = role_sys_privs.role AND privilege = 'CREATE ROLE'"; + $stmt = oci_parse($connection, $query); + if (!$stmt) { + $entry='DB Error: "'.oci_last_error($connection).'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + $result = oci_execute($stmt); + if($result) { + $row = oci_fetch_row($stmt); + } + if($result and $row[0] > 0) { + //use the admin login data for the new database user + + //add prefix to the oracle user name to prevent collisions + $dbusername='oc_'.$username; + //create a new password so we don't need to store the admin config in the config file + $dbpassword=md5(time().$dbpass); + + //oracle passwords are treated as identifiers: + // must start with aphanumeric char + // needs to be shortened to 30 bytes, as the two " needed to escape the identifier count towards the identifier length. + $dbpassword=substr($dbpassword, 0, 30); + + self::oci_createDBUser($dbusername, $dbpassword, $dbtablespace, $connection); + + OC_Config::setValue('dbuser', $dbusername); + OC_Config::setValue('dbname', $dbusername); + OC_Config::setValue('dbpassword', $dbpassword); + + //create the database not neccessary, oracle implies user = schema + //self::oci_createDatabase($dbname, $dbusername, $connection); + } else { + + OC_Config::setValue('dbuser', $dbuser); + OC_Config::setValue('dbname', $dbname); + OC_Config::setValue('dbpassword', $dbpass); + + //create the database not neccessary, oracle implies user = schema + //self::oci_createDatabase($dbname, $dbuser, $connection); + } + + //FIXME check tablespace exists: select * from user_tablespaces + + // the connection to dbname=oracle is not needed anymore + oci_close($connection); + + // connect to the oracle database (schema=$dbuser) an check if the schema needs to be filled + $dbuser = OC_Config::getValue('dbuser'); + //$dbname = OC_Config::getValue('dbname'); + $dbpass = OC_Config::getValue('dbpassword'); + + $e_host = addslashes($dbhost); + $e_dbname = addslashes($dbname); + + if ($e_host == '') { + $easy_connect_string = $e_dbname; // use dbname as easy connect name + } else { + $easy_connect_string = '//'.$e_host.'/'.$e_dbname; + } + $connection = @oci_connect($dbuser, $dbpass, $easy_connect_string); + if(!$connection) { + throw new Exception('Oracle username and/or password not valid'); + } + $query = "SELECT count(*) FROM user_tables WHERE table_name = :un"; + $stmt = oci_parse($connection, $query); + $un = $dbtableprefix.'users'; + oci_bind_by_name($stmt, ':un', $un); + if (!$stmt) { + $entry='DB Error: "'.oci_last_error($connection).'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + $result = oci_execute($stmt); + + if($result) { + $row = oci_fetch_row($stmt); + } + if(!$result or $row[0]==0) { + OC_DB::createDbFromStructure('db_structure.xml'); + } + } + /** * * @param String $name @@ -548,7 +533,15 @@ class OC_Setup { * create .htaccess files for apache hosts */ private static function createHtaccess() { - $content = "ErrorDocument 403 ".OC::$WEBROOT."/core/templates/403.php\n";//custom 403 error page + $content = "<IfModule mod_fcgid.c>\n"; + $content.= "<IfModule mod_setenvif.c>\n"; + $content.= "<IfModule mod_headers.c>\n"; + $content.= "SetEnvIfNoCase ^Authorization$ \"(.+)\" XAUTHORIZATION=$1\n"; + $content.= "RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION\n"; + $content.= "</IfModule>\n"; + $content.= "</IfModule>\n"; + $content.= "</IfModule>\n"; + $content.= "ErrorDocument 403 ".OC::$WEBROOT."/core/templates/403.php\n";//custom 403 error page $content.= "ErrorDocument 404 ".OC::$WEBROOT."/core/templates/404.php\n";//custom 404 error page $content.= "<IfModule mod_php5.c>\n"; $content.= "php_value upload_max_filesize 512M\n";//upload limit @@ -567,9 +560,17 @@ class OC_Setup { $content.= "RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L]\n"; $content.= "RewriteRule ^remote/(.*) remote.php [QSA,L]\n"; $content.= "</IfModule>\n"; + $content.= "<IfModule mod_mime.c>\n"; + $content.= "AddType image/svg+xml svg svgz\n"; + $content.= "AddEncoding gzip svgz\n"; + $content.= "</IfModule>\n"; $content.= "Options -Indexes\n"; @file_put_contents(OC::$SERVERROOT.'/.htaccess', $content); //supress errors in case we don't have permissions for it + self::protectDataDirectory(); + } + + public static function protectDataDirectory() { $content = "deny from all\n"; $content.= "IndexIgnore *"; file_put_contents(OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data').'/.htaccess', $content); diff --git a/lib/streamwrappers.php b/lib/streamwrappers.php index 1e5b19a11f0..981c280f0dd 100644 --- a/lib/streamwrappers.php +++ b/lib/streamwrappers.php @@ -5,8 +5,8 @@ class OC_FakeDirStream{ private $name; private $index; - public function dir_opendir($path,$options) { - $this->name=substr($path,strlen('fakedir://')); + public function dir_opendir($path, $options) { + $this->name=substr($path, strlen('fakedir://')); $this->index=0; if(!isset(self::$dirs[$this->name])) { self::$dirs[$this->name]=array(); @@ -223,9 +223,9 @@ class OC_CloseStreamWrapper{ private $source; private static $open=array(); public function stream_open($path, $mode, $options, &$opened_path) { - $path=substr($path,strlen('close://')); + $path=substr($path, strlen('close://')); $this->path=$path; - $this->source=fopen($path,$mode); + $this->source=fopen($path, $mode); if(is_resource($this->source)) { $this->meta=stream_get_meta_data($this->source); } @@ -234,7 +234,7 @@ class OC_CloseStreamWrapper{ } public function stream_seek($offset, $whence=SEEK_SET) { - fseek($this->source,$offset,$whence); + fseek($this->source, $offset, $whence); } public function stream_tell() { @@ -242,23 +242,23 @@ class OC_CloseStreamWrapper{ } public function stream_read($count) { - return fread($this->source,$count); + return fread($this->source, $count); } public function stream_write($data) { - return fwrite($this->source,$data); + return fwrite($this->source, $data); } - public function stream_set_option($option,$arg1,$arg2) { + public function stream_set_option($option, $arg1, $arg2) { switch($option) { case STREAM_OPTION_BLOCKING: - stream_set_blocking($this->source,$arg1); + stream_set_blocking($this->source, $arg1); break; case STREAM_OPTION_READ_TIMEOUT: - stream_set_timeout($this->source,$arg1,$arg2); + stream_set_timeout($this->source, $arg1, $arg2); break; case STREAM_OPTION_WRITE_BUFFER: - stream_set_write_buffer($this->source,$arg1,$arg2); + stream_set_write_buffer($this->source, $arg1, $arg2); } } @@ -267,7 +267,7 @@ class OC_CloseStreamWrapper{ } public function stream_lock($mode) { - flock($this->source,$mode); + flock($this->source, $mode); } public function stream_flush() { @@ -279,7 +279,7 @@ class OC_CloseStreamWrapper{ } public function url_stat($path) { - $path=substr($path,strlen('close://')); + $path=substr($path, strlen('close://')); if(file_exists($path)) { return stat($path); }else{ @@ -290,12 +290,12 @@ class OC_CloseStreamWrapper{ public function stream_close() { fclose($this->source); if(isset(self::$callBacks[$this->path])) { - call_user_func(self::$callBacks[$this->path],$this->path); + call_user_func(self::$callBacks[$this->path], $this->path); } } public function unlink($path) { - $path=substr($path,strlen('close://')); + $path=substr($path, strlen('close://')); return unlink($path); } } diff --git a/lib/template.php b/lib/template.php index 1c529932a30..04667d73a2c 100644 --- a/lib/template.php +++ b/lib/template.php @@ -22,6 +22,22 @@ */ /** + * Prints an XSS escaped string + * @param string $string the string which will be escaped and printed + */ +function p($string) { + print(OC_Util::sanitizeHTML($string)); +} + +/** + * Prints an unescaped string + * @param string $string the string which will be printed as it is + */ +function print_unescaped($string) { + print($string); +} + +/** * @brief make OC_Helper::linkTo available as a simple function * @param string $app app * @param string $file file @@ -69,7 +85,7 @@ function human_file_size( $bytes ) { } function simple_file_size($bytes) { - $mbytes = round($bytes/(1024*1024),1); + $mbytes = round($bytes/(1024*1024), 1); if($bytes == 0) { return '0'; } else if($mbytes < 0.1) { return '< 0.1'; } else if($mbytes > 1000) { return '> 1000'; } @@ -86,14 +102,14 @@ function relative_modified_date($timestamp) { if($timediff < 60) { return $l->t('seconds ago'); } else if($timediff < 120) { return $l->t('1 minute ago'); } - else if($timediff < 3600) { return $l->t('%d minutes ago',$diffminutes); } - //else if($timediff < 7200) { return '1 hour ago'; } - //else if($timediff < 86400) { return $diffhours.' hours ago'; } + else if($timediff < 3600) { return $l->t('%d minutes ago', $diffminutes); } + else if($timediff < 7200) { return $l->t('1 hour ago'); } + else if($timediff < 86400) { return $l->t('%d hours ago', $diffhours); } else if((date('G')-$diffhours) > 0) { return $l->t('today'); } else if((date('G')-$diffhours) > -24) { return $l->t('yesterday'); } - else if($timediff < 2678400) { return $l->t('%d days ago',$diffdays); } + else if($timediff < 2678400) { return $l->t('%d days ago', $diffdays); } else if($timediff < 5184000) { return $l->t('last month'); } - else if((date('n')-$diffmonths) > 0) { return $l->t('months ago'); } + else if((date('n')-$diffmonths) > 0) { return $l->t('%d months ago', $diffmonths); } else if($timediff < 63113852) { return $l->t('last year'); } else { return $l->t('years ago'); } } @@ -156,7 +172,6 @@ class OC_Template{ $this->application = $app; $this->vars = array(); $this->vars['requesttoken'] = OC_Util::callRegister(); - $this->vars['requestlifespan'] = OC_Util::$callLifespan; $parts = explode('/', $app); // fix translation when app is something like core/lostpassword $this->l10n = OC_L10N::get($parts[0]); @@ -180,11 +195,11 @@ class OC_Template{ 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) { + if(stripos($_SERVER['HTTP_USER_AGENT'], 'ipad')>0) { $mode='tablet'; - }elseif(stripos($_SERVER['HTTP_USER_AGENT'],'iphone')>0) { + }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)) { + }elseif((stripos($_SERVER['HTTP_USER_AGENT'], 'N9')>0) and (stripos($_SERVER['HTTP_USER_AGENT'], 'nokia')>0)) { $mode='mobile'; }else{ $mode='default'; @@ -341,7 +356,7 @@ class OC_Template{ * @param string $text the text content for the element */ public function addHeader( $tag, $attributes, $text='') { - $this->headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text); + $this->headers[]=array('tag'=>$tag,'attributes'=>$attributes, 'text'=>$text); } /** @@ -375,13 +390,12 @@ class OC_Template{ $page = new OC_TemplateLayout($this->renderas); if($this->renderas == 'user') { $page->assign('requesttoken', $this->vars['requesttoken']); - $page->assign('requestlifespan', $this->vars['requestlifespan']); } // Add custom headers - $page->assign('headers',$this->headers, false); + $page->assign('headers', $this->headers, false); foreach(OC_Util::$headers as $header) { - $page->append('headers',$header); + $page->append('headers', $header); } $page->assign( "content", $data, false ); @@ -405,7 +419,7 @@ class OC_Template{ // Execute the template ob_start(); - include( $this->template ); // <-- we have to use include because we pass $_! + include $this->template; // <-- we have to use include because we pass $_! $data = ob_get_contents(); @ob_end_clean(); @@ -430,7 +444,7 @@ class OC_Template{ // Include ob_start(); - include( $this->path.$file.'.php' ); + include $this->path.$file.'.php'; $data = ob_get_contents(); @ob_end_clean(); @@ -482,4 +496,15 @@ class OC_Template{ } return $content->printPage(); } + + /** + * @brief Print a fatal error page and terminates the script + * @param string $error The error message to show + * @param string $hint An option hint message + */ + public static function printErrorPage( $error_msg, $hint = '' ) { + $errors = array(array('error' => $error_msg, 'hint' => $hint)); + OC_Template::printGuestPage("", "error", array("errors" => $errors)); + die(); + } } diff --git a/lib/templatelayout.php b/lib/templatelayout.php index 4f26775b48e..1a0570a270d 100644 --- a/lib/templatelayout.php +++ b/lib/templatelayout.php @@ -12,10 +12,10 @@ class OC_TemplateLayout extends OC_Template { if( $renderas == 'user' ) { parent::__construct( 'core', 'layout.user' ); - if(in_array(OC_APP::getCurrentApp(),array('settings','admin','help'))!==false) { - $this->assign('bodyid','body-settings', false); + if(in_array(OC_APP::getCurrentApp(), array('settings','admin', 'help'))!==false) { + $this->assign('bodyid', 'body-settings', false); }else{ - $this->assign('bodyid','body-user', false); + $this->assign('bodyid', 'body-user', false); } // Add navigation entry @@ -38,7 +38,7 @@ class OC_TemplateLayout extends OC_Template { 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 + $this->assign( 'apps_paths', str_replace('\\/', '/', json_encode($apps_paths)), false ); // Ugly unescape slashes waiting for better solution if (OC_Config::getValue('installed', false) && !OC_AppConfig::getValue('core', 'remote_core.css', false)) { OC_AppConfig::setValue('core', 'remote_core.css', '/core/minimizer.php'); @@ -49,7 +49,7 @@ class OC_TemplateLayout extends OC_Template { $jsfiles = self::findJavascriptFiles(OC_Util::$scripts); $this->assign('jsfiles', array(), false); if (!empty(OC_Util::$core_scripts)) { - $this->append( 'jsfiles', OC_Helper::linkToRemote('core.js', false)); + $this->append( 'jsfiles', OC_Helper::linkToRemoteBase('core.js', false)); } foreach($jsfiles as $info) { $root = $info[0]; @@ -62,7 +62,7 @@ class OC_TemplateLayout extends OC_Template { $cssfiles = self::findStylesheetFiles(OC_Util::$styles); $this->assign('cssfiles', array()); if (!empty(OC_Util::$core_styles)) { - $this->append( 'cssfiles', OC_Helper::linkToRemote('core.css', false)); + $this->append( 'cssfiles', OC_Helper::linkToRemoteBase('core.css', false)); } foreach($cssfiles as $info) { $root = $info[0]; diff --git a/lib/updater.php b/lib/updater.php index b3b289ef276..11081eded63 100644 --- a/lib/updater.php +++ b/lib/updater.php @@ -29,8 +29,8 @@ class OC_Updater{ * Check if a new version is available */ public static function check() { - OC_Appconfig::setValue('core', 'lastupdatedat',microtime(true)); - if(OC_Appconfig::getValue('core', 'installedat','')=='') OC_Appconfig::setValue('core', 'installedat',microtime(true)); + OC_Appconfig::setValue('core', 'lastupdatedat', microtime(true)); + if(OC_Appconfig::getValue('core', 'installedat', '')=='') OC_Appconfig::setValue('core', 'installedat', microtime(true)); $updaterurl='http://apps.owncloud.com/updater.php'; $version=OC_Util::getVersion(); @@ -38,21 +38,21 @@ class OC_Updater{ $version['updated']=OC_Appconfig::getValue('core', 'lastupdatedat'); $version['updatechannel']='stable'; $version['edition']=OC_Util::getEditionString(); - $versionstring=implode('x',$version); + $versionstring=implode('x', $version); //fetch xml data from updater $url=$updaterurl.'?version='.$versionstring; // set a sensible timeout of 10 sec to stay responsive even if the update server is down. $ctx = stream_context_create( - array( - 'http' => array( - 'timeout' => 10 - ) - ) - ); + array( + 'http' => array( + 'timeout' => 10 + ) + ) + ); $xml=@file_get_contents($url, 0, $ctx); - if($xml==FALSE) { + if($xml==false) { return array(); } $data=@simplexml_load_string($xml); @@ -72,7 +72,7 @@ class OC_Updater{ if(OC_Config::getValue('updatechecker', true)==true) { $data=OC_Updater::check(); if(isset($data['version']) and $data['version']<>'') { - $txt='<span style="color:#AA0000; font-weight:bold;">'.$l->t('%s is available. Get <a href="%s">more information</a>',array($data['versionstring'], $data['web'])).'</span>'; + $txt='<span style="color:#AA0000; font-weight:bold;">'.$l->t('%s is available. Get <a href="%s">more information</a>', array($data['versionstring'], $data['web'])).'</span>'; }else{ $txt=$l->t('up to date'); } diff --git a/lib/user.php b/lib/user.php index 7de2a4b7fe6..31c93740d77 100644 --- a/lib/user.php +++ b/lib/user.php @@ -120,11 +120,11 @@ class OC_User { * setup the configured backends in config.php */ public static function setupBackends() { - $backends=OC_Config::getValue('user_backends',array()); + $backends=OC_Config::getValue('user_backends', array()); foreach($backends as $i=>$config) { $class=$config['class']; $arguments=$config['arguments']; - if(class_exists($class) and array_search($i,self::$_setupedBackends)===false) { + if(class_exists($class) and array_search($i, self::$_setupedBackends)===false) { // make a reflection object $reflectionObj = new ReflectionClass($class); @@ -133,7 +133,7 @@ class OC_User { self::useBackend($backend); $_setupedBackends[]=$i; }else{ - OC_Log::write('core','User backend '.$class.' not found.',OC_Log::ERROR); + OC_Log::write('core', 'User backend '.$class.' not found.', OC_Log::ERROR); } } } @@ -179,10 +179,10 @@ class OC_User { if(!$backend->implementsActions(OC_USER_BACKEND_CREATE_USER)) continue; - $backend->createUser($uid,$password); + $backend->createUser($uid, $password); OC_Hook::emit( "OC_User", "post_createUser", array( "uid" => $uid, "password" => $password )); - return true; + return self::userExists($uid); } } return false; @@ -204,12 +204,19 @@ class OC_User { foreach(self::$_usedBackends as $backend) { $backend->deleteUser($uid); } + if (self::userExists($uid)) { + return false; + } // We have to delete the user from all groups foreach( OC_Group::getUserGroups( $uid ) as $i ) { OC_Group::removeFromGroup( $uid, $i ); } // Delete the user's keys in preferences OC_Preferences::deleteUser($uid); + + // Delete user files in /data/ + OC_Helper::rmdirr(OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ) . '/'.$uid.'/'); + // Emit and exit OC_Hook::emit( "OC_User", "post_deleteUser", array( "uid" => $uid )); return true; @@ -325,10 +332,12 @@ class OC_User { foreach(self::$_usedBackends as $backend) { if($backend->implementsActions(OC_USER_BACKEND_SET_PASSWORD)) { if($backend->userExists($uid)) { - $success |= $backend->setPassword($uid,$password); + $success |= $backend->setPassword($uid, $password); } } } + // invalidate all login cookies + OC_Preferences::deleteApp($uid, 'login_token'); OC_Hook::emit( "OC_User", "post_setPassword", array( "uid" => $uid, "password" => $password )); return $success; } @@ -363,8 +372,7 @@ class OC_User { * @param $password The password * @returns string * - * Check if the password is correct without logging in the user - * returns the user id or false + * returns the path to the users home directory */ public static function getHome($uid) { foreach(self::$_usedBackends as $backend) { @@ -472,9 +480,10 @@ class OC_User { */ public static function setMagicInCookie($username, $token) { $secure_cookie = OC_Config::getValue("forcessl", false); - setcookie("oc_username", $username, time()+60*60*24*15, '', '', $secure_cookie); - setcookie("oc_token", $token, time()+60*60*24*15, '', '', $secure_cookie); - setcookie("oc_remember_login", true, time()+60*60*24*15, '', '', $secure_cookie); + $expires = time() + OC_Config::getValue('remember_login_cookie_lifetime', 60*60*24*15); + setcookie("oc_username", $username, $expires, '', '', $secure_cookie); + setcookie("oc_token", $token, $expires, '', '', $secure_cookie, true); + setcookie("oc_remember_login", true, $expires, '', '', $secure_cookie); } /** @@ -484,8 +493,8 @@ class OC_User { unset($_COOKIE["oc_username"]); unset($_COOKIE["oc_token"]); unset($_COOKIE["oc_remember_login"]); - setcookie("oc_username", NULL, -1); - setcookie("oc_token", NULL, -1); - setcookie("oc_remember_login", NULL, -1); + setcookie("oc_username", null, -1); + setcookie("oc_token", null, -1); + setcookie("oc_remember_login", null, -1); } } diff --git a/lib/user/database.php b/lib/user/database.php index 25e24fcf7e4..f33e338e2e4 100644 --- a/lib/user/database.php +++ b/lib/user/database.php @@ -48,7 +48,7 @@ class OC_User_Database extends OC_User_Backend { if(!self::$hasher) { //we don't want to use DES based crypt(), since it doesn't return a has with a recognisable prefix $forcePortable=(CRYPT_BLOWFISH!=1); - self::$hasher=new PasswordHash(8,$forcePortable); + self::$hasher=new PasswordHash(8, $forcePortable); } return self::$hasher; @@ -137,7 +137,7 @@ class OC_User_Database extends OC_User_Backend { }else{//old sha1 based hashing if(sha1($password)==$storedHash) { //upgrade to new hashing - $this->setPassword($row['uid'],$password); + $this->setPassword($row['uid'], $password); return $row['uid']; }else{ return false; @@ -155,7 +155,7 @@ class OC_User_Database extends OC_User_Backend { * Get a list of all users. */ public function getUsers($search = '', $limit = null, $offset = null) { - $query = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users` WHERE LOWER(`uid`) LIKE LOWER(?)',$limit,$offset); + $query = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users` WHERE LOWER(`uid`) LIKE LOWER(?)', $limit, $offset); $result = $query->execute(array($search.'%')); $users = array(); while ($row = $result->fetchRow()) { @@ -172,7 +172,10 @@ class OC_User_Database extends OC_User_Backend { public function userExists($uid) { $query = OC_DB::prepare( 'SELECT * FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)' ); $result = $query->execute( array( $uid )); - + if (OC_DB::isError($result)) { + OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } return $result->numRows() > 0; } diff --git a/lib/user/http.php b/lib/user/http.php index 2668341408d..944ede73a0b 100644 --- a/lib/user/http.php +++ b/lib/user/http.php @@ -40,7 +40,7 @@ class OC_User_HTTP extends OC_User_Backend { if(isset($parts['query'])) { $url.='?'.$parts['query']; } - return array($parts['user'],$url); + return array($parts['user'], $url); } @@ -50,7 +50,7 @@ class OC_User_HTTP extends OC_User_Backend { * @return boolean */ private function matchUrl($url) { - return ! is_null(parse_url($url,PHP_URL_USER)); + return ! is_null(parse_url($url, PHP_URL_USER)); } /** @@ -66,7 +66,7 @@ class OC_User_HTTP extends OC_User_Backend { if(!$this->matchUrl($uid)) { return false; } - list($user,$url)=$this->parseUrl($uid); + list($user, $url)=$this->parseUrl($uid); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); diff --git a/lib/util.php b/lib/util.php index afbea9a00cb..2ee3f0e4efb 100755 --- a/lib/util.php +++ b/lib/util.php @@ -24,6 +24,11 @@ class OC_Util { $user = OC_User::getUser(); } + // load all filesystem apps before, so no setup-hook gets lost + if(!isset($RUNTIME_NOAPPS) || !$RUNTIME_NOAPPS) { + OC_App::loadApps(array('filesystem')); + } + // the filesystem will finish when $user is not empty, // mark fs setup here to avoid doing the setup from loading // OC_Filesystem @@ -34,7 +39,7 @@ class OC_Util { $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),'/'); + OC_Filesystem::mount('OC_Filestorage_Local', array('datadir'=>$CONFIG_DATADIRECTORY), '/'); self::$rootMounted=true; } @@ -47,27 +52,13 @@ class OC_Util { } //jail the user into his "home" directory OC_Filesystem::mount('OC_Filestorage_Local', array('datadir' => $user_root), $user); - OC_Filesystem::init($user_dir); + OC_Filesystem::init($user_dir, $user); $quotaProxy=new OC_FileProxy_Quota(); $fileOperationProxy = new OC_FileProxy_FileOperations(); OC_FileProxy::register($quotaProxy); OC_FileProxy::register($fileOperationProxy); // Load personal mount config - if (is_file($user_root.'/mount.php')) { - $mountConfig = include($user_root.'/mount.php'); - if (isset($mountConfig['user'][$user])) { - foreach ($mountConfig['user'][$user] as $mountPoint => $options) { - OC_Filesystem::mount($options['class'], $options['options'], $mountPoint); - } - } - - $mtime=filemtime($user_root.'/mount.php'); - $previousMTime=OC_Preferences::getValue($user,'files','mountconfigmtime',0); - if($mtime>$previousMTime) {//mount config has changed, filecache needs to be updated - OC_FileCache::triggerUpdate($user); - OC_Preferences::setValue($user,'files','mountconfigmtime',$mtime); - } - } + self::loadUserMountPoints($user); OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $user_dir)); } } @@ -77,13 +68,34 @@ class OC_Util { self::$fsSetup=false; } + public static function loadUserMountPoints($user) { + $user_dir = '/'.$user.'/files'; + $user_root = OC_User::getHome($user); + $userdirectory = $user_root . '/files'; + if (is_file($user_root.'/mount.php')) { + $mountConfig = include $user_root.'/mount.php'; + if (isset($mountConfig['user'][$user])) { + foreach ($mountConfig['user'][$user] as $mountPoint => $options) { + OC_Filesystem::mount($options['class'], $options['options'], $mountPoint); + } + } + + $mtime=filemtime($user_root.'/mount.php'); + $previousMTime=OC_Preferences::getValue($user, 'files', 'mountconfigmtime', 0); + if($mtime>$previousMTime) {//mount config has changed, filecache needs to be updated + OC_FileCache::triggerUpdate($user); + OC_Preferences::setValue($user, 'files', 'mountconfigmtime', $mtime); + } + } + } + /** * get the current installed version of ownCloud * @return array */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,00); + return array(4, 91, 02); } /** @@ -145,7 +157,7 @@ class OC_Util { * @param string $text the text content for the element */ public static function addHeader( $tag, $attributes, $text='') { - self::$headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text); + self::$headers[]=array('tag'=>$tag,'attributes'=>$attributes, 'text'=>$text); } /** @@ -154,16 +166,16 @@ class OC_Util { * @param int timestamp $timestamp * @param bool dateOnly option to ommit time from the result */ - public static function formatDate( $timestamp,$dateOnly=false) { + public static function formatDate( $timestamp, $dateOnly=false) { if(isset($_SESSION['timezone'])) {//adjust to clients timezone if we know it $systemTimeZone = intval(date('O')); - $systemTimeZone=(round($systemTimeZone/100,0)*60)+($systemTimeZone%100); + $systemTimeZone=(round($systemTimeZone/100, 0)*60)+($systemTimeZone%100); $clientTimeZone=$_SESSION['timezone']*60; $offset=$clientTimeZone-$systemTimeZone; $timestamp=$timestamp+$offset*60; } - $timeformat=$dateOnly?'F j, Y':'F j, Y, H:i'; - return date($timeformat,$timestamp); + $l=OC_L10N::get('lib'); + return $l->l($dateOnly ? 'date' : 'datetime', $timestamp); } /** @@ -174,7 +186,7 @@ class OC_Util { * @param string $url * @return OC_Template */ - public static function getPageNavi($pagecount,$page,$url) { + public static function getPageNavi($pagecount, $page, $url) { $pagelinkcount=8; if ($pagecount>1) { @@ -184,11 +196,11 @@ class OC_Util { if($pagestop>$pagecount) $pagestop=$pagecount; $tmpl = new OC_Template( '', 'part.pagenavi', '' ); - $tmpl->assign('page',$page); - $tmpl->assign('pagecount',$pagecount); - $tmpl->assign('pagestart',$pagestart); - $tmpl->assign('pagestop',$pagestop); - $tmpl->assign('url',$url); + $tmpl->assign('page', $page); + $tmpl->assign('pagecount', $pagecount); + $tmpl->assign('pagestart', $pagestart); + $tmpl->assign('pagestop', $pagestop); + $tmpl->assign('url', $url); return $tmpl; } } @@ -205,7 +217,7 @@ class OC_Util { $web_server_restart= false; //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 + $errors[]=array('error'=>'No database drivers (sqlite, mysql, or postgresql) installed.<br/>', 'hint'=>'');//TODO: sane hint $web_server_restart= true; } @@ -214,13 +226,13 @@ class OC_Util { // Check if config folder is writable. if(!is_writable(OC::$SERVERROOT."/config/") or !is_readable(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"); + $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()) || !is_readable(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 + $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."); } } @@ -229,24 +241,24 @@ class OC_Util { //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)),-3); - if(substr($prems,-1)!='0') { - OC_Helper::chmodr($CONFIG_DATADIRECTORY,0770); + $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY)), -3); + if(substr($prems, -1)!='0') { + OC_Helper::chmodr($CONFIG_DATADIRECTORY, 0770); clearstatcache(); - $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY)),-3); - if(substr($prems,2,1)!='0') { - $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY.') is readable for other users<br/>','hint'=>$permissionsModHint); + $prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY)), -3); + if(substr($prems, 2, 1)!='0') { + $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); + $prems=substr(decoct(@fileperms($CONFIG_BACKUPDIRECTORY)), -3); + if(substr($prems, -1)!='0') { + OC_Helper::chmodr($CONFIG_BACKUPDIRECTORY, 0770); clearstatcache(); - $prems=substr(decoct(@fileperms($CONFIG_BACKUPDIRECTORY)),-3); - if(substr($prems,2,1)!='0') { - $errors[]=array('error'=>'Data directory ('.$CONFIG_BACKUPDIRECTORY.') is readable for other users<br/>','hint'=>$permissionsModHint); + $prems=substr(decoct(@fileperms($CONFIG_BACKUPDIRECTORY)), -3); + if(substr($prems, 2, 1)!='0') { + $errors[]=array('error'=>'Data directory ('.$CONFIG_BACKUPDIRECTORY.') is readable for other users<br/>', 'hint'=>$permissionsModHint); } } } @@ -257,62 +269,67 @@ class OC_Util { 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' "); + $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) or !is_readable($CONFIG_DATADIRECTORY)) { - $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY.') not writable by ownCloud<br/>','hint'=>$permissionsHint); + $errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY.') not writable by ownCloud<br/>', 'hint'=>$permissionsHint); } // check if all required php modules are present if(!class_exists('ZipArchive')) { - $errors[]=array('error'=>'PHP module zip not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module zip not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if(!function_exists('mb_detect_encoding')) { - $errors[]=array('error'=>'PHP module mb multibyte not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module mb multibyte not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if(!function_exists('ctype_digit')) { - $errors[]=array('error'=>'PHP module ctype is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module ctype is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if(!function_exists('json_encode')) { - $errors[]=array('error'=>'PHP module JSON is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module JSON is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if(!function_exists('imagepng')) { - $errors[]=array('error'=>'PHP module GD is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module GD is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if(!function_exists('gzencode')) { - $errors[]=array('error'=>'PHP module zlib is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module zlib is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); + $web_server_restart= false; + } + if(!function_exists('iconv')) { + $errors[]=array('error'=>'PHP module iconv is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } - if(!function_exists('simplexml_load_string')) { - $errors[]=array('error'=>'PHP module SimpleXML is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP module SimpleXML is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if(floatval(phpversion())<5.3) { - $errors[]=array('error'=>'PHP 5.3 is required.<br/>','hint'=>'Please ask your server administrator to update PHP to version 5.3 or higher. PHP 5.2 is no longer supported by ownCloud and the PHP community.'); + $errors[]=array('error'=>'PHP 5.3 is required.<br/>', 'hint'=>'Please ask your server administrator to update PHP to version 5.3 or higher. PHP 5.2 is no longer supported by ownCloud and the PHP community.'); $web_server_restart= false; } if(!defined('PDO::ATTR_DRIVER_NAME')) { - $errors[]=array('error'=>'PHP PDO module is not installed.<br/>','hint'=>'Please ask your server administrator to install the module.'); + $errors[]=array('error'=>'PHP PDO module is not installed.<br/>', 'hint'=>'Please ask your server administrator to install the module.'); $web_server_restart= false; } if($web_server_restart) { - $errors[]=array('error'=>'PHP modules have been installed, but they are still listed as missing?<br/>','hint'=>'Please ask your server administrator to restart the web server.'); + $errors[]=array('error'=>'PHP modules have been installed, but they are still listed as missing?<br/>', 'hint'=>'Please ask your server administrator to restart the web server.'); } return $errors; } - public static function displayLoginPage($display_lostpassword) { + public static function displayLoginPage($errors = array()) { $parameters = array(); - $parameters['display_lostpassword'] = $display_lostpassword; + foreach( $errors as $key => $value ) { + $parameters[$value] = true; + } if (!empty($_POST['user'])) { $parameters["username"] = OC_Util::sanitizeHTML($_POST['user']).'"'; @@ -359,6 +376,7 @@ class OC_Util { public static function checkAdminUser() { // Check if we are a user self::checkLoggedIn(); + self::verifyUser(); if( !OC_Group::inGroup( OC_User::getUser(), 'admin' )) { header( 'Location: '.OC_Helper::linkToAbsolute( '', 'index.php' )); exit(); @@ -372,7 +390,8 @@ class OC_Util { public static function checkSubAdminUser() { // Check if we are a user self::checkLoggedIn(); - if(OC_Group::inGroup(OC_User::getUser(),'admin')) { + self::verifyUser(); + if(OC_Group::inGroup(OC_User::getUser(), 'admin')) { return true; } if(!OC_SubAdmin::isSubAdmin(OC_User::getUser())) { @@ -383,6 +402,40 @@ class OC_Util { } /** + * Check if the user verified the login with his password in the last 15 minutes + * If not, the user will be shown a password verification page + */ + public static function verifyUser() { + if(OC_Config::getValue('enhancedauth', false) === true) { + // Check password to set session + if(isset($_POST['password'])) { + if (OC_User::login(OC_User::getUser(), $_POST["password"] ) === true) { + $_SESSION['verifiedLogin']=time() + OC_Config::getValue('enhancedauthtime', 15 * 60); + } + } + + // Check if the user verified his password + if(!isset($_SESSION['verifiedLogin']) OR $_SESSION['verifiedLogin'] < time()) { + OC_Template::printGuestPage("", "verify", array('username' => OC_User::getUser())); + exit(); + } + } + } + + /** + * Check if the user verified the login with his password + * @return bool + */ + public static function isUserVerified() { + if(OC_Config::getValue('enhancedauth', false) === true) { + if(!isset($_SESSION['verifiedLogin']) OR $_SESSION['verifiedLogin'] < time()) { + return false; + } + } + return true; + } + + /** * Redirect to the user default page */ public static function redirectToDefaultPage() { @@ -411,26 +464,15 @@ class OC_Util { * @return string */ public static function getInstanceId() { - $id=OC_Config::getValue('instanceid',null); + $id=OC_Config::getValue('instanceid', null); if(is_null($id)) { $id=uniqid(); - OC_Config::setValue('instanceid',$id); + OC_Config::setValue('instanceid', $id); } return $id; } /** - * @brief Static lifespan (in seconds) when a request token expires. - * @see OC_Util::callRegister() - * @see OC_Util::isCallRegistered() - * @description - * Also required for the client side to compute the piont in time when to - * request a fresh token. The client will do so when nearly 97% of the - * timespan coded here has expired. - */ - public static $callLifespan = 3600; // 3600 secs = 1 hour - - /** * @brief Register an get/post call. Important to prevent CSRF attacks. * @todo Write howto: CSRF protection guide * @return $token Generated token. @@ -438,40 +480,25 @@ class OC_Util { * Creates a 'request token' (random) and stores it inside the session. * Ever subsequent (ajax) request must use such a valid token to succeed, * otherwise the request will be denied as a protection against CSRF. - * The tokens expire after a fixed lifespan. - * @see OC_Util::$callLifespan * @see OC_Util::isCallRegistered() */ public static function callRegister() { - // generate a random token. - $token = self::generate_random_bytes(20); - - // store the token together with a timestamp in the session. - $_SESSION['requesttoken-'.$token]=time(); - - // cleanup old tokens garbage collector - // only run every 20th time so we don't waste cpu cycles - if(rand(0,20)==0) { - foreach($_SESSION as $key=>$value) { - // search all tokens in the session - if(substr($key,0,12)=='requesttoken') { - // check if static lifespan has expired - if($value+self::$callLifespan<time()) { - // remove outdated tokens - unset($_SESSION[$key]); - } - } - } + // Check if a token exists + if(!isset($_SESSION['requesttoken'])) { + // No valid token found, generate a new one. + $requestToken = self::generate_random_bytes(20); + $_SESSION['requesttoken']=$requestToken; + } else { + // Valid token already exists, send it + $requestToken = $_SESSION['requesttoken']; } - // return the token - return($token); + return($requestToken); } /** * @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. - * @see OC_Util::$callLifespan - * @see OC_Util::calLRegister() + * @see OC_Util::callRegister() */ public static function isCallRegistered() { if(isset($_GET['requesttoken'])) { @@ -484,17 +511,14 @@ class OC_Util { //no token found. return false; } - if(isset($_SESSION['requesttoken-'.$token])) { - $timestamp=$_SESSION['requesttoken-'.$token]; - // check if static lifespan has expired - if($timestamp+self::$callLifespan<time()) { - return false; - }else{ - //token valid - return true; - } - }else{ + + // Check if the token is valid + if($token !== $_SESSION['requesttoken']) { + // Not valid return false; + } else { + // Valid token + return true; } } @@ -518,7 +542,7 @@ class OC_Util { * @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'); + if (is_array($value) || is_object($value)) array_walk_recursive($value, 'OC_Util::sanitizeHTML'); else $value = htmlentities($value, ENT_QUOTES, 'UTF-8'); //Specify encoding for PHP<5.4 return $value; } @@ -534,6 +558,11 @@ class OC_Util { // creating a test file $testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename; + + if(file_exists($testfile)) {// already running this test, possible recursive call + return false; + } + $fp = @fopen($testfile, 'w'); @fwrite($fp, $testcontent); @fclose($fp); @@ -555,30 +584,137 @@ class OC_Util { } } - /* - * @brief Generates random bytes with "openssl_random_pseudo_bytes" with a fallback for systems without openssl - * Inspired by gorgo on php.net - * @param Int with the length of the random - * @return String with the random bytes + + /** + * Check if the ownCloud server can connect to the internet + */ + public static function isinternetconnectionworking() { + + // try to connect to owncloud.org to see if http connections to the internet are possible. + $connected = @fsockopen("www.owncloud.org", 80); + if ($connected) { + fclose($connected); + return true; + }else{ + + // second try in case one server is down + $connected = @fsockopen("apps.owncloud.com", 80); + if ($connected) { + fclose($connected); + return true; + }else{ + return false; + } + + } + + } + + /** + * clear all levels of output buffering + */ + public static function obEnd(){ + while (ob_get_level()) { + ob_end_clean(); + } + } + + + /** + * @brief Generates a cryptographical secure pseudorandom string + * @param Int with the length of the random string + * @return String + * Please also update secureRNG_available if you change something here */ public static function generate_random_bytes($length = 30) { - if(function_exists('openssl_random_pseudo_bytes')) { + + // Try to use openssl_random_pseudo_bytes + if(function_exists('openssl_random_pseudo_bytes')) { $pseudo_byte = bin2hex(openssl_random_pseudo_bytes($length, $strong)); - if($strong == TRUE) { + if($strong == true) { return substr($pseudo_byte, 0, $length); // Truncate it to match the length } } - // fallback to mt_rand() + // Try to use /dev/urandom + $fp = @file_get_contents('/dev/urandom', false, null, 0, $length); + if ($fp !== false) { + $string = substr(bin2hex($fp), 0, $length); + return $string; + } + + // Fallback to mt_rand() $characters = '0123456789'; - $characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + $characters .= 'abcdefghijklmnopqrstuvwxyz'; $charactersLength = strlen($characters)-1; $pseudo_byte = ""; // Select some random characters for ($i = 0; $i < $length; $i++) { $pseudo_byte .= $characters[mt_rand(0, $charactersLength)]; - } + } return $pseudo_byte; } + + /** + * @brief Checks if a secure random number generator is available + * @return bool + */ + public static function secureRNG_available() { + + // Check openssl_random_pseudo_bytes + if(function_exists('openssl_random_pseudo_bytes')) { + openssl_random_pseudo_bytes(1, $strong); + if($strong == true) { + return true; + } + } + + // Check /dev/urandom + $fp = @file_get_contents('/dev/urandom', false, null, 0, 1); + if ($fp !== false) { + return true; + } + + return false; + } + + /** + * @Brief Get file content via curl. + * @param string $url Url to get content + * @return string of the response or false on error + * This function get the content of a page via curl, if curl is enabled. + * If not, file_get_element is used. + */ + + public static function getUrlContent($url){ + + if (function_exists('curl_init')) { + + $curl = curl_init(); + + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_USERAGENT, "ownCloud Server Crawler"); + $data = curl_exec($curl); + curl_close($curl); + + } else { + + $ctx = stream_context_create( + array( + 'http' => array( + 'timeout' => 10 + ) + ) + ); + $data=@file_get_contents($url, 0, $ctx); + + } + + return $data; + } + } diff --git a/lib/vcategories.php b/lib/vcategories.php index 6b1d6a316f1..406a4eb1074 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -21,6 +21,7 @@ * */ +OC_Hook::connect('OC_User', 'post_deleteUser', 'OC_VCategories', 'post_deleteUser'); /** * Class for easy access to categories in VCARD, VEVENT, VTODO and VJOURNAL. @@ -28,50 +29,261 @@ * anything else that is either parsed from a vobject or that the user chooses * to add. * Category names are not case-sensitive, but will be saved with the case they - * are entered in. If a user already has a category 'family' for an app, and + * are entered in. If a user already has a category 'family' for a type, and * tries to add a category named 'Family' it will be silently ignored. - * NOTE: There is a limitation in that the the configvalue field in the - * preferences table is a varchar(255). */ class OC_VCategories { - const PREF_CATEGORIES_LABEL = 'extra_categories'; + /** * Categories */ private $categories = array(); - private $app = null; + /** + * Used for storing objectid/categoryname pairs while rescanning. + */ + private static $relations = array(); + + private $type = null; private $user = null; + const CATEGORY_TABLE = '*PREFIX*vcategory'; + const RELATION_TABLE = '*PREFIX*vcategory_to_object'; + + const CATEGORY_FAVORITE = '_$!<Favorite>!$_'; + + const FORMAT_LIST = 0; + const FORMAT_MAP = 1; + /** * @brief Constructor. - * @param $app The application identifier e.g. 'contacts' or 'calendar'. + * @param $type The type identifier e.g. 'contact' or 'event'. * @param $user The user whos data the object will operate on. This * parameter should normally be omitted but to make an app able to * update categories for all users it is made possible to provide it. * @param $defcategories An array of default categories to be used if none is stored. */ - public function __construct($app, $user=null, $defcategories=array()) { - $this->app = $app; + public function __construct($type, $user=null, $defcategories=array()) { + $this->type = $type; $this->user = is_null($user) ? OC_User::getUser() : $user; - $categories = trim(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); - if ($categories) { - $categories = @unserialize($categories); + + $this->loadCategories(); + OCP\Util::writeLog('core', __METHOD__ . ', categories: ' + . print_r($this->categories, true), + OCP\Util::DEBUG + ); + + if($defcategories && count($this->categories) === 0) { + $this->addMulti($defcategories, true); + } + } + + /** + * @brief Load categories from db. + */ + private function loadCategories() { + $this->categories = array(); + $result = null; + $sql = 'SELECT `id`, `category` FROM `' . self::CATEGORY_TABLE . '` ' + . 'WHERE `uid` = ? AND `type` = ? ORDER BY `category`'; + try { + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array($this->user, $this->type)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + } + + if(!is_null($result)) { + while( $row = $result->fetchRow()) { + // The keys are prefixed because array_search wouldn't work otherwise :-/ + $this->categories[$row['id']] = $row['category']; + } + } + OCP\Util::writeLog('core', __METHOD__.', categories: ' . print_r($this->categories, true), + OCP\Util::DEBUG); + } + + + /** + * @brief Check if any categories are saved for this type and user. + * @returns boolean. + * @param $type The type identifier e.g. 'contact' or 'event'. + * @param $user The user whos categories will be checked. If not set current user will be used. + */ + public static function isEmpty($type, $user = null) { + $user = is_null($user) ? OC_User::getUser() : $user; + $sql = 'SELECT COUNT(*) FROM `' . self::CATEGORY_TABLE . '` ' + . 'WHERE `uid` = ? AND `type` = ?'; + try { + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array($user, $type)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + return ($result->numRows() == 0); + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; } - $this->categories = is_array($categories) ? $categories : $defcategories; } /** * @brief Get the categories for a specific user. + * @param * @returns array containing the categories as strings. */ - public function categories() { - //OC_Log::write('core','OC_VCategories::categories: '.print_r($this->categories, true), OC_Log::DEBUG); + public function categories($format = null) { if(!$this->categories) { return array(); } - usort($this->categories, 'strnatcasecmp'); // usort to also renumber the keys - return $this->categories; + $categories = array_values($this->categories); + uasort($categories, 'strnatcasecmp'); + if($format == self::FORMAT_MAP) { + $catmap = array(); + foreach($categories as $category) { + if($category !== self::CATEGORY_FAVORITE) { + $catmap[] = array( + 'id' => $this->array_searchi($category, $this->categories), + 'name' => $category + ); + } + } + return $catmap; + } + + // Don't add favorites to normal categories. + $favpos = array_search(self::CATEGORY_FAVORITE, $categories); + if($favpos !== false) { + return array_splice($categories, $favpos); + } else { + return $categories; + } + } + + /** + * Get the a list if items belonging to $category. + * + * Throws an exception if the category could not be found. + * + * @param string|integer $category Category id or name. + * @returns array An array of object ids or false on error. + */ + public function idsForCategory($category) { + $result = null; + if(is_numeric($category)) { + $catid = $category; + } elseif(is_string($category)) { + $catid = $this->array_searchi($category, $this->categories); + } + OCP\Util::writeLog('core', __METHOD__.', category: '.$catid.' '.$category, OCP\Util::DEBUG); + if($catid === false) { + $l10n = OC_L10N::get('core'); + throw new Exception( + $l10n->t('Could not find category "%s"', $category) + ); + } + + $ids = array(); + $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE + . '` WHERE `categoryid` = ?'; + + try { + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array($catid)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; + } + + if(!is_null($result)) { + while( $row = $result->fetchRow()) { + $ids[] = (int)$row['objid']; + } + } + + return $ids; + } + + /** + * Get the a list if items belonging to $category. + * + * Throws an exception if the category could not be found. + * + * @param string|integer $category Category id or name. + * @param array $tableinfo Array in the form {'tablename' => table, 'fields' => ['field1', 'field2']} + * @param int $limit + * @param int $offset + * + * This generic method queries a table assuming that the id + * field is called 'id' and the table name provided is in + * the form '*PREFIX*table_name'. + * + * If the category name cannot be resolved an exception is thrown. + * + * TODO: Maybe add the getting permissions for objects? + * + * @returns array containing the resulting items or false on error. + */ + public function itemsForCategory($category, $tableinfo, $limit = null, $offset = null) { + $result = null; + if(is_numeric($category)) { + $catid = $category; + } elseif(is_string($category)) { + $catid = $this->array_searchi($category, $this->categories); + } + OCP\Util::writeLog('core', __METHOD__.', category: '.$catid.' '.$category, OCP\Util::DEBUG); + if($catid === false) { + $l10n = OC_L10N::get('core'); + throw new Exception( + $l10n->t('Could not find category "%s"', $category) + ); + } + $fields = ''; + foreach($tableinfo['fields'] as $field) { + $fields .= '`' . $tableinfo['tablename'] . '`.`' . $field . '`,'; + } + $fields = substr($fields, 0, -1); + + $items = array(); + $sql = 'SELECT `' . self::RELATION_TABLE . '`.`categoryid`, ' . $fields + . ' FROM `' . $tableinfo['tablename'] . '` JOIN `' + . self::RELATION_TABLE . '` ON `' . $tableinfo['tablename'] + . '`.`id` = `' . self::RELATION_TABLE . '`.`objid` WHERE `' + . self::RELATION_TABLE . '`.`categoryid` = ?'; + + try { + $stmt = OCP\DB::prepare($sql, $limit, $offset); + $result = $stmt->execute(array($catid)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; + } + + if(!is_null($result)) { + while( $row = $result->fetchRow()) { + $items[] = $row; + } + } + //OCP\Util::writeLog('core', __METHOD__.', count: ' . count($items), OCP\Util::DEBUG); + //OCP\Util::writeLog('core', __METHOD__.', sql: ' . $sql, OCP\Util::DEBUG); + + return $items; } /** @@ -84,22 +296,51 @@ class OC_VCategories { } /** - * @brief Add a new category name. + * @brief Add a new category. + * @param $name A string with a name of the category + * @returns int the id of the added category or false if it already exists. + */ + public function add($name) { + OCP\Util::writeLog('core', __METHOD__.', name: ' . $name, OCP\Util::DEBUG); + if($this->hasCategory($name)) { + OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', OCP\Util::DEBUG); + return false; + } + OCP\DB::insertIfNotExist(self::CATEGORY_TABLE, + array( + 'uid' => $this->user, + 'type' => $this->type, + 'category' => $name, + )); + $id = OCP\DB::insertid(self::CATEGORY_TABLE); + OCP\Util::writeLog('core', __METHOD__.', id: ' . $id, OCP\Util::DEBUG); + $this->categories[$id] = $name; + return $id; + } + + /** + * @brief Add a new category. * @param $names A string with a name or an array of strings containing * the name(s) of the categor(y|ies) to add. * @param $sync bool When true, save the categories + * @param $id int Optional object id to add to this|these categor(y|ies) * @returns bool Returns false on error. */ - public function add($names, $sync=false) { + public function addMulti($names, $sync=false, $id = null) { if(!is_array($names)) { $names = array($names); } $names = array_map('trim', $names); $newones = array(); foreach($names as $name) { - if(($this->in_arrayi($name, $this->categories) == false) && $name != '') { + if(($this->in_arrayi( + $name, $this->categories) == false) && $name != '') { $newones[] = $name; } + if(!is_null($id) ) { + // Insert $objectid, $categoryid pairs if not exist. + self::$relations[] = array('objid' => $id, 'category' => $name); + } } if(count($newones) > 0) { $this->categories = array_merge($this->categories, $newones); @@ -114,8 +355,8 @@ class OC_VCategories { * @brief Extracts categories from a vobject and add the ones not already present. * @param $vobject The instance of OC_VObject to load the categories from. */ - public function loadFromVObject($vobject, $sync=false) { - $this->add($vobject->getAsArray('CATEGORIES'), $sync); + public function loadFromVObject($id, $vobject, $sync=false) { + $this->addMulti($vobject->getAsArray('CATEGORIES'), $sync, $id); } /** @@ -128,23 +369,62 @@ class OC_VCategories { * $result = $stmt->execute(); * $objects = array(); * if(!is_null($result)) { - * while( $row = $result->fetchRow()) { - * $objects[] = $row['carddata']; + * while( $row = $result->fetchRow()){ + * $objects[] = array($row['id'], $row['carddata']); * } * } * $categories->rescan($objects); */ public function rescan($objects, $sync=true, $reset=true) { + if($reset === true) { + $result = null; + // Find all objectid/categoryid pairs. + try { + $stmt = OCP\DB::prepare('SELECT `id` FROM `' . self::CATEGORY_TABLE . '` ' + . 'WHERE `uid` = ? AND `type` = ?'); + $result = $stmt->execute(array($this->user, $this->type)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + } + + // And delete them. + if(!is_null($result)) { + $stmt = OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `categoryid` = ? AND `type`= ?'); + while( $row = $result->fetchRow()) { + $stmt->execute(array($row['id'], $this->type)); + } + } + try { + $stmt = OCP\DB::prepare('DELETE FROM `' . self::CATEGORY_TABLE . '` ' + . 'WHERE `uid` = ? AND `type` = ?'); + $result = $stmt->execute(array($this->user, $this->type)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + return; + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), OCP\Util::ERROR); + return; + } $this->categories = array(); } + // Parse all the VObjects foreach($objects as $object) { - //OC_Log::write('core','OC_VCategories::rescan: '.substr($object, 0, 100).'(...)', OC_Log::DEBUG); - $vobject = OC_VObject::parse($object); + $vobject = OC_VObject::parse($object[1]); if(!is_null($vobject)) { - $this->loadFromVObject($vobject, $sync); + // Load the categories + $this->loadFromVObject($object[0], $vobject, $sync); } else { - OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.', '.substr($object, 0, 100).'(...)', OC_Log::DEBUG); + OC_Log::write('core', __METHOD__ . ', unable to parse. ID: ' . ', ' + . substr($object, 0, 100) . '(...)', OC_Log::DEBUG); } } $this->save(); @@ -155,16 +435,224 @@ class OC_VCategories { */ private function save() { if(is_array($this->categories)) { - usort($this->categories, 'strnatcasecmp'); // usort to also renumber the keys - $escaped_categories = serialize($this->categories); - OC_Preferences::setValue($this->user, $this->app, self::PREF_CATEGORIES_LABEL, $escaped_categories); - OC_Log::write('core','OC_VCategories::save: '.print_r($this->categories, true), OC_Log::DEBUG); + foreach($this->categories as $category) { + OCP\DB::insertIfNotExist(self::CATEGORY_TABLE, + array( + 'uid' => $this->user, + 'type' => $this->type, + 'category' => $category, + )); + } + // reload categories to get the proper ids. + $this->loadCategories(); + // Loop through temporarily cached objectid/categoryname pairs + // and save relations. + $categories = $this->categories; + // For some reason this is needed or array_search(i) will return 0..? + ksort($categories); + foreach(self::$relations as $relation) { + $catid = $this->array_searchi($relation['category'], $categories); + OC_Log::write('core', __METHOD__ . 'catid, ' . $relation['category'] . ' ' . $catid, OC_Log::DEBUG); + if($catid) { + OCP\DB::insertIfNotExist(self::RELATION_TABLE, + array( + 'objid' => $relation['objid'], + 'categoryid' => $catid, + 'type' => $this->type, + )); + } + } + self::$relations = array(); // reset } else { - OC_Log::write('core','OC_VCategories::save: $this->categories is not an array! '.print_r($this->categories, true), OC_Log::ERROR); + OC_Log::write('core', __METHOD__.', $this->categories is not an array! ' + . print_r($this->categories, true), OC_Log::ERROR); } } /** + * @brief Delete categories and category/object relations for a user. + * For hooking up on post_deleteUser + * @param string $uid The user id for which entries should be purged. + */ + public static function post_deleteUser($arguments) { + // Find all objectid/categoryid pairs. + $result = null; + try { + $stmt = OCP\DB::prepare('SELECT `id` FROM `' . self::CATEGORY_TABLE . '` ' + . 'WHERE `uid` = ?'); + $result = $stmt->execute(array($arguments['uid'])); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + } + + if(!is_null($result)) { + try { + $stmt = OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `categoryid` = ?'); + while( $row = $result->fetchRow()) { + try { + $stmt->execute(array($row['id'])); + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + } + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + } + } + try { + $stmt = OCP\DB::prepare('DELETE FROM `' . self::CATEGORY_TABLE . '` ' + . 'WHERE `uid` = ? AND'); + $result = $stmt->execute(array($arguments['uid'])); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), OCP\Util::ERROR); + } + } + + /** + * @brief Delete category/object relations from the db + * @param int $id The id of the object + * @param string $type The type of object (event/contact/task/journal). + * Defaults to the type set in the instance + * @returns boolean Returns false on error. + */ + public function purgeObject($id, $type = null) { + $type = is_null($type) ? $this->type : $type; + try { + $stmt = OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `objid` = ? AND `type`= ?'); + $result = $stmt->execute(array($id, $type)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; + } + return true; + } + + /** + * Get favorites for an object type + * + * @param string $type The type of object (event/contact/task/journal). + * Defaults to the type set in the instance + * @returns array An array of object ids. + */ + public function getFavorites($type = null) { + $type = is_null($type) ? $this->type : $type; + + try { + return $this->idsForCategory(self::CATEGORY_FAVORITE); + } catch(Exception $e) { + // No favorites + return array(); + } + } + + /** + * Add an object to favorites + * + * @param int $objid The id of the object + * @param string $type The type of object (event/contact/task/journal). + * Defaults to the type set in the instance + * @returns boolean + */ + public function addToFavorites($objid, $type = null) { + $type = is_null($type) ? $this->type : $type; + if(!$this->hasCategory(self::CATEGORY_FAVORITE)) { + $this->add(self::CATEGORY_FAVORITE, true); + } + return $this->addToCategory($objid, self::CATEGORY_FAVORITE, $type); + } + + /** + * Remove an object from favorites + * + * @param int $objid The id of the object + * @param string $type The type of object (event/contact/task/journal). + * Defaults to the type set in the instance + * @returns boolean + */ + public function removeFromFavorites($objid, $type = null) { + $type = is_null($type) ? $this->type : $type; + return $this->removeFromCategory($objid, self::CATEGORY_FAVORITE, $type); + } + + /** + * @brief Creates a category/object relation. + * @param int $objid The id of the object + * @param int|string $category The id or name of the category + * @param string $type The type of object (event/contact/task/journal). + * Defaults to the type set in the instance + * @returns boolean Returns false on database error. + */ + public function addToCategory($objid, $category, $type = null) { + $type = is_null($type) ? $this->type : $type; + if(is_string($category) && !is_numeric($category)) { + if(!$this->hasCategory($category)) { + $this->add($category, true); + } + $categoryid = $this->array_searchi($category, $this->categories); + } else { + $categoryid = $category; + } + try { + OCP\DB::insertIfNotExist(self::RELATION_TABLE, + array( + 'objid' => $objid, + 'categoryid' => $categoryid, + 'type' => $type, + )); + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; + } + return true; + } + + /** + * @brief Delete single category/object relation from the db + * @param int $objid The id of the object + * @param int|string $category The id or name of the category + * @param string $type The type of object (event/contact/task/journal). + * Defaults to the type set in the instance + * @returns boolean + */ + public function removeFromCategory($objid, $category, $type = null) { + $type = is_null($type) ? $this->type : $type; + $categoryid = (is_string($category) && !is_numeric($category)) + ? $this->array_searchi($category, $this->categories) + : $category; + try { + $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?'; + OCP\Util::writeLog('core', __METHOD__.', sql: ' . $objid . ' ' . $categoryid . ' ' . $type, + OCP\Util::DEBUG); + $stmt = OCP\DB::prepare($sql); + $stmt->execute(array($objid, $categoryid, $type)); + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; + } + return true; + } + + /** * @brief Delete categories from the db and from all the vobject supplied * @param $names An array of categories to delete * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. @@ -173,37 +661,87 @@ class OC_VCategories { if(!is_array($names)) { $names = array($names); } - OC_Log::write('core','OC_VCategories::delete, before: '.print_r($this->categories, true), OC_Log::DEBUG); + + OC_Log::write('core', __METHOD__ . ', before: ' + . print_r($this->categories, true), OC_Log::DEBUG); foreach($names as $name) { - OC_Log::write('core','OC_VCategories::delete: '.$name, OC_Log::DEBUG); + $id = null; + OC_Log::write('core', __METHOD__.', '.$name, OC_Log::DEBUG); if($this->hasCategory($name)) { - //OC_Log::write('core','OC_VCategories::delete: '.$name.' got it', OC_Log::DEBUG); - unset($this->categories[$this->array_searchi($name, $this->categories)]); + $id = $this->array_searchi($name, $this->categories); + unset($this->categories[$id]); + } + try { + $stmt = OCP\DB::prepare('DELETE FROM `' . self::CATEGORY_TABLE . '` WHERE ' + . '`uid` = ? AND `type` = ? AND `category` = ?'); + $result = $stmt->execute(array($this->user, $this->type, $name)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), OCP\Util::ERROR); + } + if(!is_null($id) && $id !== false) { + try { + $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `categoryid` = ?'; + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array($id)); + if (OC_DB::isError($result)) { + OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); + } + } catch(Exception $e) { + OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + OCP\Util::ERROR); + return false; + } } } - $this->save(); - OC_Log::write('core','OC_VCategories::delete, after: '.print_r($this->categories, true), OC_Log::DEBUG); + OC_Log::write('core', __METHOD__.', after: ' + . print_r($this->categories, true), OC_Log::DEBUG); if(!is_null($objects)) { foreach($objects as $key=>&$value) { $vobject = OC_VObject::parse($value[1]); if(!is_null($vobject)) { - $categories = $vobject->getAsArray('CATEGORIES'); - //OC_Log::write('core','OC_VCategories::delete, before: '.$key.': '.print_r($categories, true), OC_Log::DEBUG); + $object = null; + $componentname = ''; + if (isset($vobject->VEVENT)) { + $object = $vobject->VEVENT; + $componentname = 'VEVENT'; + } else + if (isset($vobject->VTODO)) { + $object = $vobject->VTODO; + $componentname = 'VTODO'; + } else + if (isset($vobject->VJOURNAL)) { + $object = $vobject->VJOURNAL; + $componentname = 'VJOURNAL'; + } else { + $object = $vobject; + } + $categories = $object->getAsArray('CATEGORIES'); foreach($names as $name) { $idx = $this->array_searchi($name, $categories); - //OC_Log::write('core','OC_VCategories::delete, loop: '.$name.', '.print_r($idx, true), OC_Log::DEBUG); if($idx !== false) { - OC_Log::write('core','OC_VCategories::delete, unsetting: '.$categories[$this->array_searchi($name, $categories)], OC_Log::DEBUG); + OC_Log::write('core', __METHOD__ + .', unsetting: ' + . $categories[$this->array_searchi($name, $categories)], + OC_Log::DEBUG); unset($categories[$this->array_searchi($name, $categories)]); - //unset($categories[$idx]); } } - //OC_Log::write('core','OC_VCategories::delete, after: '.$key.': '.print_r($categories, true), OC_Log::DEBUG); - $vobject->setString('CATEGORIES', implode(',', $categories)); + + $object->setString('CATEGORIES', implode(',', $categories)); + if($vobject !== $object) { + $vobject[$componentname] = $object; + } $value[1] = $vobject->serialize(); $objects[$key] = $value; } else { - OC_Log::write('core','OC_VCategories::delete, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 50).'(...)', OC_Log::DEBUG); + OC_Log::write('core', __METHOD__ + .', unable to parse. ID: ' . $value[0] . ', ' + . substr($value[1], 0, 50) . '(...)', OC_Log::DEBUG); } } } @@ -222,7 +760,7 @@ class OC_VCategories { if(!is_array($haystack)) { return false; } - return array_search(strtolower($needle),array_map('strtolower',$haystack)); + return array_search(strtolower($needle), array_map('strtolower', $haystack)); } - } + diff --git a/lib/vobject.php b/lib/vobject.php index b5a04b4bf65..267176ebc07 100644 --- a/lib/vobject.php +++ b/lib/vobject.php @@ -24,11 +24,11 @@ * This class provides a streamlined interface to the Sabre VObject classes */ class OC_VObject{ - /** @var Sabre_VObject_Component */ + /** @var Sabre\VObject\Component */ protected $vobject; /** - * @returns Sabre_VObject_Component + * @returns Sabre\VObject\Component */ public function getVObject() { return $this->vobject; @@ -41,9 +41,9 @@ class OC_VObject{ */ public static function parse($data) { try { - Sabre_VObject_Property::$classMap['LAST-MODIFIED'] = 'Sabre_VObject_Property_DateTime'; - $vobject = Sabre_VObject_Reader::read($data); - if ($vobject instanceof Sabre_VObject_Component) { + Sabre\VObject\Property::$classMap['LAST-MODIFIED'] = 'Sabre\VObject\Property\DateTime'; + $vobject = Sabre\VObject\Reader::read($data); + if ($vobject instanceof Sabre\VObject\Component) { $vobject = new OC_VObject($vobject); } return $vobject; @@ -62,7 +62,7 @@ class OC_VObject{ foreach($value as &$i ) { $i = implode("\\\\;", explode(';', $i)); } - return implode(';',$value); + return implode(';', $value); } /** @@ -71,15 +71,15 @@ class OC_VObject{ * @return array */ public static function unescapeSemicolons($value) { - $array = explode(';',$value); + $array = explode(';', $value); for($i=0;$i<count($array);$i++) { - if(substr($array[$i],-2,2)=="\\\\") { + if(substr($array[$i], -2, 2)=="\\\\") { if(isset($array[$i+1])) { - $array[$i] = substr($array[$i],0,count($array[$i])-2).';'.$array[$i+1]; + $array[$i] = substr($array[$i], 0, count($array[$i])-2).';'.$array[$i+1]; unset($array[$i+1]); } else{ - $array[$i] = substr($array[$i],0,count($array[$i])-2).';'; + $array[$i] = substr($array[$i], 0, count($array[$i])-2).';'; } $i = $i - 1; } @@ -89,13 +89,13 @@ class OC_VObject{ /** * Constuctor - * @param Sabre_VObject_Component or string + * @param Sabre\VObject\Component or string */ public function __construct($vobject_or_name) { if (is_object($vobject_or_name)) { $this->vobject = $vobject_or_name; } else { - $this->vobject = new Sabre_VObject_Component($vobject_or_name); + $this->vobject = new Sabre\VObject\Component($vobject_or_name); } } @@ -117,9 +117,9 @@ class OC_VObject{ if(is_array($value)) { $value = OC_VObject::escapeSemicolons($value); } - $property = new Sabre_VObject_Property( $name, $value ); + $property = new Sabre\VObject\Property( $name, $value ); foreach($parameters as $name => $value) { - $property->parameters[] = new Sabre_VObject_Parameter($name, $value); + $property->parameters[] = new Sabre\VObject\Parameter($name, $value); } $this->vobject->add($property); @@ -127,8 +127,8 @@ class OC_VObject{ } public function setUID() { - $uid = substr(md5(rand().time()),0,10); - $this->vobject->add('UID',$uid); + $uid = substr(md5(rand().time()), 0, 10); + $this->vobject->add('UID', $uid); } public function setString($name, $string) { @@ -150,12 +150,12 @@ class OC_VObject{ * @param int $dateType * @return void */ - public function setDateTime($name, $datetime, $dateType=Sabre_VObject_Property_DateTime::LOCALTZ) { + public function setDateTime($name, $datetime, $dateType=Sabre\VObject\Property\DateTime::LOCALTZ) { if ($datetime == 'now') { $datetime = new DateTime(); } if ($datetime instanceof DateTime) { - $datetime_element = new Sabre_VObject_Property_DateTime($name); + $datetime_element = new Sabre\VObject\Property\DateTime($name); $datetime_element->setDateTime($datetime, $dateType); $this->vobject->__set($name, $datetime_element); }else{ @@ -183,7 +183,7 @@ class OC_VObject{ return $this->vobject->children; } $return = $this->vobject->__get($name); - if ($return instanceof Sabre_VObject_Component) { + if ($return instanceof Sabre\VObject\Component) { $return = new OC_VObject($return); } return $return; @@ -201,7 +201,7 @@ class OC_VObject{ return $this->vobject->__isset($name); } - public function __call($function,$arguments) { + public function __call($function, $arguments) { return call_user_func_array(array($this->vobject, $function), $arguments); } } |