From 496c1581d76a920fb6c6a50bc080c936d6ffbd31 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 16 Feb 2012 23:24:23 +0100 Subject: [PATCH 001/193] ETags must be quoted. --- lib/response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/response.php b/lib/response.php index 2fa0a5adcd..9431d7af1c 100644 --- a/lib/response.php +++ b/lib/response.php @@ -85,7 +85,7 @@ class OC_Response { self::setStatus(self::STATUS_NOT_MODIFIED); exit; } - header('ETag: '.$etag); + header('ETag: "'.$etag.'"'); } static public function setLastModifiedHeader($lastModified) { From 98b2d2db3dfb4a4ae286b0773b2297296c2cb70f Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Fri, 17 Feb 2012 09:35:18 +0100 Subject: [PATCH 002/193] Removed obsolete commented code and made minor speed improvements. Added stub function for loading categories. --- apps/contacts/js/contacts.js | 63 +++++++----------------- apps/contacts/templates/part.contact.php | 53 -------------------- 2 files changed, 17 insertions(+), 99 deletions(-) diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 3079362574..c9d1dc30f0 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -117,7 +117,7 @@ Contacts={ $('#carddav_url_close').show(); }, messageBox:function(title, msg) { - if(msg.toLowerCase().indexOf('auth') > 0) { + if(msg.toLowerCase().indexOf('auth') != -1) { // fugly hack, I know alert(msg); } @@ -335,17 +335,6 @@ Contacts={ // Load first in list. if($('#contacts li').length > 0) { Contacts.UI.Card.update(); - /* - var firstid = $('#contacts li:first-child').data('id'); - console.log('trying to load: ' + firstid); - $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':firstid},function(jsondata){ - if(jsondata.status == 'success'){ - Contacts.UI.Card.loadContact(jsondata.data); - } - else{ - Contacts.UI.messageBox(t('contacts', 'Error'), jsondata.data.message); - } - });*/ } else { // load intro page $.getJSON('ajax/loadintro.php',{},function(jsondata){ @@ -374,6 +363,7 @@ Contacts={ $('#rightcontent').data('id',this.id); //console.log('loaded: ' + this.data.FN[0]['value']); this.populateNameFields(); + this.loadCategories(); this.loadPhoto(); this.loadMails(); this.loadPhones(); @@ -455,9 +445,6 @@ Contacts={ this.fullname += ', ' + this.honsuf; } $('#n').html(this.fullname); - //$('.jecEditableOption').attr('title', 'Custom'); - //$('.jecEditableOption').text(this.fn); - //$('.jecEditableOption').attr('value', 0); $('#fn_select option').remove(); $('#fn_select').combobox('value', this.fn); var names = [this.fullname, this.givname + ' ' + this.famname, this.famname + ' ' + this.givname, this.famname + ', ' + this.givname]; @@ -466,17 +453,16 @@ Contacts={ .append($('') .text(value)); }); - /*$('#full').text(this.fullname); - $('#short').text(this.givname + ' ' + this.famname); - $('#reverse').text(this.famname + ' ' + this.givname); - $('#reverse_comma').text(this.famname + ', ' + this.givname);*/ $('#contact_identity').find('*[data-element="N"]').data('checksum', this.data.N[0]['checksum']); $('#contact_identity').find('*[data-element="FN"]').data('checksum', this.data.FN[0]['checksum']); $('#contact_identity').show(); }, + loadCategories:function(){ + if(this.data.CATEGORIES) { + // + } + }, editNew:function(){ // add a new contact - //Contacts.UI.notImplemented(); - //return false; this.id = ''; this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; $.getJSON('ajax/newcontact.php',{},function(jsondata){ if(jsondata.status == 'success'){ @@ -713,12 +699,6 @@ Contacts={ .text(value)); }); - /*$('#short').text(n[1] + ' ' + n[0]); - $('#full').text(this.fullname); - $('#reverse').text(n[0] + ' ' + n[1]); - $('#reverse_comma').text(n[0] + ', ' + n[1]);*/ - //$('#n').html(full); - //$('#fn').val(0); if(this.id == '') { var aid = $(dlg).find('#aid').val(); Contacts.UI.Card.add(n.join(';'), $('#short').text(), aid); @@ -889,21 +869,22 @@ Contacts={ }, loadPhoto:function(){ if(this.data.PHOTO) { + $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){ + if(jsondata.status == 'success'){ + //alert(jsondata.data.page); + $('#contacts_details_photo_wrapper').html(jsondata.data.page); + } + else{ + Contacts.UI.messageBox(jsondata.data.message); + } + }); $('#file_upload_form').show(); $('#contacts_propertymenu a[data-type="PHOTO"]').parent().hide(); } else { + $('#contacts_details_photo_wrapper').empty(); $('#file_upload_form').hide(); $('#contacts_propertymenu a[data-type="PHOTO"]').parent().show(); } - $.getJSON('ajax/loadphoto.php',{'id':this.id},function(jsondata){ - if(jsondata.status == 'success'){ - //alert(jsondata.data.page); - $('#contacts_details_photo_wrapper').html(jsondata.data.page); - } - else{ - Contacts.UI.messageBox(jsondata.data.message); - } - }); }, editPhoto:function(id, tmp_path){ //alert('editPhoto: ' + tmp_path); @@ -1143,13 +1124,6 @@ $(document).ready(function(){ return false; }); - /** - * Open blank form to add new contact. - * FIXME: Load the same page but only show name data and popup the name edit dialog. - * On save load the page again with an id and show all fields. - * NOTE: Or: Load the full page and popup name dialog modal. On success set the newly aquired ID, on - * Cancel or failure give appropriate message and show ... something else :-P - */ $('#contacts_newcontact').click(function(){ Contacts.UI.Card.editNew(); }); @@ -1175,9 +1149,6 @@ $(document).ready(function(){ return false; }); - /** - * Delete currently selected contact TODO: and clear page - */ $('#contacts_deletecard').live('click',function(){ Contacts.UI.Card.delete(); }); diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php index 408b595dc9..5be20964f4 100644 --- a/apps/contacts/templates/part.contact.php +++ b/apps/contacts/templates/part.contact.php @@ -70,14 +70,6 @@ $id = isset($_['id']) ? $_['id'] : ''; - -
  • - /> - -
  • - @@ -93,17 +85,6 @@ $id = isset($_['id']) ? $_['id'] : ''; - -
  • - /> - - -
  • - @@ -118,40 +99,6 @@ $id = isset($_['id']) ? $_['id'] : '';
      - -
      -
      - - - - 0) { - //array_walk($address['parameters'], ) Nah, this wont work... - $translated = array(); - foreach($address['parameters'] as $type) { - $translated[] = $l->t(ucwords(strtolower($type))); - } - echo implode('/', $translated); - } - ?> -
      -
      -
        - '.$adr[0].'':''); - $tmp .= ($adr[1]?'
      • '.$adr[1].'
      • ':''); - $tmp .= ($adr[2]?'
      • '.$adr[2].'
      • ':''); - $tmp .= ($adr[3]||$adr[5]?'
      • '.$adr[5].' '.$adr[3].'
      • ':''); - $tmp .= ($adr[4]?'
      • '.$adr[4].'
      • ':''); - $tmp .= ($adr[6]?'
      • '.$adr[6].'
      • ':''); - echo $tmp; - - ?> -
      -
      -
      - From 6e35d50cbb7b131bdc4bb245e3dc80042d4d8f99 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Fri, 17 Feb 2012 19:04:12 +0100 Subject: [PATCH 003/193] Avoid errors from missing GD library. --- apps/contacts/photo.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/contacts/photo.php b/apps/contacts/photo.php index 8dfbcb6fb1..298f1215e3 100644 --- a/apps/contacts/photo.php +++ b/apps/contacts/photo.php @@ -13,10 +13,19 @@ require_once('../../lib/base.php'); OC_Util::checkLoggedIn(); OC_Util::checkAppEnabled('contacts'); +function getStandardImage(){ + OC_Response::setExpiresHeader('P10D'); + OC_Response::enableCaching(); + OC_Response::redirect(OC_Helper::imagePath('contacts', 'person_large.png')); +} + $id = $_GET['id']; $contact = OC_Contacts_App::getContactVCard($id); $image = new OC_Image(); +if(!$image) { + getStandardImage(); +} // invalid vcard if( is_null($contact)) { OC_Log::write('contacts','photo.php. The VCard for ID '.$id.' is not RFC compatible',OC_Log::ERROR); @@ -45,7 +54,8 @@ if( is_null($contact)) { } if (!$image->valid()) { // Not found :-( - $image->loadFromFile('img/person_large.png'); + getStandardImage(); + //$image->loadFromFile('img/person_large.png'); } header('Content-Type: '.$image->mimeType()); $image->show(); From a3e58157eeb51eea414d6320cccc7188cb5995d4 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Sat, 18 Feb 2012 11:42:58 +0100 Subject: [PATCH 004/193] Strip tags on address on client side. --- apps/contacts/js/contacts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index c9d1dc30f0..d33f983a42 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -815,7 +815,7 @@ Contacts={ checksum = Contacts.UI.checksumFor(obj); container = Contacts.UI.propertyContainerFor(obj); } - var adr = new Array($(dlg).find('#adr_pobox').val(),$(dlg).find('#adr_extended').val(),$(dlg).find('#adr_street').val(),$(dlg).find('#adr_city').val(),$(dlg).find('#adr_region').val(),$(dlg).find('#adr_zipcode').val(),$(dlg).find('#adr_country').val()); + var adr = new Array($(dlg).find('#adr_pobox').val().strip_tags(),$(dlg).find('#adr_extended').val().strip_tags(),$(dlg).find('#adr_street').val().strip_tags(),$(dlg).find('#adr_city').val().strip_tags(),$(dlg).find('#adr_region').val().strip_tags(),$(dlg).find('#adr_zipcode').val().strip_tags(),$(dlg).find('#adr_country').val().strip_tags()); $(container).find('.adr').val(adr.join(';')); $(container).find('.adr_type').val($(dlg).find('#adr_type').val()); $(container).find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); From 0203f55fbfc985ea45ffff375b356d6ab0795336 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Sat, 18 Feb 2012 11:45:36 +0100 Subject: [PATCH 005/193] Added first draft of OC_VCategories. --- lib/vcategories.php | 198 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 lib/vcategories.php diff --git a/lib/vcategories.php b/lib/vcategories.php new file mode 100644 index 0000000000..69955d109a --- /dev/null +++ b/lib/vcategories.php @@ -0,0 +1,198 @@ + +* +* 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 . +* +*/ + + +/** + * Class for easy access to categories in VCARD, VEVENT, VTODO and VJOURNAL. + * A Category can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or 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 tries to add + * a category named 'Family' it will be silently ignored. + */ +OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_VCategories', 'deleteUser'); +class OC_VCategories { + /** + * cache + */ + protected static $cache = array(); + + /** + * Categories + */ + private $categories = array(); + + /** + * @brief Constructor. + * @param $app The application identifier e.g. 'contacts' or 'calendar'. + */ + public function __construct($app, $user=null) { + if(is_null($user)) { + $user = OC_User::getUser(); + } + // Use cache if possible - I doubt this is ever the case. Copy/paste from OC_L10N. + if(array_key_exists($app.'::'.$user, self::$cache)){ + OC_Log::write('core','OC_Categories::ctor, using cache', OC_Log::DEBUG); + $this->categories = self::$cache[$app.'::'.$user]; + } else { + $result = null; + try { + $stmt = OC_DB::prepare('SELECT DISTINCT name FROM *PREFIX*categories WHERE userid = ? AND appid = ? ORDER BY name'); + $result = $stmt->execute(array($user, $app)); + } catch(Exception $e) { + OC_Log::write('core','OC_VCategories::ctor, exception: '.$e->getMessage(), OC_Log::ERROR); + OC_Log::write('core','OC_VCategories::ctor, app: '.$app.', user: '.$user, OC_Log::ERROR); + } + if(!is_null($result)) { + while( $row = $result->fetchRow()){ + $this->categories[] = $row['name']; + } + self::$cache[$app.'::'.$user] = $this->categories; + } + } + } + + /** + * @brief Get the categories for a specific. + * @returns array containing the categories as strings. + */ + public function categories() { + return $this->categories; + } + + /** + * @brief Checks whether a category is already saved. + * @param $name The name to check for. + * @returns bool + */ + public function hasCategory($name) { + return ($this->in_arrayi($name, $this->categories) == false ? false : true); + } + + /** + * @brief Add a new category name. + * @param $names A string with a name or an array of strings containing the name(s) of the categor(y|ies) to add. + * @returns bool Returns false on error. + */ + public function add($app, $names) { + $user = OC_User::getUser(); + $newones = array(); + if(!is_array($names)) { + $names = array($names); + } + $names = array_map('trim', $names); + foreach($names as $name) { + if(($this->in_arrayi($name, $this->categories) == false) && $name != '') { + $newones[] = $name; + } + } + if(count($newones) > 0) { + $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*categories (userid,appid,name) VALUES(?,?,?)' ); + foreach($newones as $name) { + $this->categories[] = $name; + try { + $result = $stmt->execute(array($user, $app, $name)); + } catch(Exception $e) { + OC_Log::write('core','OC_VCategories::add, exception: '.$e->getMessage(), OC_Log::ERROR); + OC_Log::write('core','OC_VCategories::add, app: '.$app.', user: '.$user.', name: '.$name, OC_Log::ERROR); + return false; + } + } + natcasesort($this->categories); // Dunno if this is necessary + } + return true; + } + + /** + * @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. + * @returns bool Returns false if the name already exist (case insensitive) or on error. + */ + public function loadFromVObject($app, $vobject) { + $this->add($vobject->getAsArray('CATEGORIES')); + } + + /** + * @brief Delete a category from the db and from all the vobject supplied + * @param $app + * @param $name + * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. + */ + public function delete($app, $name, array &$objects) { + if(!$this->hasCategory($name)) { + return; + } + try { + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*categories WHERE UPPER(name) = ?'); + $result = $stmt->execute(array(strtoupper($name),)); + } catch(Exception $e) { + OC_Log::write('core','OC_VCategories::delete, exception: '.$e->getMessage(), OC_Log::ERROR); + OC_Log::write('core','OC_VCategories::delete, name: '.$name, OC_Log::ERROR); + return false; + } + unset($this->categories[$this->array_searchi($name, $this->categories)]); + foreach($objects as $key=>&$value) { + $vobject = OC_VObject::parse($value[1]); + if(!is_null($vobject)){ + $categories = $vobject->getAsArray('CATEGORIES'); + $idx = $this->array_searchi($name, $categories); + if($idx) { + unset($categories[$this->array_searchi($name, $categories)]); + $vobject->setString('CATEGORIES', implode(',', $categories)); + $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, 10).'(...)', OC_Log::DEBUG); + } + } + } + + /** + * @brief Delete all categories for a specific user. Connected to OC_User::post_deleteUser + * @param $parameters The id of the user. + * @returns bool Returns false on error. + */ + public function deleteUser($parameters) { + $user = $parameters['uid']; + try { + $stmt = OC_DB::prepare('DELETE FROM *PREFIX*categories WHERE user = ?'); + $result = $stmt->execute(array($user,)); + } catch(Exception $e) { + OC_Log::write('core','OC_VCategories::deleteFromUser, exception: '.$e->getMessage(), OC_Log::ERROR); + OC_Log::write('core','OC_VCategories::deleteFromUser, user: '.$user, OC_Log::ERROR); + return false; + } + return true; + } + + // case-insensitive in_array + private function in_arrayi($needle, $haystack) { + return in_array(strtolower($needle), array_map('strtolower', $haystack)); + } + + // case-insensitive array_search + private function array_searchi($needle, $haystack) { + return array_search(strtolower($needle),array_map('strtolower',$haystack)); + } +} +?> \ No newline at end of file From 8a1b671fdd902b1ec1b6b6e7f23ef8fe732905ce Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Sun, 19 Feb 2012 17:00:07 +0100 Subject: [PATCH 006/193] Switch from using separate db table to use OC_Preferences. There is a limitation in that the the configvalue field in the preferences table is a varchar(255). --- lib/vcategories.php | 96 ++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 67 deletions(-) diff --git a/lib/vcategories.php b/lib/vcategories.php index 69955d109a..6cc7511e65 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -28,47 +28,26 @@ * 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 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). */ -OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_VCategories', 'deleteUser'); class OC_VCategories { - /** - * cache - */ - protected static $cache = array(); - /** * Categories */ private $categories = array(); + + private $app = ''; /** * @brief Constructor. * @param $app The application identifier e.g. 'contacts' or 'calendar'. */ public function __construct($app, $user=null) { + $this->app = $app; if(is_null($user)) { $user = OC_User::getUser(); } - // Use cache if possible - I doubt this is ever the case. Copy/paste from OC_L10N. - if(array_key_exists($app.'::'.$user, self::$cache)){ - OC_Log::write('core','OC_Categories::ctor, using cache', OC_Log::DEBUG); - $this->categories = self::$cache[$app.'::'.$user]; - } else { - $result = null; - try { - $stmt = OC_DB::prepare('SELECT DISTINCT name FROM *PREFIX*categories WHERE userid = ? AND appid = ? ORDER BY name'); - $result = $stmt->execute(array($user, $app)); - } catch(Exception $e) { - OC_Log::write('core','OC_VCategories::ctor, exception: '.$e->getMessage(), OC_Log::ERROR); - OC_Log::write('core','OC_VCategories::ctor, app: '.$app.', user: '.$user, OC_Log::ERROR); - } - if(!is_null($result)) { - while( $row = $result->fetchRow()){ - $this->categories[] = $row['name']; - } - self::$cache[$app.'::'.$user] = $this->categories; - } - } + $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($user, $app, 'extra categories', '')); } /** @@ -93,7 +72,7 @@ class OC_VCategories { * @param $names A string with a name or an array of strings containing the name(s) of the categor(y|ies) to add. * @returns bool Returns false on error. */ - public function add($app, $names) { + public function add($names) { $user = OC_User::getUser(); $newones = array(); if(!is_array($names)) { @@ -106,17 +85,8 @@ class OC_VCategories { } } if(count($newones) > 0) { - $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*categories (userid,appid,name) VALUES(?,?,?)' ); - foreach($newones as $name) { - $this->categories[] = $name; - try { - $result = $stmt->execute(array($user, $app, $name)); - } catch(Exception $e) { - OC_Log::write('core','OC_VCategories::add, exception: '.$e->getMessage(), OC_Log::ERROR); - OC_Log::write('core','OC_VCategories::add, app: '.$app.', user: '.$user.', name: '.$name, OC_Log::ERROR); - return false; - } - } + $this->categories = $this->cleanArray(array_merge($this->categories, $newones)); + OC_Preferences::setValue(OC_User::getUser(), $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); natcasesort($this->categories); // Dunno if this is necessary } return true; @@ -127,29 +97,23 @@ class OC_VCategories { * @param $vobject The instance of OC_VObject to load the categories from. * @returns bool Returns false if the name already exist (case insensitive) or on error. */ - public function loadFromVObject($app, $vobject) { + public function loadFromVObject($vobject) { $this->add($vobject->getAsArray('CATEGORIES')); } /** * @brief Delete a category from the db and from all the vobject supplied - * @param $app * @param $name * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. */ - public function delete($app, $name, array &$objects) { + public function delete($name, array &$objects) { + $user = OC_User::getUser(); if(!$this->hasCategory($name)) { return; } - try { - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*categories WHERE UPPER(name) = ?'); - $result = $stmt->execute(array(strtoupper($name),)); - } catch(Exception $e) { - OC_Log::write('core','OC_VCategories::delete, exception: '.$e->getMessage(), OC_Log::ERROR); - OC_Log::write('core','OC_VCategories::delete, name: '.$name, OC_Log::ERROR); - return false; - } unset($this->categories[$this->array_searchi($name, $this->categories)]); + $this->categories = $this->cleanArray($this->categories); + OC_Preferences::setValue($user, $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); foreach($objects as $key=>&$value) { $vobject = OC_VObject::parse($value[1]); if(!is_null($vobject)){ @@ -167,24 +131,6 @@ class OC_VCategories { } } - /** - * @brief Delete all categories for a specific user. Connected to OC_User::post_deleteUser - * @param $parameters The id of the user. - * @returns bool Returns false on error. - */ - public function deleteUser($parameters) { - $user = $parameters['uid']; - try { - $stmt = OC_DB::prepare('DELETE FROM *PREFIX*categories WHERE user = ?'); - $result = $stmt->execute(array($user,)); - } catch(Exception $e) { - OC_Log::write('core','OC_VCategories::deleteFromUser, exception: '.$e->getMessage(), OC_Log::ERROR); - OC_Log::write('core','OC_VCategories::deleteFromUser, user: '.$user, OC_Log::ERROR); - return false; - } - return true; - } - // case-insensitive in_array private function in_arrayi($needle, $haystack) { return in_array(strtolower($needle), array_map('strtolower', $haystack)); @@ -194,5 +140,21 @@ class OC_VCategories { private function array_searchi($needle, $haystack) { return array_search(strtolower($needle),array_map('strtolower',$haystack)); } + + private function cleanArray($array, $remove_null_number = true){ + $new_array = array(); + $null_exceptions = array(); + + foreach ($array as $key => $value){ + $value = trim($value); + if($remove_null_number){ + $null_exceptions[] = '0'; + } + if(!in_array($value, $null_exceptions) && $value != "") { + $new_array[] = $value; + } + } + return $new_array; + } } ?> \ No newline at end of file From c62673d3601abdefba2ca81ac7d8f8d42fd7f6a5 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Sun, 19 Feb 2012 23:32:09 +0100 Subject: [PATCH 007/193] Added public static OC_VCategories object to OC_Contacts_App. Parse cards for new categories in add and edit methods. --- apps/contacts/lib/app.php | 2 ++ apps/contacts/lib/vcard.php | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php index ff348403a9..48298952c1 100644 --- a/apps/contacts/lib/app.php +++ b/apps/contacts/lib/app.php @@ -10,8 +10,10 @@ * This class manages our app actions */ OC_Contacts_App::$l10n = new OC_L10N('contacts'); +OC_Contacts_App::$categories = new OC_VCategories('contacts'); class OC_Contacts_App { public static $l10n; + public static $categories; /** * Render templates/part.details to json output diff --git a/apps/contacts/lib/vcard.php b/apps/contacts/lib/vcard.php index ece203bd45..de95e73255 100644 --- a/apps/contacts/lib/vcard.php +++ b/apps/contacts/lib/vcard.php @@ -114,6 +114,8 @@ class OC_Contacts_VCard{ $card = OC_VObject::parse($data); if(!is_null($card)){ + OC_Contacts_App::$categories->loadFromVObject($card); + $fn = $card->getAsString('FN'); if(!$fn){ // Fix missing 'FN' field. $n = $card->getAsString('N'); @@ -187,6 +189,7 @@ class OC_Contacts_VCard{ $email = null; $card = OC_VObject::parse($data); if(!is_null($card)){ + OC_Contacts_App::$categories->loadFromVObject($card); foreach($card->children as $property){ if($property->name == 'FN'){ $fn = $property->value; @@ -245,6 +248,7 @@ class OC_Contacts_VCard{ $card = OC_VObject::parse($data); if(!is_null($card)){ + OC_Contacts_App::$categories->loadFromVObject($card); foreach($card->children as $property){ if($property->name == 'FN'){ $fn = $property->value; @@ -279,6 +283,7 @@ class OC_Contacts_VCard{ $fn = null; $card = OC_VObject::parse($data); if(!is_null($card)){ + OC_Contacts_App::$categories->loadFromVObject($card); foreach($card->children as $property){ if($property->name == 'FN'){ $fn = $property->value; From 430ccef09cb303e1b2136cd0599f3baddf4acbcd Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 20 Feb 2012 13:16:51 +0100 Subject: [PATCH 008/193] Added OC_VCategories::rescan() --- lib/vcategories.php | 55 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/vcategories.php b/lib/vcategories.php index 6cc7511e65..63ed367230 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -36,18 +36,21 @@ class OC_VCategories { */ private $categories = array(); - private $app = ''; + private $app = null; + private $user = null; /** * @brief Constructor. * @param $app The application identifier e.g. 'contacts' or 'calendar'. + * @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. */ public function __construct($app, $user=null) { $this->app = $app; if(is_null($user)) { - $user = OC_User::getUser(); + $this->user = OC_User::getUser(); } - $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($user, $app, 'extra categories', '')); + $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($this->user, $app, 'extra categories', '')); } /** @@ -72,8 +75,8 @@ class OC_VCategories { * @param $names A string with a name or an array of strings containing the name(s) of the categor(y|ies) to add. * @returns bool Returns false on error. */ - public function add($names) { - $user = OC_User::getUser(); + public function add($names, $sync=true) { + $user = is_null($this->user) ? OC_User::getUser() : $this->user; $newones = array(); if(!is_array($names)) { $names = array($names); @@ -86,7 +89,9 @@ class OC_VCategories { } if(count($newones) > 0) { $this->categories = $this->cleanArray(array_merge($this->categories, $newones)); - OC_Preferences::setValue(OC_User::getUser(), $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); + if($sync) { + OC_Preferences::setValue($user, $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); + } natcasesort($this->categories); // Dunno if this is necessary } return true; @@ -97,8 +102,38 @@ class OC_VCategories { * @param $vobject The instance of OC_VObject to load the categories from. * @returns bool Returns false if the name already exist (case insensitive) or on error. */ - public function loadFromVObject($vobject) { - $this->add($vobject->getAsArray('CATEGORIES')); + public function loadFromVObject($vobject, $sync=true) { + $this->add($vobject->getAsArray('CATEGORIES'), $sync); + } + + /** + * @brief Reset saved categories and rescan supplied vobjects for categories. + * @param $objects An array of vobjects (as text). + * To get the object array, do something like: + * // For Addressbook: + * $categories = new OC_VCategories('contacts'); + * $stmt = OC_DB::prepare( 'SELECT carddata FROM *PREFIX*contacts_cards' ); + * $result = $stmt->execute(); + * $objects = array(); + * if(!is_null($result)) { + * while( $row = $result->fetchRow()){ + * $objects[] = $row['carddata']; + * } + * } + * $categories->rescan($objects); + */ + public function rescan($objects) { + $user = is_null($this->user) ? OC_User::getUser() : $this->user; + $this->categories = array(); + foreach($objects as $object) { + $vobject = OC_VObject::parse($object); + if(!is_null($vobject)){ + $this->loadFromVObject($vobject, false); + } else { + OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 20).'(...)', OC_Log::DEBUG); + } + } + OC_Preferences::setValue($user, $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); } /** @@ -107,7 +142,7 @@ class OC_VCategories { * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. */ public function delete($name, array &$objects) { - $user = OC_User::getUser(); + $user = is_null($this->user) ? OC_User::getUser() : $this->user; if(!$this->hasCategory($name)) { return; } @@ -126,7 +161,7 @@ class OC_VCategories { $objects[$key] = $value; } } else { - OC_Log::write('core','OC_VCategories::delete, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 10).'(...)', OC_Log::DEBUG); + OC_Log::write('core','OC_VCategories::delete, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 20).'(...)', OC_Log::DEBUG); } } } From 77ab89a7cb1003c88a3035a225ae78e1c5037b5b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Mon, 20 Feb 2012 22:32:57 +0100 Subject: [PATCH 009/193] Review changes of OC_VCategory --- lib/vcategories.php | 66 +++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/lib/vcategories.php b/lib/vcategories.php index 63ed367230..8bd03d4423 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -4,6 +4,7 @@ * * @author Thomas Tanghus * @copyright 2012 Thomas Tanghus +* @copyright 2012 Bart Visscher bartv@thisnet.nl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -23,14 +24,17 @@ /** * Class for easy access to categories in VCARD, VEVENT, VTODO and VJOURNAL. - * A Category can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or 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 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). + * A Category can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or + * 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 + * 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 */ @@ -38,23 +42,24 @@ class OC_VCategories { private $app = null; private $user = null; - + /** * @brief Constructor. * @param $app The application identifier e.g. 'contacts' or 'calendar'. - * @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 $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. */ public function __construct($app, $user=null) { $this->app = $app; if(is_null($user)) { $this->user = OC_User::getUser(); } - $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($this->user, $app, 'extra categories', '')); + $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); } /** - * @brief Get the categories for a specific. + * @brief Get the categories for a specific user. * @returns array containing the categories as strings. */ public function categories() { @@ -67,21 +72,22 @@ class OC_VCategories { * @returns bool */ public function hasCategory($name) { - return ($this->in_arrayi($name, $this->categories) == false ? false : true); + return $this->in_arrayi($name, $this->categories); } /** * @brief Add a new category name. - * @param $names A string with a name or an array of strings containing the name(s) of the categor(y|ies) to add. + * @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 * @returns bool Returns false on error. */ public function add($names, $sync=true) { - $user = is_null($this->user) ? OC_User::getUser() : $this->user; - $newones = array(); 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 != '') { $newones[] = $name; @@ -89,10 +95,10 @@ class OC_VCategories { } if(count($newones) > 0) { $this->categories = $this->cleanArray(array_merge($this->categories, $newones)); - if($sync) { - OC_Preferences::setValue($user, $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); - } natcasesort($this->categories); // Dunno if this is necessary + if($sync) { + $this->save(); + } } return true; } @@ -100,7 +106,6 @@ 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. - * @returns bool Returns false if the name already exist (case insensitive) or on error. */ public function loadFromVObject($vobject, $sync=true) { $this->add($vobject->getAsArray('CATEGORIES'), $sync); @@ -123,17 +128,24 @@ class OC_VCategories { * $categories->rescan($objects); */ public function rescan($objects) { - $user = is_null($this->user) ? OC_User::getUser() : $this->user; $this->categories = array(); foreach($objects as $object) { $vobject = OC_VObject::parse($object); - if(!is_null($vobject)){ + if(!is_null($vobject)) { $this->loadFromVObject($vobject, false); } else { OC_Log::write('core','OC_VCategories::rescan, unable to parse. ID: '.$value[0].', '.substr($value[1], 0, 20).'(...)', OC_Log::DEBUG); } } - OC_Preferences::setValue($user, $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); + $this->save(); + } + + /** + * @brief Save the list with categories + */ + public function save() { + $escaped_categories = OC_VObject::escapeSemicolons($this->categories); + OC_Preferences::setValue($this->user, $this->app, self::PREF_CATEGORIES_LABEL, $escaped_categories); } /** @@ -142,13 +154,12 @@ class OC_VCategories { * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. */ public function delete($name, array &$objects) { - $user = is_null($this->user) ? OC_User::getUser() : $this->user; if(!$this->hasCategory($name)) { return; } unset($this->categories[$this->array_searchi($name, $this->categories)]); $this->categories = $this->cleanArray($this->categories); - OC_Preferences::setValue($user, $this->app, 'extra categories', OC_VObject::escapeSemicolons($this->categories)); + $this->save(); foreach($objects as $key=>&$value) { $vobject = OC_VObject::parse($value[1]); if(!is_null($vobject)){ @@ -172,10 +183,13 @@ class OC_VCategories { } // case-insensitive array_search - private function array_searchi($needle, $haystack) { + private function array_searchi($needle, $haystack) { return array_search(strtolower($needle),array_map('strtolower',$haystack)); } + /* + * this is for a bug in the code, need to check if it is still needed + */ private function cleanArray($array, $remove_null_number = true){ $new_array = array(); $null_exceptions = array(); @@ -192,4 +206,4 @@ class OC_VCategories { return $new_array; } } -?> \ No newline at end of file +?> From 7c7031df44bd78bf8253d44a801b8bf25490f4bb Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 21 Feb 2012 00:02:27 +0100 Subject: [PATCH 010/193] Forgot to assign param. --- lib/vcategories.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/vcategories.php b/lib/vcategories.php index 8bd03d4423..f9e4d1b348 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -52,9 +52,7 @@ class OC_VCategories { */ public function __construct($app, $user=null) { $this->app = $app; - if(is_null($user)) { - $this->user = OC_User::getUser(); - } + $this->user = is_null($user) ? OC_User::getUser() : $user; $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); } From be948a9baad3eda5821f1dfaebefc8271454872b Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 21 Feb 2012 09:53:03 +0100 Subject: [PATCH 011/193] Check for empty value in ctor and being conservative about configvalue name ;-) --- lib/vcategories.php | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/lib/vcategories.php b/lib/vcategories.php index f9e4d1b348..250aa608c1 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -34,7 +34,7 @@ * preferences table is a varchar(255). */ class OC_VCategories { - const PREF_CATEGORIES_LABEL = 'extra categories'; + const PREF_CATEGORIES_LABEL = 'extra_categories'; /** * Categories */ @@ -53,7 +53,8 @@ class OC_VCategories { public function __construct($app, $user=null) { $this->app = $app; $this->user = is_null($user) ? OC_User::getUser() : $user; - $this->categories = OC_VObject::unescapeSemicolons(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); + $categories = trim(OC_Preferences::getValue($this->user, $app, self::PREF_CATEGORIES_LABEL, '')); + $this->categories = $categories != '' ? OC_VObject::unescapeSemicolons($categories) : array(); } /** @@ -92,7 +93,7 @@ class OC_VCategories { } } if(count($newones) > 0) { - $this->categories = $this->cleanArray(array_merge($this->categories, $newones)); + $this->categories = array_merge($this->categories, $newones); natcasesort($this->categories); // Dunno if this is necessary if($sync) { $this->save(); @@ -156,7 +157,6 @@ class OC_VCategories { return; } unset($this->categories[$this->array_searchi($name, $this->categories)]); - $this->categories = $this->cleanArray($this->categories); $this->save(); foreach($objects as $key=>&$value) { $vobject = OC_VObject::parse($value[1]); @@ -185,23 +185,5 @@ class OC_VCategories { return array_search(strtolower($needle),array_map('strtolower',$haystack)); } - /* - * this is for a bug in the code, need to check if it is still needed - */ - private function cleanArray($array, $remove_null_number = true){ - $new_array = array(); - $null_exceptions = array(); - - foreach ($array as $key => $value){ - $value = trim($value); - if($remove_null_number){ - $null_exceptions[] = '0'; - } - if(!in_array($value, $null_exceptions) && $value != "") { - $new_array[] = $value; - } - } - return $new_array; - } } ?> From b6ed61eab2fce2de3653b376bef0eeddd431562a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 21 Oct 2011 15:17:39 +0200 Subject: [PATCH 012/193] pass paramters to file proxies by reference so they can be modified --- lib/fileproxy.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 235fc8bf28..7e0722b960 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -88,11 +88,11 @@ class OC_FileProxy{ $operation='pre'.$operation; foreach($proxies as $proxy){ if($filepath2){ - if(!$proxy->$operation($filepath,$filepath2)){ + if(!$proxy->$operation(&$filepath,&$filepath2)){ return false; } }else{ - if(!$proxy->$operation($filepath)){ + if(!$proxy->$operation(&$filepath)){ return false; } } From abc749feeb14c49d483135f879195b70ee89654f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 21 Oct 2011 17:01:41 +0200 Subject: [PATCH 013/193] make documentation reflect reality a bit better --- lib/fileproxy.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 7e0722b960..1fb22bd113 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -34,7 +34,7 @@ * A post-proxy recieves 2 arguments, the filepath and the result of the operation. * The return calue of the post-proxy will be used as the new result of the operation * The operations that have a post-proxy are - * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, filemtime, filectime, file_get_contents, getMimeType, hash, free_space and search + * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, fileatime, filemtime, filectime, file_get_contents, getMimeType, hash, fopen, free_space and search */ class OC_FileProxy{ From 3d67cd51c2f42029435343004b3ebe608bcba375 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 21 Oct 2011 17:02:11 +0200 Subject: [PATCH 014/193] encryption proxy wip --- apps/files_encryption/appinfo/app.php | 11 ++ apps/files_encryption/lib/cryptstream.php | 121 ++++++++++++++++++++++ apps/files_encryption/lib/proxy.php | 70 +++++++++++++ lib/crypt.php | 39 +++++-- lib/fileproxy.php | 8 +- lib/filestorage/local.php | 2 +- lib/filesystemview.php | 2 +- 7 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 apps/files_encryption/appinfo/app.php create mode 100644 apps/files_encryption/lib/cryptstream.php create mode 100644 apps/files_encryption/lib/proxy.php diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php new file mode 100644 index 0000000000..82d2544dd1 --- /dev/null +++ b/apps/files_encryption/appinfo/app.php @@ -0,0 +1,11 @@ +. + * + */ + +/** + * transparently encrypted filestream + */ + +class OC_CryptStream{ + private $source; + + public function stream_open($path, $mode, $options, &$opened_path){ + $path=str_replace('crypt://','',$path); + $this->source=OC_FileSystem::fopen($path.'.enc',$mode); + if(!is_resource($this->source)){ + OC_Log::write('files_encryption','failed to open '.$path.'.enc',OC_Log::ERROR); + } + return is_resource($this->source); + } + + public function stream_seek($offset, $whence=SEEK_SET){ + fseek($this->source,$offset,$whence); + } + + public function stream_tell(){ + return ftell($this->source); + } + + public function stream_read($count){ + $pos=0; + $currentPos=ftell($this->source); + $offset=$currentPos%8192; + fseek($this->source,-$offset,SEEK_CUR); + $result=''; + while($count>$pos){ + $data=fread($this->source,8192); + $pos+=8192; + $result.=OC_Crypt::decrypt($data); + } + return substr($result,$offset,$count); + } + + public function stream_write($data){ + $length=strlen($data); + $written=0; + $currentPos=ftell($this->source); + if($currentPos%8192!=0){ + //make sure we always start on a block start + fseek($this->source,-($currentPos%8192),SEEK_CUR); + $encryptedBlock=fread($this->source,8192); + fseek($this->source,-($currentPos%8192),SEEK_CUR); + $block=OC_Crypt::decrypt($encryptedBlock); + $data=substr($block,0,$currentPos%8192).$data; + } + while(strlen($data)>0){ + if(strlen($data)<8192){ + //fetch the current data in that block and append it to the input so we always write entire blocks + $oldPos=ftell($this->source); + $encryptedBlock=fread($this->source,8192); + fseek($this->source,$oldPos); + $block=OC_Crypt::decrypt($encryptedBlock); + $data.=substr($block,strlen($data)); + } + $encrypted=OC_Crypt::encrypt(substr($data,0,8192)); + fwrite($this->source,$encrypted); + $data=substr($data,8192); + } + return $length; + } + + public function stream_set_option($option,$arg1,$arg2){ + switch($option){ + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->source,$arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->source,$arg1,$arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->source,$arg1,$arg2); + } + } + + public function stream_stat(){ + return fstat($this->source); + } + + public function stream_lock($mode){ + flock($this->source,$mode); + } + + public function stream_flush(){ + return fflush($this->source); + } + + public function stream_eof(){ + return feof($this->source); + } + + public function stream_close(){ + return fclose($this->source); + } +} \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php new file mode 100644 index 0000000000..f7a991a344 --- /dev/null +++ b/apps/files_encryption/lib/proxy.php @@ -0,0 +1,70 @@ +. +* +*/ + +/** + * transparent encryption + */ + +class OC_FileProxy_Encryption extends OC_FileProxy{ + public function preFile_put_contents($path,&$data){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','file put contents',OC_Log::DEBUG); + if (is_resource($data)) { + $newData=''; + while(!feof($data)){ + $block=fread($data,8192); + $newData.=OC_Crypt::encrypt($block); + } + $data=$newData; + }else{ + $data=OC_Crypt::blockEncrypt($data); + } + } + } + + public function postFile_get_contents($path,$data){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','file get contents',OC_Log::DEBUG); + return OC_Crypt::blockDecrypt($data); + } + } + + public function postFopen($path,&$result){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','fopen',OC_Log::DEBUG); + fclose($result); + $result=fopen('crypt://'.substr($path,0,-4));//remove the .enc extention so we don't catch the fopen request made by cryptstream + } + } + + public function preReadFile($path){ + if(substr($path,-4)=='.enc'){ + OC_Log::write('files_encryption','readline',OC_Log::DEBUG); + $stream=fopen('crypt://'.substr($path,0,-4)); + while(!feof($stream)){ + print(fread($stream,8192)); + } + return false;//cancel the original request + } + } +} diff --git a/lib/crypt.php b/lib/crypt.php index 6002067948..3e6fa05b85 100644 --- a/lib/crypt.php +++ b/lib/crypt.php @@ -113,14 +113,13 @@ class OC_Crypt { return($bf->encrypt($contents)); } - - /** - * @brief encryption of a file - * @param $filename - * @param $key the encryption key - * - * This function encrypts a file - */ + /** + * @brief encryption of a file + * @param $filename + * @param $key the encryption key + * + * This function encrypts a file + */ public static function encryptfile( $filename, $key) { $handleread = fopen($filename, "rb"); if($handleread<>FALSE) { @@ -158,6 +157,30 @@ class OC_Crypt { } fclose($handleread); } + + /** + * encrypt data in 8192b sized blocks + */ + public static function blockEncrypt($data){ + $result=''; + while(strlen($data)){ + $result=self::encrypt(substr($data,0,8192)); + $data=substr($data,8192); + } + return $result; + } + + /** + * decrypt data in 8192b sized blocks + */ + public static function blockDecrypt($data){ + $result=''; + while(strlen($data)){ + $result=self::decrypt(substr($data,0,8192)); + $data=substr($data,8192); + } + return $result; + } diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 1fb22bd113..796fd95cb3 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -83,16 +83,16 @@ class OC_FileProxy{ return $proxies; } - public static function runPreProxies($operation,$filepath,$filepath2=null){ + public static function runPreProxies($operation,&$filepath,&$filepath2=null){ $proxies=self::getProxies($operation,false); $operation='pre'.$operation; foreach($proxies as $proxy){ - if($filepath2){ - if(!$proxy->$operation(&$filepath,&$filepath2)){ + if(!is_null($filepath2)){ + if($proxy->$operation($filepath,$filepath2)===false){ return false; } }else{ - if(!$proxy->$operation(&$filepath)){ + if($proxy->$operation($filepath)===false){ return false; } } diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index dcb516a3af..ee4b267bcd 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -74,7 +74,7 @@ class OC_Filestorage_Local extends OC_Filestorage{ public function file_get_contents($path){ return file_get_contents($this->datadir.$path); } - public function file_put_contents($path,$data){ + public function file_put_contents($path,$data=null){ if($return=file_put_contents($this->datadir.$path,$data)){ } } diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 91c6cd1772..a78f3f652a 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -302,7 +302,7 @@ class OC_FilesystemView { } } if($run){ - if($extraParam){ + if(!is_null($extraParam)){ $result=$storage->$operation($interalPath,$extraParam); }else{ $result=$storage->$operation($interalPath); From 82394f9527817673f3ecbf7e5fd1d4857f0f3fe1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Oct 2011 14:10:51 +0200 Subject: [PATCH 015/193] add option to dissable fileproxies --- lib/fileproxy.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 796fd95cb3..46fc2f49c5 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -39,6 +39,7 @@ class OC_FileProxy{ private static $proxies=array(); + public static $enabled=true; /** * check if this proxy implments a specific proxy operation @@ -84,6 +85,9 @@ class OC_FileProxy{ } public static function runPreProxies($operation,&$filepath,&$filepath2=null){ + if(!self::$enabled){ + return true; + } $proxies=self::getProxies($operation,false); $operation='pre'.$operation; foreach($proxies as $proxy){ @@ -101,6 +105,9 @@ class OC_FileProxy{ } public static function runPostProxies($operation,$path,$result){ + if(!self::$enabled){ + return $result; + } $proxies=self::getProxies($operation,true); $operation='post'.$operation; foreach($proxies as $proxy){ From e2b49541760dabbdfce11bcc8063d31139b6caa3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Oct 2011 14:11:15 +0200 Subject: [PATCH 016/193] simple file encryption wip --- apps/files_encryption/lib/cryptstream.php | 7 +++++-- apps/files_encryption/lib/proxy.php | 17 +++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index e4544313f6..7fbfeaa7a8 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -29,9 +29,12 @@ class OC_CryptStream{ public function stream_open($path, $mode, $options, &$opened_path){ $path=str_replace('crypt://','',$path); - $this->source=OC_FileSystem::fopen($path.'.enc',$mode); + OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); + OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file + $this->source=OC_FileSystem::fopen($path,$mode); + OC_FileProxy::$enabled=true; if(!is_resource($this->source)){ - OC_Log::write('files_encryption','failed to open '.$path.'.enc',OC_Log::ERROR); + OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); } return is_resource($this->source); } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index f7a991a344..053ac786c3 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -28,7 +28,6 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function preFile_put_contents($path,&$data){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','file put contents',OC_Log::DEBUG); if (is_resource($data)) { $newData=''; while(!feof($data)){ @@ -44,27 +43,33 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function postFile_get_contents($path,$data){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','file get contents',OC_Log::DEBUG); return OC_Crypt::blockDecrypt($data); } } public function postFopen($path,&$result){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','fopen',OC_Log::DEBUG); + $meta=stream_get_meta_data($result); fclose($result); - $result=fopen('crypt://'.substr($path,0,-4));//remove the .enc extention so we don't catch the fopen request made by cryptstream + OC_log::write('file_encryption','mode: '.$meta['mode']); + $result=fopen('crypt://'.$path,$meta['mode']); } } public function preReadFile($path){ if(substr($path,-4)=='.enc'){ - OC_Log::write('files_encryption','readline',OC_Log::DEBUG); - $stream=fopen('crypt://'.substr($path,0,-4)); + $stream=fopen('crypt://'.$path,'r'); while(!feof($stream)){ print(fread($stream,8192)); } return false;//cancel the original request } } + + public function postGetMimeType($path,$result){ + if(substr($path,-4)=='.enc'){ + return 'text/plain'; + } + return $result; + } } From e9af2185625f5012067cd5fe2ee04cee9828f11b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 20:49:32 +0100 Subject: [PATCH 017/193] use streams instead of temporary files for cross-storage copy and rename --- lib/filesystemview.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/filesystemview.php b/lib/filesystemview.php index a78f3f652a..27552d25f2 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -179,9 +179,13 @@ class OC_FilesystemView { if($storage=$this->getStorage($path1)){ $result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2)); } - }elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){ - $tmpFile=$storage1->toTmpFile($this->getInternalPath($path1)); - $result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2)); + }else{ + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + while (!feof($source)){ + fwrite($target,fread($source,8192)); + } + $storage1=$this->getStorage($path1); $storage1->unlink($this->getInternalPath($path1)); } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2)); @@ -207,9 +211,14 @@ class OC_FilesystemView { if($storage=$this->getStorage($path1)){ $result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2)); } - }elseif($storage1=$this->getStorage($path1) and $storage2=$this->getStorage($path2)){ - $tmpFile=$storage1->toTmpFile($this->getInternalPath($path1)); - $result=$storage2->fromTmpFile($tmpFile,$this->getInternalPath($path2)); + }else{ + $source=$this->fopen($path1,'r'); + $target=$this->fopen($path2,'w'); + if($target and $source){ + while (!feof($source)){ + fwrite($target,fread($source,8192)); + } + } } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2)); if(!$exists){ From fd4b30ac6f81193cac1e558cc115802717aa88c1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 20:50:35 +0100 Subject: [PATCH 018/193] no post hooks for fopen --- lib/filesystemview.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 27552d25f2..a7003e84f1 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -317,9 +317,11 @@ class OC_FilesystemView { $result=$storage->$operation($interalPath); } $result=OC_FileProxy::runPostProxies($operation,$path,$result); - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + if($operation!='fopen'){//no post hooks for fopen, the file stream is still open + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + } } } return $result; From e53e7990c404e3ff2a1b7abad1e4c8ad4f89ee2a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 22 Nov 2011 01:48:08 +0100 Subject: [PATCH 019/193] improve get_temp_dir() implementation --- lib/base.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/base.php b/lib/base.php index 880645ff79..5b6ed9bcb1 100644 --- a/lib/base.php +++ b/lib/base.php @@ -271,6 +271,8 @@ if(!function_exists('get_temp_dir')) { unlink($temp); return dirname($temp); } + if( $temp=sys_get_temp_dir()) return $temp; + return null; } } From f1cbb9effc7e0672dd9dd6fa810aba36c5749898 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 24 Nov 2011 01:44:54 +0100 Subject: [PATCH 020/193] initial integration of encryption --- apps/files_encryption/appinfo/info.xml | 11 +++ {lib => apps/files_encryption/lib}/crypt.php | 89 +++++++++++-------- apps/files_encryption/lib/cryptstream.php | 49 +++++++++-- apps/files_encryption/lib/proxy.php | 91 ++++++++++++++++---- lib/user.php | 3 +- 5 files changed, 178 insertions(+), 65 deletions(-) create mode 100644 apps/files_encryption/appinfo/info.xml rename {lib => apps/files_encryption/lib}/crypt.php (68%) diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml new file mode 100644 index 0000000000..a8db06aa3d --- /dev/null +++ b/apps/files_encryption/appinfo/info.xml @@ -0,0 +1,11 @@ + + + files_encryption + Encryption + Server side encryption of files + 0.1 + AGPL + Robin Appelman + 3 + + diff --git a/lib/crypt.php b/apps/files_encryption/lib/crypt.php similarity index 68% rename from lib/crypt.php rename to apps/files_encryption/lib/crypt.php index 3e6fa05b85..4e7739b1d0 100644 --- a/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -37,20 +37,42 @@ require_once('Crypt_Blowfish/Blowfish.php'); * This class is for crypting and decrypting */ class OC_Crypt { + static private $bf = null; - static $encription_extension='.encrypted'; + public static function loginListener($params){ + self::init($params['uid'],$params['password']); + } public static function init($login,$password) { - $_SESSION['user_password'] = $password; // save the password as passcode for the encryption if(OC_User::isLoggedIn()){ - // does key exist? - if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){ - OC_Crypt::createkey($_SESSION['user_password']); + $view=new OC_FilesystemView('/'.$login); + if(!$view->file_exists('/encryption.key')){// does key exist? + OC_Crypt::createkey($password); } + $key=$view->file_get_contents('/encryption.key'); + $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); } } - + /** + * get the blowfish encryption handeler for a key + * @param string $key (optional) + * + * if the key is left out, the default handeler will be used + */ + public static function getBlowfish($key=''){ + if($key){ + return new Crypt_Blowfish($key); + }else{ + if(!isset($_SESSION['enckey'])){ + return false; + } + if(!self::$bf){ + self::$bf=new Crypt_Blowfish($_SESSION['enckey']); + } + return self::$bf; + } + } public static function createkey($passcode) { if(OC_User::isLoggedIn()){ @@ -61,57 +83,58 @@ class OC_Crypt { $enckey=OC_Crypt::encrypt($key,$passcode); // Write the file - $username=OC_USER::getUser(); - @file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey ); + $username=OC_USER::getUser(); + OC_FileProxy::$enabled=false; + $view=new OC_FilesystemView('/'.$username); + $view->file_put_contents('/encryption.key',$enckey); + OC_FileProxy::$enabled=true; } } - public static function changekeypasscode( $newpasscode) { + public static function changekeypasscode($oldPassword, $newPassword) { if(OC_User::isLoggedIn()){ - $username=OC_USER::getUser(); + $username=OC_USER::getUser(); + $view=new OC_FilesystemView('/'.$username); // read old key - $key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key'); + $key=$view->file_get_contents('/encryption.key'); // decrypt key with old passcode - $key=OC_Crypt::decrypt($key, $_SESSION['user_password']); + $key=OC_Crypt::decrypt($key, $oldPassword); // encrypt again with new passcode - $key=OC_Crypt::encrypt($key,$newpassword); + $key=OC_Crypt::encrypt($key, $newPassword); // store the new key - file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key ); - - $_SESSION['user_password']=$newpasscode; + $view->file_put_contents('/encryption.key', $key ); } } /** * @brief encrypts an content * @param $content the cleartext message you want to encrypt - * @param $key the encryption key + * @param $key the encryption key (optional) * @returns encrypted content * * This function encrypts an content */ - public static function encrypt( $content, $key) { - $bf = new Crypt_Blowfish($key); + public static function encrypt( $content, $key='') { + $bf = self::getBlowfish($key); return($bf->encrypt($content)); } - - /** - * @brief decryption of an content - * @param $content the cleartext message you want to decrypt - * @param $key the encryption key - * @returns cleartext content - * - * This function decrypts an content - */ - public static function decrypt( $content, $key) { - $bf = new Crypt_Blowfish($key); + /** + * @brief decryption of an content + * @param $content the cleartext message you want to decrypt + * @param $key the encryption key (optional) + * @returns cleartext content + * + * This function decrypts an content + */ + public static function decrypt( $content, $key='') { + $bf = self::getBlowfish($key); return($bf->encrypt($contents)); - } + } /** * @brief encryption of a file @@ -181,8 +204,4 @@ class OC_Crypt { } return $result; } - - - - } diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 7fbfeaa7a8..00dda7352b 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -22,19 +22,35 @@ /** * transparently encrypted filestream + * + * you can use it as wrapper around an existing stream by setting OC_CryptStream::$sourceStreams['foo']=array('path'=>$path,'stream'=>$stream) + * and then fopen('crypt://streams/foo'); */ class OC_CryptStream{ + public static $sourceStreams=array(); private $source; + private $path; + private $readBuffer;//for streams that dont support seeking + private $meta=array();//header/meta for source stream public function stream_open($path, $mode, $options, &$opened_path){ $path=str_replace('crypt://','',$path); - OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); - OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file - $this->source=OC_FileSystem::fopen($path,$mode); - OC_FileProxy::$enabled=true; - if(!is_resource($this->source)){ - OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); + if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])){ + $this->source=self::$sourceStreams[basename($path)]['stream']; + $this->path=self::$sourceStreams[basename($path)]['path']; + }else{ + $this->path=$path; + OC_Log::write('files_encryption','open encrypted '.$path. ' in '.$mode,OC_Log::DEBUG); + OC_FileProxy::$enabled=false;//disable fileproxies so we can open the source file + $this->source=OC_FileSystem::fopen($path,$mode); + OC_FileProxy::$enabled=true; + if(!is_resource($this->source)){ + OC_Log::write('files_encryption','failed to open '.$path,OC_Log::ERROR); + } + } + if(is_resource($this->source)){ + $this->meta=stream_get_meta_data($this->source); } return is_resource($this->source); } @@ -51,14 +67,26 @@ class OC_CryptStream{ $pos=0; $currentPos=ftell($this->source); $offset=$currentPos%8192; - fseek($this->source,-$offset,SEEK_CUR); $result=''; + if($offset>0){ + if($this->meta['seekable']){ + fseek($this->source,-$offset,SEEK_CUR);//if seeking isnt supported the internal read buffer will be used + }else{ + $pos=strlen($this->readBuffer); + $result=$this->readBuffer; + } + } while($count>$pos){ $data=fread($this->source,8192); $pos+=8192; - $result.=OC_Crypt::decrypt($data); + if(strlen($data)){ + $result.=OC_Crypt::decrypt($data); + } } - return substr($result,$offset,$count); + if(!$this->meta['seekable']){ + $this->readBuffer=substr($result,$count); + } + return substr($result,0,$count); } public function stream_write($data){ @@ -119,6 +147,9 @@ class OC_CryptStream{ } public function stream_close(){ + if(OC_FileCache::inCache($this->path)){ + OC_FileCache::put($this->path,array('encrypted'=>true)); + } return fclose($this->source); } } \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 053ac786c3..173aea785e 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -26,38 +26,98 @@ */ class OC_FileProxy_Encryption extends OC_FileProxy{ + private static $blackList=null; //mimetypes blacklisted from encryption + private static $metaData=array(); //metadata cache + + /** + * check if a file should be encrypted during write + * @param string $path + * @return bool + */ + private static function shouldEncrypt($path){ + if(is_null(self::$blackList)){ + self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); + } + if(isset(self::$metaData[$path])){ + $metadata=self::$metaData[$path]; + }else{ + $metadata=OC_FileCache::get($path); + self::$metaData[$path]=$metadata; + } + if($metadata['encrypted']){ + return true; + } + $extention=substr($path,strrpos($path,'.')+1); + if(array_search($extention,self::$blackList)===false){ + return true; + } + } + + /** + * check if a file is encrypted + * @param string $path + * @return bool + */ + private static function isEncrypted($path){ + if(isset(self::$metaData[$path])){ + $metadata=self::$metaData[$path]; + }else{ + $metadata=OC_FileCache::get($path); + self::$metaData[$path]=$metadata; + } + return (bool)$metadata['encrypted']; + } + public function preFile_put_contents($path,&$data){ - if(substr($path,-4)=='.enc'){ + if(self::shouldEncrypt($path)){ + $exists=OC_Filesystem::file_exists($path); + $target=fopen('crypt://'.$path,'w'); if (is_resource($data)) { - $newData=''; while(!feof($data)){ - $block=fread($data,8192); - $newData.=OC_Crypt::encrypt($block); + fwrite($target,fread($data,8192)); } - $data=$newData; }else{ - $data=OC_Crypt::blockEncrypt($data); + fwrite($target,$data); } + //fake the normal hooks + OC_Hook::emit( 'OC_Filesystem', 'post_create', array( 'path' => $path)); + OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path)); + return false; } } public function postFile_get_contents($path,$data){ - if(substr($path,-4)=='.enc'){ - return OC_Crypt::blockDecrypt($data); + if(self::isEncrypted($path)){ + $data=OC_Crypt::blockDecrypt($data); } + return $data; } public function postFopen($path,&$result){ - if(substr($path,-4)=='.enc'){ - $meta=stream_get_meta_data($result); + if(!$result){ + return $result; + } + $meta=stream_get_meta_data($result); + if(self::isEncrypted($path)){ fclose($result); - OC_log::write('file_encryption','mode: '.$meta['mode']); + $result=fopen('crypt://'.$path,$meta['mode']); + }elseif(self::shouldEncrypt($path) and $meta['mode']!='r'){ + if(OC_Filesystem::file_exists($path)){ + //first encrypt the target file so we don't end up with a half encrypted file + OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG); + if($result){ + fclose($result); + } + $tmpFile=OC_Filesystem::toTmpFile($path); + OC_Filesystem::fromTmpFile($tmpFile,$path); + } $result=fopen('crypt://'.$path,$meta['mode']); } + return $result; } public function preReadFile($path){ - if(substr($path,-4)=='.enc'){ + if(self::isEncrypted($path)){ $stream=fopen('crypt://'.$path,'r'); while(!feof($stream)){ print(fread($stream,8192)); @@ -65,11 +125,4 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ return false;//cancel the original request } } - - public function postGetMimeType($path,$result){ - if(substr($path,-4)=='.enc'){ - return 'text/plain'; - } - return $result; - } } diff --git a/lib/user.php b/lib/user.php index 34f44f572e..aa828de52f 100644 --- a/lib/user.php +++ b/lib/user.php @@ -195,8 +195,8 @@ class OC_User { if( $run ){ $uid=self::checkPassword( $uid, $password ); if($uid){ - OC_Crypt::init($uid,$password); return self::setUserId($uid); + OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password )); } } return false; @@ -209,7 +209,6 @@ class OC_User { */ public static function setUserId($uid) { $_SESSION['user_id'] = $uid; - OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid )); return true; } From 501678f981cf8e320d11cf1a780aefeae1537050 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 21:45:41 +0100 Subject: [PATCH 021/193] always mount the root filesystem, sometimes we need the filesystem when not logged in --- lib/util.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util.php b/lib/util.php index 1b1e29b674..3329789de9 100644 --- a/lib/util.php +++ b/lib/util.php @@ -35,9 +35,9 @@ class OC_Util { $user = OC_User::getUser(); } + //first set up the local "root" storage + OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem - //first set up the local "root" storage - OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root"; if( !is_dir( OC::$CONFIG_DATADIRECTORY )){ From b3a974d8bbcd88602a54cf32cd472802583ab4aa Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 21:49:22 +0100 Subject: [PATCH 022/193] only trigger hooks for the default filesystem view --- apps/files_encryption/lib/crypt.php | 41 ++++++++++++++--------------- lib/filesystemview.php | 22 +++++++++------- lib/user.php | 3 ++- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 4e7739b1d0..0a593b98c4 100644 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -44,19 +44,20 @@ class OC_Crypt { } public static function init($login,$password) { - if(OC_User::isLoggedIn()){ - $view=new OC_FilesystemView('/'.$login); - if(!$view->file_exists('/encryption.key')){// does key exist? - OC_Crypt::createkey($password); - } - $key=$view->file_get_contents('/encryption.key'); - $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); + $view=new OC_FilesystemView('/'.$login); + OC_FileProxy::$enabled=false; + if(!$view->file_exists('/encryption.key')){// does key exist? + OC_Crypt::createkey($login,$password); } + $key=$view->file_get_contents('/encryption.key'); + OC_FileProxy::$enabled=true; + $_SESSION['enckey']=OC_Crypt::decrypt($key, $password); } /** * get the blowfish encryption handeler for a key * @param string $key (optional) + * @return Crypt_Blowfish * * if the key is left out, the default handeler will be used */ @@ -74,21 +75,19 @@ class OC_Crypt { } } - public static function createkey($passcode) { - if(OC_User::isLoggedIn()){ - // generate a random key - $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999); + public static function createkey($username,$passcode) { + // generate a random key + $key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999); - // encrypt the key with the passcode of the user - $enckey=OC_Crypt::encrypt($key,$passcode); + // encrypt the key with the passcode of the user + $enckey=OC_Crypt::encrypt($key,$passcode); - // Write the file - $username=OC_USER::getUser(); - OC_FileProxy::$enabled=false; - $view=new OC_FilesystemView('/'.$username); - $view->file_put_contents('/encryption.key',$enckey); - OC_FileProxy::$enabled=true; - } + // Write the file + $proxyEnabled=OC_FileProxy::$enabled; + OC_FileProxy::$enabled=false; + $view=new OC_FilesystemView('/'.$username); + $view->file_put_contents('/encryption.key',$enckey); + OC_FileProxy::$enabled=$proxyEnabled; } public static function changekeypasscode($oldPassword, $newPassword) { @@ -133,7 +132,7 @@ class OC_Crypt { */ public static function decrypt( $content, $key='') { $bf = self::getBlowfish($key); - return($bf->encrypt($contents)); + return($bf->decrypt($content)); } /** diff --git a/lib/filesystemview.php b/lib/filesystemview.php index a7003e84f1..0ce803be2b 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -303,11 +303,13 @@ class OC_FilesystemView { if(OC_FileProxy::runPreProxies($operation,$path, $extraParam) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ $interalPath=$this->getInternalPath($path); $run=true; - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - }else{ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); + }else{ + OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + } } } if($run){ @@ -317,10 +319,12 @@ class OC_FilesystemView { $result=$storage->$operation($interalPath); } $result=OC_FileProxy::runPostProxies($operation,$path,$result); - if($operation!='fopen'){//no post hooks for fopen, the file stream is still open - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ + if($operation!='fopen'){//no post hooks for fopen, the file stream is still open + foreach($hooks as $hook){ + if($hook!='read'){ + OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + } } } } diff --git a/lib/user.php b/lib/user.php index aa828de52f..826215b228 100644 --- a/lib/user.php +++ b/lib/user.php @@ -195,8 +195,9 @@ class OC_User { if( $run ){ $uid=self::checkPassword( $uid, $password ); if($uid){ - return self::setUserId($uid); + self::setUserId($uid); OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid, 'password'=>$password )); + return true; } } return false; From 6658f510986aff0e41fee37319a1b0eefa98a4d0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 5 Feb 2012 23:49:22 +0100 Subject: [PATCH 023/193] provide early file system when using webdav --- lib/connector/sabre/auth.php | 1 + lib/util.php | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php index 1e87c7cee0..8964ef7d0d 100644 --- a/lib/connector/sabre/auth.php +++ b/lib/connector/sabre/auth.php @@ -23,6 +23,7 @@ class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic { * @return bool */ protected function validateUserPass($username, $password){ + OC_Util::setUpFS();//login hooks may need early access to the filesystem if(OC_User::login($username,$password)){ OC_Util::setUpFS(); return true; diff --git a/lib/util.php b/lib/util.php index 3329789de9..18a5db3e4e 100644 --- a/lib/util.php +++ b/lib/util.php @@ -8,6 +8,7 @@ class OC_Util { public static $scripts=array(); public static $styles=array(); public static $headers=array(); + private static $rootMounted=false; private static $fsSetup=false; // Can be set up @@ -36,7 +37,10 @@ class OC_Util { } //first set up the local "root" storage - OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); + if(!self::$rootMounted){ + OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY_ROOT),'/'); + self::$rootMounted=true; + } if( $user != "" ){ //if we aren't logged in, there is no use to set up the filesystem OC::$CONFIG_DATADIRECTORY = $CONFIG_DATADIRECTORY_ROOT."/$user/$root"; From 1cffeefa069075054f9c2f676b84ddc02b56232c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 11 Feb 2012 15:48:31 +0100 Subject: [PATCH 024/193] move implementation of from/toTmpFile from the file storage to the filesystem --- lib/filestorage.php | 2 -- lib/filestorage/local.php | 22 ---------------------- lib/filesystemview.php | 34 +++++++++++++++------------------- 3 files changed, 15 insertions(+), 43 deletions(-) diff --git a/lib/filestorage.php b/lib/filestorage.php index 4523144f6f..d420427225 100644 --- a/lib/filestorage.php +++ b/lib/filestorage.php @@ -45,8 +45,6 @@ class OC_Filestorage{ public function rename($path1,$path2){} public function copy($path1,$path2){} public function fopen($path,$mode){} - public function toTmpFile($path){}//copy the file to a temporary file, used for cross-storage file actions - public function fromTmpFile($tmpPath,$path){}//copy a file from a temporary file, used for cross-storage file actions public function getMimeType($path){} public function hash($type,$path,$raw){} public function free_space($path){} diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index ee4b267bcd..de1f83e3e1 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -171,28 +171,6 @@ class OC_Filestorage_Local extends OC_Filestorage{ } } - public function toTmpFile($path){ - $tmpFolder=get_temp_dir(); - $filename=tempnam($tmpFolder,'OC_TEMP_FILE_'.substr($path,strrpos($path,'.'))); - $fileStats = stat($this->datadir.$path); - if(copy($this->datadir.$path,$filename)){ - touch($filename, $fileStats['mtime'], $fileStats['atime']); - return $filename; - }else{ - return false; - } - } - - public function fromTmpFile($tmpFile,$path){ - $fileStats = stat($tmpFile); - if(rename($tmpFile,$this->datadir.$path)){ - touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']); - return true; - }else{ - return false; - } - } - private function delTree($dir) { $dirRelative=$dir; $dir=$this->datadir.$dir; diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 0ce803be2b..592fd972a7 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -254,29 +254,25 @@ class OC_FilesystemView { return $this->basicOperation('fopen',$path,$hooks,$mode); } public function toTmpFile($path){ - if(OC_FileProxy::runPreProxies('toTmpFile',$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_read, array( OC_Filesystem::signal_param_path => $path)); - return $storage->toTmpFile($this->getInternalPath($path)); + if(OC_Filesystem::isValidPath($path)){ + $source=$this->fopen($path,'r'); + $tmpFile=tempnam(get_temp_dir(),'OC_TMP_').substr($path,strrpos($path,'.')); + if($source){ + return file_put_contents($tmpFile,$source); + } } } public function fromTmpFile($tmpFile,$path){ - if(OC_FileProxy::runPreProxies('copy',$tmpFile,$path) and OC_Filesystem::isValidPath($path) and $storage=$this->getStorage($path)){ - $run=true; - $exists=$this->file_exists($path); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - } - if($run){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - } - if($run){ - $result=$storage->fromTmpFile($tmpFile,$this->getInternalPath($path)); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path)); - } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); - return $result; + if(OC_Filesystem::isValidPath($path)){ + $source=fopen($tmpFile,'r'); + if($source){ + $this->file_put_contents($path,$source); + unlink($tmpFile); + return true; + }else{ } + }else{ + error_log('invalid path'); } } From 95459d068e47b83ed13348da3e1d03d062209e41 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 11 Feb 2012 16:06:34 +0100 Subject: [PATCH 025/193] non existing files can never be updated --- lib/filecache.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/filecache.php b/lib/filecache.php index 6ae2f8253d..5d299ff24e 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -515,6 +515,9 @@ class OC_FileCache{ } $view=new OC_FilesystemView($root); } + if(!$view->file_exists($path)){ + return false; + } $mtime=$view->filemtime($path); $isDir=$view->is_dir($path); $path=$root.$path; From 6a8364c3ffeb9d6227f141ae77ba317a1afbfdf6 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 12 Feb 2012 15:56:32 +0100 Subject: [PATCH 026/193] rework the way file_put_contents is handeled --- apps/files_encryption/lib/cryptstream.php | 1 + apps/files_encryption/lib/proxy.php | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 00dda7352b..97e0846187 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -90,6 +90,7 @@ class OC_CryptStream{ } public function stream_write($data){ + error_log('write to '. $this->path); $length=strlen($data); $written=0; $currentPos=ftell($this->source); diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 173aea785e..be6ffa4f44 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -70,19 +70,13 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function preFile_put_contents($path,&$data){ if(self::shouldEncrypt($path)){ - $exists=OC_Filesystem::file_exists($path); - $target=fopen('crypt://'.$path,'w'); if (is_resource($data)) { - while(!feof($data)){ - fwrite($target,fread($data,8192)); - } + $id=md5($path); + OC_CryptStream::$sourceStreams[$id]=array('path'=>$path,'stream'=>$data); + $data=fopen('crypt://streams/'.$id,'r'); }else{ - fwrite($target,$data); + $data=OC_Crypt::blockEncrypt($data); } - //fake the normal hooks - OC_Hook::emit( 'OC_Filesystem', 'post_create', array( 'path' => $path)); - OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path)); - return false; } } From c121a1a1e7d9ae554005b8438d8ad999cdacc601 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Feb 2012 16:23:00 +0100 Subject: [PATCH 027/193] implement file_put_contents with stream data using fopen --- apps/files_encryption/lib/proxy.php | 6 +----- lib/filesystemview.php | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index be6ffa4f44..7974d68d48 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -70,11 +70,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function preFile_put_contents($path,&$data){ if(self::shouldEncrypt($path)){ - if (is_resource($data)) { - $id=md5($path); - OC_CryptStream::$sourceStreams[$id]=array('path'=>$path,'stream'=>$data); - $data=fopen('crypt://streams/'.$id,'r'); - }else{ + if (!is_resource($data)) {//stream put contents should have been converter to fopen $data=OC_Crypt::blockEncrypt($data); } } diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 592fd972a7..58d5b3af71 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -163,7 +163,21 @@ class OC_FilesystemView { return $this->basicOperation('file_get_contents',$path,array('read')); } public function file_put_contents($path,$data){ - return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + if(is_resource($data)){//not having to deal with streams in file_put_contents makes life easier + $target=$this->fopen($path,'w'); + if($target){ + while(!feof($data)){ + fwrite($target,fread($data,8192)); + } + fclose($target); + fclose($data); + return true; + }else{ + return false; + } + }else{ + return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + } } public function unlink($path){ return $this->basicOperation('unlink',$path,array('delete')); From 325858e9e239b726a207ac150404863df509935c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Feb 2012 20:19:48 +0100 Subject: [PATCH 028/193] add stream wrapper for in-memory files and dont use global variables for the fakedir stream wrapper --- apps/files_sharing/sharedstorage.php | 3 +- lib/base.php | 2 +- lib/fakedirstream.php | 45 ------ lib/streamwrappers.php | 223 +++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 48 deletions(-) delete mode 100644 lib/fakedirstream.php create mode 100644 lib/streamwrappers.php diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index cb641e68a8..07653fc11b 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -79,7 +79,6 @@ class OC_Filestorage_Shared extends OC_Filestorage { if ($path == "" || $path == "/") { $path = $this->datadir.$path; $sharedItems = OC_Share::getItemsInFolder($path); - global $FAKEDIRS; $files = array(); foreach ($sharedItems as $item) { // If item is in the root of the shared storage provider and the item exists add it to the fakedirs @@ -87,7 +86,7 @@ class OC_Filestorage_Shared extends OC_Filestorage { $files[] = basename($item['target']); } } - $FAKEDIRS['shared'] = $files; + OC_FakeDirStream::$dirs['shared']=$files; return opendir('fakedir://shared'); } else { $source = $this->getSource($path); diff --git a/lib/base.php b/lib/base.php index 5b6ed9bcb1..94e4907fed 100644 --- a/lib/base.php +++ b/lib/base.php @@ -279,7 +279,7 @@ if(!function_exists('get_temp_dir')) { OC::init(); -require_once('fakedirstream.php'); +require_once('streamwrappers.php'); diff --git a/lib/fakedirstream.php b/lib/fakedirstream.php deleted file mode 100644 index fa3e64da62..0000000000 --- a/lib/fakedirstream.php +++ /dev/null @@ -1,45 +0,0 @@ -name=substr($path,strlen('fakedir://')); - $this->index=0; - if(isset($FAKEDIRS[$this->name])){ - $this->data=$FAKEDIRS[$this->name]; - }else{ - $this->data=array(); - } - return true; - } - - public function dir_readdir(){ - if($this->index>=count($this->data)){ - return false; - } - $filename=$this->data[$this->index]; - $this->index++; - return $filename; - } - - public function dir_closedir() { - $this->data=false; - $this->name=''; - return true; - } - - public function dir_rewinddir() { - $this->index=0; - return true; - } -} - -stream_wrapper_register("fakedir", "fakeDirStream"); - diff --git a/lib/streamwrappers.php b/lib/streamwrappers.php new file mode 100644 index 0000000000..1454a99e8d --- /dev/null +++ b/lib/streamwrappers.php @@ -0,0 +1,223 @@ +name=substr($path,strlen('fakedir://')); + $this->index=0; + if(!isset(self::$dirs[$this->name])){ + self::$dirs[$this->name]=array(); + } + return true; + } + + public function dir_readdir(){ + if($this->index>=count(self::$dirs[$this->name])){ + return false; + } + $filename=self::$dirs[$this->name][$this->index]; + $this->index++; + return $filename; + } + + public function dir_closedir() { + $this->name=''; + return true; + } + + public function dir_rewinddir() { + $this->index=0; + return true; + } +} + +class OC_StaticStreamWrapper { + public $context; + protected static $data = array(); + + protected $path = ''; + protected $pointer = 0; + protected $writable = false; + + public function stream_close() {} + + public function stream_eof() { + return $this->pointer >= strlen(self::$data[$this->path]); + } + + public function stream_flush() {} + + public function stream_open($path, $mode, $options, &$opened_path) { + switch ($mode[0]) { + case 'r': + if (!isset(self::$data[$path])) return false; + $this->path = $path; + $this->writable = isset($mode[1]) && $mode[1] == '+'; + break; + case 'w': + self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + break; + case 'a': + if (!isset(self::$data[$path])) self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + $this->pointer = strlen(self::$data[$path]); + break; + case 'x': + if (isset(self::$data[$path])) return false; + $this->path = $path; + $this->writable = true; + break; + case 'c': + if (!isset(self::$data[$path])) self::$data[$path] = ''; + $this->path = $path; + $this->writable = true; + break; + default: + return false; + } + $opened_path = $this->path; + return true; + } + + public function stream_read($count) { + $bytes = min(strlen(self::$data[$this->path]) - $this->pointer, $count); + $data = substr(self::$data[$this->path], $this->pointer, $bytes); + $this->pointer += $bytes; + return $data; + } + + public function stream_seek($offset, $whence = SEEK_SET) { + $len = strlen(self::$data[$this->path]); + switch ($whence) { + case SEEK_SET: + if ($offset <= $len) { + $this->pointer = $offset; + return true; + } + break; + case SEEK_CUR: + if ($this->pointer + $offset <= $len) { + $this->pointer += $offset; + return true; + } + break; + case SEEK_END: + if ($len + $offset <= $len) { + $this->pointer = $len + $offset; + return true; + } + break; + } + return false; + } + + public function stream_stat() { + $size = strlen(self::$data[$this->path]); + $time = time(); + return array( + 0 => 0, + 'dev' => 0, + 1 => 0, + 'ino' => 0, + 2 => 0777, + 'mode' => 0777, + 3 => 1, + 'nlink' => 1, + 4 => 0, + 'uid' => 0, + 5 => 0, + 'gid' => 0, + 6 => '', + 'rdev' => '', + 7 => $size, + 'size' => $size, + 8 => $time, + 'atime' => $time, + 9 => $time, + 'mtime' => $time, + 10 => $time, + 'ctime' => $time, + 11 => -1, + 'blksize' => -1, + 12 => -1, + 'blocks' => -1, + ); + } + + public function stream_tell() { + return $this->pointer; + } + + public function stream_write($data) { + if (!$this->writable) return 0; + $size = strlen($data); + $len = strlen(self::$data[$this->path]); + if ($this->stream_eof()) { + self::$data[$this->path] .= $data; + } else { + self::$data[$this->path] = substr_replace( + self::$data[$this->path], + $data, + $this->pointer + ); + } + $this->pointer += $size; + return $size; + } + + public function unlink($path) { + if (isset(self::$data[$path])) { + unset(self::$data[$path]); + } + return true; + } + + public function url_stat($path) { + if (isset(self::$data[$path])) { + $size = strlen(self::$data[$path]); + $time = time(); + return array( + 0 => 0, + 'dev' => 0, + 1 => 0, + 'ino' => 0, + 2 => 0777, + 'mode' => 0777, + 3 => 1, + 'nlink' => 1, + 4 => 0, + 'uid' => 0, + 5 => 0, + 'gid' => 0, + 6 => '', + 'rdev' => '', + 7 => $size, + 'size' => $size, + 8 => $time, + 'atime' => $time, + 9 => $time, + 'mtime' => $time, + 10 => $time, + 'ctime' => $time, + 11 => -1, + 'blksize' => -1, + 12 => -1, + 'blocks' => -1, + ); + } + return false; + } +} + +stream_wrapper_register("fakedir", "OC_FakeDirStream"); +stream_wrapper_register('static', 'OC_StaticStreamWrapper'); From d9c7e4c333f858efaaee35d26ea12733d29bd694 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 15 Feb 2012 21:44:58 +0100 Subject: [PATCH 029/193] proper mimetypes for encrypted files --- apps/files_encryption/lib/proxy.php | 4 ++ lib/filestorage/local.php | 47 ++--------------------- lib/helper.php | 58 +++++++++++++++++++++++++++++ tests/lib/streamwrappers.php | 47 +++++++++++++++++++++++ 4 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 tests/lib/streamwrappers.php diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 7974d68d48..c53d567ad1 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -115,4 +115,8 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ return false;//cancel the original request } } + + public function postGetMimeType($path,$mime){ + return OC_Helper::getMimeType('crypt://'.$path,'w'); + } } diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php index de1f83e3e1..6f4f68c503 100644 --- a/lib/filestorage/local.php +++ b/lib/filestorage/local.php @@ -122,50 +122,9 @@ class OC_Filestorage_Local extends OC_Filestorage{ return $return; } - public function getMimeType($fspath){ - if($this->is_readable($fspath)){ - $mimeType='application/octet-stream'; - if ($mimeType=='application/octet-stream') { - self::$mimetypes = include('mimetypes.fixlist.php'); - $extention=strtolower(strrchr(basename($fspath), ".")); - $extention=substr($extention,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; - - } - if (@is_dir($this->datadir.$fspath)) { - // directories are easy - return "httpd/unix-directory"; - } - if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ - $mimeType =strtolower(finfo_file($finfo,$this->datadir.$fspath)); - $mimeType=substr($mimeType,0,strpos($mimeType,';')); - finfo_close($finfo); - } - if ($mimeType=='application/octet-stream' && function_exists("mime_content_type")) { - // use mime magic extension if available - $mimeType = mime_content_type($this->datadir.$fspath); - } - if ($mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) { - // it looks like we have a 'file' command, - // lets see it it does have mime support - $fspath=str_replace("'","\'",$fspath); - $fp = popen("file -i -b '{$this->datadir}$fspath' 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,' ')); - } - if ($mimeType=='application/octet-stream') { - // Fallback solution: (try to guess the type by the file extension - if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ - self::$mimetypes=include('mimetypes.list.php'); - } - $extention=strtolower(strrchr(basename($fspath), ".")); - $extention=substr($extention,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; - } - return $mimeType; + public function getMimeType($path){ + if($this->is_readable($path)){ + return OC_Helper::getMimeType($this->datadir.$path); }else{ return false; } diff --git a/lib/helper.php b/lib/helper.php index 2f71bdad2d..6dea4a6a8c 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -25,6 +25,8 @@ * Collection of useful functions */ class OC_Helper { + private static $mimetypes=array(); + /** * @brief Creates an url * @param $app app @@ -267,6 +269,62 @@ class OC_Helper { unlink($dir); } } + + /** + * get the mimetype form a local file + * @param string path + * @return string + * 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://'); + $mimeType='application/octet-stream'; + if ($mimeType=='application/octet-stream') { + if(count(self::$mimetypes)>0){ + self::$mimetypes = include('mimetypes.fixlist.php'); + } + $extention=strtolower(strrchr(basename($path), ".")); + $extention=substr($extention,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; + + } + if (@is_dir($path)) { + // directories are easy + return "httpd/unix-directory"; + } + if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ + $info = @strtolower(finfo_file($finfo,$path)); + if($info){ + $mimeType=substr($info,0,strpos($info,';')); + } + finfo_close($finfo); + } + if (!$isWrapped and $mimeType=='application/octet-stream' && function_exists("mime_content_type")) { + // use mime magic extension if available + $mimeType = mime_content_type($path); + } + 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 + $path=str_replace("'","\'",$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,' ')); + } + if ($mimeType=='application/octet-stream') { + // Fallback solution: (try to guess the type by the file extension + if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ + self::$mimetypes=include('mimetypes.list.php'); + } + $extention=strtolower(strrchr(basename($path), ".")); + $extention=substr($extention,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream'; + } + return $mimeType; + } /** * @brief Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d. diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php new file mode 100644 index 0000000000..c4784a6297 --- /dev/null +++ b/tests/lib/streamwrappers.php @@ -0,0 +1,47 @@ +. +* +*/ + +class Test_StreamWrappers extends UnitTestCase { + public function testFakeDir(){ + $items=array('foo','bar'); + OC_FakeDirStream::$dirs['test']=$items; + $dh=opendir('fakedir://test'); + $result=array(); + while($file=readdir($dh)){ + $result[]=$file; + $this->assertNotIdentical(false,array_search($file,$items)); + } + $this->assertEqual(count($items),count($result)); + } + + public function testStaticStream(){ + $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $staticFile='static://test'; + $this->assertFalse(file_exists($staticFile)); + file_put_contents($staticFile,file_get_contents($sourceFile)); + $this->assertTrue(file_exists($staticFile)); + $this->assertEqual(file_get_contents($sourceFile),file_get_contents($staticFile)); + unlink($staticFile); + clearstatcache(); + $this->assertFalse(file_exists($staticFile)); + } +} \ No newline at end of file From c20319d37701efb9d32c38dd71880739ca75b33f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 21 Feb 2012 20:48:14 +0100 Subject: [PATCH 030/193] fix incorrect information in the filecache when using encryption --- apps/files_encryption/lib/cryptstream.php | 5 +-- apps/files_encryption/lib/proxy.php | 31 ++++++++++-------- apps/media/lib_media.php | 1 + files/ajax/upload.php | 3 +- lib/filecache.php | 40 ++++++++++++++++++++--- lib/filesystemview.php | 6 +++- 6 files changed, 62 insertions(+), 24 deletions(-) diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 97e0846187..86583096f1 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -90,7 +90,6 @@ class OC_CryptStream{ } public function stream_write($data){ - error_log('write to '. $this->path); $length=strlen($data); $written=0; $currentPos=ftell($this->source); @@ -148,9 +147,7 @@ class OC_CryptStream{ } public function stream_close(){ - if(OC_FileCache::inCache($this->path)){ - OC_FileCache::put($this->path,array('encrypted'=>true)); - } + OC_FileCache::put($this->path,array('encrypted'=>true)); return fclose($this->source); } } \ No newline at end of file diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index c53d567ad1..ed3907cccf 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -38,13 +38,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ if(is_null(self::$blackList)){ self::$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); } - if(isset(self::$metaData[$path])){ - $metadata=self::$metaData[$path]; - }else{ - $metadata=OC_FileCache::get($path); - self::$metaData[$path]=$metadata; - } - if($metadata['encrypted']){ + if(self::isEncrypted($path)){ return true; } $extention=substr($path,strrpos($path,'.')+1); @@ -62,7 +56,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ if(isset(self::$metaData[$path])){ $metadata=self::$metaData[$path]; }else{ - $metadata=OC_FileCache::get($path); + $metadata=OC_FileCache::getCached($path); self::$metaData[$path]=$metadata; } return (bool)$metadata['encrypted']; @@ -92,14 +86,19 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ fclose($result); $result=fopen('crypt://'.$path,$meta['mode']); }elseif(self::shouldEncrypt($path) and $meta['mode']!='r'){ - if(OC_Filesystem::file_exists($path)){ + if(OC_Filesystem::file_exists($path) and OC_Filesystem::filesize($path)>0){ //first encrypt the target file so we don't end up with a half encrypted file OC_Log::write('files_encryption','Decrypting '.$path.' before writing',OC_Log::DEBUG); - if($result){ - fclose($result); + $tmp=fopen('php://temp'); + while(!feof($result)){ + $chunk=fread($result,8192); + if($chunk){ + fwrite($tmp,$chunk); + } } - $tmpFile=OC_Filesystem::toTmpFile($path); - OC_Filesystem::fromTmpFile($tmpFile,$path); + fclose($result); + OC_Filesystem::file_put_contents($path,$tmp); + fclose($tmp); } $result=fopen('crypt://'.$path,$meta['mode']); } @@ -117,6 +116,10 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ } public function postGetMimeType($path,$mime){ - return OC_Helper::getMimeType('crypt://'.$path,'w'); + if((!OC_FileCache::inCache($path) and self::shouldEncrypt($path)) or self::isEncrypted($path)){ + return OC_Helper::getMimeType('crypt://'.$path,'w'); + }else{ + return $mime; + } } } diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php index 1bcd0f08c8..a2109c151a 100644 --- a/apps/media/lib_media.php +++ b/apps/media/lib_media.php @@ -56,6 +56,7 @@ class OC_MEDIA{ */ public static function updateFile($params){ $path=$params['path']; + if(!$path) return; require_once 'lib_scanner.php'; require_once 'lib_collection.php'; //fix a bug where there were multiply '/' in front of the path, it should only be one diff --git a/files/ajax/upload.php b/files/ajax/upload.php index 241edc216f..034b891460 100644 --- a/files/ajax/upload.php +++ b/files/ajax/upload.php @@ -48,7 +48,8 @@ if(strpos($dir,'..') === false){ for($i=0;$i<$fileCount;$i++){ $target=stripslashes($dir) . $files['name'][$i]; if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i],$target)){ - $result[]=array( "status" => "success", 'mime'=>OC_Filesystem::getMimeType($target),'size'=>OC_Filesystem::filesize($target),'name'=>$files['name'][$i]); + $meta=OC_FileCache::getCached($target); + $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'],'name'=>$files['name'][$i]); } } OC_JSON::encodedPrint($result); diff --git a/lib/filecache.php b/lib/filecache.php index 5d299ff24e..83eaffb171 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -28,6 +28,8 @@ * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache */ class OC_FileCache{ + private static $savedData=array(); + /** * get the filesystem info from the cache * @param string path @@ -93,6 +95,14 @@ class OC_FileCache{ self::update($id,$data); return; } + if(isset(self::$savedData[$path])){ + $data=array_merge($data,self::$savedData[$path]); + unset(self::$savedData[$path]); + } + if(!isset($data['size']) or !isset($data['mtime'])){//save incomplete data for the next time we write it + self::$savedData[$path]=$data; + return; + } if(!isset($data['encrypted'])){ $data['encrypted']=false; } @@ -101,9 +111,8 @@ class OC_FileCache{ } $mimePart=dirname($data['mimetype']); $user=OC_User::getUser(); - $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable) VALUES(?,?,?,?,?,?,?,?,?,?)'); - $query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'])); - + $query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)'); + $query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned'])); } /** @@ -323,7 +332,29 @@ class OC_FileCache{ } self::increaseSize(dirname($fullPath),$size-$cachedSize); } - + + public static function getCached($path,$root=''){ + if(!$root){ + $root=OC_Filesystem::getRoot(); + }else{ + if($root=='/'){ + $root=''; + } + } + $path=$root.$path; + $query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?'); + $result=$query->execute(array($path))->fetchRow(); + if(is_array($result)){ + if(isset(self::$savedData[$path])){ + $result=array_merge($result,self::$savedData[$path]); + } + return $result; + }else{ + OC_Log::write('get(): file not found in cache ('.$path.')','core',OC_Log::DEBUG); + return false; + } + } + private static function getCachedSize($path,$root){ if(!$root){ $root=OC_Filesystem::getRoot(); @@ -332,6 +363,7 @@ class OC_FileCache{ $root=''; } } + $path=$root.$path; $query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?'); $result=$query->execute(array($path)); if($row=$result->fetchRow()){ diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 58d5b3af71..c4d5ff3514 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -171,6 +171,7 @@ class OC_FilesystemView { } fclose($target); fclose($data); + OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); return true; }else{ return false; @@ -278,6 +279,9 @@ class OC_FilesystemView { } public function fromTmpFile($tmpFile,$path){ if(OC_Filesystem::isValidPath($path)){ + if(!$tmpFile){ + debug_print_backtrace(); + } $source=fopen($tmpFile,'r'); if($source){ $this->file_put_contents($path,$source); @@ -286,7 +290,7 @@ class OC_FilesystemView { }else{ } }else{ - error_log('invalid path'); + return false; } } From faf6055baa224fbf4248a70d28ae4416c9c7eb1c Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 22 Feb 2012 14:19:23 +0100 Subject: [PATCH 031/193] Translate and fix copy/paste error. --- apps/contacts/ajax/loadphoto.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php index 358e046942..1f4cde0b53 100644 --- a/apps/contacts/ajax/loadphoto.php +++ b/apps/contacts/ajax/loadphoto.php @@ -18,8 +18,6 @@ * You should have received a copy of the GNU Affero General Public * License along with this library. If not, see . * - * TODO: Translatable strings. - * Remember to delete tmp file at some point. */ // Init owncloud require_once('../../../lib/base.php'); @@ -33,7 +31,7 @@ OC_JSON::checkAppEnabled('contacts'); function bailOut($msg) { OC_JSON::error(array('data' => array('message' => $msg))); - OC_Log::write('contacts','ajax/savecrop.php: '.$msg, OC_Log::DEBUG); + OC_Log::write('contacts','ajax/loadphoto.php: '.$msg, OC_Log::DEBUG); exit(); } @@ -42,7 +40,7 @@ $image = null; $id = isset($_GET['id']) ? $_GET['id'] : ''; if($id == '') { - bailOut('Missing contact id.'); + bailOut(OC_Contacts_App::$l10n->t('Missing contact id.')); } $tmpl = new OC_TEMPLATE("contacts", "part.contactphoto"); From ed0c99ef149d63a350c7ffcb87d398b90ed3b0a2 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 22 Feb 2012 15:18:22 +0100 Subject: [PATCH 032/193] make sure we always have the encryption key unlocked --- apps/files_encryption/appinfo/app.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index 82d2544dd1..23f3955aa4 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -9,3 +9,9 @@ OC_FileProxy::register(new OC_FileProxy_Encryption()); OC_Hook::connect('OC_User','post_login','OC_Crypt','loginListener'); stream_wrapper_register('crypt','OC_CryptStream'); + +if(!isset($_SESSION['enckey']) and OC_User::isLoggedIn()){//force the user to re-loggin if the encryption key isn't unlocked (happens when a user is logged in before the encryption app is enabled) + OC_User::logout(); + header("Location: ".OC::$WEBROOT.'/'); + exit(); +} From 6c6b570ff15cfa9da6b1ab8b7e56ef0a82c96086 Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 22 Feb 2012 18:04:21 +0000 Subject: [PATCH 033/193] avoid notice in error log from user_webfinger app --- apps/user_webfinger/host-meta.php | 2 +- apps/user_webfinger/webfinger.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php index ceb15f22da..dbb4377e8c 100644 --- a/apps/user_webfinger/host-meta.php +++ b/apps/user_webfinger/host-meta.php @@ -10,7 +10,7 @@ echo "<"; ?xml version="1.0" encoding="UTF-8"?> - + diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index d695a833f3..a953718201 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -22,7 +22,7 @@ if($_GET['q']) { if(substr($userName, 0, 5) == 'acct:') { $userName = substr($userName, 5); } -if($_SERVER['HTTPS']) { +if(isset($_SERVER['HTTPS'])) { $baseAddress = 'https://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; } else { $baseAddress = 'http://'.$_SERVER['SERVER_NAME'].'/apps/remoteStorage/'; From 9850820b4276b197433bb64d84ed085cdcd01e0e Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 22 Feb 2012 18:05:52 +0000 Subject: [PATCH 034/193] BearerAuth and multiple tokens support in remoteStorage app --- apps/remoteStorage/BearerAuth.php | 61 ++++++++++++++++++++++++ apps/remoteStorage/WebDAV.php | 1 + apps/remoteStorage/auth.php | 4 +- apps/remoteStorage/lib_remoteStorage.php | 32 +++++++------ apps/remoteStorage/oauth_ro_auth.php | 4 +- 5 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 apps/remoteStorage/BearerAuth.php diff --git a/apps/remoteStorage/BearerAuth.php b/apps/remoteStorage/BearerAuth.php new file mode 100644 index 0000000000..ebcf189dfb --- /dev/null +++ b/apps/remoteStorage/BearerAuth.php @@ -0,0 +1,61 @@ +httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { + + return array($user,$pass); + + } + + // Most other webservers + $auth = $this->httpRequest->getHeader('Authorization'); + + if (!$auth) return false; + + if (strpos(strtolower($auth),'bearer')!==0) return false; + + return explode(':', base64_decode(substr($auth, 7))); + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); + $this->httpResponse->sendStatus(401); + + } + +} diff --git a/apps/remoteStorage/WebDAV.php b/apps/remoteStorage/WebDAV.php index e048d19e8f..06520b4021 100644 --- a/apps/remoteStorage/WebDAV.php +++ b/apps/remoteStorage/WebDAV.php @@ -33,6 +33,7 @@ require_once('../../lib/base.php'); OC_Util::checkAppEnabled('remoteStorage'); require_once('Sabre/autoload.php'); require_once('lib_remoteStorage.php'); +require_once('BearerAuth.php'); require_once('oauth_ro_auth.php'); ini_set('default_charset', 'UTF-8'); diff --git a/apps/remoteStorage/auth.php b/apps/remoteStorage/auth.php index 85421ba3d8..75e0aac419 100644 --- a/apps/remoteStorage/auth.php +++ b/apps/remoteStorage/auth.php @@ -68,14 +68,14 @@ if(count($pathParts) == 2 && $pathParts[0] == '') { } else if($k=='redirect_uri'){ $appUrl=$v; } else if($k=='scope'){ - $category=$v; + $categories=$v; } } $currUser = OC_User::getUser(); if($currUser == $ownCloudUser) { if(isset($_POST['allow'])) { //TODO: check if this can be faked by editing the cookie in firebug! - $token=OC_remoteStorage::createCategory($appUrl, $category); + $token=OC_remoteStorage::createCategories($appUrl, $categories); header('Location: '.$_GET['redirect_uri'].'#access_token='.$token.'&token_type=bearer'); } else { echo '
      '; diff --git a/apps/remoteStorage/lib_remoteStorage.php b/apps/remoteStorage/lib_remoteStorage.php index 4f19310904..4f5c966450 100644 --- a/apps/remoteStorage/lib_remoteStorage.php +++ b/apps/remoteStorage/lib_remoteStorage.php @@ -2,11 +2,13 @@ class OC_remoteStorage { public static function getValidTokens($ownCloudUser, $category) { - $query=OC_DB::prepare("SELECT token,appUrl FROM *PREFIX*authtoken WHERE user=? AND category=? LIMIT 100"); - $result=$query->execute(array($ownCloudUser,$category)); + $query=OC_DB::prepare("SELECT token,appUrl,category FROM *PREFIX*authtoken WHERE user=? LIMIT 100"); + $result=$query->execute(array($ownCloudUser)); $ret = array(); while($row=$result->fetchRow()){ - $ret[$row['token']]=true; + if(in_array($category, explode(',', $row['category']))) { + $ret[$row['token']]=true; + } } return $ret; } @@ -19,7 +21,7 @@ class OC_remoteStorage { while($row=$result->fetchRow()){ $ret[$row['token']] = array( 'appUrl' => $row['appurl'], - 'category' => $row['category'], + 'categories' => $row['category'], ); } return $ret; @@ -30,21 +32,23 @@ class OC_remoteStorage { $query=OC_DB::prepare("DELETE FROM *PREFIX*authtoken WHERE token=? AND user=?"); $result=$query->execute(array($token,$user)); } - private static function addToken($token, $appUrl, $category){ + private static function addToken($token, $appUrl, $categories){ $user=OC_User::getUser(); $query=OC_DB::prepare("INSERT INTO *PREFIX*authtoken (`token`,`appUrl`,`user`,`category`) VALUES(?,?,?,?)"); - $result=$query->execute(array($token,$appUrl,$user,$category)); + $result=$query->execute(array($token,$appUrl,$user,$categories)); } - public static function createCategory($appUrl, $category) { + public static function createCategories($appUrl, $categories) { $token=uniqid(); - self::addToken($token, $appUrl, $category); - //TODO: input checking on $category OC_Util::setupFS(OC_User::getUser()); - $scopePathParts = array('remoteStorage', $category); - for($i=0;$i<=count($scopePathParts);$i++){ - $thisPath = '/'.implode('/', array_slice($scopePathParts, 0, $i)); - if(!OC_Filesystem::file_exists($thisPath)) { - OC_Filesystem::mkdir($thisPath); + self::addToken($token, $appUrl, $categories); + foreach($categories as $category) { + //TODO: input checking on $category + $scopePathParts = array('remoteStorage', $category); + for($i=0;$i<=count($scopePathParts);$i++){ + $thisPath = '/'.implode('/', array_slice($scopePathParts, 0, $i)); + if(!OC_Filesystem::file_exists($thisPath)) { + OC_Filesystem::mkdir($thisPath); + } } } return base64_encode('remoteStorage:'.$token); diff --git a/apps/remoteStorage/oauth_ro_auth.php b/apps/remoteStorage/oauth_ro_auth.php index 5403fbe20c..d4a5506149 100644 --- a/apps/remoteStorage/oauth_ro_auth.php +++ b/apps/remoteStorage/oauth_ro_auth.php @@ -34,7 +34,7 @@ class OC_Connector_Sabre_Auth_ro_oauth extends Sabre_DAV_Auth_Backend_AbstractBa if(in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD', 'OPTIONS'))) { OC_Util::setUpFS(); return true; - } else if(isset($this->validTokens[$password]) && $this->validTokens[$password] == $username) { + } else if(isset($this->validTokens[$password])) { OC_Util::setUpFS(); return true; } else { @@ -47,7 +47,7 @@ die('not getting in with "'.$username.'"/"'.$password.'"!'); //overwriting this to make it not automatically fail if no auth header is found: public function authenticate(Sabre_DAV_Server $server,$realm) { - $auth = new Sabre_HTTP_BasicAuth(); + $auth = new Sabre_HTTP_BearerAuth(); $auth->setHTTPRequest($server->httpRequest); $auth->setHTTPResponse($server->httpResponse); $auth->setRealm($realm); From 3f87c2cedb271d498f5e198ce90df4948bc032fa Mon Sep 17 00:00:00 2001 From: Michiel de Jong Date: Wed, 22 Feb 2012 19:05:41 +0000 Subject: [PATCH 035/193] correct header Content-Type: application/xrd+xml --- apps/user_webfinger/host-meta.php | 2 +- apps/user_webfinger/webfinger.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user_webfinger/host-meta.php b/apps/user_webfinger/host-meta.php index dbb4377e8c..ac577cf9a0 100644 --- a/apps/user_webfinger/host-meta.php +++ b/apps/user_webfinger/host-meta.php @@ -4,7 +4,7 @@ if($_SERVER['SCRIPT_NAME'] == '/.well-known/host-meta.php') { } else { header('Please-first: activate'); } -header("Content-Type: application/xml+xrd"); +header("Content-Type: application/xrd+xml"); echo "<"; ?> ?xml version="1.0" encoding="UTF-8"?> diff --git a/apps/user_webfinger/webfinger.php b/apps/user_webfinger/webfinger.php index a953718201..5c2a24aa07 100644 --- a/apps/user_webfinger/webfinger.php +++ b/apps/user_webfinger/webfinger.php @@ -4,7 +4,7 @@ if($_SERVER['SCRIPT_NAME'] == '/.well-known/webfinger.php') { } else { header('Please-first: activate'); } -// header("Content-Type: application/xml+xrd"); +header("Content-Type: application/xrd+xml"); // calculate the documentroot // modified version of the one in lib/base.php that takes the .well-known symlink into account From 8bfc73c188216f5f0cb21a8c2c30b145435d042b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 22 Feb 2012 21:38:15 +0100 Subject: [PATCH 036/193] some multiselect improvements --- core/js/multiselect.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/core/js/multiselect.js b/core/js/multiselect.js index 96fc09a075..541dddf0f7 100644 --- a/core/js/multiselect.js +++ b/core/js/multiselect.js @@ -12,6 +12,11 @@ 'minWidth': 'default;', }; $.extend(settings,options); + $.each(this.children(),function(i,option){ + if($(option).attr('selected') && settings.checked.indexOf($(option).val())==-1){ + settings.checked.push($(option).val()); + } + }); var button=$('
      '+settings.title+'â–¾
      '); var span=$(''); span.append(button); @@ -46,9 +51,11 @@ }); button.addClass('active'); event.stopPropagation(); - var options=$(this).parent().next().children().map(function(){return $(this).val();}); + var options=$(this).parent().next().children(); var list=$('