diff --git a/3rdparty/MDB2/Driver/mysql.php b/3rdparty/MDB2/Driver/mysql.php index b9b46c0d76..2cec1e9c03 100644 --- a/3rdparty/MDB2/Driver/mysql.php +++ b/3rdparty/MDB2/Driver/mysql.php @@ -794,7 +794,7 @@ class MDB2_Driver_mysql extends MDB2_Driver_Common ? 'mysql_query' : 'mysql_unbuffered_query'; $result = @$function($query, $connection); if (!$result) { - $err =& $this->raiseError(null, null, null, + $err =$this->raiseError(null, null, null, 'Could not execute statement', __FUNCTION__); return $err; } diff --git a/3rdparty/phpass/PasswordHash.php b/3rdparty/phpass/PasswordHash.php new file mode 100644 index 0000000000..12958c7f19 --- /dev/null +++ b/3rdparty/phpass/PasswordHash.php @@ -0,0 +1,253 @@ + in 2004-2006 and placed in +# the public domain. Revised in subsequent years, still public domain. +# +# There's absolutely no warranty. +# +# The homepage URL for this framework is: +# +# http://www.openwall.com/phpass/ +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime(); + if (function_exists('getmypid')) + $this->random_state .= getmypid(); + } + + function get_random_bytes($count) + { + $output = ''; + if (is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash == $stored_hash; + } +} + +?> diff --git a/3rdparty/phpass/c/Makefile b/3rdparty/phpass/c/Makefile new file mode 100644 index 0000000000..fe48917f7f --- /dev/null +++ b/3rdparty/phpass/c/Makefile @@ -0,0 +1,21 @@ +# +# Written by Solar Designer and placed in the public domain. +# See crypt_private.c for more information. +# +CC = gcc +LD = $(CC) +RM = rm -f +CFLAGS = -Wall -O2 -fomit-frame-pointer -funroll-loops +LDFLAGS = -s +LIBS = -lcrypto + +all: crypt_private-test + +crypt_private-test: crypt_private-test.o + $(LD) $(LDFLAGS) $(LIBS) crypt_private-test.o -o $@ + +crypt_private-test.o: crypt_private.c + $(CC) -c $(CFLAGS) crypt_private.c -DTEST -o $@ + +clean: + $(RM) crypt_private-test* diff --git a/3rdparty/phpass/c/crypt_private.c b/3rdparty/phpass/c/crypt_private.c new file mode 100644 index 0000000000..6abc05bc1d --- /dev/null +++ b/3rdparty/phpass/c/crypt_private.c @@ -0,0 +1,106 @@ +/* + * This code exists for the sole purpose to serve as another implementation + * of the "private" password hashing method implemened in PasswordHash.php + * and thus to confirm that these password hashes are indeed calculated as + * intended. + * + * Other uses of this code are discouraged. There are much better password + * hashing algorithms available to C programmers; one of those is bcrypt: + * + * http://www.openwall.com/crypt/ + * + * Written by Solar Designer in 2005 and placed in + * the public domain. + * + * There's absolutely no warranty. + */ + +#include +#include + +#ifdef TEST +#include +#endif + +static char *itoa64 = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void encode64(char *dst, char *src, int count) +{ + int i, value; + + i = 0; + do { + value = (unsigned char)src[i++]; + *dst++ = itoa64[value & 0x3f]; + if (i < count) + value |= (unsigned char)src[i] << 8; + *dst++ = itoa64[(value >> 6) & 0x3f]; + if (i++ >= count) + break; + if (i < count) + value |= (unsigned char)src[i] << 16; + *dst++ = itoa64[(value >> 12) & 0x3f]; + if (i++ >= count) + break; + *dst++ = itoa64[(value >> 18) & 0x3f]; + } while (i < count); +} + +char *crypt_private(char *password, char *setting) +{ + static char output[35]; + MD5_CTX ctx; + char hash[MD5_DIGEST_LENGTH]; + char *p, *salt; + int count_log2, length, count; + + strcpy(output, "*0"); + if (!strncmp(setting, output, 2)) + output[1] = '1'; + + if (strncmp(setting, "$P$", 3)) + return output; + + p = strchr(itoa64, setting[3]); + if (!p) + return output; + count_log2 = p - itoa64; + if (count_log2 < 7 || count_log2 > 30) + return output; + + salt = setting + 4; + if (strlen(salt) < 8) + return output; + + length = strlen(password); + + MD5_Init(&ctx); + MD5_Update(&ctx, salt, 8); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + + count = 1 << count_log2; + do { + MD5_Init(&ctx); + MD5_Update(&ctx, hash, MD5_DIGEST_LENGTH); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + } while (--count); + + memcpy(output, setting, 12); + encode64(&output[12], hash, MD5_DIGEST_LENGTH); + + return output; +} + +#ifdef TEST +int main(int argc, char **argv) +{ + if (argc != 3) return 1; + + puts(crypt_private(argv[1], argv[2])); + + return 0; +} +#endif diff --git a/3rdparty/phpass/test.php b/3rdparty/phpass/test.php new file mode 100644 index 0000000000..2f4a41c8c3 --- /dev/null +++ b/3rdparty/phpass/test.php @@ -0,0 +1,72 @@ +HashPassword($correct); + +print 'Hash: ' . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$wrong = 'test12346'; +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +unset($t_hasher); + +# Force the use of weaker portable hashes. +$t_hasher = new PasswordHash(8, TRUE); + +$hash = $t_hasher->HashPassword($correct); + +print 'Hash: ' . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +# A correct portable hash for 'test12345'. +# Please note the use of single quotes to ensure that the dollar signs will +# be interpreted literally. Of course, a real application making use of the +# framework won't store password hashes within a PHP source file anyway. +# We only do this for testing. +$hash = '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0'; + +print 'Hash: ' . $hash . "\n"; + +$check = $t_hasher->CheckPassword($correct, $hash); +if ($check) $ok++; +print "Check correct: '" . $check . "' (should be '1')\n"; + +$check = $t_hasher->CheckPassword($wrong, $hash); +if (!$check) $ok++; +print "Check wrong: '" . $check . "' (should be '0' or '')\n"; + +if ($ok == 6) + print "All tests have PASSED\n"; +else + print "Some tests have FAILED\n"; + +?> diff --git a/apps/bookmarks/addBm.php b/apps/bookmarks/addBm.php index f56022c42c..861b677222 100644 --- a/apps/bookmarks/addBm.php +++ b/apps/bookmarks/addBm.php @@ -28,19 +28,6 @@ OC_Util::checkLoggedIn(); OC_Util::checkAppEnabled('bookmarks'); require_once('bookmarksHelper.php'); +addBookmark($_GET['url'], '', 'Read-Later'); -OC_App::setActiveNavigationEntry( 'bookmarks_index' ); - -OC_Util::addScript('bookmarks','addBm'); -OC_Util::addStyle('bookmarks', 'bookmarks'); - -$tmpl = new OC_Template( 'bookmarks', 'addBm', 'user' ); - -$url = isset($_GET['url']) ? urldecode($_GET['url']) : ''; -$metadata = getURLMetadata($url); - -$tmpl->assign('URL', htmlentities($metadata['url'],ENT_COMPAT,'utf-8')); -$title = isset($metadata['title']) ? $metadata['title'] : (isset($_GET['title']) ? $_GET['title'] : ''); -$tmpl->assign('TITLE', htmlentities($title,ENT_COMPAT,'utf-8')); - -$tmpl->printPage(); +include 'templates/addBm.php'; diff --git a/apps/bookmarks/ajax/addBookmark.php b/apps/bookmarks/ajax/addBookmark.php index 45b16ae5fa..8cda7f0f06 100644 --- a/apps/bookmarks/ajax/addBookmark.php +++ b/apps/bookmarks/ajax/addBookmark.php @@ -30,50 +30,6 @@ require_once('../../../lib/base.php'); OC_JSON::checkLoggedIn(); OC_JSON::checkAppEnabled('bookmarks'); -$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); -if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){ - $_ut = "strftime('%s','now')"; -} elseif($CONFIG_DBTYPE == 'pgsql') { - $_ut = 'date_part(\'epoch\',now())::integer'; -} else { - $_ut = "UNIX_TIMESTAMP()"; -} - -//FIXME: Detect when user adds a known URL -$query = OC_DB::prepare(" - INSERT INTO *PREFIX*bookmarks - (url, title, user_id, public, added, lastmodified) - VALUES (?, ?, ?, 0, $_ut, $_ut) - "); - - -$params=array( - htmlspecialchars_decode($_GET["url"]), - htmlspecialchars_decode($_GET["title"]), - OC_User::getUser() - ); -$query->execute($params); - -$b_id = OC_DB::insertid('*PREFIX*bookmarks'); - - -if($b_id !== false) { - $query = OC_DB::prepare(" - INSERT INTO *PREFIX*bookmarks_tags - (bookmark_id, tag) - VALUES (?, ?) - "); - - $tags = explode(' ', urldecode($_GET["tags"])); - foreach ($tags as $tag) { - if(empty($tag)) { - //avoid saving blankspaces - continue; - } - $params = array($b_id, trim($tag)); - $query->execute($params); - } - - OC_JSON::success(array('data' => $b_id)); -} - +require_once('../bookmarksHelper.php'); +$id = addBookmark($_GET['url'], $_GET['title'], $_GET['tags']); +OC_JSON::success(array('data' => $id)); \ No newline at end of file diff --git a/apps/bookmarks/ajax/getMeta.php b/apps/bookmarks/ajax/getMeta.php deleted file mode 100644 index ca797315ef..0000000000 --- a/apps/bookmarks/ajax/getMeta.php +++ /dev/null @@ -1,39 +0,0 @@ -. -* -*/ - -//no apps or filesystem -$RUNTIME_NOSETUPFS=true; - -require_once('../../../lib/base.php'); - -// Check if we are a user -OC_JSON::checkLoggedIn(); -OC_JSON::checkAppEnabled('bookmarks'); - -// $metadata = array(); - -require '../bookmarksHelper.php'; -$metadata = getURLMetadata(htmlspecialchars_decode($_GET["url"])); - - -OC_JSON::success(array('data' => $metadata)); diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php index 479d8ed476..09d7b5df52 100644 --- a/apps/bookmarks/appinfo/app.php +++ b/apps/bookmarks/appinfo/app.php @@ -1,6 +1,6 @@ +* Copyright (c) 2011 Marvin Thomas Rabe * Copyright (c) 2011 Arthur Schiwon * This file is licensed under the Affero General Public License version 3 or * later. @@ -8,6 +8,7 @@ */ OC::$CLASSPATH['OC_Bookmarks_Bookmarks'] = 'apps/bookmarks/lib/bookmarks.php'; +OC::$CLASSPATH['OC_Search_Provider_Bookmarks'] = 'apps/bookmarks/lib/search.php'; OC_App::register( array( 'order' => 70, 'id' => 'bookmark', 'name' => 'Bookmarks' )); @@ -15,5 +16,5 @@ $l = new OC_l10n('bookmarks'); OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'href' => OC_Helper::linkTo( 'bookmarks', 'index.php' ), 'icon' => OC_Helper::imagePath( 'bookmarks', 'bookmarks.png' ), 'name' => $l->t('Bookmarks'))); OC_App::registerPersonal('bookmarks', 'settings'); -require_once('apps/bookmarks/lib/search.php'); OC_Util::addScript('bookmarks','bookmarksearch'); +OC_Search::registerProvider('OC_Search_Provider_Bookmarks'); diff --git a/apps/bookmarks/appinfo/database.xml b/apps/bookmarks/appinfo/database.xml index fca38ad84b..f2fc68e4b5 100644 --- a/apps/bookmarks/appinfo/database.xml +++ b/apps/bookmarks/appinfo/database.xml @@ -75,14 +75,6 @@ descending - diff --git a/apps/bookmarks/appinfo/info.xml b/apps/bookmarks/appinfo/info.xml index 23aa6c219a..862ab805a6 100644 --- a/apps/bookmarks/appinfo/info.xml +++ b/apps/bookmarks/appinfo/info.xml @@ -3,8 +3,8 @@ bookmarks Bookmarks Bookmark manager for ownCloud - 0.1 + 0.2 AGPL - Arthur Schiwon + Arthur Schiwon, Marvin Thomas Rabe 2 \ No newline at end of file diff --git a/apps/bookmarks/bookmarksHelper.php b/apps/bookmarks/bookmarksHelper.php index ac512fbc24..8def7401e2 100644 --- a/apps/bookmarks/bookmarksHelper.php +++ b/apps/bookmarks/bookmarksHelper.php @@ -70,3 +70,55 @@ function getURLMetadata($url) { return $metadata; } + +function addBookmark($url, $title='', $tags='') { + $CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" ); + if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){ + $_ut = "strftime('%s','now')"; + } elseif($CONFIG_DBTYPE == 'pgsql') { + $_ut = 'date_part(\'epoch\',now())::integer'; + } else { + $_ut = "UNIX_TIMESTAMP()"; + } + + //FIXME: Detect when user adds a known URL + $query = OC_DB::prepare(" + INSERT INTO *PREFIX*bookmarks + (url, title, user_id, public, added, lastmodified) + VALUES (?, ?, ?, 0, $_ut, $_ut) + "); + + if(empty($title)) { + $metadata = getURLMetadata($url); + $title = $metadata['title']; + } + + $params=array( + htmlspecialchars_decode($url), + htmlspecialchars_decode($title), + OC_User::getUser() + ); + $query->execute($params); + + $b_id = OC_DB::insertid('*PREFIX*bookmarks'); + + if($b_id !== false) { + $query = OC_DB::prepare(" + INSERT INTO *PREFIX*bookmarks_tags + (bookmark_id, tag) + VALUES (?, ?) + "); + + $tags = explode(' ', urldecode($tags)); + foreach ($tags as $tag) { + if(empty($tag)) { + //avoid saving blankspaces + continue; + } + $params = array($b_id, trim($tag)); + $query->execute($params); + } + + return $b_id; + } +} \ No newline at end of file diff --git a/apps/bookmarks/css/bookmarks.css b/apps/bookmarks/css/bookmarks.css index 8dfdc8a07b..3a3e0fbf6b 100644 --- a/apps/bookmarks/css/bookmarks.css +++ b/apps/bookmarks/css/bookmarks.css @@ -1,4 +1,8 @@ -#content { overflow: auto; } +#content { overflow: auto; height: 100%; } +#firstrun { width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em; color:#777;} +#firstrun small { display: block; font-weight: normal; font-size: 0.5em; margin-bottom: 1.5em; } +#firstrun .button { font-size: 0.7em; } +#firstrun #selections { font-size:0.8em; font-weight: normal; width: 100%; margin: 2em auto auto auto; clear: both; } .bookmarks_headline { font-size: large; @@ -12,13 +16,10 @@ padding: 0.5ex; } -.bookmarks_add { - display: none; - margin-top: 45px; -} - .bookmarks_list { - margin-top: 36px; + overflow: auto; + position: fixed; + top: 6.5em; } .bookmarks_addBml { @@ -32,7 +33,7 @@ } .bookmarks_input { - width: 20em; + width: 8em; } .bookmark_actions { @@ -84,16 +85,3 @@ display: none; margin-left: 5px; } - -#footer { - color: #999; - font-size: medium; - text-align: center; - position: absolute; - bottom: 10px; - left: 0px; - width: 100%; - height: 20px; - visibility: visible; - display: block -} diff --git a/apps/bookmarks/js/addBm.js b/apps/bookmarks/js/addBm.js index 6e13b59bb2..d64e55e892 100644 --- a/apps/bookmarks/js/addBm.js +++ b/apps/bookmarks/js/addBm.js @@ -4,13 +4,12 @@ $(document).ready(function() { function addBookmark(event) { var url = $('#bookmark_add_url').val(); - var title = $('#bookmark_add_title').val(); var tags = $('#bookmark_add_tags').val(); $.ajax({ url: 'ajax/addBookmark.php', - data: 'url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&tags=' + encodeURI(tags), + data: 'url=' + encodeURI(url) + '&tags=' + encodeURI(tags), success: function(data){ - location.href='index.php'; + window.close(); } }); } \ No newline at end of file diff --git a/apps/bookmarks/js/bookmarks.js b/apps/bookmarks/js/bookmarks.js index fadbbd5513..fa5adde254 100644 --- a/apps/bookmarks/js/bookmarks.js +++ b/apps/bookmarks/js/bookmarks.js @@ -3,19 +3,16 @@ var bookmarks_loading = false; var bookmarks_sorting = 'bookmarks_sorting_recent'; -$(document).ready(function() { - $('.bookmarks_addBtn').click(function(event){ - $('.bookmarks_add').slideToggle(); - }); - +$(document).ready(function() { $('#bookmark_add_submit').click(addOrEditBookmark); - $(window).scroll(updateOnBottom); - - $('#bookmark_add_url').focusout(getMetadata); + $(window).resize(function () { + fillWindow($('.bookmarks_list')); + }); + $(window).resize(); + $($('.bookmarks_list')).scroll(updateOnBottom); $('.bookmarks_list').empty(); getBookmarks(); - }); function getBookmarks() { @@ -28,13 +25,19 @@ function getBookmarks() { url: 'ajax/updateList.php', data: 'tag=' + encodeURI($('#bookmarkFilterTag').val()) + '&page=' + bookmarks_page + '&sort=' + bookmarks_sorting, success: function(bookmarks){ - bookmarks_page += 1; + if (bookmarks.data.length) { + bookmarks_page += 1; + } $('.bookmark_link').unbind('click', recordClick); $('.bookmark_delete').unbind('click', delBookmark); $('.bookmark_edit').unbind('click', showBookmark); for(var i in bookmarks.data) { updateBookmarksList(bookmarks.data[i]); + $("#firstrun").hide(); + } + if($('.bookmarks_list').is(':empty')) { + $("#firstrun").show(); } $('.bookmark_link').click(recordClick); @@ -42,24 +45,13 @@ function getBookmarks() { $('.bookmark_edit').click(showBookmark); bookmarks_loading = false; + if (bookmarks.data.length) { + updateOnBottom() + } } }); } -function getMetadata() { - var url = encodeEntities($('#bookmark_add_url').val()); - $('.loading_meta').css('display','inline'); - $.ajax({ - url: 'ajax/getMeta.php', - data: 'url=' + encodeURIComponent(url), - success: function(pageinfo){ - $('#bookmark_add_url').val(pageinfo.data.url); - $('#bookmark_add_title').val(pageinfo.data.title); - $('.loading_meta').css('display','none'); - } - }); -} - // function addBookmark() { // Instead of creating editBookmark() function, Converted the one above to // addOrEditBookmark() to make .js file more compact. @@ -69,35 +61,17 @@ function addOrEditBookmark(event) { var url = encodeEntities($('#bookmark_add_url').val()); var title = encodeEntities($('#bookmark_add_title').val()); var tags = encodeEntities($('#bookmark_add_tags').val()); - var taglist = tags.split(' '); - var tagshtml = ''; - for ( var i=0, len=taglist.length; i' + taglist[i] + ' '; - } + $("#firstrun").hide(); if (id == 0) { $.ajax({ url: 'ajax/addBookmark.php', data: 'url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&tags=' + encodeURI(tags), success: function(response){ - var bookmark_id = response.data; - $('.bookmarks_add').slideToggle(); - $('.bookmarks_add').children('p').children('.bookmarks_input').val(''); - $('.bookmarks_list').prepend( - '
' + - '

' + - '' + - '' + - ' ' + - '' + - '' + - '' + - '

' + - '

' + title + '

' + - '

' + tagshtml + '

' + - '

' + url + '

' + - '
' - ); + $('.bookmarks_input').val(''); + $('.bookmarks_list').empty(); + bookmarks_page = 0; + getBookmarks(); } }); } @@ -106,18 +80,11 @@ function addOrEditBookmark(event) { url: 'ajax/editBookmark.php', data: 'id=' + id + '&url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&tags=' + encodeURI(tags), success: function(){ - $('.bookmarks_add').slideToggle(); - $('.bookmarks_add').children('p').children('.bookmarks_input').val(''); + $('.bookmarks_input').val(''); $('#bookmark_add_id').val('0'); - - var record = $('.bookmark_single[data-id = "' + id + '"]'); - record.children('.bookmark_url:first').text(url); - - var record_title = record.children('.bookmark_title:first').children('a:first'); - record_title.attr('href', url); - record_title.text(title); - - record.children('.bookmark_tags:first').html(tagshtml); + $('.bookmarks_list').empty(); + bookmarks_page = 0; + getBookmarks(); } }); } @@ -129,7 +96,12 @@ function delBookmark(event) { $.ajax({ url: 'ajax/delBookmark.php', data: 'url=' + encodeURI($(this).parent().parent().children('.bookmark_url:first').text()), - success: function(data){ record.animate({ opacity: 'hide' }, 'fast'); } + success: function(data){ + record.remove(); + if($('.bookmarks_list').is(':empty')) { + $("#firstrun").show(); + } + } }); } @@ -159,15 +131,16 @@ function updateBookmarksList(bookmark) { if(!hasProtocol(bookmark.url)) { bookmark.url = 'http://' + bookmark.url; } + if(bookmark.title == '') bookmark.title = bookmark.url; $('.bookmarks_list').append( '
' + '

' + - '' + - '' + - ' ' + '' + '' + '' + + '' + + '' + + ' ' + '

' + '

'+ '' + encodeEntities(bookmark.title) + '' + @@ -182,7 +155,11 @@ function updateBookmarksList(bookmark) { function updateOnBottom() { //check wether user is on bottom of the page - if ($('body').height() <= ($(window).height() + $(window).scrollTop())) { + var top = $('.bookmarks_list>:last-child').position().top; + var height = $('.bookmarks_list').height(); + // use a bit of margin to begin loading before we are really at the + // bottom + if (top < height * 1.2) { getBookmarks(); } } diff --git a/apps/bookmarks/lib/search.php b/apps/bookmarks/lib/search.php index 59495db82e..235587855d 100644 --- a/apps/bookmarks/lib/search.php +++ b/apps/bookmarks/lib/search.php @@ -20,8 +20,8 @@ * */ -class OC_Search_Provider_Bookmarks extends OC_Search_Provider{ - function search($query){ +class OC_Search_Provider_Bookmarks implements OC_Search_Provider{ + static function search($query){ $results=array(); $offset = 0; @@ -45,6 +45,3 @@ class OC_Search_Provider_Bookmarks extends OC_Search_Provider{ return $results; } } -new OC_Search_Provider_Bookmarks(); - -?> diff --git a/apps/bookmarks/settings.php b/apps/bookmarks/settings.php index 0ace04fa2c..9d945f64da 100644 --- a/apps/bookmarks/settings.php +++ b/apps/bookmarks/settings.php @@ -1,6 +1,6 @@ + * Copyright (c) 2011 Marvin Thomas Rabe * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -8,6 +8,4 @@ $tmpl = new OC_Template( 'bookmarks', 'settings'); -//OC_Util::addScript('bookmarks','settings'); - return $tmpl->fetchPage(); diff --git a/apps/bookmarks/templates/addBm.php b/apps/bookmarks/templates/addBm.php index c285c3579c..dbe673f53a 100644 --- a/apps/bookmarks/templates/addBm.php +++ b/apps/bookmarks/templates/addBm.php @@ -1,7 +1,11 @@ -

-

-

-

-

-

-
+ + + + + Read later - ownCloud + + + +

Saved!

+ + \ No newline at end of file diff --git a/apps/bookmarks/templates/bookmarklet.php b/apps/bookmarks/templates/bookmarklet.php new file mode 100644 index 0000000000..5ea67f04df --- /dev/null +++ b/apps/bookmarks/templates/bookmarklet.php @@ -0,0 +1,8 @@ +' . $l->t('Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:') . '' + . '' + . $l->t('Read later') . ''; +} diff --git a/apps/bookmarks/templates/list.php b/apps/bookmarks/templates/list.php index d44a0ecbcd..1abdbb7f83 100644 --- a/apps/bookmarks/templates/list.php +++ b/apps/bookmarks/templates/list.php @@ -1,6 +1,6 @@ + * Copyright (c) 2011 Marvin Thomas Rabe * Copyright (c) 2011 Arthur Schiwon * This file is licensed under the Affero General Public License version 3 or * later. @@ -9,21 +9,18 @@ ?>
- -
-
-

-

-

-

-

-

+ + + +
- t('You have no bookmarks'); ?>
- diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php index 979634874e..f74a45203e 100644 --- a/apps/calendar/templates/settings.php +++ b/apps/calendar/templates/settings.php @@ -37,6 +37,13 @@ + + + + t('Calendar CalDAV syncing address:');?> diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php index 9e424aa89f..85c383c4c3 100644 --- a/apps/contacts/appinfo/app.php +++ b/apps/contacts/appinfo/app.php @@ -4,7 +4,11 @@ OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php'; OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php'; OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php'; OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php'; +OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php'; OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser'); +OC_HOOK::connect('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents'); +OC_HOOK::connect('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources'); +OC_Hook::connect('OC_DAV', 'initialize', 'OC_Contacts_Hooks', 'initializeCardDAV'); OC_App::register( array( 'order' => 10, @@ -21,4 +25,4 @@ OC_App::addNavigationEntry( array( OC_APP::registerPersonal('contacts','settings'); OC_UTIL::addScript('contacts', 'loader'); -require_once('apps/contacts/lib/search.php'); +OC_Search::registerProvider('OC_Search_Provider_Contacts'); diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php index a2bf492e20..654aeb66a7 100644 --- a/apps/contacts/carddav.php +++ b/apps/contacts/carddav.php @@ -39,7 +39,7 @@ $nodes = array( // Fire up server $server = new Sabre_DAV_Server($nodes); -$server->setBaseUri(OC::$WEBROOT.'/apps/contacts/carddav.php'); +$server->setBaseUri(OC::$APPSWEBROOT.'/apps/contacts/carddav.php'); // Add plugins $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud')); $server->addPlugin(new Sabre_CardDAV_Plugin()); diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 65a6eb2480..a6f7d9316f 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -17,8 +17,8 @@ #contacts_propertymenu li a:hover { color: #fff } #actionbar { height: 30px; width: 200px; position: fixed; right: 0px; top: 75px; margin: 0 0 0 0; padding: 0 0 0 0;} #card { /*max-width: 70em; border: thin solid lightgray; display: block;*/ } -#firstrun { /*border: thin solid lightgray;*/ width: 80%; margin: 5em auto auto auto; text-align: center; font-weight:bold; font-size:1.5em; color:#777;} -#firstrun #selections { /*border: thin solid lightgray;*/ font-size:0.8em; width: 100%; margin: 2em auto auto auto; clear: both; } +#firstrun { width: 100%; position: absolute; top: 5em; left: 0; text-align: center; font-weight:bold; font-size:1.5em; color:#777; } +#firstrun #selections { font-size:0.8em; margin: 2em auto auto auto; clear: both; } #card input[type="text"].contacts_property,input[type="email"].contacts_property { width: 14em; } .categories { float: left; width: 16em; } @@ -194,4 +194,4 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } /*#categorylist { border:1px solid #ddd;}*/ #categorylist li { background:#f8f8f8; padding:.3em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; } #categorylist li:hover, li:active { background:#eee; } -#category_addinput { width: 10em; } \ No newline at end of file +#category_addinput { width: 10em; } diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 7306e5714c..626672cf78 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -4,6 +4,12 @@ function ucwords (str) { }); } +String.prototype.strip_tags = function(){ + tags = this; + stripped = tags.replace(/[\<\>]/gi, ""); + return stripped; +}; + Categories={ edit:function(){ console.log('Categories.edit'); @@ -238,7 +244,7 @@ Contacts={ alert(jsondata.data.message); } }); - } + }; }, loadListHandlers:function() { //$('.add,.delete').hide(); @@ -425,7 +431,7 @@ Contacts={ } }); }, - delete:function() { + delete: function() { $('#contacts_deletecard').tipsy('hide'); $.getJSON('ajax/deletecard.php',{'id':this.id},function(jsondata){ if(jsondata.status == 'success'){ @@ -1508,10 +1514,3 @@ $(document).ready(function(){ $('#contacts_propertymenu').hide(); }); }); - -String.prototype.strip_tags = function(){ - tags = this; - stripped = tags.replace(/[\<\>]/gi, ""); - return stripped; -} - diff --git a/apps/contacts/lib/app.php b/apps/contacts/lib/app.php index 48298952c1..47220e0bc8 100644 --- a/apps/contacts/lib/app.php +++ b/apps/contacts/lib/app.php @@ -85,7 +85,7 @@ class OC_Contacts_App { $vcard = OC_VObject::parse($card['carddata']); // Try to fix cards with missing 'N' field from pre ownCloud 4. Hot damn, this is ugly... if(!is_null($vcard) && !$vcard->__isset('N')) { - $appinfo = $info=OC_App::getAppInfo('contacts'); + $appinfo = OC_App::getAppInfo('contacts'); if($appinfo['version'] >= 5) { OC_Log::write('contacts','OC_Contacts_App::getContactVCard. Deprecated check for missing N field', OC_Log::DEBUG); } diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php index 155cf40f91..e09da20be8 100644 --- a/apps/contacts/lib/hooks.php +++ b/apps/contacts/lib/hooks.php @@ -29,7 +29,7 @@ class OC_Contacts_Hooks{ * @param paramters parameters from postDeleteUser-Hook * @return array */ - public function deleteUser($parameters) { + static public function deleteUser($parameters) { $addressbooks = OC_Contacts_Addressbook::all($parameters['uid']); foreach($addressbooks as $addressbook) { @@ -38,4 +38,62 @@ class OC_Contacts_Hooks{ return true; } + + /** + * @brief Adds the CardDAV resource to the DAV server + * @param paramters parameters from initialize-Hook + * @return array + */ + static public function initializeCardDAV($parameters){ + // We need a backend, the root node and the carddav plugin + $parameters['backends']['carddav'] = new OC_Connector_Sabre_CardDAV(); + $parameters['nodes'][] = new Sabre_CardDAV_AddressBookRoot($parameters['backends']['principal'], $parameters['backends']['carddav']); + $parameters['plugins'][] = new Sabre_CardDAV_Plugin(); + return true; + } + + static public function getCalenderSources($parameters) { + $base_url = OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id='; + foreach(OC_Contacts_Addressbook::all(OC_User::getUser()) as $addressbook) { + $parameters['sources'][] = + array( + 'url' => $base_url.'birthday_'. $addressbook['id'], + 'backgroundColor' => '#cccccc', + 'borderColor' => '#888', + 'textColor' => 'black', + 'cache' => true, + 'editable' => false, + ); + } + } + + static public function getBirthdayEvents($parameters) { + $name = $parameters['calendar_id']; + if (strpos('birthday_', $name) != 0) { + return; + } + $info = explode('_', $name); + $aid = $info[1]; + OC_Contacts_App::getAddressbook($aid); + foreach(OC_Contacts_VCard::all($aid) as $card){ + $vcard = OC_VObject::parse($card['carddata']); + $birthday = $vcard->BDAY; + if ($birthday) { + $date = new DateTime($birthday); + $vevent = new OC_VObject('VEVENT'); + $vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV)); + $vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE); + $vevent->setString('DURATION', 'P1D'); + // DESCRIPTION? + $vevent->setString('RRULE', 'FREQ=YEARLY'); + $title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday')); + $parameters['events'][] = array( + 'id' => 0,//$card['id'], + 'vevent' => $vevent, + 'repeating' => true, + 'summary' => $title, + ); + } + } + } } diff --git a/apps/contacts/lib/search.php b/apps/contacts/lib/search.php index 5aad6a25f0..cf0a5fe699 100644 --- a/apps/contacts/lib/search.php +++ b/apps/contacts/lib/search.php @@ -1,6 +1,6 @@ + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +require_once('../../../lib/base.php'); +OC_Util::checkAdminUser(); + +$sites = array(); +for ($i = 0; $i < sizeof($_POST['site_name']); $i++) { + if (!empty($_POST['site_name'][$i]) && !empty($_POST['site_url'][$i])) { + array_push($sites, array($_POST['site_name'][$i], $_POST['site_url'][$i])); + } +} + +if (sizeof($sites) == 0) + OC_Appconfig::deleteKey('external', 'sites'); +else + OC_Appconfig::setValue('external', 'sites', json_encode($sites)); + +echo 'true'; +?> diff --git a/apps/external/ajax/seturls.php b/apps/external/ajax/seturls.php deleted file mode 100644 index e994385a19..0000000000 --- a/apps/external/ajax/seturls.php +++ /dev/null @@ -1,24 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - -require_once('../../../lib/base.php'); -OC_Util::checkAdminUser(); - -if(isset($_POST['s1name'])) OC_Appconfig::setValue( 'external','site1name', $_POST['s1name'] ); -if(isset($_POST['s1url'])) OC_Appconfig::setValue( 'external','site1url', $_POST['s1url'] ); -if(isset($_POST['s2name'])) OC_Appconfig::setValue( 'external','site2name', $_POST['s2name'] ); -if(isset($_POST['s2url'])) OC_Appconfig::setValue( 'external','site2url', $_POST['s2url'] ); -if(isset($_POST['s3name'])) OC_Appconfig::setValue( 'external','site3name', $_POST['s3name'] ); -if(isset($_POST['s3url'])) OC_Appconfig::setValue( 'external','site3url', $_POST['s3url'] ); -if(isset($_POST['s4name'])) OC_Appconfig::setValue( 'external','site4name', $_POST['s4name'] ); -if(isset($_POST['s4url'])) OC_Appconfig::setValue( 'external','site4url', $_POST['s4url'] ); -if(isset($_POST['s5name'])) OC_Appconfig::setValue( 'external','site5name', $_POST['s5name'] ); -if(isset($_POST['s5url'])) OC_Appconfig::setValue( 'external','site5url', $_POST['s5url'] ); - -echo 'true'; - -?> diff --git a/apps/external/appinfo/app.php b/apps/external/appinfo/app.php index 0f536cbf41..74e6d5c94c 100644 --- a/apps/external/appinfo/app.php +++ b/apps/external/appinfo/app.php @@ -1,37 +1,35 @@ . -* -*/ + * ownCloud - External plugin + * + * @author Frank Karlitschek + * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ -OC_APP::registerAdmin('external','settings'); +OC::$CLASSPATH['OC_External'] = 'apps/external/lib/external.php'; +OC_Util::addStyle( 'external', 'style'); -OC_App::register( array( 'order' => 70, 'id' => 'external', 'name' => 'External' )); +OC_APP::registerAdmin('external', 'settings'); -if(OC_Appconfig::getValue( "external","site1name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index1', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=1', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site1name", '' ))); - -if(OC_Appconfig::getValue( "external","site2name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index2', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=2', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site2name", '' ))); - -if(OC_Appconfig::getValue( "external","site3name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index3', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=3', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site3name", '' ))); - -if(OC_Appconfig::getValue( "external","site4name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index4', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=4', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site4name", '' ))); - -if(OC_Appconfig::getValue( "external","site5name", '' )<>'') OC_App::addNavigationEntry( array( 'id' => 'external_index5', 'order' => 80, 'href' => OC_Helper::linkTo( 'external', 'index.php' ).'?id=5', 'icon' => OC_Helper::imagePath( 'external', 'external.png' ), 'name' => OC_Appconfig::getValue( "external","site5name", '' ))); +OC_App::register(array('order' => 70, 'id' => 'external', 'name' => 'External')); +$sites = OC_External::getSites(); +for ($i = 0; $i < sizeof($sites); $i++) { + OC_App::addNavigationEntry( + array('id' => 'external_index' . ($i + 1), 'order' => 80 + $i, 'href' => OC_Helper::linkTo('external', 'index.php') . '?id=' . ($i + 1), 'icon' => OC_Helper::imagePath('external', 'external.png'), 'name' => $sites[$i][0])); +} \ No newline at end of file diff --git a/apps/external/css/style.css b/apps/external/css/style.css new file mode 100644 index 0000000000..f891cb4bc5 --- /dev/null +++ b/apps/external/css/style.css @@ -0,0 +1,14 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / +/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ + +.site_url { + width: 250px; +} + +.delete_button { + display: none; +} + +.external_sites { + width: 450px; +} diff --git a/apps/external/index.php b/apps/external/index.php index 51cdc344bb..1c20f59eaf 100644 --- a/apps/external/index.php +++ b/apps/external/index.php @@ -1,42 +1,43 @@ . -* -*/ - + * ownCloud - External plugin + * + * @author Frank Karlitschek + * @copyright 2011 Frank Karlitschek karlitschek@kde.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ require_once('../../lib/base.php'); +require_once('lib/external.php'); OC_Util::checkLoggedIn(); -if(isset($_GET['id'])){ +if (isset($_GET['id'])) { - $id=$_GET['id']; + $id = $_GET['id']; $id = (int) $id; - $url=OC_Appconfig::getValue( "external","site".$id."url", '' ); - OC_App::setActiveNavigationEntry( 'external_index'.$id ); - - $tmpl = new OC_Template( 'external', 'frame', 'user' ); - $tmpl->assign('url',$url); - $tmpl->printPage(); + $sites = OC_External::getSites(); + if (sizeof($sites) >= $id) { + $url = $sites[$id - 1][1]; + OC_App::setActiveNavigationEntry('external_index' . $id); + $tmpl = new OC_Template('external', 'frame', 'user'); + $tmpl->assign('url', $url); + $tmpl->printPage(); + } } - ?> diff --git a/apps/external/js/admin.js b/apps/external/js/admin.js index 6b9b6c6773..0caaabd0b9 100644 --- a/apps/external/js/admin.js +++ b/apps/external/js/admin.js @@ -1,68 +1,57 @@ $(document).ready(function(){ + newSiteHtml = '
  • \n\ + \n\ +
  • '; - - - $('#s1name').blur(function(event){ + // Handler functions + function addSiteEventHandler(event) { event.preventDefault(); - var post = $( "#s1name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s1name .msg', data); }); - }); + + saveSites(); + } - $('#s2name').blur(function(event){ + function deleteButtonEventHandler(event) { event.preventDefault(); - var post = $( "#s2name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s2name .msg', data); }); - }); - $('#s3name').blur(function(event){ + $(this).tipsy('hide'); + $(this).parent().remove(); + + saveSites(); + } + + function saveSites() { + var post = $('#external').serialize(); + $.post( OC.filePath('external','ajax','setsites.php') , post, function(data) { + // OC.msg.finishedSaving('#site_name .msg', data); + }); + } + + function showDeleteButton(event) { + $(this).find('img.delete_button').fadeIn(100); + } + + function hideDeleteButton(event) { + $(this).find('img.delete_button').fadeOut(100); + } + + // Initialize events + $('input[name^=site_]').change(addSiteEventHandler); + $('img.delete_button').click(deleteButtonEventHandler); + $('img.delete_button').tipsy(); + + $('#external li').hover(showDeleteButton, hideDeleteButton); + + $('#add_external_site').click(function(event) { event.preventDefault(); - var post = $( "#s3name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s3name .msg', data); }); - }); + $('#external ul').append(newSiteHtml); - $('#s4name').blur(function(event){ - event.preventDefault(); - var post = $( "#s4name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s4name .msg', data); }); + $('input.site_url:last').prev('input.site_name').andSelf().change(addSiteEventHandler); + $('img.delete_button').click(deleteButtonEventHandler); + $('img.delete_button:last').tipsy(); + $('#external li:last').hover(showDeleteButton, hideDeleteButton); + }); - $('#s5name').blur(function(event){ - event.preventDefault(); - var post = $( "#s5name" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s5name .msg', data); }); - }); - - $('#s1url').blur(function(event){ - event.preventDefault(); - var post = $( "#s1url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s1url .msg', data); }); - }); - - $('#s2url').blur(function(event){ - event.preventDefault(); - var post = $( "#s2url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s2url .msg', data); }); - }); - - $('#s3url').blur(function(event){ - event.preventDefault(); - var post = $( "#s3url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s3url .msg', data); }); - }); - - $('#s4url').blur(function(event){ - event.preventDefault(); - var post = $( "#s4url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s4url .msg', data); }); - }); - - $('#s5url').blur(function(event){ - event.preventDefault(); - var post = $( "#s5url" ).serialize(); - $.post( OC.filePath('external','ajax','seturls.php') , post, function(data){ OC.msg.finishedSaving('#s5url .msg', data); }); - }); - - }); diff --git a/apps/external/lib/external.php b/apps/external/lib/external.php new file mode 100644 index 0000000000..9dd3232113 --- /dev/null +++ b/apps/external/lib/external.php @@ -0,0 +1,36 @@ +. + * + */ + +class OC_External { + + public static function getSites() { + if (($sites = json_decode(OC_Appconfig::getValue("external", "sites", ''))) != NULL) { + return $sites; + } + + return array(); + } + +} + +?> diff --git a/apps/external/settings.php b/apps/external/settings.php index 3e0c342512..416c9a5c11 100644 --- a/apps/external/settings.php +++ b/apps/external/settings.php @@ -6,17 +6,5 @@ OC_Util::addScript( "external", "admin" ); $tmpl = new OC_Template( 'external', 'settings'); - $tmpl->assign('s1name',OC_Appconfig::getValue( "external","site1name", '' )); - $tmpl->assign('s2name',OC_Appconfig::getValue( "external","site2name", '' )); - $tmpl->assign('s3name',OC_Appconfig::getValue( "external","site3name", '' )); - $tmpl->assign('s4name',OC_Appconfig::getValue( "external","site4name", '' )); - $tmpl->assign('s5name',OC_Appconfig::getValue( "external","site5name", '' )); - - $tmpl->assign('s1url',OC_Appconfig::getValue( "external","site1url", '' )); - $tmpl->assign('s2url',OC_Appconfig::getValue( "external","site2url", '' )); - $tmpl->assign('s3url',OC_Appconfig::getValue( "external","site3url", '' )); - $tmpl->assign('s4url',OC_Appconfig::getValue( "external","site4url", '' )); - $tmpl->assign('s5url',OC_Appconfig::getValue( "external","site5url", '' )); - return $tmpl->fetchPage(); ?> diff --git a/apps/external/templates/settings.php b/apps/external/templates/settings.php index a72327d35c..a130477d46 100644 --- a/apps/external/templates/settings.php +++ b/apps/external/templates/settings.php @@ -1,23 +1,21 @@
    External Sites
    - - -
    - - -
    - - -
    - - -
    - - -
    +
      + + + + '; + } + ?> - +
    + + +
    diff --git a/apps/files_archive/appinfo/app.php b/apps/files_archive/appinfo/app.php new file mode 100644 index 0000000000..693c28d98a --- /dev/null +++ b/apps/files_archive/appinfo/app.php @@ -0,0 +1,18 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +OC::$CLASSPATH['OC_Archive'] = 'apps/files_archive/lib/archive.php'; +foreach(array('ZIP') as $type){ + OC::$CLASSPATH['OC_Archive_'.$type] = 'apps/files_archive/lib/'.strtolower($type).'.php'; +} + +OC::$CLASSPATH['OC_Filestorage_Archive']='apps/files_archive/lib/storage.php'; + +OC_Hook::connect('OC_Filesystem','get_mountpoint','OC_Filestorage_Archive','autoMount'); + +OC_Util::addScript( 'files_archive', 'archive' ); diff --git a/apps/files_archive/appinfo/info.xml b/apps/files_archive/appinfo/info.xml new file mode 100644 index 0000000000..df767d39f6 --- /dev/null +++ b/apps/files_archive/appinfo/info.xml @@ -0,0 +1,10 @@ + + + files_archive + Archive support + Transparent opening of archives + 0.1 + AGPL + Robin Appelman + 3 + diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js new file mode 100644 index 0000000000..ec316c7bf2 --- /dev/null +++ b/apps/files_archive/js/archive.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2012 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +$(document).ready(function() { + if(typeof FileActions!=='undefined'){ + FileActions.register('application/zip','Open','',function(filename){ + window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); + }); + FileActions.setDefault('application/zip','Open'); + } +}); diff --git a/apps/files_archive/lib/archive.php b/apps/files_archive/lib/archive.php new file mode 100644 index 0000000000..be89f894fb --- /dev/null +++ b/apps/files_archive/lib/archive.php @@ -0,0 +1,99 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +abstract class OC_Archive{ + /** + * open any of the supporeted archive types + * @param string path + * @return OC_Archive + */ + public static function open($path){ + $ext=substr($path,strrpos($path,'.')); + switch($ext){ + case '.zip': + return new OC_Archive_ZIP($path); + } + } + + abstract function __construct($source); + /** + * add an empty folder to the archive + * @param string path + * @return bool + */ + abstract function addFolder($path); + /** + * add a file to the archive + * @param string path + * @param string source either a local file or string data + * @return bool + */ + abstract function addFile($path,$source=''); + /** + * rename a file or folder in the archive + * @param string source + * @param string dest + * @return bool + */ + abstract function rename($source,$dest); + /** + * get the uncompressed size of a file in the archive + * @param string path + * @return int + */ + abstract function filesize($path); + /** + * get the last modified time of a file in the archive + * @param string path + * @return int + */ + abstract function mtime($path); + /** + * get the files in a folder + * @param path + * @return array + */ + abstract function getFolder($path); + /** + *get all files in the archive + * @return array + */ + abstract function getFiles(); + /** + * get the content of a file + * @param string path + * @return string + */ + abstract function getFile($path); + /** + * extract a single file from the archive + * @param string path + * @param string dest + * @return bool + */ + abstract function extractFile($path,$dest); + /** + * check if a file or folder exists in the archive + * @param string path + * @return bool + */ + abstract function fileExists($path); + /** + * remove a file or folder from the archive + * @param string path + * @return bool + */ + abstract function remove($path); + /** + * get a file handler + * @param string path + * @param string mode + * @return resource + */ + abstract function getStream($path,$mode); +} diff --git a/apps/files_archive/lib/storage.php b/apps/files_archive/lib/storage.php new file mode 100644 index 0000000000..72a96ca5a5 --- /dev/null +++ b/apps/files_archive/lib/storage.php @@ -0,0 +1,142 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Filestorage_Archive extends OC_Filestorage_Common{ + /** + * underlying local storage used for missing functions + * @var OC_Archive + */ + private $archive; + private $path; + private static $mounted=array(); + private static $enableAutomount=true; + private static $rootView; + + private function stripPath($path){//files should never start with / + if(substr($path,0,1)=='/'){ + $path=substr($path,1); + } + return $path; + } + + public function __construct($params){ + $this->archive=OC_Archive::open($params['archive']); + $this->path=$params['archive']; + } + + public function mkdir($path){ + $path=$this->stripPath($path); + return $this->archive->addFolder($path); + } + public function rmdir($path){ + $path=$this->stripPath($path); + return $this->archive->remove($path.'/'); + } + public function opendir($path){ + $path=$this->stripPath($path); + $content=$this->archive->getFolder($path); + foreach($content as &$file){ + if(substr($file,-1)=='/'){ + $file=substr($file,0,-1); + } + } + $id=md5($this->path.$path); + OC_FakeDirStream::$dirs[$id]=$content; + return opendir('fakedir://'.$id); + } + public function stat($path){ + $ctime=filectime($this->path); + $path=$this->stripPath($path); + if($path==''){ + $stat=stat($this->path); + }else{ + if($this->is_dir($path)){ + $stat=array('size'=>0); + $stat['mtime']=filemtime($this->path); + }else{ + $stat=array(); + $stat['mtime']=$this->archive->mtime($path); + $stat['size']=$this->archive->filesize($path); + } + } + $stat['ctime']=$ctime; + return $stat; + } + public function filetype($path){ + $path=$this->stripPath($path); + if($path==''){ + return 'dir'; + } + if(substr($path,-1)=='/'){ + return $this->archive->fileExists($path)?'dir':'file'; + }else{ + return $this->archive->fileExists($path.'/')?'dir':'file'; + } + } + public function is_readable($path){ + return is_readable($this->path); + } + public function is_writable($path){ + return is_writable($this->path); + } + public function file_exists($path){ + $path=$this->stripPath($path); + if($path==''){ + return file_exists($this->path); + } + return $this->archive->fileExists($path) or $this->archive->fileExists($path.'/'); + } + public function unlink($path){ + $path=$this->stripPath($path); + return $this->archive->remove($path); + } + public function fopen($path,$mode){ + $path=$this->stripPath($path); + return $this->archive->getStream($path,$mode); + } + public function free_space($path){ + return 0; + } + public function touch($path, $mtime=null){ + if(is_null($mtime)){ + $tmpFile=OC_Helper::tmpFile(); + $this->archive->extractFile($path,$tmpFile); + $this->archive->addfile($path,$tmpFile); + }else{ + return false;//not supported + } + } + + /** + * automount paths from file hooks + * @param aray params + */ + public static function autoMount($params){ + if(!self::$enableAutomount){ + return; + } + $path=$params['path']; + if(!self::$rootView){ + self::$rootView=new OC_FilesystemView(''); + } + self::$enableAutomount=false;//prevent recursion + $supported=array('zip'); + foreach($supported as $type){ + $ext='.'.$type.'/'; + if(($pos=strpos(strtolower($path),$ext))!==false){ + $archive=substr($path,0,$pos+strlen($ext)-1); + if(self::$rootView->file_exists($archive) and array_search($archive,self::$mounted)===false){ + $localArchive=self::$rootView->getLocalFile($archive); + OC_Filesystem::mount('OC_Filestorage_Archive',array('archive'=>$localArchive),$archive.'/'); + self::$mounted[]=$archive; + } + } + } + self::$enableAutomount=true; + } +} diff --git a/apps/files_archive/lib/zip.php b/apps/files_archive/lib/zip.php new file mode 100644 index 0000000000..eab101b3a5 --- /dev/null +++ b/apps/files_archive/lib/zip.php @@ -0,0 +1,182 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class OC_Archive_ZIP extends OC_Archive{ + /** + * @var ZipArchive zip + */ + private $zip=null; + private $contents=array(); + private $success=false; + private $path; + + function __construct($source){ + $this->path=$source; + $this->zip=new ZipArchive(); + if($this->zip->open($source,ZipArchive::CREATE)){ + }else{ + OC_LOG::write('files_archive','Error while opening archive '.$source,OC_Log::WARN); + } + } + /** + * add an empty folder to the archive + * @param string path + * @return bool + */ + function addFolder($path){ + return $this->zip->addEmptyDir($path); + } + /** + * add a file to the archive + * @param string path + * @param string source either a local file or string data + * @return bool + */ + function addFile($path,$source=''){ + if(file_exists($source)){ + $result=$this->zip->addFile($source,$path); + }else{ + $result=$this->zip->addFromString($path,$source); + } + if($result){ + $this->zip->close();//close and reopen to save the zip + $this->zip->open($this->path); + } + return $result; + } + /** + * rename a file or folder in the archive + * @param string source + * @param string dest + * @return bool + */ + function rename($source,$dest){ + return $this->zip->renameName($source,$dest); + } + /** + * get the uncompressed size of a file in the archive + * @param string path + * @return int + */ + function filesize($path){ + $stat=$this->zip->statName($path); + return $stat['size']; + } + /** + * get the last modified time of a file in the archive + * @param string path + * @return int + */ + function mtime($path){ + $stat=$this->zip->statName($path); + return $stat['mtime']; + } + /** + * get the files in a folder + * @param path + * @return array + */ + function getFolder($path){ + $files=$this->getFiles(); + $folderContent=array(); + $pathLength=strlen($path); + foreach($files as $file){ + if(substr($file,0,$pathLength)==$path and $file!=$path){ + if(strrpos(substr($file,0,-1),'/')<=$pathLength){ + $folderContent[]=substr($file,$pathLength); + } + } + } + return $folderContent; + } + /** + *get all files in the archive + * @return array + */ + function getFiles(){ + if(count($this->contents)){ + return $this->contents; + } + $fileCount=$this->zip->numFiles; + $files=array(); + for($i=0;$i<$fileCount;$i++){ + $files[]=$this->zip->getNameIndex($i); + } + $this->contents=$files; + return $files; + } + /** + * get the content of a file + * @param string path + * @return string + */ + function getFile($path){ + return $this->zip->getFromName($path); + } + /** + * extract a single file from the archive + * @param string path + * @param string dest + * @return bool + */ + function extractFile($path,$dest){ + $fp = $this->zip->getStream($path); + file_put_contents($dest,$fp); + } + /** + * check if a file or folder exists in the archive + * @param string path + * @return bool + */ + function fileExists($path){ + return $this->zip->locateName($path)!==false; + } + /** + * remove a file or folder from the archive + * @param string path + * @return bool + */ + function remove($path){ + return $this->zip->deleteName($path); + } + /** + * get a file handler + * @param string path + * @param string mode + * @return resource + */ + function getStream($path,$mode){ + if($mode=='r' or $mode=='rb'){ + return $this->zip->getStream($path); + }else{//since we cant directly get a writable stream, make a temp copy of the file and put it back in the archive when the stream is closed + if(strrpos($path,'.')!==false){ + $ext=substr($path,strrpos($path,'.')); + }else{ + $ext=''; + } + $tmpFile=OC_Helper::tmpFile($ext); + OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + if($this->fileExists($path)){ + $this->extractFile($path,$tmpFile); + } + self::$tempFiles[$tmpFile]=$path; + return fopen('close://'.$tmpFile,$mode); + } + } + + private static $tempFiles=array(); + /** + * write back temporary files + */ + function writeBack($tmpFile){ + if(isset(self::$tempFiles[$tmpFile])){ + $this->addFile(self::$tempFiles[$tmpFile],$tmpFile); + unlink($tmpFile); + } + } +} diff --git a/apps/files_archive/tests/archive.php b/apps/files_archive/tests/archive.php new file mode 100644 index 0000000000..2e26b5e03b --- /dev/null +++ b/apps/files_archive/tests/archive.php @@ -0,0 +1,97 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +abstract class Test_Archive extends UnitTestCase { + /** + * @var OC_Archive + */ + protected $instance; + + /** + * get the existing test archive + * @return OC_Archive + */ + abstract protected function getExisting(); + /** + * get a new archive for write testing + * @return OC_Archive + */ + abstract protected function getNew(); + + public function testGetFiles(){ + $this->instance=$this->getExisting(); + $allFiles=$this->instance->getFiles(); + $expected=array('lorem.txt','logo-wide.png','dir/','dir/lorem.txt'); + $this->assertEqual(4,count($allFiles)); + foreach($expected as $file){ + $this->assertNotIdentical(false,array_search($file,$allFiles),'cant find '.$file.' in archive'); + $this->assertTrue($this->instance->fileExists($file)); + } + $this->assertFalse($this->instance->fileExists('non/existing/file')); + + $rootContent=$this->instance->getFolder(''); + $expected=array('lorem.txt','logo-wide.png','dir/'); + $this->assertEqual(3,count($rootContent)); + foreach($expected as $file){ + $this->assertNotIdentical(false,array_search($file,$rootContent),'cant find '.$file.' in archive'); + } + + $dirContent=$this->instance->getFolder('dir/'); + $expected=array('lorem.txt'); + $this->assertEqual(1,count($dirContent)); + foreach($expected as $file){ + $this->assertNotIdentical(false,array_search($file,$dirContent),'cant find '.$file.' in archive'); + } + } + + public function testContent(){ + $this->instance=$this->getExisting(); + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $textFile=$dir.'/lorem.txt'; + $this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt')); + + $tmpFile=OC_Helper::tmpFile('.txt'); + $this->instance->extractFile('lorem.txt',$tmpFile); + $this->assertEqual(file_get_contents($textFile),file_get_contents($tmpFile)); + } + + public function testWrite(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $textFile=$dir.'/lorem.txt'; + $this->instance=$this->getNew(); + $this->assertEqual(0,count($this->instance->getFiles())); + $this->instance->addFile('lorem.txt',$textFile); + $this->assertEqual(1,count($this->instance->getFiles())); + $this->assertTrue($this->instance->fileExists('lorem.txt')); + + $this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt')); + $this->instance->addFile('lorem.txt','foobar'); + $this->assertEqual('foobar',$this->instance->getFile('lorem.txt')); + } + + public function testReadStream(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $this->instance=$this->getExisting(); + $fh=$this->instance->getStream('lorem.txt','r'); + $this->assertTrue($fh); + $content=fread($fh,$this->instance->filesize('lorem.txt')); + fclose($fh); + $this->assertEqual(file_get_contents($dir.'/lorem.txt'),$content); + } + public function testWriteStream(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + $this->instance=$this->getNew(); + $fh=$this->instance->getStream('lorem.txt','w'); + $source=fopen($dir.'/lorem.txt','r'); + OC_Helper::streamCopy($source,$fh); + fclose($source); + fclose($fh); + $this->assertTrue($this->instance->fileExists('lorem.txt')); + $this->assertEqual(file_get_contents($dir.'/lorem.txt'),$this->instance->getFile('lorem.txt')); + } +} diff --git a/apps/files_archive/tests/storage.php b/apps/files_archive/tests/storage.php new file mode 100644 index 0000000000..4d0a83356b --- /dev/null +++ b/apps/files_archive/tests/storage.php @@ -0,0 +1,25 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_Filestorage_Archive_Zip extends Test_FileStorage { + /** + * @var string tmpDir + */ + private $tmpFile; + + public function setUp(){ + $this->tmpFile=OC_Helper::tmpFile('.zip'); + $this->instance=new OC_Filestorage_Archive(array('archive'=>$this->tmpFile)); + } + + public function tearDown(){ + unlink($this->tmpFile); + } +} + +?> \ No newline at end of file diff --git a/apps/files_archive/tests/zip.php b/apps/files_archive/tests/zip.php new file mode 100644 index 0000000000..3ff713eda7 --- /dev/null +++ b/apps/files_archive/tests/zip.php @@ -0,0 +1,20 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +require_once('archive.php'); + +class Test_Archive_ZIP extends Test_Archive{ + protected function getExisting(){ + $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data'; + return new OC_Archive_ZIP($dir.'/data.zip'); + } + + protected function getNew(){ + return new OC_Archive_ZIP(OC_Helper::tmpFile('.zip')); + } +} diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php new file mode 100644 index 0000000000..68c445d5d7 --- /dev/null +++ b/apps/files_encryption/appinfo/app.php @@ -0,0 +1,19 @@ + + + files_encryption + Encryption + Server side encryption of files + 0.1 + AGPL + Robin Appelman + 3 + diff --git a/apps/files_encryption/js/settings.js b/apps/files_encryption/js/settings.js new file mode 100644 index 0000000000..adbf0c8724 --- /dev/null +++ b/apps/files_encryption/js/settings.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2011, Robin Appelman + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + + +$(document).ready(function(){ + $('#encryption_blacklist').multiSelect({ + oncheck:blackListChange, + onuncheck:blackListChange, + createText:'...', + }); + + function blackListChange(){ + var blackList=$('#encryption_blacklist').val().join(','); + OC.AppConfig.setValue('files_encryption','type_blacklist',blackList); + } +}) \ No newline at end of file diff --git a/lib/crypt.php b/apps/files_encryption/lib/crypt.php similarity index 51% rename from lib/crypt.php rename to apps/files_encryption/lib/crypt.php index 6002067948..0a593b98c4 100644 --- a/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -37,90 +37,111 @@ 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); + 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 + */ + 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($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); - 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); - - // encrypt the key with the passcode of the user - $enckey=OC_Crypt::encrypt($key,$passcode); - - // Write the file - $username=OC_USER::getUser(); - @file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey ); - } + // 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( $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 (optional) + * @returns cleartext content + * + * This function decrypts an content + */ + public static function decrypt( $content, $key='') { + $bf = self::getBlowfish($key); + return($bf->decrypt($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); - 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,8 +179,28 @@ 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/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php new file mode 100644 index 0000000000..86583096f1 --- /dev/null +++ b/apps/files_encryption/lib/cryptstream.php @@ -0,0 +1,153 @@ +. + * + */ + +/** + * 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); + 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); + } + + 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; + $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; + if(strlen($data)){ + $result.=OC_Crypt::decrypt($data); + } + } + if(!$this->meta['seekable']){ + $this->readBuffer=substr($result,$count); + } + return substr($result,0,$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(){ + 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 new file mode 100644 index 0000000000..c1c26d7754 --- /dev/null +++ b/apps/files_encryption/lib/proxy.php @@ -0,0 +1,115 @@ +. +* +*/ + +/** + * transparent encryption + */ + +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(self::isEncrypted($path)){ + 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::getCached($path); + self::$metaData[$path]=$metadata; + } + return (bool)$metadata['encrypted']; + } + + public function preFile_put_contents($path,&$data){ + if(self::shouldEncrypt($path)){ + if (!is_resource($data)) {//stream put contents should have been converter to fopen + $data=OC_Crypt::blockEncrypt($data); + OC_FileCache::put($path,array('encrypted'=>true)); + } + } + } + + public function postFile_get_contents($path,$data){ + if(self::isEncrypted($path)){ + $data=OC_Crypt::blockDecrypt($data); + } + return $data; + } + + public function postFopen($path,&$result){ + if(!$result){ + return $result; + } + $meta=stream_get_meta_data($result); + if(self::isEncrypted($path)){ + fclose($result); + $result=fopen('crypt://'.$path,$meta['mode']); + }elseif(self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb'){ + 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); + $tmp=fopen('php://temp'); + while(!feof($result)){ + $chunk=fread($result,8192); + if($chunk){ + fwrite($tmp,$chunk); + } + } + fclose($result); + OC_Filesystem::file_put_contents($path,$tmp); + fclose($tmp); + } + $result=fopen('crypt://'.$path,$meta['mode']); + } + return $result; + } + + public function postGetMimeType($path,$mime){ + if(self::isEncrypted($path)){ + $mime=OC_Helper::getMimeType('crypt://'.$path,'w'); + } + return $mime; + } +} diff --git a/apps/files_encryption/settings.php b/apps/files_encryption/settings.php new file mode 100644 index 0000000000..396ad1ba78 --- /dev/null +++ b/apps/files_encryption/settings.php @@ -0,0 +1,16 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +$tmpl = new OC_Template( 'files_encryption', 'settings'); +$blackList=explode(',',OC_Appconfig::getValue('files_encryption','type_blacklist','jpg,png,jpeg,avi,mpg,mpeg,mkv,mp3,oga,ogv,ogg')); +$tmpl->assign('blacklist',$blackList); + +OC_Util::addScript('files_encryption','settings'); +OC_Util::addScript('core','multiselect'); + +return $tmpl->fetchPage(); diff --git a/apps/files_encryption/templates/settings.php b/apps/files_encryption/templates/settings.php new file mode 100644 index 0000000000..724a03836a --- /dev/null +++ b/apps/files_encryption/templates/settings.php @@ -0,0 +1,11 @@ +
    +
    + t('Encryption'); ?> + t("Exclude the following file types from encryption"); ?> + +
    +
    diff --git a/apps/files_pdfviewer/appinfo/info.xml b/apps/files_pdfviewer/appinfo/info.xml index 86a6c3f22f..f133f1900d 100755 --- a/apps/files_pdfviewer/appinfo/info.xml +++ b/apps/files_pdfviewer/appinfo/info.xml @@ -1,7 +1,8 @@ files_pdfviewer - PDF viewer (pdfjs-based) + PDF Viewer + Inline PDF viewer (pdfjs-based) 0.1 GPL Joan Creus diff --git a/apps/files_sharing/css/sharing.css b/apps/files_sharing/css/sharing.css index 0759af2c27..db59a3d340 100644 --- a/apps/files_sharing/css/sharing.css +++ b/apps/files_sharing/css/sharing.css @@ -5,4 +5,5 @@ #shared_list { padding:0.5em; list-style-type: none; } #public { border-top:1px solid #ddd; padding-top:0.5em; } a.unshare { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; opacity:.5; } -a.unshare:hover { opacity:1; } \ No newline at end of file +a.unshare:hover { opacity:1; } +#share_with { width: 16em; } \ No newline at end of file diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index d01a07447a..fc9e17c25c 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -174,7 +174,7 @@ $(document).ready(function() { function createDropdown(filename, files) { var html = '