summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <machniak@kolabsys.com>2014-09-26 14:13:20 (GMT)
committerAleksander Machniak <machniak@kolabsys.com>2014-09-26 14:13:20 (GMT)
commitfd5a5403e773a4d8c2679fbdb4bd1b20a3a2f4f0 (patch)
tree08bbe8a261d285b61df62f06f0c8455a37c70900
parentbaec5fa4bd55d97218c7c62746e439a39b314d9c (diff)
downloadkolab-chwala-fd5a5403e773a4d8c2679fbdb4bd1b20a3a2f4f0.tar.gz
Implemented SeaFile storage driver.
Currently can be used only as a kolab driver replacement.
-rw-r--r--lib/drivers/kolab/kolab_file_plugin_api.php (renamed from lib/kolab/kolab_file_plugin_api.php)0
-rw-r--r--lib/drivers/kolab/kolab_file_storage.php (renamed from lib/kolab/kolab_file_storage.php)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/LICENSE (renamed from lib/kolab/plugins/libkolab/LICENSE)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/config.inc.php.dist (renamed from lib/kolab/plugins/kolab_auth/config.inc.php.dist)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/kolab_auth.php (renamed from lib/kolab/plugins/kolab_auth/kolab_auth.php)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/kolab_auth_ldap.php (renamed from lib/kolab/plugins/kolab_auth/kolab_auth_ldap.php)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/bg_BG.inc (renamed from lib/kolab/plugins/kolab_auth/localization/bg_BG.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/de_CH.inc (renamed from lib/kolab/plugins/kolab_auth/localization/de_CH.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/de_DE.inc (renamed from lib/kolab/plugins/kolab_auth/localization/de_DE.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/en_US.inc (renamed from lib/kolab/plugins/kolab_auth/localization/en_US.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/es_ES.inc (renamed from lib/kolab/plugins/kolab_auth/localization/es_ES.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/et_EE.inc (renamed from lib/kolab/plugins/kolab_auth/localization/et_EE.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/fr_FR.inc (renamed from lib/kolab/plugins/kolab_auth/localization/fr_FR.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/ja_JP.inc (renamed from lib/kolab/plugins/kolab_auth/localization/ja_JP.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/nl_NL.inc (renamed from lib/kolab/plugins/kolab_auth/localization/nl_NL.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/pl_PL.inc (renamed from lib/kolab/plugins/kolab_auth/localization/pl_PL.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/pt_BR.inc (renamed from lib/kolab/plugins/kolab_auth/localization/pt_BR.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/localization/ru_RU.inc (renamed from lib/kolab/plugins/kolab_auth/localization/ru_RU.inc)0
-rw-r--r--lib/drivers/kolab/plugins/kolab_auth/package.xml (renamed from lib/kolab/plugins/kolab_auth/package.xml)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/LICENSE (renamed from lib/kolab/plugins/kolab_auth/LICENSE)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/README (renamed from lib/kolab/plugins/libkolab/README)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/SQL/mysql.initial.sql (renamed from lib/kolab/plugins/libkolab/SQL/mysql.initial.sql)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013011000.sql (renamed from lib/kolab/plugins/libkolab/SQL/mysql/2013011000.sql)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013041900.sql (renamed from lib/kolab/plugins/libkolab/SQL/mysql/2013041900.sql)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013100400.sql (renamed from lib/kolab/plugins/libkolab/SQL/mysql/2013100400.sql)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/SQL/postgres.initial.sql (renamed from lib/kolab/plugins/libkolab/SQL/postgres.initial.sql)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/UPGRADING (renamed from lib/kolab/plugins/libkolab/UPGRADING)0
-rwxr-xr-xlib/drivers/kolab/plugins/libkolab/bin/modcache.sh (renamed from lib/kolab/plugins/libkolab/bin/modcache.sh)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/config.inc.php.dist (renamed from lib/kolab/plugins/libkolab/config.inc.php.dist)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_date_recurrence.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_date_recurrence.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_configuration.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_configuration.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_contact.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_contact.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_event.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_event.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_file.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_file.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_journal.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_journal.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_note.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_note.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_task.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_task.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_format_xcal.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_format_xcal.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_folder.php (renamed from lib/kolab/plugins/libkolab/lib/kolab_storage_folder.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/libkolab.php (renamed from lib/kolab/plugins/libkolab/libkolab.php)0
-rw-r--r--lib/drivers/kolab/plugins/libkolab/package.xml (renamed from lib/kolab/plugins/libkolab/package.xml)0
-rw-r--r--lib/drivers/seafile/seafile_api.php841
-rw-r--r--lib/drivers/seafile/seafile_file_storage.php994
-rw-r--r--lib/drivers/seafile/seafile_request_observer.php49
-rw-r--r--lib/file_api.php2
-rw-r--r--lib/file_utils.php48
-rw-r--r--lib/init.php2
60 files changed, 1934 insertions, 2 deletions
diff --git a/lib/kolab/kolab_file_plugin_api.php b/lib/drivers/kolab/kolab_file_plugin_api.php
index eda15c7..eda15c7 100644
--- a/lib/kolab/kolab_file_plugin_api.php
+++ b/lib/drivers/kolab/kolab_file_plugin_api.php
diff --git a/lib/kolab/kolab_file_storage.php b/lib/drivers/kolab/kolab_file_storage.php
index 6acfa3e..6acfa3e 100644
--- a/lib/kolab/kolab_file_storage.php
+++ b/lib/drivers/kolab/kolab_file_storage.php
diff --git a/lib/kolab/plugins/libkolab/LICENSE b/lib/drivers/kolab/plugins/kolab_auth/LICENSE
index dba13ed..dba13ed 100644
--- a/lib/kolab/plugins/libkolab/LICENSE
+++ b/lib/drivers/kolab/plugins/kolab_auth/LICENSE
diff --git a/lib/kolab/plugins/kolab_auth/config.inc.php.dist b/lib/drivers/kolab/plugins/kolab_auth/config.inc.php.dist
index e7b9d15..e7b9d15 100644
--- a/lib/kolab/plugins/kolab_auth/config.inc.php.dist
+++ b/lib/drivers/kolab/plugins/kolab_auth/config.inc.php.dist
diff --git a/lib/kolab/plugins/kolab_auth/kolab_auth.php b/lib/drivers/kolab/plugins/kolab_auth/kolab_auth.php
index 7ff5761..7ff5761 100644
--- a/lib/kolab/plugins/kolab_auth/kolab_auth.php
+++ b/lib/drivers/kolab/plugins/kolab_auth/kolab_auth.php
diff --git a/lib/kolab/plugins/kolab_auth/kolab_auth_ldap.php b/lib/drivers/kolab/plugins/kolab_auth/kolab_auth_ldap.php
index b9b3e4a..b9b3e4a 100644
--- a/lib/kolab/plugins/kolab_auth/kolab_auth_ldap.php
+++ b/lib/drivers/kolab/plugins/kolab_auth/kolab_auth_ldap.php
diff --git a/lib/kolab/plugins/kolab_auth/localization/bg_BG.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/bg_BG.inc
index 1f7a573..1f7a573 100644
--- a/lib/kolab/plugins/kolab_auth/localization/bg_BG.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/bg_BG.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/de_CH.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/de_CH.inc
index 5e85a01..5e85a01 100644
--- a/lib/kolab/plugins/kolab_auth/localization/de_CH.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/de_CH.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/de_DE.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/de_DE.inc
index 5e85a01..5e85a01 100644
--- a/lib/kolab/plugins/kolab_auth/localization/de_DE.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/de_DE.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/en_US.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/en_US.inc
index e1adb3f..e1adb3f 100644
--- a/lib/kolab/plugins/kolab_auth/localization/en_US.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/en_US.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/es_ES.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/es_ES.inc
index acb6c35..acb6c35 100644
--- a/lib/kolab/plugins/kolab_auth/localization/es_ES.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/es_ES.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/et_EE.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/et_EE.inc
index acb6c35..acb6c35 100644
--- a/lib/kolab/plugins/kolab_auth/localization/et_EE.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/et_EE.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/fr_FR.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/fr_FR.inc
index 6f72695..6f72695 100644
--- a/lib/kolab/plugins/kolab_auth/localization/fr_FR.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/fr_FR.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/ja_JP.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/ja_JP.inc
index ed0358a..ed0358a 100644
--- a/lib/kolab/plugins/kolab_auth/localization/ja_JP.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/ja_JP.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/nl_NL.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/nl_NL.inc
index a98283f..a98283f 100644
--- a/lib/kolab/plugins/kolab_auth/localization/nl_NL.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/nl_NL.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/pl_PL.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/pl_PL.inc
index 124c373..124c373 100644
--- a/lib/kolab/plugins/kolab_auth/localization/pl_PL.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/pl_PL.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/pt_BR.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/pt_BR.inc
index 26e9541..26e9541 100644
--- a/lib/kolab/plugins/kolab_auth/localization/pt_BR.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/pt_BR.inc
diff --git a/lib/kolab/plugins/kolab_auth/localization/ru_RU.inc b/lib/drivers/kolab/plugins/kolab_auth/localization/ru_RU.inc
index 9e28c12..9e28c12 100644
--- a/lib/kolab/plugins/kolab_auth/localization/ru_RU.inc
+++ b/lib/drivers/kolab/plugins/kolab_auth/localization/ru_RU.inc
diff --git a/lib/kolab/plugins/kolab_auth/package.xml b/lib/drivers/kolab/plugins/kolab_auth/package.xml
index 5a2093b..5a2093b 100644
--- a/lib/kolab/plugins/kolab_auth/package.xml
+++ b/lib/drivers/kolab/plugins/kolab_auth/package.xml
diff --git a/lib/kolab/plugins/kolab_auth/LICENSE b/lib/drivers/kolab/plugins/libkolab/LICENSE
index dba13ed..dba13ed 100644
--- a/lib/kolab/plugins/kolab_auth/LICENSE
+++ b/lib/drivers/kolab/plugins/libkolab/LICENSE
diff --git a/lib/kolab/plugins/libkolab/README b/lib/drivers/kolab/plugins/libkolab/README
index 2f94839..2f94839 100644
--- a/lib/kolab/plugins/libkolab/README
+++ b/lib/drivers/kolab/plugins/libkolab/README
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql.initial.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql.initial.sql
index 4f23a52..4f23a52 100644
--- a/lib/kolab/plugins/libkolab/SQL/mysql.initial.sql
+++ b/lib/drivers/kolab/plugins/libkolab/SQL/mysql.initial.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql/2013011000.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013011000.sql
index fe6741a..fe6741a 100644
--- a/lib/kolab/plugins/libkolab/SQL/mysql/2013011000.sql
+++ b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013011000.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql/2013041900.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013041900.sql
index 76577e6..76577e6 100644
--- a/lib/kolab/plugins/libkolab/SQL/mysql/2013041900.sql
+++ b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013041900.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/mysql/2013100400.sql b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013100400.sql
index d41d0e1..d41d0e1 100644
--- a/lib/kolab/plugins/libkolab/SQL/mysql/2013100400.sql
+++ b/lib/drivers/kolab/plugins/libkolab/SQL/mysql/2013100400.sql
diff --git a/lib/kolab/plugins/libkolab/SQL/postgres.initial.sql b/lib/drivers/kolab/plugins/libkolab/SQL/postgres.initial.sql
index e06346c..e06346c 100644
--- a/lib/kolab/plugins/libkolab/SQL/postgres.initial.sql
+++ b/lib/drivers/kolab/plugins/libkolab/SQL/postgres.initial.sql
diff --git a/lib/kolab/plugins/libkolab/UPGRADING b/lib/drivers/kolab/plugins/libkolab/UPGRADING
index e7f04d8..e7f04d8 100644
--- a/lib/kolab/plugins/libkolab/UPGRADING
+++ b/lib/drivers/kolab/plugins/libkolab/UPGRADING
diff --git a/lib/kolab/plugins/libkolab/bin/modcache.sh b/lib/drivers/kolab/plugins/libkolab/bin/modcache.sh
index da6e4f8..da6e4f8 100755
--- a/lib/kolab/plugins/libkolab/bin/modcache.sh
+++ b/lib/drivers/kolab/plugins/libkolab/bin/modcache.sh
diff --git a/lib/kolab/plugins/libkolab/config.inc.php.dist b/lib/drivers/kolab/plugins/libkolab/config.inc.php.dist
index 0c612a3..0c612a3 100644
--- a/lib/kolab/plugins/libkolab/config.inc.php.dist
+++ b/lib/drivers/kolab/plugins/libkolab/config.inc.php.dist
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_date_recurrence.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_date_recurrence.php
index 85ffd91..85ffd91 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_date_recurrence.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_date_recurrence.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format.php
index aa88f69..aa88f69 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_configuration.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_configuration.php
index 5a8d3ff..5a8d3ff 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_configuration.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_configuration.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_contact.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_contact.php
index 0d0bc75..0d0bc75 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_contact.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_contact.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php
index 46dda01..46dda01 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_distributionlist.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_event.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_event.php
index 9be9bdf..9be9bdf 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_event.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_event.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_file.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_file.php
index 5f73bf1..5f73bf1 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_file.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_file.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_journal.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_journal.php
index f7ccd31..f7ccd31 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_journal.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_journal.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_note.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_note.php
index 04a8421..04a8421 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_note.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_note.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_task.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_task.php
index a15cb0b..a15cb0b 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_task.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_task.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_format_xcal.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_xcal.php
index 500dfa2..500dfa2 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_format_xcal.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage.php
index 5f8b9c6..5f8b9c6 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache.php
index 651dc18..651dc18 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php
index 8380aa8..8380aa8 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_configuration.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php
index e17923d..e17923d 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_contact.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php
index 876c3b4..876c3b4 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_event.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php
index ea1823d..ea1823d 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_file.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
index d8ab554..d8ab554 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php
index a63577b..a63577b 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_journal.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
index 8ae95e4..8ae95e4 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php
index 8546927..8546927 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_note.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php
index a1953f6..a1953f6 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_cache_task.php
diff --git a/lib/kolab/plugins/libkolab/lib/kolab_storage_folder.php b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_folder.php
index aabc130..aabc130 100644
--- a/lib/kolab/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/lib/drivers/kolab/plugins/libkolab/lib/kolab_storage_folder.php
diff --git a/lib/kolab/plugins/libkolab/libkolab.php b/lib/drivers/kolab/plugins/libkolab/libkolab.php
index 48a5033..48a5033 100644
--- a/lib/kolab/plugins/libkolab/libkolab.php
+++ b/lib/drivers/kolab/plugins/libkolab/libkolab.php
diff --git a/lib/kolab/plugins/libkolab/package.xml b/lib/drivers/kolab/plugins/libkolab/package.xml
index cd3e3a0..cd3e3a0 100644
--- a/lib/kolab/plugins/libkolab/package.xml
+++ b/lib/drivers/kolab/plugins/libkolab/package.xml
diff --git a/lib/drivers/seafile/seafile_api.php b/lib/drivers/seafile/seafile_api.php
new file mode 100644
index 0000000..dcc5fe1
--- /dev/null
+++ b/lib/drivers/seafile/seafile_api.php
@@ -0,0 +1,841 @@
+<?php
+/*
+ +--------------------------------------------------------------------------+
+ | This file is part of the Kolab File API |
+ | |
+ | Copyright (C) 2012-2014, Kolab Systems AG |
+ | |
+ | This program is free software: you can redistribute it and/or modify |
+ | it under the terms of the GNU Affero General Public License as published |
+ | by the Free Software Foundation, either version 3 of the License, or |
+ | (at your option) any later version. |
+ | |
+ | This program 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 program. If not, see <http://www.gnu.org/licenses/> |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <machniak@kolabsys.com> |
+ +--------------------------------------------------------------------------+
+*/
+
+/**
+ * Class implementing access via SeaFile Web API v2
+ */
+class seafile_api
+{
+ const STATUS_OK = 200;
+ const CREATED = 201;
+ const ACCEPTED = 202;
+ const MOVED_PERMANENTLY = 301;
+ const BAD_REQUEST = 400;
+ const FORBIDDEN = 403;
+ const NOT_FOUND = 404;
+ const CONFLICT = 409;
+ const TOO_MANY_REQUESTS = 429;
+ const REPO_PASSWD_REQUIRED = 440;
+ const REPO_PASSWD_MAGIC_REQUIRED = 441;
+ const INTERNAL_SERVER_ERROR = 500;
+ const OPERATION_FAILED = 520;
+
+ const CONNECTION_ERROR = 550;
+
+ /**
+ * Specifies how long max. we'll wait and renew throttled request (in seconds)
+ */
+ const WAIT_LIMIT = 30;
+
+
+ /**
+ * Configuration
+ *
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * HTTP request handle
+ *
+ * @var HTTP_Request
+ */
+ protected $request;
+
+ /**
+ * Web API URI prefix
+ *
+ * @var string
+ */
+ protected $url;
+
+ /**
+ * Session token
+ *
+ * @var string
+ */
+ protected $token;
+
+
+ public function __construct($config = array())
+ {
+ $this->config = $config;
+
+ // set Web API URI
+ $this->url = rtrim('https://' . ($config['host'] ?: 'localhost'), '/');
+ if (!preg_match('|/api2$|', $this->url)) {
+ $this->url .= '/api2/';
+ }
+ }
+
+ /**
+ *
+ * @param array Configuration for this Request instance, that will be merged
+ * with default configuration
+ *
+ * @return HTTP_Request2 Request object
+ */
+ public static function http_request($config = array())
+ {
+ // load HTTP_Request2
+ require_once 'HTTP/Request2.php';
+
+ // remove unknown config, otherwise HTTP_Request will throw an error
+ $config = array_intersect_key($config, array_flip(array(
+ 'connect_timeout', 'timeout', 'use_brackets', 'protocol_version',
+ 'buffer_size', 'store_body', 'follow_redirects', 'max_redirects',
+ 'strict_redirects', 'ssl_verify_peer', 'ssl_verify_host',
+ 'ssl_cafile', 'ssl_capath', 'ssl_local_cert', 'ssl_passphrase'
+ )));
+
+ try {
+ $request = new HTTP_Request2();
+ $request->setConfig($config);
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return;
+ }
+
+ return $request;
+ }
+
+ /**
+ * Send HTTP request
+ *
+ * @param string $method Request method ('OPTIONS','GET','HEAD','POST','PUT','DELETE','TRACE','CONNECT')
+ * @param string $url Request API URL
+ * @param array $get GET parameters
+ * @param array $post POST parameters
+ * @param array $upload Uploaded files data
+ *
+ * @return string|array Server response
+ */
+ protected function request($method, $url, $get = null, $post = null, $upload = null)
+ {
+ if (!preg_match('/^https?:\/\//', $url)) {
+ $url = $this->url . $url;
+ // Note: It didn't work for me without the last backslash
+ $url = rtrim($url, '/') . '/';
+ }
+
+ if (!$this->request) {
+ $this->config['store_body'] = true;
+ // some methods respond with 301 redirect, we'll not follow them
+ // also because of https://github.com/haiwen/seahub/issues/288
+ $this->config['follow_redirects'] = false;
+
+ $this->request = self::http_request($this->config);
+
+ if (!$this->request) {
+ $this->status = self::CONNECTION_ERROR;
+ return;
+ }
+ }
+
+ // cleanup
+ try {
+ $this->request->setBody('');
+ $this->request->setUrl($url);
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ $this->status = self::CONNECTION_ERROR;
+ return;
+ }
+
+ if ($this->config['debug']) {
+ $log_line = "SeaFile $method: $url";
+ $json_opt = PHP_VERSION_ID >= 50400 ? JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE : 0;
+
+ if (!empty($get)) {
+ $log_line .= ", GET: " . @json_encode($get, $json_opt);
+ }
+
+ if (!empty($post)) {
+ $log_line .= ", POST: " . preg_replace('/("password":)[^\},]+/', '\\1"*"', @json_encode($post, $json_opt));
+ }
+
+ if (!empty($upload)) {
+ $log_line .= ", Files: " . @json_encode(array_keys($upload), $json_opt);
+ }
+
+ rcube::write_log('console', $log_line);
+ }
+
+ $this->request->setMethod($method ?: HTTP_Request2::METHOD_GET);
+
+ if (!empty($get)) {
+ $url = $this->request->getUrl();
+ $url->setQueryVariables($get);
+ $this->request->setUrl($url);
+ }
+
+ if (!empty($post)) {
+ $this->request->addPostParameter($post);
+ }
+
+ if (!empty($upload)) {
+ foreach ($upload as $field_name => $file) {
+ $this->request->addUpload($field_name, $file['data'], $file['name'], $file['type']);
+ }
+ }
+
+ if ($this->token) {
+ $this->request->setHeader('Authorization', "Token " . $this->token);
+ }
+
+ // some HTTP server configurations require this header
+ $this->request->setHeader('Accept', "application/json,text/javascript,*/*");
+
+ // proxy User-Agent string
+ $this->request->setHeader('User-Agent', $_SERVER['HTTP_USER_AGENT']);
+
+ // send request to the SeaFile API server
+ try {
+ $response = $this->request->send();
+ $this->status = $response->getStatus();
+ $body = $response->getBody();
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ $this->status = self::CONNECTION_ERROR;
+ }
+
+ if ($this->config['debug']) {
+ rcube::write_log('console', "SeaFile Response [$this->status]: " . trim($body));
+ }
+
+ // request throttled, try again?
+ if ($this->status == self::TOO_MANY_REQUESTS) {
+ if (preg_match('/([0-9]+) second/', $body['detail'], $m) && ($seconds = $m[1]) < self::WAIT_LIMIT) {
+ sleep($seconds/2); // try to be smart and wait only a half of it
+ return $this->request($url, $method, $get, $post, $upload);
+ }
+ }
+
+ // decode response
+ return $this->status >= 400 ? false : @json_decode($body, true);
+ }
+
+ /**
+ * Return error code of last operation
+ */
+ public function is_error()
+ {
+ return $this->status >= 400 ? $this->status : false;
+ }
+
+ /**
+ * Authenticate to SeaFile API and get auth token
+ *
+ * @param string $username User name (email)
+ * @param string $password User password
+ *
+ * @return string Authentication token
+ */
+ public function authenticate($username, $password)
+ {
+ $result = $this->request('POST', 'auth-token', null, array(
+ 'username' => $username,
+ 'password' => $password,
+ ));
+
+ if ($result['token']) {
+ return $this->token = $result['token'];
+ }
+ }
+
+ /**
+ * Get account information
+ *
+ * @return array Account info (usage, total, email)
+ */
+ public function account_info()
+ {
+ return $this->request('GET', "account/info");
+ }
+
+ /**
+ * Delete a directory
+ *
+ * @param string $repo_id Library identifier
+ * @param string $dir Directory name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function directory_delete($repo_id, $dir)
+ {
+ // sanity checks
+ if ($dir === '' || $dir === '/' || !is_string($dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $this->request('DELETE', "repos/$repo_id/dir", array('p' => $dir));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Rename a directory
+ *
+ * @param string $repo_id Library identifier
+ * @param string $src_dir Directory name (with path)
+ * @param string $dest_dir New directory name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function directory_rename($repo_id, $src_dir, $dest_dir)
+ {
+ // sanity checks
+ if ($src_dir === '' || $src_dir === '/' || !is_string($src_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dest_dir === '' || $dest_dir === '/' || !is_string($dest_dir) || $dest_dir === $src_dir) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/dir", array('p' => $src_dir), array(
+ 'operation' => 'rename',
+ 'newname' => $dest_dir,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Rename a directory
+ *
+ * @param string $repo_id Library identifier
+ * @param string $dir Directory name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function directory_create($repo_id, $dir)
+ {
+ // sanity checks
+ if ($dir === '' || $dir === '/' || !is_string($dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/dir", array('p' => $dir), array(
+ 'operation' => 'mkdir',
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * List directory entries (files and directories)
+ *
+ * @param string $repo_id Library identifier
+ * @param string $dir Directory name (with path)
+ *
+ * @return bool|array List of directories/files on success, False on failure
+ */
+ public function directory_entries($repo_id, $dir)
+ {
+ // sanity checks
+ if (!is_string($dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dir === '') {
+ $dir = '/';
+ }
+
+ // args: p=<$name> ('/' is a root, default), oid=?
+ // sample result
+ // [{
+ // "id": "0000000000000000000000000000000000000000",
+ // "type": "file",
+ // "name": "test1.c",
+ // "size": 0
+ // },{
+ // "id": "e4fe14c8cda2206bb9606907cf4fca6b30221cf9",
+ // "type": "dir",
+ // "name": "test_dir"
+ // }]
+
+ return $this->request('GET', "repos/$repo_id/dir", array('p' => $dir));
+ }
+
+ /**
+ * Update a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param array $file File data (data, type, name)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_update($repo_id, $filename, $file)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // first get the update link
+ $result = $this->request('GET', "repos/$repo_id/update-link");
+
+ if ($this->is_error() || empty($result)) {
+ return false;
+ }
+
+ $path = explode('/', $filename);
+ $fn = array_pop($path);
+
+ // then update file
+ $result = $this->request('POST', $result, null, array(
+ 'filename' => $fn,
+ 'target_file' => $filename,
+ ),
+ array('file' => $file)
+ );
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Upload a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param array $file File data (data, type, name)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_upload($repo_id, $filename, $file)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // first get upload link
+ $result = $this->request('GET', "repos/$repo_id/upload-link");
+
+ if ($this->is_error() || empty($result)) {
+ return false;
+ }
+
+ $path = explode('/', $filename);
+ $filename = array_pop($path);
+ $dir = '/' . ltrim(implode('/', $path), '/');
+
+ // then update file
+ $result = $this->request('POST', $result, null, array(
+ 'parent_dir' => $dir
+ ),
+ array('file' => $file)
+ );
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Delete a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_delete($repo_id, $filename)
+ {
+ // sanity check
+ if ($filename === '' || $filename === '/' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $this->request('DELETE', "repos/$repo_id/file", array('p' => $filename));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Copy file(s) (no rename here)
+ *
+ * @param string $repo_id Library identifier
+ * @param string|array $files List of files (without path)
+ * @param string $src_dir Source directory
+ * @param string $dest_dir Destination directory
+ * @param string $dest_repo Destination library (optional)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_copy($repo_id, $files, $src_dir, $dest_dir, $dest_repo)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($src_dir === '' || !is_string($src_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dest_dir === '' || !is_string($dest_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ((!is_array($files) && !strlen($files)) || (is_array($files) && empty($files))) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if (empty($dest_repo)) {
+ $dest_repo = $repo_id;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/fileops/copy", array('p' => $src_dir), array(
+ 'file_names' => implode(':', (array) $files),
+ 'dst_dir' => $dest_dir,
+ 'dst_repo' => $dest_repo,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Move a file (no rename here)
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param string $dst_dir Destination directory
+ * @param string $dst_repo Destination library (optional)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_move($repo_id, $filename, $dst_dir, $dst_repo = null)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($dst_dir === '' || !is_string($dst_dir)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if (empty($dst_repo)) {
+ $dst_repo = $repo_id;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/file", array('p' => $filename), array(
+ 'operation' => 'move',
+ 'dst_dir' => $dst_dir,
+ 'dst_repo' => $dst_repo,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Rename a file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ * @param string $new_name New file name (without path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_rename($repo_id, $filename, $new_name)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($new_name === '' || !is_string($new_name)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/file", array('p' => $filename), array(
+ 'operation' => 'rename',
+ 'newname' => $new_name,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Create an empty file
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool True on success, False on failure
+ */
+ public function file_create($repo_id, $filename)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $result = $this->request('POST', "repos/$repo_id/file", array('p' => $filename), array(
+ 'operation' => 'create',
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Get file info
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool|array File info on success, False on failure
+ */
+ public function file_info($repo_id, $filename)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // sample result:
+ // "id": "013d3d38fed38b3e8e26b21bb3463eab6831194f",
+ // "mtime": 1398148877,
+ // "type": "file",
+ // "name": "foo.py",
+ // "size": 22
+
+ return $this->request('GET', "repos/$repo_id/file/detail", array('p' => $filename));
+ }
+
+ /**
+ * Get file content
+ *
+ * @param string $repo_id Library identifier
+ * @param string $filename File name (with path)
+ *
+ * @return bool|string File download URI on success, False on failure
+ */
+ public function file_get($repo_id, $filename)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($filename === '' || !is_string($filename)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ return $this->request('GET', "repos/$repo_id/file", array('p' => $filename));
+ }
+
+ /**
+ * List libraries (repositories)
+ *
+ * @return array|bool List of libraries on success, False on failure
+ */
+ public function library_list()
+ {
+ $result = $this->request('GET', "repos");
+
+ // sample result
+ // [{
+ // "permission": "rw",
+ // "encrypted": false,
+ // "mtime": 1400054900,
+ // "owner": "user@mail.com",
+ // "id": "f158d1dd-cc19-412c-b143-2ac83f352290",
+ // "size": 0,
+ // "name": "foo",
+ // "type": "repo",
+ // "virtual": false,
+ // "desc": "new library",
+ // "root": "0000000000000000000000000000000000000000"
+ // }]
+
+ return $result;
+ }
+
+ /**
+ * Get library info
+ *
+ * @param string $repo_id Library identifier
+ *
+ * @return array|bool Library info on success, False on failure
+ */
+ public function library_info($repo_id)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ return $this->request('GET', "repos/$repo_id");
+ }
+
+ /**
+ * Create library
+ *
+ * @param string $name Library name
+ * @param string $description Library description
+ *
+ * @return bool|array Library info on success, False on failure
+ */
+ public function library_create($name, $description = '')
+ {
+ if ($name === '' || !is_string($name)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ return $this->request('POST', "repos", null, array(
+ 'name' => $name,
+ 'desc' => $description,
+ ));
+ }
+
+ /**
+ * Rename library
+ *
+ * @param string $repo_id Library identifier
+ * @param string $new_name Library description
+ *
+ * @return bool True on success, False on failure
+ */
+ public function library_rename($repo_id, $name, $description = '')
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ if ($name === '' || !is_string($name)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ // Note: probably by mistake the 'op' is a GET parameter
+ // maybe changed in future to be consistent with other methods
+ $this->request('POST', "repos/$repo_id", array('op' => 'rename'), array(
+ 'repo_name' => $name,
+ 'repo_desc' => $description,
+ ));
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Delete library
+ *
+ * @param string $repo_id Library identifier
+ *
+ * @return bool True on success, False on failure
+ */
+ public function library_delete($repo_id)
+ {
+ if ($repo_id === '' || !is_string($repo_id)) {
+ $this->status = self::BAD_REQUEST;
+ return false;
+ }
+
+ $this->request('DELETE', "repos/$repo_id");
+
+ return $this->is_error() === false;
+ }
+
+ /**
+ * Ping the API server
+ *
+ * @param string $token If set, auth token will be used
+ *
+ * @param bool True on success, False on failure
+ */
+ public function ping($token = null)
+ {
+ // can be used to check if token is still valid
+ if ($token) {
+ $this->token = $token;
+
+ $result = $this->request('GET', 'auth/ping', null, null);
+ }
+ // or if api works
+ else {
+ $result = $this->request('GET', 'ping', null, null);
+ }
+
+ return $this->is_error() === false;
+ }
+}
diff --git a/lib/drivers/seafile/seafile_file_storage.php b/lib/drivers/seafile/seafile_file_storage.php
new file mode 100644
index 0000000..524cf78
--- /dev/null
+++ b/lib/drivers/seafile/seafile_file_storage.php
@@ -0,0 +1,994 @@
+<?php
+/*
+ +--------------------------------------------------------------------------+
+ | This file is part of the Kolab File API |
+ | |
+ | Copyright (C) 2012-2014, Kolab Systems AG |
+ | |
+ | This program is free software: you can redistribute it and/or modify |
+ | it under the terms of the GNU Affero General Public License as published |
+ | by the Free Software Foundation, either version 3 of the License, or |
+ | (at your option) any later version. |
+ | |
+ | This program 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 program. If not, see <http://www.gnu.org/licenses/> |
+ +--------------------------------------------------------------------------+
+ | Author: Aleksander Machniak <machniak@kolabsys.com> |
+ +--------------------------------------------------------------------------+
+*/
+
+class seafile_file_storage implements file_storage
+{
+ /**
+ * @var rcube
+ */
+ protected $rc;
+
+ /**
+ * @var array
+ */
+ protected $config = array();
+
+ /**
+ * @var seafile_api
+ */
+ protected $api;
+
+ /**
+ * List of SeaFile libraries
+ *
+ * @var array
+ */
+ protected $libraries;
+
+
+ /**
+ * Class constructor
+ */
+ public function __construct()
+ {
+ $this->rc = rcube::get_instance();
+ }
+
+ /**
+ * Authenticates a user
+ *
+ * @param string $username User name
+ * @param string $password User password
+ *
+ * @param bool True on success, False on failure
+ */
+ public function authenticate($username, $password)
+ {
+ $this->init(true);
+
+ $token = $this->api->authenticate($username, $password);
+
+ if ($token) {
+ $_SESSION['seafile_user'] = $username;
+ $_SESSION['seafile_token'] = $this->rc->encrypt($token);
+ $_SESSION['seafile_pass'] = $this->rc->encrypt($password);
+
+ return true;
+ }
+
+ $this->api = false;
+
+ return false;
+ }
+
+ /**
+ * Initialize SeaFile Web API connection
+ */
+ protected function init($skip_auth = false)
+ {
+ if ($this->api !== null) {
+ return $this->api !== false;
+ }
+
+ // read configuration
+ $config = array(
+ 'host' => $this->rc->config->get('seafile_host', 'localhost'),
+ 'ssl_verify_peer' => $this->rc->config->get('seafile_ssl_verify_peer', true),
+ 'ssl_verify_host' => $this->rc->config->get('seafile_ssl_verify_host', true),
+ 'debug' => $this->rc->config->get('seafile_debug', false),
+ );
+
+ $this->config = array_merge($this->config, $config);
+
+ // initialize Web API
+ $this->api = new seafile_api($this->config);
+
+ if ($skip_auth) {
+ return true;
+ }
+
+ // try session token
+ if ($_SESSION['seafile_token'] && ($token = $this->rc->decrypt($_SESSION['seafile_token']))) {
+ $valid = $this->api->ping($token);
+ }
+
+ if (!$valid && $_SESSION['seafile_password'] && $_SESSION['seafile_user']) {
+ $pass = $this->rc->decrypt($_SESSION['seafile_pass']);
+ $valid = $this->authenticate($_SESSION['seafile_user'], $pass);
+ }
+
+ return $valid;
+ }
+
+ /**
+ * Configures environment
+ *
+ * @param array $config Configuration
+ */
+ public function configure($config)
+ {
+ $this->config = array_merge($this->config, $config);
+ }
+
+ /**
+ * Storage driver capabilities
+ *
+ * @return array List of capabilities
+ */
+ public function capabilities()
+ {
+ // find max filesize value
+ $max_filesize = parse_bytes(ini_get('upload_max_filesize'));
+ $max_postsize = parse_bytes(ini_get('post_max_size'));
+ if ($max_postsize && $max_postsize < $max_filesize) {
+ $max_filesize = $max_postsize;
+ }
+
+ return array(
+ file_storage::CAPS_MAX_UPLOAD => $max_filesize,
+ file_storage::CAPS_QUOTA => true,
+ file_storage::CAPS_LOCKS => true,
+ );
+ }
+
+ /**
+ * Create a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param array $file File data (path, type)
+ *
+ * @throws Exception
+ */
+ public function file_create($file_name, $file)
+ {
+ list($fn, $repo_id) = $this->find_library($file_name);
+
+ if (empty($repo_id)) {
+ throw new Exception("Storage error. Folder not found.", file_storage::ERROR);
+ }
+
+ $file['data'] = $file['path'];
+
+ $created = $this->api->file_upload($repo_id, $fn, $file);
+
+ if (!$created) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error saving file to SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. Saving file failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Update a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param array $file File data (path, type)
+ *
+ * @throws Exception
+ */
+ public function file_update($file_name, $file)
+ {
+ list($fn, $repo_id) = $this->find_library($file_name);
+
+ if (empty($repo_id)) {
+ throw new Exception("Storage error. Folder not found.", file_storage::ERROR);
+ }
+
+ if ($file['path']) {
+ $file['data'] = $file['path'];
+ }
+ else {
+ $fp = fopen('php://temp', 'wb');
+ fwrite($fp, $file['content'], strlen($file['content']));
+ $file['data'] = $fp;
+ unset($file['content']);
+ }
+
+ $saved = $this->api->file_update($repo_id, $fn, $file);
+
+ if ($fp) {
+ fclose($fp);
+ }
+
+ if (!$saved) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error saving file to SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. Saving file failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Delete a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_delete($file_name)
+ {
+ list($file_name, $repo_id) = $this->find_library($file_name);
+
+ if ($repo_id && $file_name != '/') {
+ $deleted = $this->api->file_delete($repo_id, $file_name);
+ }
+
+ if (!$deleted) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error deleting object from SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. Deleting file failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Return file body.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param array $params Parameters (force-download)
+ * @param resource $fp Print to file pointer instead (send no headers)
+ *
+ * @throws Exception
+ */
+ public function file_get($file_name, $params = array(), $fp = null)
+ {
+ list($fn, $repo_id) = $this->find_library($file_name);
+
+ $file = $this->api->file_info($repo_id, $fn);
+
+ if (empty($file)) {
+ throw new Exception("Storage error. File not found.", file_storage::ERROR);
+ }
+
+ $file = $this->from_file_object($file);
+
+ // get file location on SeaFile server for download
+ if ($file['size']) {
+ $link = $this->api->file_get($repo_id, $fn);
+ }
+
+ // write to file pointer, send no headers
+ if ($fp) {
+ if ($file['size']) {
+ $this->save_file_content($link, $fp);
+ }
+
+ return;
+ }
+
+ if (!empty($params['force-download'])) {
+ $disposition = 'attachment';
+ header("Content-Type: application/octet-stream");
+// @TODO
+// if ($browser->ie)
+// header("Content-Type: application/force-download");
+ }
+ else {
+ $mimetype = file_utils::real_mimetype($params['force-type'] ? $params['force-type'] : $file['type']);
+ $disposition = 'inline';
+
+ header("Content-Transfer-Encoding: binary");
+ header("Content-Type: $mimetype");
+ }
+
+ $filename = addcslashes($file['name'], '"');
+
+ // Workaround for nasty IE bug (#1488844)
+ // If Content-Disposition header contains string "attachment" e.g. in filename
+ // IE handles data as attachment not inline
+/*
+@TODO
+ if ($disposition == 'inline' && $browser->ie && $browser->ver < 9) {
+ $filename = str_ireplace('attachment', 'attach', $filename);
+ }
+*/
+ header("Content-Length: " . $file['size']);
+ header("Content-Disposition: $disposition; filename=\"$filename\"");
+
+ // just send redirect to SeaFile server
+ if ($file['size']) {
+ header("Location: $link");
+ }
+ die;
+ }
+
+ /**
+ * Returns file metadata.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_info($file_name)
+ {
+ list($file, $repo_id) = $this->find_library($file_name);
+
+ $file = $this->api->file_info($repo_id, $file);
+
+ if (empty($file)) {
+ throw new Exception("Storage error. File not found.", file_storage::ERROR);
+ }
+
+ $file = $this->from_file_object($file);
+
+ return array(
+ 'name' => $file['name'],
+ 'size' => (int) $file['size'],
+ 'type' => (string) $file['type'],
+ 'mtime' => $file['changed'] ? $file['changed']->format($this->config['date_format']) : '',
+ 'ctime' => $file['created'] ? $file['created']->format($this->config['date_format']) : '',
+ 'modified' => $file['changed'] ? $file['changed']->format('U') : 0,
+ 'created' => $file['created'] ? $file['created']->format('U') : 0,
+ );
+ }
+
+ /**
+ * List files in a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ * @param array $params List parameters ('sort', 'reverse', 'search')
+ *
+ * @return array List of files (file properties array indexed by filename)
+ * @throws Exception
+ */
+ public function file_list($folder_name, $params = array())
+ {
+ list($folder, $repo_id) = $this->find_library($folder_name);
+
+ // prepare search filter
+ if (!empty($params['search'])) {
+ foreach ($params['search'] as $idx => $value) {
+ if ($idx == 'name') {
+ $params['search'][$idx] = mb_strtoupper($value);
+ }
+ else if ($idx == 'class') {
+ $params['search'][$idx] = file_utils::class2mimetypes($value);
+ }
+ }
+ }
+
+ // get directory entries
+ $entries = $this->api->directory_entries($repo_id, $folder);
+ $result = array();
+
+ foreach ((array) $entries as $idx => $file) {
+ if ($file['type'] != 'file') {
+ continue;
+ }
+
+ $file = $this->from_file_object($file);
+
+ // search filter
+ if (!empty($params['search'])) {
+ foreach ($params['search'] as $idx => $value) {
+ if ($idx == 'name') {
+ if (strpos(mb_strtoupper($file['name']), $value) === false) {
+ continue 2;
+ }
+ }
+ else if ($idx == 'class') {
+ foreach ($value as $v) {
+ if (stripos($file['type'], $v) === 0) {
+ break 2;
+ }
+ }
+
+ continue 2;
+ }
+ }
+ }
+
+ $filename = $folder_name . file_storage::SEPARATOR . $file['name'];
+
+ $result[$filename] = array(
+ 'name' => $file['name'],
+ 'size' => (int) $file['size'],
+ 'type' => (string) $file['type'],
+ 'mtime' => $file['changed'] ? $file['changed']->format($this->config['date_format']) : '',
+ 'ctime' => $file['created'] ? $file['created']->format($this->config['date_format']) : '',
+ 'modified' => $file['changed'] ? $file['changed']->format('U') : 0,
+ 'created' => $file['created'] ? $file['created']->format('U') : 0,
+ );
+
+ unset($files[$idx]);
+ }
+
+ // @TODO: pagination, search (by filename, mimetype)
+
+ // Sorting
+ $sort = !empty($params['sort']) ? $params['sort'] : 'name';
+ $index = array();
+
+ if ($sort == 'mtime') {
+ $sort = 'modified';
+ }
+
+ if (in_array($sort, array('name', 'size', 'modified'))) {
+ foreach ($result as $key => $val) {
+ $index[$key] = $val[$sort];
+ }
+ array_multisort($index, SORT_ASC, SORT_NUMERIC, $result);
+ }
+
+ if ($params['reverse']) {
+ $result = array_reverse($result, true);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Copy a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param string $new_name New name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_copy($file_name, $new_name)
+ {
+ list($src_name, $repo_id) = $this->find_library($file_name);
+ list($dst_name, $dst_repo_id) = $this->find_library($new_name);
+
+ if ($repo_id && $dst_repo_id) {
+ $path_src = explode('/', $src_name);
+ $path_dst = explode('/', $dst_name);
+ $f_src = array_pop($path_src);
+ $f_dst = array_pop($path_dst);
+ $src_dir = '/' . ltrim(implode('/', $path_src), '/');
+ $dst_dir = '/' . ltrim(implode('/', $path_dst), '/');
+
+ $success = $this->api->file_copy($repo_id, $f_old, $src_dir, $dst_dir, $dst_repo_id);
+
+ // now rename the file if needed
+ if ($success && $f_src != $f_dst) {
+ $success = $this->api->file_rename($dst_repo_id, rtrim($dst_dir, '/') . '/' . $f_src, $f_dst);
+ }
+ }
+
+ if (!$saved) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error copying file on SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. File copying failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Move (or rename) a file.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param string $new_name New name of a file (with folder path)
+ *
+ * @throws Exception
+ */
+ public function file_move($file_name, $new_name)
+ {
+ list($src_name, $repo_id) = $this->find_library($file_name);
+ list($dst_name, $dst_repo_id) = $this->find_library($new_name);
+
+ if ($repo_id && $dst_repo_id) {
+ $path_src = explode('/', $src_name);
+ $path_dst = explode('/', $dst_name);
+ $f_src = array_pop($path_src);
+ $f_dst = array_pop($path_dst);
+ $src_dir = '/' . ltrim(implode('/', $path_src), '/');
+ $dst_dir = '/' . ltrim(implode('/', $path_dst), '/');
+
+ if ($src_dir == $dst_dir && $repo_id == $dst_repo_id) {
+ $success = true;
+ }
+ else {
+ $success = $this->api->file_move($repo_id, $src_name, $dst_dir, $dst_repo_id);
+ }
+
+ // now rename the file if needed
+ if ($success && $f_src != $f_dst) {
+ $success = $this->api->file_rename($dst_repo_id, rtrim($dst_dir, '/') . '/' . $f_src, $f_dst);
+ }
+ }
+
+ if (!$success) {
+ rcube::raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Error moving file on SeaFile server"),
+ true, false);
+
+ throw new Exception("Storage error. File rename failed.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Create a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ *
+ * @throws Exception on error
+ */
+ public function folder_create($folder_name)
+ {
+ list($folder, $repo_id) = $this->find_library($folder_name, true);
+
+ if (empty($repo_id)) {
+ $success = $this->api->library_create($folder_name);
+ }
+ else if ($folder != '/') {
+ $success = $this->api->directory_create($repo_id, $folder);
+ }
+
+ if (!$success) {
+ throw new Exception("Storage error. Unable to create folder", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Delete a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ *
+ * @throws Exception on error
+ */
+ public function folder_delete($folder_name)
+ {
+ list($folder, $repo_id) = $this->find_library($folder_name, true);
+
+ if ($repo_id && $folder == '/') {
+ $success = $this->api->library_delete($repo_id);
+ }
+ else if ($repo_id) {
+ $success = $this->api->directory_delete($repo_id, $folder);
+ }
+
+ if (!$success) {
+ throw new Exception("Storage error. Unable to delete folder.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Move/Rename a folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ * @param string $new_name New name of a folder with full path
+ *
+ * @throws Exception on error
+ */
+ public function folder_move($folder_name, $new_name)
+ {
+ list($folder, $repo_id, $library) = $this->find_library($folder_name, true);
+ list($dest_folder, $dest_repo_id) = $this->find_library($new_name, true);
+
+ // folders rename/move is possible only in the same library and folder
+ // @TODO: support folder move between libraries and folders
+ // @TODO: support converting library into a folder and vice-versa
+
+ // library rename
+ if ($repo_id && !$dest_repo_id && $folder == '/' && strpos($new_name, '/') === false) {
+ $success = $this->api->library_rename($repo_id, $new_name, $library['desc']);
+ }
+ // folder rename
+ else if ($folder != '/' && $dest_folder != '/' && $repo_id && $repo_id == $dest_repo_id) {
+ $path_src = explode('/', $folder);
+ $path_dst = explode('/', $dest_folder);
+ $f_src = array_pop($path_src);
+ $f_dst = array_pop($path_dst);
+ $src_dir = implode('/', $path_src);
+ $dst_dir = implode('/', $path_dst);
+
+ if ($src_dir == $dst_dir) {
+ $success = $this->api->directory_rename($repo_id, $folder, $f_dst);
+ }
+ }
+
+ if (!$success) {
+ throw new Exception("Storage error. Unable to rename/move folder", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Returns list of folders.
+ *
+ * @return array List of folders
+ * @throws Exception
+ */
+ public function folder_list()
+ {
+ $libraries = $this->libraries();
+ $folders = array();
+
+ foreach ($this->libraries as $library) {
+ if ($library['virtual'] || $library['encrypted']) {
+ continue;
+ }
+
+ $folders[] = $library['name'];
+
+ if ($folder_tree = $this->folders_tree($library, '')) {
+ $folders = array_merge($folders, $folder_tree);
+ }
+ }
+
+ if (empty($folders)) {
+ throw new Exception("Storage error. Unable to get folders list.", file_storage::ERROR);
+ }
+
+ // sort folders
+ usort($folders, array($this, 'sort_folder_comparator'));
+
+ return $folders;
+ }
+
+ /**
+ * Recursively builds folders list
+ */
+ protected function folders_tree($library, $folder)
+ {
+ $folders = array();
+ $length = strlen($folder);
+
+ if ($content = $this->api->directory_entries($library['id'], '/' . $folder)) {
+ foreach ($content as $item) {
+ if ($item['type'] == 'dir' && strlen($item['name'])) {
+ $f = ($length ? $folder . '/' : '') . $item['name'];
+ $folders[] = $library['name'] . '/' . $f;
+
+ $folders_tree = $this->folders_tree($library, $f);
+ if (!empty($folders_tree)) {
+ $folders = array_merge($folders, $folders_tree);
+ }
+ }
+ }
+ }
+
+ return $folders;
+ }
+
+ /**
+ * Callback for uasort() that implements correct
+ * locale-aware case-sensitive sorting
+ */
+ protected function sort_folder_comparator($str1, $str2)
+ {
+ $path1 = explode('/', $str1);
+ $path2 = explode('/', $str2);
+
+ foreach ($path1 as $idx => $folder1) {
+ $folder2 = $path2[$idx];
+
+ if ($folder1 === $folder2) {
+ continue;
+ }
+
+ return strcoll($folder1, $folder2);
+ }
+ }
+
+ /**
+ * Get list of SeaFile libraries
+ */
+ protected function libraries()
+ {
+ // get from memory, @TODO: cache in rcube_cache?
+ if ($this->libraries !== null) {
+ return $this->libraries;
+ }
+
+ if (!$this->init()) {
+ throw new Exception("Storage error. Unable to get list of SeaFile libraries.", file_storage::ERROR);
+ }
+
+ if ($list = $this->api->library_list()) {
+ $this->libraries = $list;
+ }
+ else {
+ $this->libraries = array();
+ }
+
+ return $this->libraries;
+ }
+
+ /**
+ * Find library ID from folder name
+ */
+ protected function find_library($folder_name, $no_exception = false)
+ {
+ $libraries = $this->libraries();
+
+ foreach ($libraries as $lib) {
+ $path = $lib['name'] . '/';
+
+ if ($folder_name == $lib['name'] || strpos($folder_name, $path) === 0) {
+ if (empty($library) || strlen($library['name']) < strlen($lib['name'])) {
+ $library = $lib;
+ }
+ }
+ }
+
+ if (empty($library)) {
+ if (!$no_exception) {
+ throw new Exception("Storage error. Library not found.", file_storage::ERROR);
+ }
+ }
+ else {
+ $folder = substr($folder_name, strlen($library['name']) + 1);
+ }
+
+ return array(
+ '/' . ($folder ? $folder : ''),
+ $library['id'],
+ $library
+ );
+ }
+
+ /**
+ * Returns a list of locks
+ *
+ * This method should return all the locks for a particular URI, including
+ * locks that might be set on a parent URI.
+ *
+ * If child_locks is set to true, this method should also look for
+ * any locks in the subtree of the URI for locks.
+ *
+ * @param string $uri URI
+ * @param bool $child_locks Enables subtree checks
+ *
+ * @return array List of locks
+ * @throws Exception
+ */
+ public function lock_list($uri, $child_locks = false)
+ {
+ $this->init_lock_db();
+
+ // convert URI to global resource string
+ $uri = $this->uri2resource($uri);
+
+ // get locks list
+ $list = $this->lock_db->lock_list($uri, $child_locks);
+
+ // convert back resource string into URIs
+ foreach ($list as $idx => $lock) {
+ $list[$idx]['uri'] = $this->resource2uri($lock['uri']);
+ }
+
+ return $list;
+ }
+
+ /**
+ * Locks a URI
+ *
+ * @param string $uri URI
+ * @param array $lock Lock data
+ * - depth: 0/'infinite'
+ * - scope: 'shared'/'exclusive'
+ * - owner: string
+ * - token: string
+ * - timeout: int
+ *
+ * @throws Exception
+ */
+ public function lock($uri, $lock)
+ {
+ $this->init_lock_db();
+
+ // convert URI to global resource string
+ $uri = $this->uri2resource($uri);
+
+ if (!$this->lock_db->lock($uri, $lock)) {
+ throw new Exception("Database error. Unable to create a lock.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Removes a lock from a URI
+ *
+ * @param string $path URI
+ * @param array $lock Lock data
+ *
+ * @throws Exception
+ */
+ public function unlock($uri, $lock)
+ {
+ $this->init_lock_db();
+
+ // convert URI to global resource string
+ $uri = $this->uri2resource($uri);
+
+ if (!$this->lock_db->unlock($uri, $lock)) {
+ throw new Exception("Database error. Unable to remove a lock.", file_storage::ERROR);
+ }
+ }
+
+ /**
+ * Return disk quota information for specified folder.
+ *
+ * @param string $folder_name Name of a folder with full path
+ *
+ * @return array Quota
+ * @throws Exception
+ */
+ public function quota($folder)
+ {
+ if (!$this->init()) {
+ throw new Exception("Storage error. Unable to get SeaFile account info.", file_storage::ERROR);
+ }
+
+ $account_info = $this->api->account_info();
+
+ if (empty($account_info)) {
+ throw new Exception("Storage error. Unable to get SeaFile account info.", file_storage::ERROR);
+ }
+
+ $quota = array(
+ 'total' => $account_info['total'],
+ 'usage' => $account_info['usage'],
+ );
+
+ return $quota;
+ }
+
+ /**
+ * Get file object.
+ *
+ * @param string $file_name Name of a file (with folder path)
+ * @param kolab_storage_folder $folder Reference to folder object
+ *
+ * @return array File data
+ * @throws Exception
+ */
+ protected function get_file_object(&$file_name, &$folder = null)
+ {
+ // extract file path and file name
+ $path = explode(file_storage::SEPARATOR, $file_name);
+ $file_name = array_pop($path);
+ $folder_name = implode(file_storage::SEPARATOR, $path);
+
+ if ($folder_name === '') {
+ throw new Exception("Missing folder name", file_storage::ERROR);
+ }
+
+ // get folder object
+ $folder = $this->get_folder_object($folder_name);
+ $files = $folder->select(array(
+ array('type', '=', 'file'),
+ array('filename', '=', $file_name)
+ ));
+
+ return $files[0];
+ }
+
+ /**
+ * Simplify internal structure of the file object
+ */
+ protected function from_file_object($file)
+ {
+ if ($file['type'] != 'file') {
+ return null;
+ }
+
+ // file modification time
+ if ($file['mtime']) {
+ try {
+ $file['changed'] = new DateTime('@' . $file['mtime']);
+ }
+ catch (Exception $e) { }
+ }
+
+ // find file mimetype from extension
+ $file['type'] = file_utils::ext_to_type($file['name']);
+
+ unset($file['id']);
+ unset($file['mtime']);
+
+ return $file;
+ }
+
+ /**
+ * Save remote file into file pointer
+ */
+ protected function save_file_content($location, $fp)
+ {
+ if (!$fp || !$location) {
+ return false;
+ }
+
+ $config = array_merge($this->config, array('store_bodies' => true));
+ $request = seafile_api::http_request($config);
+
+ if (!$request) {
+ return false;
+ }
+
+ $observer = new seafile_request_observer();
+ $observer->set_fp($fp);
+
+ try {
+ $request->setUrl($location);
+ $request->attach($observer);
+
+ $response = $request->send();
+ $status = $response->getStatus();
+
+ $response->getBody(); // returns nothing
+ $request->detach($observer);
+
+ if ($status != 200) {
+ throw new Exception("Unable to save file. Status $status.");
+ }
+ }
+ catch (Exception $e) {
+ rcube::raise_error($e, true, false);
+ return false;
+ }
+
+ return true;
+ }
+
+ protected function uri2resource($uri)
+ {
+ list($file, $repo_id, $library) = $this->find_library($uri);
+
+ // convert to imap charset (to be safe to store in DB)
+ $uri = rcube_charset::convert($uri, RCUBE_CHARSET, 'UTF7-IMAP');
+
+ return 'seafile://' . urlencode($library['owner']) . '@' . $this->config['host'] . '/' . $uri;
+ }
+
+ protected function resource2uri($resource)
+ {
+ if (!preg_match('|^seafile://([^@]+)@([^/]+)/(.*)$|', $resource, $matches)) {
+ throw new Exception("Internal storage error. Unexpected data format.", file_storage::ERROR);
+ }
+
+ $user = urldecode($matches[1]);
+ $uri = $matches[3];
+
+ // convert from imap charset (to be safe to store in DB)
+ $uri = rcube_charset::convert($uri, 'UTF7-IMAP', RCUBE_CHARSET);
+
+ return $uri;
+ }
+
+ /**
+ * Initializes file_locks object
+ */
+ protected function init_lock_db()
+ {
+ if (!$this->lock_db) {
+ $this->lock_db = new file_locks;
+ }
+ }
+}
diff --git a/lib/drivers/seafile/seafile_request_observer.php b/lib/drivers/seafile/seafile_request_observer.php
new file mode 100644
index 0000000..ec09c1f
--- /dev/null
+++ b/lib/drivers/seafile/seafile_request_observer.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * Observer for HTTP_Request2 implementing saving response body into a file
+ */
+class seafile_request_observer implements SplObserver
+{
+ protected $file;
+ protected $fp;
+
+ public function set_file($file)
+ {
+ $this->file = $file;
+ }
+
+ public function set_fp($fp)
+ {
+ $this->fp = $fp;
+ }
+
+ public function update(SplSubject $subject)
+ {
+ $event = $subject->getLastEvent();
+
+ switch ($event['name']) {
+ case 'receivedHeaders':
+ if ($this->file) {
+ $target = $this->dir . DIRECTORY_SEPARATOR . $this->file;
+ if (!($this->fp = @fopen($target, 'wb'))) {
+ throw new Exception("Cannot open target file '{$target}'");
+ }
+ }
+ else if (!$this->fp) {
+ throw new Exception("Cannot open target file '{$target}'");
+ }
+
+ break;
+
+ case 'receivedBodyPart':
+ case 'receivedEncodedBodyPart':
+ fwrite($this->fp, $event['data']);
+ break;
+
+ case 'receivedBody':
+ fclose($this->fp);
+ break;
+ }
+ }
+}
diff --git a/lib/file_api.php b/lib/file_api.php
index c9631c6..3464f1c 100644
--- a/lib/file_api.php
+++ b/lib/file_api.php
@@ -61,7 +61,7 @@ class file_api
$driver = $this->conf->get('fileapi_backend', 'kolab');
$class = $driver . '_file_storage';
- $include_path = RCUBE_INSTALL_PATH . '/lib/' . $driver . PATH_SEPARATOR;
+ $include_path = RCUBE_INSTALL_PATH . "/lib/drivers/$driver" . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
diff --git a/lib/file_utils.php b/lib/file_utils.php
index 8ad36a9..416d151 100644
--- a/lib/file_utils.php
+++ b/lib/file_utils.php
@@ -68,6 +68,25 @@ class file_utils
),
);
+ // list of known file extensions, more in Roundcube config
+ static $ext_map = array(
+ 'doc' => 'application/msword',
+ 'gz' => 'application/gzip',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'mp3' => 'audio/mpeg',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'ogg' => 'application/ogg',
+ 'pdf' => 'application/pdf',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'rar' => 'application/x-rar-compressed',
+ 'tgz' => 'application/gzip',
+ 'txt' => 'text/plain',
+ 'zip' => 'application/zip',
+ );
+
/**
* Return list of mimetype prefixes for specified file class
@@ -121,6 +140,35 @@ class file_utils
}
/**
+ * Find mimetype from file name (extension)
+ *
+ * @param string $filename File name
+ * @param string $fallback Follback mimetype
+ *
+ * @return string File mimetype
+ */
+ static function ext_to_type($filename, $fallback = 'application/octet-stream')
+ {
+ static $mime_ext = array();
+
+ $config = rcube::get_instance()->config;
+ $ext = substr($filename, strrpos($filename, '.') + 1);
+
+ if (empty($mime_ext)) {
+ $mime_ext = self::$ext_map;
+ foreach ($config->resolve_paths('mimetypes.php') as $fpath) {
+ $mime_ext = array_merge($mime_ext, (array) @include($fpath));
+ }
+ }
+
+ if (is_array($mime_ext) && $ext) {
+ $mimetype = $mime_ext[strtolower($ext)];
+ }
+
+ return $mimetype ?: $fallback;
+ }
+
+ /**
* Returns script URI
*
* @return string Script URI
diff --git a/lib/init.php b/lib/init.php
index 6b8e86a..e57abd9 100644
--- a/lib/init.php
+++ b/lib/init.php
@@ -27,7 +27,7 @@
define('FILE_API_START', microtime(true));
define('RCUBE_INSTALL_PATH', realpath(dirname(__FILE__)) . '/../');
define('RCUBE_CONFIG_DIR', RCUBE_INSTALL_PATH . 'config/');
-define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'lib/kolab/plugins');
+define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'lib/drivers/kolab/plugins');
// Define include path
$include_path = RCUBE_INSTALL_PATH . '/lib' . PATH_SEPARATOR;