2012-10-14 23:04:08 +04:00
< ? 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 .
2012-10-26 22:53:32 +04:00
*
* It provides the following hooks :
* - post_shared
2012-10-14 23:04:08 +04:00
*/
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 ;
}
2012-10-16 23:38:52 +04:00
/**
* @ 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 ) {
2012-10-17 00:26:55 +04:00
if ( $shares = self :: getItemShared ( $itemType , $itemSource )) {
foreach ( $shares as $share ) {
self :: delete ( $share [ 'id' ]);
}
2012-10-16 23:38:52 +04:00
return true ;
}
return false ;
}
2012-10-14 23:04:08 +04:00
/**
* @ 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 ;
}
2012-10-27 01:06:40 +04:00
\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
));
2012-10-14 23:04:08 +04:00
// 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 ));
2012-10-26 22:53:32 +04:00
$id = \OC_DB :: insertid ( '*PREFIX*share' );
2012-10-14 23:04:08 +04:00
}
}
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' );
2012-10-26 22:53:32 +04:00
\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
));
2012-10-14 23:04:08 +04:00
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 );
}